perm filename CSREPT.SAI[USE,CSR]41 blob sn#656163 filedate 1982-04-25 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00021 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00004 00002	Declarations
C00013 00003	I/O preparation: ttin,lookupfail,enterfail,renamefail,inuse
C00017 00004	Procedures for input: inscan, resp, ynresp, cresp
C00021 00005	Forms: textinfail, invout
C00029 00006	Procedures onhandin, onhandout, new_report for onhand file input/output
C00034 00007	Procedure rdaddr, buildtree, and addfilin for address file input
C00041 00008	Procedures wraddr, untree, addfilout for address file output
C00045 00009	binary search tree maintenance routines: search,insert,delete
C00050 00010	Procedures to access the address file: unpack,display,find
C00054 00011	Sub-procedures for update actions: zipcheck,gethash
C00058 00012	procedures for update actions: look,ins,mfy,del,update,shorten
C00064 00013	The procedure which records orders received
C00073 00014	The `receive' procedure, which handles virtual money
C00079 00015	Procedures for making labels: lab,emitlab,endlab
C00083 00016	The MAIL procedure and its subprocedures abst,invo,inv,status,scanorders
C00103 00017	The `send' procedure, for isolated orders
C00122 00018	The `adjust' procedure, to adjust the inventory
C00127 00019	The president (chief executive)
C00136 00020	The program starts here (sets string constants, including HELPs)
C00147 00021	Set breaks, open channels, call main procedure, end gracefully
C00151 ENDMK
C⊗;
comment Declarations;
begin "report"
      comment This is the CS report system coded by D. Knuth, October 1976,
    	modified and extended since then by Jim Davidson
	Cheshire labels fiddled by Les Earnest, March 1980;

EXTERNAL PROCEDURE BAIL;

require 200 system_pdl;
require 30000 string_space;

require "⊂⊃" delimiters;
define # = ⊂;comment⊃;
define crlf = ⊂('15&'12)⊃;
define crlf2 = ⊂('15&'12&'12)⊃;
define icr = ⊂'15⊃;
define ialt = ⊂'175⊃;
define iff = ⊂'14⊃;
define asize = ⊂1900⊃ # maximum number of addresses in the mailing list;
define logasize = ⊂11⊃ # must be equal to 1 + floor(lg asize);
define bsize = ⊂1600⊃ # maximum number of reports in ONHAND.DSK file;
define csize = ⊂1000⊃ # maximum number of customers for a single mailing;

define cvc(i) = ⊂(if i<10 then "0"+i else ("A"-10)+i)⊃ # encodes
			small integer as a single character -- digit or letter;
define ugetf(val,chan,add) = ⊂val ← code('073000000000+'40000000*chan,add)⊃
	# used to skip to the end of the LOG file when necessary;

define btt = ⊂1⊃ # breaktable for ttin;
define bfflf = ⊂2⊃ # break on formfeed (end page) or linefeed (end line);
define blf = ⊂3⊃ # break on linefeed;
define babs = ⊂4⊃ # break on |;
define bast = ⊂5⊃ # break on *;
define bff = ⊂6⊃ # break on formfeed;
define bpar = ⊂7⊃ # break on );
define bsp = ⊂8⊃ # break on space (or tab);
define bch = ⊂9⊃ # break on non-blank;
define blfdollar = ⊂10⊃ # break on lf or "$";
define bvar = ⊂11⊃ # variable break table, set dynamically;

boolean eof # end of file indicator;
boolean logused, repeating # are we using input from a file, after a system crash? ;
integer brchar # break character;
integer flag # input-output flag;
integer linecnt, charcnt # the numbers of lines and chars not yet saved on the LOG file;
integer ichan # channel for input;
comment there used to be two channels here, ichan & iichan, for character input
	and address file input, respectively.  The change of 12/11/77 obviated this;
integer ochan, oochan, ooochan # channels for output;
comment ochan is used for invoices, ONHAND.DSK file, ORDERS file.
	oochan gets the `compressed' version of the invoices.
	ooochan is used to write out the new version of the address file;
integer lchan # channel for mailing label output;
integer bchan # used for the LOG file of commands;

integer c,d,i,j,k,m,p,q,t, chr # miscellaneous temporary integers, pointers, etc.;
string s,st,stt,str # miscellaneous temporary strings;

string typein # input returned by ttin, ends with cr;
integer scale # scale factor returned by inscan;
integer nl # number of lines returned by unpack;
string array lne[0:6] # individual lines of an address;

integer array mtape[0:1] # used to call the update MTAPE UUO, to reset pointers;
	
integer array llink,rlink,key,balance[0:asize] # binary search tree structure;
	comment the address file is organized as a binary tree.
	key[p] is the hashcode, in binary form, of the addressee whose
	serial number is p. balance[p] is the number of pennies he owes.
	Unused positions of the table are doubly-linked into an AVAIL list
	whose head is at position 0. Such entries have key=0;
string array nmline,lines[0:asize] # nmline[p] is line 1 of an address,
	ending with crlf. lines[p] contains the rest of the address information,
	as follows: Let s=lines[p], then
		s[1 to 1] is the mailing category ("C","F","A","N","M", or"B")
		s[2 to 6] is the zip code or country, or IDMAI, AUTOM, etc.
		s[7 to 18] is the activity code for last 12 mailings
			(0,1,...,9,A,B,... for 0, 1, ..., 9, 10, 11, ...
			orders, or Z if there were back-orders)
		s[19 to ∞] is lines 2,3, etc. of the address, including
			carriage returns and line feeds but not US zip code;
integer troot # the root of the main binary search tree (contains the 
		addresses on the mailing list);

integer recd # total money (in cents) received in today's transactions;
integer fixd # total accounting adjustments in today's transactions;
integer chgd # total money charged to accounts in today's transactions;
integer calrecd # total amount of receipts from California residents;

boolean mailed # has MAIL already set up output to be spooled?;
boolean sended # has SEND already set up output to be spooled?;
boolean afchanged # should ADDFIL.DSK be written out after processing?;
 
string date,invoicedate # today's date in form dd MON 19yy (for invoice, MONdd19yy);
string mon # month whose orders are being processed (3-letter abbr);

string lf,ff,tab,nulls,cshelp,findhelp1,findhelp2,codehelp,updhelp,ordhelp1,ordhelp2,
	yesnohelp,blanks,mailhelp,acthelp,sendhelp,onhandhelp,splithelp,rhelp,
	namehelp1, namehelp2, onhand_filled_msg;
comment constant strings, see page 16;

string array canned[0:31] # text used to write invoices;

preload_with "JAN","FEB","MAR","APR","MAY","JUN",
	"JUL","AUG","SEP","OCT","NOV","DEC"; string array months[1:12];
preload_with "Y","N"; string array yesnoopts[1:2];
preload_with ""; string array nullopt[1:1];
preload_with "UPD","ORD","REC","MAI","SEN","ADJ"; string array csopts[1:6];
preload_with "C","F","N","M","A","B"; string array codeopts[1:6];
preload_with "INS","DEL","MOD","LOOK"; string array updopts[1:4];
preload_with "AVE","CHE";string array labelopts[1:2];
preload_with "ADD","CHA"; string array ordopts[1:2];
preload_with "ABS", "REP"; string array mailopts[1:2];
comment I/O preparation: ttin,lookupfail,enterfail,renamefail,inuse;

procedure ttin;
begin comment sets typein to the line typed in and echoes it also on
	the PRINT file, then gets rid of leading blanks;
integer i;
label retry;
retry:
if ¬repeating then begin "interactive"
    typein←inchwl&icr; comment alternate for  ttyin(btt,brchar);
    setprint(null,"I"); print(typein,lf); setprint(null,"C");
    out(bchan,typein&lf);
    linecnt←linecnt+1;
    charcnt←charcnt+length(typein)+1;
    if linecnt≥15 ∨ charcnt≥540 then begin 
	    comment flush the buffer, to ensure saving of these lines;
	    out(bchan,nulls[(charcnt+1) to 640]);
	    comment update pointer and header information for log file
		(doesn't happen automatically in read-alter mode);
	    i ← code ((('072000+(bchan lsh 5)) lsh 18),mtape[0]);
	    linecnt←charcnt←0;
    end;
end "interactive"
else begin "input from file"
    typein←input(bchan,blf) # get the next line from the file;
    if eof then begin
	comment have come to the end of the file. revert to TTY input;
	repeating ← FALSE;
	ugetf(i,bchan,0) # skip to end of file, before starting output;
	print(crlf2,"	SWITCHING BACK TO TERMINAL INPUT.",crlf2);
	goto retry;
    end;
    print(typein);
    typein←typein[1 to ∞-1] # strip off lf;
end "input from file";
while typein = " " do i←lop(typein);
end;

boolean procedure lookupfail(integer chan; string file);
begin close(chan); lookup(chan,file,flag);
if flag then print(crlf,"Whoa, I can't find ",file,", so I'm stuck.",crlf);
return(flag);
end;

boolean procedure enterfail(integer chan; string file);
begin close(chan); enter(chan,file,flag);
if flag then print(crlf,"Whoa, system error trying to enter ",file,
	", so I'm stuck.",crlf);
return(flag);
end;

boolean procedure renamefail(integer chan; string file; integer pro; reference integer flag);
begin comment the file open on CHAN is renamed to FILE, with protection PRO;
rename(chan,file,pro,flag);
if flag then print(crlf,"Whoa, error in RENAME of ", file,
			   ", so the files might have strange names.",crlf);
return(flag);
end;

boolean procedure inuse(string file);
comment a kludge -- checks to see if file is in use. 'oochan' is used as a temp channel;
begin lookup(oochan,file,flag); enter(oochan,file,flag);
if (flag land '000000777777)=3 then
     print(crlf,"Someone else is using the program, so I'm stuck.",crlf);
close(oochan);
return(flag);
end;

comment Procedures for input: inscan, resp, ynresp, cresp;

integer procedure inscan;
begin comment returns integer contents of typein, ignoring nondigits;
comment sets brchar to last nondigit, scale to no. of digits after ".";
integer t,d; 
t←scale←0; brchar←0;
while typein≠icr do
	begin d←lop(typein);
	if d≥"0" and d≤"9" then
		begin t←10*t+d-"0";
		if brchar="." then scale←scale+1;
		end
	else brchar←d;
	end;
return(t);
end;

integer procedure resp(string q; reference string h; string array opts);
begin comment q is the question asked of the user, h is the HELP string, and opts
	lists the initial characters of allowable responses;
comment the output is 0 if the response was <cr>, otherwise it is the
	index of the option typed;
integer i;
while true do
	begin print(q); ttin;
	if typein=icr then return(0);
	if typein≠"?" then
		begin if equ(typein[1 to 4],"HELP") then
			begin print(crlf,h,crlf,crlf);continue;
			end;
		for i←1 step 1 until arrinfo(opts,2) do
			if equ(typein[1 to length(opts[i])],opts[i])
			then return(i);
		end;
	print("?The responses I can understand at this point are:",crlf);
	for i←1 step 1 until arrinfo(opts,2) do
		print(opts[i],"...,");
	print(crlf,"or <cr> (to get out of this loop),",crlf);
	print("or HELP<cr> (for more information).",crlf);
	end;
end;

integer procedure ynresp(string q);
return(resp(q&" (Y or N) ",yesnohelp,yesnoopts));

integer procedure cresp(string q);
begin comment q asks for a response in dollars and cents;
comment this procedure returns the amount in cents, or -1 if response is just <cr>;
comment also brchar is set to the last nondigit typed;
integer c;
while true do
	begin print(q,"$"); ttin;
	if typein=icr then return(-1);
	c←inscan;
	if scale=2 then return(c);
	print("?I wanted you to type a dollars-and-cents number like 3.14<cr>",
	"----",crlf,"Please try again, or just type <cr> to get out of this.",crlf);
	end;
end;
comment Forms: textinfail, invout;

boolean procedure textinfail;
begin comment the canned text for invoices is read into memory;
if lookupfail(ichan,"FORM.DAT") then return(true);
do st←input(ichan,bfflf) until equ(st[1 to 7],"INVOICE") ∨ eof # bypass directory;
if eof then
	begin print("Whoa, file FORM.DAT has been clobbered, so I'm stuck.",crlf);
	return(true);
	end;
for i←0 step 1 until 31 do canned[i]←input(ichan,bast);
comment for the desired form of FORM.DAT, see the example in the
	user manual and/or the procedure invout below;
return(false);
end;


procedure invout(reference string send,sorry,name,addrlabel;
	integer oldbal,charges, handling; boolean Calif);
begin comment outputs an invoice to ochan, and a compressed version to oochan;

   string procedure short (value string reptlist);
   comment returns a compressed version of the report list, with the names deleted;
   begin "short"
	   string shortlist, name;
	   shortlist←null;
	   name←scan(reptlist,bpar,brchar);
	   while brchar do
		   if ¬equ(name[5 to 12],"Shipping") then begin
		       shortlist←shortlist&name&crlf;
		       scan(reptlist,blf,brchar);
		       name←scan(reptlist,bpar,brchar);
		   end else scan(name,blf,brchar);
	   return(shortlist);
   end "short";

integer newbal; string str;
out(ochan,canned[0]&date&canned[1]&name&canned[2]) # heading, salutation, ret. addr;
out(oochan,date&crlf2&(if ¬equ(addrlabel[45 to 50],"      ")
			 then addrlabel[1 to 50] else addrlabel)&crlf2);
			 comment what's this for?;
if sorry≠0 then begin "sorry list" 
	out(ochan,canned[6]&crlf);
	if equ(sorry[1 to 2],crlf) then out(ochan,canned[18])
	    # crlf at the beginning means that one order had to be split -- say so;
    	out(ochan,sorry&crlf&canned[7]&crlf);
	out(oochan,canned[6]&crlf&short(sorry)&crlf); end "sorry list";
newbal←oldbal+charges; str←cvf(abs(newbal)/100) # notice that str is always positive;
if send≠0 then begin "send list"
	comment modification here September 1979. Check to see if there's any
	    charge, and if not, print 'packing slip' instead of 'invoice';
	out(ochan,canned[3]&(if newbal>0 then canned[4] else canned[5]));
	out(ochan,crlf&send&crlf);
	if Calif and charges>0 then out(ochan,canned[17]&crlf);
	out(oochan,canned[3]&(if newbal>0 then canned[4] else canned[5])&crlf&short(send)&crlf);
	end;
if charges>0 then comment print account status, dep on old and current balance;
	begin if oldbal=0 then out(ochan,canned[10]&str&canned[11])
	else if newbal>0 then out(ochan,canned[12]&str&canned[13])
	else if newbal<0 then out(ochan,canned[14]&str&canned[15])
	else out(ochan,canned[16])
	end
else if oldbal>0 then out(ochan,canned[8]&str&canned[9]);
out(oochan,"balance= "&cvf(newbal/100)&crlf2&"*******************************"&crlf);
if newbal>0 then begin "actual invoice"
    comment if there's actually any money owing, print a real invoice;
    comment 19: INVOICE ↔ invoiceno:. 20: ↔ DATE:. 21 : ↔ customer no.:
	22 : ↔ P.O. no.: ↔. 23 : (header) Descr , Price ↔. 24 : ↔ sub total
	25 : ↔ previously due. 26: previous credit. 27 : ↔ shipping. 28: ↔ taxes
	29 : ↔ total due. 30,31 : ↔ all the rest;
    string rept;
    out(ochan,crlf&canned[19]&invoicedate);
    out(ochan,canned[20]&date&canned[21]);
    comment print reference no., if applicable.  Get it from the addrlabel.
	    Only '9999's won't have a reference no (hash code);
    if addrlabel[45 to 45]="#" then out(ochan,addrlabel[46 to 50]);
    out(ochan,canned[22]&addrlabel&crlf);
    if charges>0 then out(ochan,canned[23]);

    comment now print out the list of reports in SEND, one at a time, with
	    the price right-justified on each line;
    brchar←1;
    while brchar≠0 do begin "one line"
	    rept←scan(send,blfdollar,brchar);
	    if brchar="$" then begin
		    comment print the cost, after right-justifying;
		    if equ(rept[1 to 4],"   (") then comment multiple copies;
			rept←rept&scan(send,blfdollar,brchar);
		    out(ochan,(rept&blanks)[1 to 74]&" $"&scan(send,blf,brchar))
	    end else
		comment no price included: free, or specified on next line;
		out(ochan,rept&lf);
    end "one line";
    comment have printed the whole list -- now print the totals;
    setformat(6,2);
    comment handling charges (if any) have already been added into CHARGES.
 	For printing, subtract them out again, to be printed separately;
    out(ochan,canned[24]&cvf((charges-handling)/100));
    comment next two lines print previous balance (due, or credit);
    out(ochan,if oldbal≥0 then canned[25] else canned[26]);
    out(ochan,cvf(if oldbal≠0 then oldbal/100 else 0.0));
    if handling>0 then out(ochan,canned[27]&cvf(handling/100));
    out(ochan,canned[28]&(if Calif then "Incl." else "N.A."));
    out(ochan,canned[29]&cvf(newbal/100)&canned[30]&canned[31]);
    setformat(0,2);
end "actual invoice"
else out(ochan,crlf&lf&lf&addrlabel&ff);
end;
comment Procedures onhandin, onhandout, new_report for onhand file input/output;

boolean procedure onhandin (string array oldrep,title; integer array onhandh,onhandm,cost;
			reference integer imax);
comment reads from the ONHAND.DSK file, and sets up the arrays and count;
begin "read onhand"
string st;
if lookupfail(ichan,"ONHAND.DSK") then return (false);
imax←-1;
for i←0 step 1 until bsize-1 do
	begin do st←input(ichan,bfflf) until lop(st)="*" or eof;
	if eof then done;
	oldrep[i]←scan(st,babs,brchar);
	if equ(st[1 to 4],"SAME") then title[i]←"" else
		begin title[i]←scan(st,babs,brchar);
		typein←scan(st,babs,brchar)&icr; onhandh[i]←inscan;
		typein←scan(st,babs,brchar)&icr; onhandm[i]←inscan;
		if st=0 then
			begin print("Bad entry in ONHAND.DSK file for ",
			oldrep[i],crlf); st←"$.00"&icr;
			end;
		typein←st; cost[i]←inscan;
		end;
	imax←i;
	end;
print("I have found ",imax+1," records about old reports in file ONHAND.DSK.",crlf);
close(ichan);
return (true);
end "read onhand";


boolean procedure onhandout(string array oldrep,title; integer array onhandh,onhandm,cost;
			value integer imax);
begin "write onhand"
if enterfail(ochan,"ONHAND.DSK") then return (false);
for i←0 step 1 until imax do
	begin out(ochan,"*"&oldrep[i]&"|");
	if title[i]=0 then oUt(ochan,"SAME"&crlf)
	else out(ochan,title[i]&"|"&cvs(onhandh[i])&"|"&
		cvs(onhandm[i])&"|$"&cvf(cost[i]/100)&crlf);
	if i mod 55 = 54 then out(ochan,ff);
	end;
close(ochan);
return (true);
end "write onhand";


boolean procedure new_report(string name; string array oldrep, title;
			 integer array onhandh, onhandm, cost; reference integer imax);
comment Adds a new report title to the ONHAND.DSK file;
begin "new report"
    k←imax+1; oldrep[k]←st;
    title[k]←name;
    cost[k]←cresp("What is the price of hardcopy? ");
    if cost[k]<0 then return (false);
    print("How many hard copies are on hand? "); ttin;
    if typein=icr then return(false) else onhandh[k]←inscan;
    print("How many microfiche copies are on hand? ");
    ttin; if typein=icr then return(false) else onhandm[k]←inscan;
    imax←k;
    comment if this is has another name (e.g., an AIM number), write a SAME line;
    j←resp("Does it have an alternate number? (Y or N) ",namehelp1,yesnoopts);
    if j=1 then begin
	if resp("What is the alternate number? ",namehelp2,nullopt)=0 then return(false)
	else begin imax←imax+1; oldrep[imax]←typein[1 to ∞-1]; title[imax]←""; end;
    end;
    return (true);
end "new report";
comment ProceDure rdaddr, buildtree, and addfilin for address file input;

integer prevk # previous key read by rdaddr;

integer proceDure rdaddr;
begin "rdaddr"
comment reads and stores the nexp valid address from ADDFIL.DSK,
	returning the serial number, or 0 if the end of file is sensed;
comment During this procedure, st represents the file line most recentlq
	read but not yet digested;
comment The ADDFIL.DSK contaans up to twenty entrieq per page.  Each entry begins
↓with a line in the format
		*CZZZZZ|AAAAAAAAAAAA#HHHHHSSSSS$BAL<crlf>
	where C=category, ZZZZZ=zipcode, AAAAAAAAAAAA=activity codes,
	HHHHH=hashcode, SSSSS=serial number, BAL=dollar balance due
	(preceded by - if negative). Then comes 2 to 5 lines of the
	address, each of which should be at most 34 characters wide
	in most cases;
comment most of this code is devoted to simple error checking;
comment when we enter this routine each time (except the first), st is already set
	to the first line of the next entry.  This is because the system has already
	read that line, to detect the end of the previous entry;
string ent,name,addr; integer loc,k; label start;
key[0]←1; nmline[0]←"Listhead"&crlf;
start:
comment if the file was in E editor format, pass over the index page, and page markers;
while st ≠ "*" do
	if eof then return(0) else st←input(ichan,bfflf);
ent←st;name←input(ichan,bfflf);
addr←input(ichan,bfflf);
st←addr[1 to 1];
comment now ent,name,addr are the first three address lines;
comment this next loop glues the remainder of the address onto addr;
while st ≠ "*" and st ≠ "#" do
	begin if length(st)>2 then addr←addr&st # I'm not sure what this checks for.
	    I think that 2 is from the length of crlf, but there's also the fact 
	    that st was set to a single character before entering here;
	if eof then done;
	st←input(ichan,bfflf);
	end;
loc←cvd(ent[27 to 31]) # loc  now contains the serial number;
if loc>asize then
	begin print(crlf,"ADDFIL.DSK error, serial number too big...
	    the following name has been deleted from the file:",crlf,name,crlf,
	    "since it had a serial number of ",loc,".",crlf,
	    "...The rest of the deleted file entry was:",crlf,ent,addr);
	go to start;
	end;
comment the key array contains the hash codes;
if key[loc]≠0 then
	begin print(crlf,"ADDFIL.DSK error, two people with same serial number...
	    the following name has been deleted from the file:",crlf,name,
	    "since it had the same serial number as:",crlf,nmline[loc],
	    "...The rest of the deleted file entry was:",crlf,ent,addr); 
	go to start;
	end;
comment convert the hash key to internal format (integer);
k←cvasc(ent[22 to 26]);
if k ≤ prevk then
	begin print(crlf,"ADDFIL.DSK error, hash codes not increasing...
	    the following name has been deleted from the file:",crlf,name,
	    "since its hash code was not greater than the preceding one.",crlf,
	    "...The rest of the deleted file entry was:",crlf,ent,addr);
	go to start;
	end;
key[loc]←k; prevk←k;
rlink[llink[loc]]←rlink[loc];llink[rlink[loc]]←llink[loc] # remove from AVAIL;
nmline[loc]←name;    
lines[loc]←ent[2 to 7]&ent[9 to 20]&addr # zip, activity codes;
typein←ent[33 to ∞]; balance[loc]←inscan;
if ent[33 to 33]="-" then balance[loc]←-balance[loc];
i←i+1; 
return(loc);
end "rdaddr";

recursive integer procedure buildtree(integer m);
begin "buildtree" comment builds a somewhat balanced binary search tree of up to
		2↑m-1 nodes, returning a pointer to the root;
integer root,subtree;
if m=0 then return(0) 
else begin 
    subtree←buildtree(m-1);
    root←rdaddr;
    if root=0 then return(subtree) comment end of file reached;
    else begin 
	llink[root]←subtree;
	rlink[root]←buiddtree(m-1);
	return(root);
    end;
end;
end "buildtree";

procedure addfilin;
begin  addfilin"
comment inputq the address file, assuming that it is on iChan+
for i←1 @MiK`@DAk]i%XACg%uJZb↓I↑~∀begin key[i]←0; llink[i]←i-1; rlink[i]←i+1;
	end;
key[0]←0;llink[0]←asize;rlink[0]←1;
key[asize]←0;llink[asize]←asize-1;rlink[asize]←0;
st←""; prevk←'400000000000;
i←0;
troot←buildtree(logasize);
print(crlf,"The address file contains a total of ",i," entries.",crlf);
end "addfilin";
comment Procedures wraddr, untree, addfilout for address file output;

integer totbal # total balance (owed to us) from all accounts in the file;
Integer cbal # total credit (prepayment by users);
integer kf,kn,km,ka,kb # total number of entries of various categories;

procedure wraddr(integer p);
begin comment appends the address for serial number p to current output page,
	and outputs if the page is full);
comment also gathers statistics about the file;
comment assumes that ooochan is attached to the file ADDFIL.TMP;
string s,t;
t←lines[p];
out(ooochan,"*"); out(ooochan,t[1 to 6]); out(ooochan,"|"); out(ooochan,t[7 to 18]);
out(ooochan,"#"); out(ooochan,cvstr(key[p]));
setformat(5,2); out(ooochan,cvs(p)); setformat(0,2); comment for serial number;
out(ooochan,"$"); out(ooochan,cvf(balance[p]/100)); out(ooochan,crlf);
out(ooochan,nmline[p]); out(ooochan,t[19 to ∞]); comment output address;
if balance[p]>0 then totbal←totbal+balance[p] else cbal←cbal+balance[p];
k←k+1;
if t≠"C" then
	begin if t="F" then kf←kf+1
	else if t="A" then ka←ka+1
	else if t="B" then kb←kb+1
	else if t="N" then kn←kn+1
	else if t="M" then km←km+1;
	end;
if k mod 20 = 0 then out(ooochan,ff);
end;

recursive procedure untree(integer p);
begin comment outputs the binary search tree rooted at p in order by key;
if p≠0 then
	begin untree(llink[p]);
	wraddr(p);
	untree(rlink[p]);
	end;
end;

procedure addfilout;
begin comment outputs the entire address file to ooochan;
k←kf←ka←kn←km←totbal←cbal←0;
untree(troot);
print(crlf,"The address file now contains a total of ",k," entries,
including the following special categories:
F = ",kf," A = ",ka," B = ",kb," N = ",kn," M = ",km,",",crlf,
"The total balance outstanding is $",cvf(totbal/100),",",crlf,
"and the total credit prepaid by customers is $",cvf(abs(cbal)/100),".",crlf);
if asize-k<50 then print("I am currently programmed to handle up to ",
	asize," entries maximum.",crlf);
close(ooochan);
end;
comment binary search tree maintenance routines: search,insert,delete;

integer lp # last position unsuccessfully probed in search routine;

integer procedure search(integer k);
begin comment binary search, returns serial number of addressee having key 
	(hash code) k, or 0 if not in the table;
comment this assumes that the binary tree is sorted by hash code.  Note that
    nothing in the construction of the tree causes it to be sorted -- we
    rely on the fact that it was written out in sorted order, and that insertions
    preserve the sortedness;
integer p;
p←troot; lp←0; key[0]←k;
while k≠key[p] do
	begin lp←p;
	if k<key[p] then p←llink[p] else p←rlink[p];
	end;
return(p);
end;

integer procedure insert(reference string name,ent; string hash; integer bal);
begin comment inserts new address file entry into an available place
	and returns the value of this place (i.e. the serial number);
comment assume that the pointer lp has already been set to the closest leaf, through
	the call to`SEARCH' from insiDe `GETHASH';
comment the entry is inserted in a place which preserves the ordering;
integer p,k;
k←cvasc(hash);
p←rlink[0] # get avaidable lOcation;
if p=0 then
	begin print("The mailing list is now completel@dAMkY0XAg↑↓∩AGC8Oh@D0~∀∩@@@ES9gKeh↓iQJA∃]ier↓M←dAQQJDY
eYLXλAM←Y1←oS]≤A]C[∀t@DY9C[JX4∀∩@@@E)↑↓S]Ge∃CgJAQQJAi¬EYJAMSuJA=]JA[¬rAgS5aYrAIKG←[ASYJA
'%!PDYGe1LX~∀$@@@@	oSiP↓CgSu∀AC]H↓Y←OCMSuJA⊃KMS]∃HAYCIOKd\~∀∩@@@Q¬UhAI↑↓oJAe∃CYYr↓oC]h↓gkGP↓BAES≤A[CS1S]NA1Sgh}$DYGe1LRv~(∪eKiUe\P`$v~∀∪∃]Hv~)eYS]-6a;?IYS]Wma:vA1YS]WmeYS]-7a;;|`@FAIK[←m∀AMe←4Aβ-β%_AYSMhv~∃9[YS]∃7a;?9C[Jv↓YS]KM7a;?∃]hvA-Ks7au?VvA	CYC]
K7a;⎇ECXv4∃YYS9W7a;⎇eYS]-7a;>@v~∃M%qI?M%qH[E¬Xv~∃%LAVy-Ks7YA:AiQ∃\AYY%]W7YA;?`A∃YgJAIYS]WmYa;?@v~∃e∃ike\!`Rv~)K]Hv4∀~∃aI←GKIUeJAI∃YKiJ!S]iK≥KdAV$v~∃E∃OS\A
←[[K9hAIK1KiKf↓K]iedAoSi AWKr↓VAMe=ZASiLAaYC
JAS\↓iQJA¬IIeKMfAMS1JX~∀%kgS]≤AiQJ↓giC]⊃CeHA¬YO←e%iQZv4∃S]i∃OKdA@YbYdl~∃a?MKCeG QVRv4∃SLA@z`Ai!K\~∀%EKOS8AaeS9hPE⊃5Z\\\↓'←[KQQS]N↓oK]h↓oe←]≤XA∩A)kghA¬iiK[AiKHAQ↑@DX4∀∩EI∃YKiJ↓BA]←9KqSgQK]hA-Kr\D1GeYL$vAeKQke\v4∀∪K]⊂v~∃G=[[K]PA]Kn↓IKYKQJA`A→e←ZA%ifAgUEieK∀XAsS∃YIS]≤ABAgUEieK∀AoSi Ae←←PAbv~)SLAY1S]W7A:z`AQQK\AE?eYS9W7a:4∃KYg∀ASLAIYS]Wma:z`↓iQK\↓c?YY%]W7at~∃KYMJ∪EK≥S\Ac⎇eYS]-7a:v4∀∪SL↓YYS]-7c:z@AiQK8AYYS9W7c;⎇YYS]-7a:~(∪KYg∀∪EKO%\AI↑↓c?YY%]W7e⎇c:Ak9iSXA1YS]Wmc:z`l~∀∩∪1YS]Wme;?e1S]W7E:vAY1S]W7E;?YY%]W7atvAeY%]W7cu?eYS9W7a:l~∀∩∪∃]Hv~(∪K]Hl~∃G←5[K]h↓]←nA¬IUkgPAiQJ↓kaaKHAaCePA←LAQQJAiIKJAC9HAiQ∀Aβ-β%_AYSMhv~∃%LAY`t`AiQ∃\Aie=←i?b4∃KYg∀ASLA,yWKsmYa:AQQK\A1YS]WmYa;?DAKYg∀AeYS9W7Yau?bv~)c?eY%]W6atvAeY%]W7au?bvA1YS]Wmc;?`lAYYS9W7a;|`vAe1S]W6A;?`v4∃G←[5K]hAQQJA]∃qhAS9gKeh↓oSYX↓O↑AS9i↑AY=GCiS=\A`A¬OCS\QiQSLAae←AKeir4∀∪Sf↓kgKH↓S\Ai!JAka⊃CiJ@	[MrD↓e←ki%]JRv4∃WKsma;>`lA][Y%]K7au?YS]∃g7a;|DDv~)MSqI⎇MSqH-ECYC9GK7atvAEC1C]GKma;>`l~∃K]⊂v~∀→G←[5K]hAAe←GK⊃keKf↓i↑AC
GKgf↓iQJA¬IIeKMfAMS1JtAk9aCGV1ISga1CrYM%]Hv~(~∃ae=GKIkIJAk]ACGVQ%]iKO∃dA`Rl~∃EK≥S\AGmment takes entry from address file position p and stores it
	in lne[0], lne[1], ..., lne[nl];
string ent,zip;
lne[1]←nmline[p] # customer name;
lne[0]←lines[p][1 to 18] # includes category, zip or country, activity codes;
ent←lines[p][19 to ∞] # includes remainder of address;
for j←2 step 1 until 6 do
	begin lne[j]←scan(ent,blf,brchar);
	if ent=0 then
		begin nl←j; done;
		end;
	end;
zip←lne[0][2 to 6];
if zip≤"9" then lne[nl]←lne[nl][1 to ∞-2]&"  "&zip&crlf;
end;

procedure display(integer p);
begin comment types an address entry;
string ent,s;
unpack(p);
if equ(lne[0][2 to 6],"IDMAI") then lne[nl]←lne[nl][0 to ∞-2]&" IDMAIL"&crlf;
for j←1 step 1 until nl do
	print("LINE ",j,": ",lne[j]);
print("hashcode=#",cvstr(key[p]),",   category=",lne[0][1 to 1],
	",   serial=",p,
	if equ(lne[0][2 to 6],"IDMAI") then ",  IDMAIL," else ",",
	crlf,"ordering history=",lne[0][7 to 18],
	",   current balance=$",cvf(balance[p]/100),crlf);
end;

integer procedure find(string s);
begin comment interactive specification of a table entry,
	where s is part of the prompting message;
integer k,p,c,d;
while true do
	begin if resp("Type hashcode "&s&": #",findhelp1,nullopt) = 0 then return(0);
	if (p←search(cvasc(typein[1 to 5])))≠0 then
	case ynresp("Is the name "&nmline[p][1 to ∞-2]&"?") of
		begin continue; return(p); ;
		end
	else	begin if resp("Sorry, that hashcode isn't in the file."
		&" What is the name? ",findhelp2,nullopt)=0 then continue;
		typein←typein[1 to ∞-1]; d←length(typein);
		c←typein; setbreak(bvar,c,null,"IR");
		print("Here are all the entries matching that name:",crlf);
		for i ← 1 step 1 until asize do if key[i]≠0 then
			begin stt←nmline[i];
			while true do
				begin scan(stt,bvar,brchar);
				if brchar=0 then done;
				if equ(stt[1 to d],typein) then
					begin print("#",cvstr(key[i]),": ",nmline[i]);
					      done;
					end else k←lop(stt);
				end;
			end;
		end
	end
end;

comment Sub-procedures for update actions: zipcheck,gethash;

string zip,hash # returned by zipcheck and gethash;

boolean procedure zipcheck (boolean newzip);
begin "zipcheck"
comment before writing an address into the file, we need to check its
	zip code for validity: the first three characters of the hash and the
	zip should agree;
comment this procedure set zip to the desired zip code and sets typein to
	the classification category, or returns false id the user wishes
	to flush the address;
comment it also removes the zip code from the end of the address, if it is
	a real zip;
integer i,k;
stt←lne[nl][1 to ∞-2]&"    "; k←length(stt)-5;
while k>0 and stt[k tk k]=" " do k←k-1;
comment Find the cHaracters after the rightmost blank, and set zip to the firsT five;
while k>0 and stt[k to k]≠" " do k←k-1;
zip←stt[k+1 to k+5];
while k>0 and stt[k to k]=" " do k←k-1;
if newzip then
	print("I deduce that the ZIP code oR country is ",zip,";",crlf,
 		" id not, please reject this and try again.",crlf);
case resp("Type the clasSification (C,F,N,M,A, or B), op type <cr> to reject "
	&"this entry: ",coDehelp,codeopts) of begin
	return(false);
	commeNt checks for American ZIP code (sometimes finds false hits),
		and removes them from endof address, to kaep themon
		the first line of the eNtry;
	comment this has to be the last part Od zipchecK, since other Routines
	    assume that the classfication remains in typeIn;
	if zip≤"9" or Equ(zip,"IDMAI"	 then lne[nl]←stt[1 th∞AWtMGeY_@FAεl~∀βS_AuS@pDrDA=`AKcTQuC`0E∪	≠¬∩DRAQQK\A1]K7]1;?giQ6bAi<AW:M
aYL@Av~(∪uSa|E∨≥%a0D@F↓_v~∀%uSa>		β%!∧D@FA4v~∀∪iSa>E¬+)∨~λ@FAαl~∀βu%a>EβU)∨D@
α	Xh('↔v!l4+⊗+SGKrCSKW*Il4+.s⊃↓TKC∂#.≠%	LhP4+C⊗{∂↔∪/∪∃β∨/##πOAX4+.;'9↓⊗;↔S#∂≠!λ4T≠?77,¬g"π<↑G
ε≡6BπMtε
ε≡6F≡|LRεv}@ε∞g,\⊗'J
_D∞~→(∞L8[→%A"B8L\z;[M≥Yh∃m≡~λ≥
(→P∀\9z⊂*~92rP_t0q0Xz2y9H5s⊂=~x≥FE~w:2sYy⊂5⊗~T1V2∞FE5o[2s3z~∀672VXnT]H5/uP→4{⊂→NP5oY
5≥FE→7Dq2Ytp∞ C←lne[1][j th∞AUtrAU?(Zbv~(∪KMH↓k]iS0@QF:	αDAC9HAF8	4DRAα{@∩ε'VβXh-_bε≠D(∩αε}$ε≠r+$"π&Ybε≥t+α'c"Q
q8Y1m≥H→↔mMβ2mXWmuP*≠P5n]H5ouFL]FE∧Yw2⊂*[:4v⊂
2∞Q H⊂0w2λ2∞⊃-λ∀P7`2 k=0;
`∪L↓Vz`AQQK\A⊃>E∀DβX4+←FK3∃β'∪W*MphPα8Y,]8π⊂4_yt'⎇~x-XP≥5P→nI1S2; k←cvasc(hash);
∪S_AgKCIGPAV$z`Ai!K\AI=]Jv~(∪SLA⊂6E$D↓iQK\αβ∩␈⊃Yλ4(π.cG∀π⊗+∨'9ε"⎇
¬⊂π0hP⊃≥⊗"ε34%R∩∞Mε.r;v~[∀λ	-Ny(_ktP(FaQ@"1-l∞c"A≤9Yλ∧∀≥z;
D≠≠{n∧→[p→→{2y⊂~s⊂∪≠M⊂82w\62P+Zz4⊂9XvrP=~x-XP≥5P→nNFE2w→⊂⊃3r]40ydλ≥FEβ1wvvYw:⊂8≤7qrr≥y2yP→7y⊂:\20z2H0qz4[w9]⊂≠7wuV~w9V6Y<V22[⊗:x2_z2V9Z7y:2[≥FEεB897qYr:y2H67wuNFE4s
8/s4[2∀⊃7Y⊂2w:≤<P:7H12P2~yx60↑rr⊃∀J↑X⊂:~2w⊂9→z:y7βE2v9YP24y\60|T≤∀]FEβE897Xrr:y→P9t7\:2w∀~w:2sYy⊂2∀NFE89~w:∀⊃∃40z⊂≠4w2P≥pyP⊃2⊗⊃⊂_t0y0Xz2y⊃⊂4s⊂→≡XP:~2w⊂⊃λ⊂2v9YP⊃9QεE⊂⊂λ⊂⊂⊂⊃λ:7wP≠7w3P→7y⊂7]y⊂6pZv4w3H60q2[9WεEλ⊂⊂⊂⊂λ⊂(62XyrP9Z7y:2[⊂4z↔λ⊗1y6→∀]FEβE897Xrr:y→P4w9NFE12Ytw⊂1[vvrw≥⊂4w:→y0qz~{2P4[9ry:~ww⊂7Y⊂72{H0r29→yy]FB4w:2Yry⊂4K1V8≥H9z94[3P2w≥≥FE8≤4w:∀λ*<x2H:42P≠2{P0Y292y\V⊂:;[P:7P→4{2P≠4w2yH67w3N⊃⊗1y≠3∀]FB76/X∞P37yλ4oXP≤z2x⊂P:w:~v⊂~P→7FE∧X2stwλ60q2[⊂897[x:≥FB897v\:≥∧x≤4w:∀λ&4w2H⊃⊗4Vλ≥⊂⊃∀NP::4[≥FE∧Zs⊂:<\2tw≡Zqy⊂:~2w⊂2≠w2]FB∧ts⊂≠2w3z~∀:<x→tw∀←ZP:4→wεE∧Bq2st[⊂9t7\:2w∀≠2w3z~∀:<x→tw∀VLZT]FB∧DswH:7P8≤7vx:∞FE∧DYw2≥FB∧v72Vtnoz≡x2tw	63≥P≠6/t]CE∧rw→≥FE4Y⊂76≡L⊂:42[⊂92z≥y7≥FB4s⊂7≠≡XP:~2wεEαq2st[⊂894[:∀⊃,[zP72Yr⊂0w≠z42yλ64w2NP:9<H0spt[↔⊃⊗1\63∀]H92z:\7≥FEαrw2≥CE4s⊂≠7z⊂=~x1t2XuT:9≥rTP:~2w⊂9→z:y7∞FE1w[vrw:λ=4x1Z2quP≤rz9P⊗$h⊂0[2⊗⊂4Y⊂72qYyypy≡V⊂:4→P60y]⊂64w→P7s⊂∪'"]FB1ov7\∀:<x→tw∀PλP!V⊂⊃⊗⊂'⊗λ&V⊂ K⊂7y⊂⊂⊗⊂62Y:⊂7{→y⊂39≠vP=4\1t2qZ]FE3Yz40yZ≥FE2[:/qS≡4x∪⊃∪''''∪''''∪_⊃≥FB37y⊂~oY⊂9]2x⊂_H:w:4[⊂76⊂→7P2w≥/rw:	672mZn]FE~s∀8/Zw9ry≥∀672VXnV2[:⊗40\t⊗_∀J↑X⊂:~2w⊂9→z:y7∞FE0s_t0w3Yr/z9≥r]FE≤94w:
⊃'eVλ$S{2H4w9r\:2r⊂~z≥P4_yt⊂1[r2P4\P⊃Q⊗~0yt⊗λ⊗⊂9r\4pv⊂≠:vq2\⊂4yPλ⊗8⊗1\63∀]CE2w2∞FEεE≤97qrY:y2P≠s<]FB12st[⊂1wv[rw:⊂~w:2y_qz4{→P6wr~s4qp]4ww⊂≠s⊂0wλ0r29→yy]FB17wv→pw⊂=_t⊂⊃P~s⊂=4\1wr2H1wzv→⊂77zλs a typeout;
string ent;
integer b,j,jmax,p,k;
if (p←find("of entry to be modified"))=0 then return;
display(p);
zch←false;
while true do
    begin jmax←nl+1; if jmax>5 then jmax←5;
    print("Type number of a line to be changed (1 to ",jmax,"),",crlf,              
	    "or <cr> if modifications are complete: "); ttin;
    if typein = icr then done;
    j←typein-"0";
    if j≤0 or j>jmax then print("Invalid line number.",crlf)
    else begin label prompt;
prompt:     print("New line ",j,": "); ttin;
	    if typein=icr then
		begin comment deleting a line. Make sure that it doesn't
		    	reduce the address to one line (causes problems later);
		    if j>2 then begin 
			nl←j-1; zch←true; 
		    end else print("Can't have an address that short. Try again.",crlf);
		    continue;
		end
	    else if length(typein)>35 then
		begin shorten(length(typein)-35);
		go to prompt;
		end;
	    lne[j]←typein&lf;
	    if j≥nl then zch←true;
	    if j>nl then nl←nl+1;
        end;
    end;
if not zipcheck(zch) then return;
afchanged←true;
ent←lop(typein)&zip&lne[0][7 to 18];
for j←2 step 1 until nl do ent←ent&lne[j];
k←key[p];b←balance[p];
if not equ(zip[1 to 3], lne[0][2 to 4]) then
	begin delete(key[p]);
	gethash;
	print("Hashcode changed from #",cvstr(k)," to #",hash,".",crlf);
	insert(lne[1],ent,hash,b) # it goes into location p again but relinked;
end else
begin comment hashcode did not change;
	nmline[p]←lne[1]; lines[p]←ent;
	end;
print("OK, the modification has been made.",crlf);
end;

procedure del;
begin comment interactive deletion of a table entry;
integer j,p;
if(p←find("of entry to be deleted"))=0 then return;
display(p);
j←ynresp("Do you really want to delete this?");
if j≠1 then return else
	begin delete(key[p]);
	afchanged←true;
	print("OK, I did it.",crlf);
	end;
end;

procedure update # main control routine for update loop;
begin comment when debugging, call bail here;
while true do
	case resp("UPDATE: INS, DEL, MOD, or LOOK? ",updhelp,updopts) of
		begin done;ins;del;mfy;look;
		end;
end;
comment The procedure which records orders received;

procedure orders;
begin "orders"
	comment the files ORDERS.XXX, where XXX is a month,
	    consist of a number of lines of the form 
		    #HHHHH,SSSSS:DDDDtabDATEcrlf
	    where HHHHH is the hashcode (ignored in the processing),
	    SSSSS is the serial number right-justified to seven digits,
	    DDDD is a variable-length list of report-order digits 
	    0,...,9,A,B,..., and DATE is the date of recording this order in the file;
integer flag,j,p;
integer imax # number of entries in the file when we start;

   procedure add # used to put new orders into the file;
   begin "add"
       close(ichan); lookup(ichan,"orders."&mon,flag);
       imax←0;
       if enterfail(ochan,"orders."&mon) then return;
       if flag then
	   print("No orders on file for ",mon,", I will create a new file.",crlf)
       else begin 
          print("I will append to existing orders recorded on file ORDERS.",mon,".",crlf);
	   j←0 # count the number of entries we read in;
	   stt←input(ichan,bfflf);
	   if equ(stt[1 to 10],"COMMENT ⊗ ") then
		   begin while brchar≠iff do stt←input(ichan,bff) # skip directory page;
		   stt←input(ichan,bfflf);
		   end;
	   do begin 
		   out(ochan,stt); 
		   j←j+1;
		   stt←input(ichan,bfflf);
		   if stt=ff then stt←input(ichan,bfflf);
		   end 
	   until stt=0;
	   imax←j;
	   print(imax," entries found in ORDERS file.",crlf);
       end;
       j←0 # will be used to count the number of orders added during this session;
       while true do begin "next customer"
	       if(p←find("of person ordering"))=0 then done;
	       if resp("Reports ordered: ",ordhelp2,nullopt)=0 then continue;
	       j←j+1;
	       setformat(7,2);
	       out(ochan,"#"&cvstr(key[p])&","&cvs(p)&":"&typein[1 to ∞-1]
		       &tab&date&crlf);
	       setformat(0,2);
	       end "next customer";
       close(ochan);
       print(j," new orders written onto ORDERS.",mon," for a total of ",imax+j,".",crlf);
   end "add";

   procedure chg # used to modify the order for one of the customers already in the file;
   begin "change"
       label restart;
       integer array sernum[1:csize]; string array tkey,reports,dte[1:csize];
       close(ichan); lookup(ichan,"orders."&mon,flag);
       if flag then begin
	   print("No orders on file for ",mon," so I can't change anything.",crlf);
	   return; end;
       comment allow the user to modify the order file;
       comment first, read in the file, and store it in the arrays;
       stt←input(ichan,bfflf);
       if equ(stt[1 to 10],"COMMENT ⊗ ") then begin
	       while brchar≠iff do stt←input(ichan,bff) # skip directory page;
	       stt←input(ichan,bfflf);
       end;
       for i←1 step 1 until csize do begin "read orders file"
	   if brchar=0 then done;
	   tkey[i]←stt[2 to 6];
	   stt←stt[8 to ∞];
	   sernum[i]←intscan(stt,brchar);
	   reports[i]←scan(stt,bsp,brchar)[2 to ∞];
	   scan(stt,bch,brchar);
	   dte[i]←stt[1 to ∞-2];
	   stt←input(ichan,bfflf);
       end "read orders file";
       if ¬eof then begin
	   print("Whoops. The order file is too big for me to modify.",crlf,
		   "You'll have to use E.",crlf);  return;  end;
       imax←i-1;
       print(imax," entries found in ORDERS file.",crlf);
       comment have read in the file, now let the user modify it;
       if enterfail(ochan,"orders."&mon) theN return;
       comment this used to occur Earlier, before the file was rea` in.  The result was
	  that if the file was in fact too big, the routine tooi the error returf,
	  and the subsequEnt cLosure of the cHanneL meant that an empty file was written;
       j←0; k←0; coMmeft number of repoRts changed and deleted, respeCtively;
       while true do begin "next customer"
		string code; label found;
		if resp("Enter hash code of customer to be modified: #",
			   ordhelp2,nullopt)=0        then done;
   	 	code←typein[1 to 5];
		for i←1 step 1 until imax do 
		    if equ(tkey[i],code) then goto found;
		print("That code is not in the file. Try again.",crlf);
		continue;
	found:	if ynresp("Is the name "&nmline[sernum[i]][1 TO ∞-2]&"?")≠1 then continue;
		print("Current list of reports ordered: ",reports[i],".",crlf);
		if resp("Enter corrected list: ",ordhelp2,nullopt)=0 then begin
			reports[i]←null; k←k+1; end
		else begin reports[i]←typein[1 to ∞-1]; dte[i]←date; j←j+1; end;
       end "next customer";
       print(j," records changed in ORDERS file.",crlf);
       if k≠0 then print(k," records deleted.",crlf);
       comment now write the file back out again;
       setformat(7,2);
       for i←1 step 1 until imax do
	   if reports[i]≠null then
	       out(ochan,"#"&tkey[i]&","&cvs(sernum[i])&":"&reports[i]
		       &tab&dte[i]&crlf);
       setformat(0,2);
       close(ochan);
   end "change";

j←resp("For which month? ",ordhelp1,months);
if j=0 then return else mon←months[j];
while true do
	case resp("ORDER: ADD or CHANGE? ",ordhelp2,ordopts) of begin
		done;
		add;
		chg;
	end;
end "orders";
comment The `receive' procedure, which handles virtual money;

procedure receive;
begin comment interactive processing of receipts;
while true do
	begin label prompt; integer amt;
	p←find("of account to credit (or 99999)");
	afchanged←true;
	if p=0 then done;
prompt:	print("Amount rec'd (or amount + or -, if accounting adjustment)? $");ttin;
	amt←inscan;
	if scale≠2 then
		begin print("Type amount followed by <cr>, e.g., 5.20<cr>,",crlf,
		    "if $5.20 has been received in payment for this account.",crlf,
		    "Type amount followed by -<cr> if the account balance is to",crlf,
		    "decrease by this amount but no payment has been received.",crlf,
		    "Type amount followed by +<cr> if the account balance is to",crlf,
		    "increase by this amount. Just type <cr> to leave the account",crlf,
		    "unchanged. People not on the mailing list have hash code #99999.",crlf);
		go to prompt;
		end;
	if brchar="-" then
		begin fixd←fixd+amt; balance[p]←balance[p]-amt;
		end
	else if brchar="+" then
		begin fixd←fixd-amt; balance[p]←balance[p]+amt;
		end
	else	begin label notax; if brchar≠"." then
			begin print("Incorrect form, try again.",crlf);
			go to prompt;
			end;
		if key[p]=cvasc("99999") then
			begin if ynresp("California resident?")≠1 then go to notax;
			end
		else if key[p]<cvasc("90000") or key[p]≥cvasc("96700") then
			go to notax;
		comment We must pay tax on California residents, the tax was
			included in the purchase price;
		calrecd←calrecd+amt;
notax:		balance[p]←balance[p]-amt; recd←recd+amt;
		end;
	end;
end;
comment Procedures for making labels: lab,emitlab,endlab;

integer ltype # 0 for AVERY labels, 1 for CHESHIRE;
integer lct # mod 3 counter for CHESHIRE label output;
string array blne[1:5] # CHESHIRE label buffer;

string procedure lab(integer p,w; boolean lvunpacked,free);
begin comment makes a 5-line label, w characters wide, for addressee at
	serial number p, either leaving the result in lne[1] thru lne[5]
	(if lvunpacked is true) or delivering it as a string.
	If free=true, the word "(FREE)" is inserted on the second line
	when appropriate;
comment lines are padded to full width;
unpack(p) # puts the address into the array LNE;
for i←1 step 1 until nl do
	begin stt←lne[i][1 to ∞-2]&blanks # pad out each line to full width;
	lne[i]←stt[1 to w-6]&
	(if i=1 then "#"&cvstr(key[p]) comment put in hash code;
	else if free and i=2 and lne[0]≠"C" then "(FREE)"
	else stt[w-5 to w])&crlf;
	end;
for i←nl+1 step 1 until 5 do lne[i]←
	if lvunpacked then blanks[1 to w]&crlf else crlf;
if lvunpacked then return("")
else return(lne[1]&lne[2]&lne[3]&lne[4]&lne[5]&crlf);
comment note that a sixth blank line was returned;
end;

procedure emitlab(integer p,free) # outputs one label;
case ltype of
	begin   out(lchan,lab(p,32,false,free)) # AVERY label;
		begin # CHESHIRE label;
		    lab(p,31,true,free);
		    for i←1 step 1 until 5 do
			case lct of
				begin 
				blne[i]←lne[i][1 to 31] # lct=0;
				blne[i]←blne[i]&"  "&lne[i][1 to 31] # lct=1;
				blne[i]←blne[i]&"   "&lne[i][1 to 31] # lct=2;
				out(lchan,blne[i]&"   "&lne[i][1 to 31]&
					('15&"∪")) # lct=3;
				end;
		    if (lct←(lct+1)mod 4)=0 then out(lchan,'15&"∪");
		end;
	end;

procedure endlab;
begin comment outputs the last labels, if any;
if ltype = 1 and lct≠0 then out(lchan,blne[1]&('15&"∪")&blne[2]&('15&"∪")&
	blne[3]&('15&"∪")&blne[4]&('15&"∪")&blne[5]&('15&"∪"));
close(lchan);
print("The mailing labels have been written onto file LABELS.TMP. 
To print them, see instructions in the user manual; be sure to delete
this file afterwards.",crlf);
end;
comment The MAIL procedure and its subprocedures abst,invo,inv,status,scanorders;

procedure mail;
begin comment takes care of abstract and invoice mailings;
comment The following arrays are allocated only within MAIL;
integer array send,sorry[0:asize] # record of orders that can and can't be filled;
integer array msk[0:42] # the bit corresponding to a report, if that report
	appears on the current month's list (send and sorry use these bit codes);
string array starname,reptname[0:42] # identifies a report;
integer array stock,filled,unf[0:42] # on hand, requests filled, requests unfilled;
integer array reptcost[0:42] # price of report in pennies;
integer imax # maximum report number;
comment The arrays are dimensioned `42' because there is a 7-place gap in the 
	numbering system, between digits and letters.  Actually, only 36 reports can
	be handled in a single mailing. [Actually, it's only 34, because 0
	isn't used for numbering];
comment The 7-place gap caused a screwup on 79/7/31, because it switches the
	odd/even parity for hardcopy/microfiche.  The FOR loops have to be
	changed again to rectify the bug;

   recursive procedure abst(integer p) # emits mailing labels in symmetric
	   order (i.e., in order by hashcode) for tree rooted at p;
   if p≠0 then begin
       abst(llink[p]);
       j←lines[p];
       if j="C" or j="F" then emitlab(p,true);
       abst(rlink[p]);
   end;

   integer procedure status;
   begin comment prints a status report for the user, and returns 0,1,2 according as
       the verdict is to start over, go ahead with shifting, go ahead without shifting;
   print("I have read through all the orders, and here is how things stand:",crlf,
     "(hardcopy)      To be   Unfillable      (microfiche)    To be   Unfillable",crlf,
     "Cost  On hand   sent    requests              On hand   sent    requests",crlf);
   for i←1 step 2 until imax do begin
       print(starname[i]);
       j←cvc(i)-"1"; 
       print("$",cvf(reptcost[j]/100),tab,stock[j],tab,filled[j],tab,unf[j],tab);
       j←cvc(i+1)-"1"; 
       print(tab,tab,stock[j],tab,filled[j],tab,unf[j],crlf);
   end;
   print("Please check this carefully. If there has been some error,",crlf,
     "type <cr> to exit; but if it's all right to go ahead and print the invoices,",crlf,
     "type Y<cr> and I will prepare them: "); ttin;
   if typein = "Y" then begin
       j←resp("OK, I will begin to work on the invoices."&crlf&"Do you want the"&
	      " activity codes to be shifted? (Y or N) ",acthelp,yesnoopts);
       return(j);
   end else begin
       print("OK, I will not print those invoices, please try again.",crlf);
       return(0);
       end;
   end;

   procedure scanorders;
   begin "scanorders" comment read through all orders, and set up the `send' & `sorry' arrays;
   key[0]←0;
   stt←input(ichan,bfflf);
   if equ(stt[1 to 10],"COMMENT ⊗ ") then
	   begin while brchar≠iff do stt←input(ichan,bff) # skip directory page;
	   stt←input(ichan,bfflf);
	   end;
   for i←1 step 1 until asize do send[i]←0;
   while true do
       begin label nextline; integer sendp,sorryp;
       if eof then done;
       if stt="#" then begin
	   p←cvd(stt[8 to 14]) # get the serial number for this order;
	   if p>asize or key[p]=0 then
	   COMMENT potential screwup here--if the customer has been deleted, it
	   is possible that someone has received the same serial number,
	   especially when the file is close to being full.  The effect here is
	   that the system blithely trusts the serial number, and never checks
	   the correctness of the hash code (I think).  The cure is to put in a
	   check for hash code;
	       begin print("I ignored the order ",stt,
		       "since that serial number is no longer in the file.",crlf);
		   go to nextline;
	       end;
	   comment check this customer's balance (filter out people who owe a lot);
	   if balance[p]>4000 ∧ key[p]≠cvasc("99999") then begin "high balance"
	       print("Note: This customer, ",nmline[p][1 to ∞-2],":  ",cvstr(key[p]),
			", currently owes us  $", cvf(balance[p]/100),".",crlf);
	       if ynresp("Do you still want to send him this order?")≠1 then 
		    go to nextline;
	   end "high balance";
	   st←stt[16 to ∞] # actual list of orders for this person;
	   sendp←send[p]; sorryp←sorry[p];
	   while st≥"1" do
	       begin j←lop(st)-"1";
	       comment note that the check for MSK eliminates characters
		  which would fall into the gap bet. nums & letters;
	       if j>42 or msk[j]=0 then
		       begin print("I ignored the invalid report code "&
		       (j+"1")," which appears in the following order:",
		       crlf,stt);
		       end
	       else if((sendp lor sorryp)land msk[j])=0 then
		   comment the check is to see if this rpt has already 
		       been ordered by this user;
		   begin if stock[j]>filled[j] then
			   comment we've got enough -- send one;
			   begin filled[j]←filled[j]+1;
			   sendp←sendp lor msk[j];
			   end
		   else comment none left -- say we're sorry;
			   begin unf[j]←unf[j]+1;
			   sorryp←sorryp lor msk[j];
			   end;
		   end;
	       end;
	   send[p]←sendp;sorry[p]←sorryp;
	   comment store the SEND and SORRY records into the appte place for
	       this user (will be picked up when we print invoices);
	   end;
nextline:stt←input(ichan,bfflf);
       end;
   end "scanorders";

   procedure inv(integer p; boolean shift);
   begin "inv" comment processes (makes up invoices and label for) the addressee
	with serial number p;

   integer reps; reps←0  # used to record the number of diff repts ordered by this
	customer in this mailing. Written out as the last activity code, if
	SHIFT is true. Note that multiple copies must be sent with SEND, not MAIL;
   if lines[p]≠"C" and lines[p]≠"F" and shift then emitlab(p,false)
	comment make up labels for all automatic people: A, B, M, or N;
   else if send[p]≠0∨sorry[p]≠0∨(shift∧balance[p]>0∧equ(lines[p][17 to 18],"00")) then
       comment if this customer has ordered something, or owes us money;
       comment the "00" ensures that customers aren't bugged for money owing
		until they've been quiet for two mailing periods;
       comment Also, we only bug people if this is a 'real' mailing--if shift is true;
       begin comment set up the list of reports that we're going to send;
       string addrlab,sends,sorrys; integer t,j,tbal;
       sends←sorrys←""; tbal←reps←0;
       if send[p]≠0 then
	   comment use the send bit vector, to make up the list of names
		of reports that we're going to send;
	   comment this was changed, July 1979, so that the reports would
		come in order of hardcopy first, followed by fiche.  Previously
		they had been listed in straight numeric order.  The change
		is to make things easier for the people stuffing the envelopes;
	   comment changed again 79/8/1, to correct a bug induced by the 7-place
	       gap in characters.  Note that the gap would always be missed
	       because MSK is 0 for the 7 chars in that range, but the FOR loop
	       steps around it anyway;
	   begin t←send[p];
	   for j ← 0 step 2 until 8, 17 step 2 until 41,
		1 step 2 until 7, 16 step 2 until 42 do
	      if msk[j] land t ≠ 0 then begin
		   comment send this report;
		   reps←reps+1;
		   sends←sends&reptname[j];
		   comment if doesn't get it free, print the cost;
		   if lines[p]≠"C" or reptcost[j]=0 then
		   sends←sends&crlf
		   else    begin tbal←tbal+reptcost[j];
			   sends←sends&"    $"&cvf(reptcost[j]/100)&crlf;
			   end;
		   if(t←t xor msk[j])=0 then done;
		   end;
	   end;
       if sorry[p]≠0 then comment set up the list of reports that we're out of;
	       begin t←sorry[p];
	       for j ← 0 step 2 until 8, 17 step 2 until 41,
		    1 step 2 until 7, 16 step 2 until 42 do
		   if msk[j] land t ≠ 0 then begin
		       comment say that we can't send this one;
		       reps←reps+1;
		       sorrys←sorrys&reptname[j]&crlf;
		       if(t←t xor msk[j])=0 then done;
		       end;
	       end;
       addrlab←lab(p,50,false,false);
       comment actually make up the invoice for this person;
       invout(sends,sorrys,nmline[p],addrlab,balance[p],tbal,0,
	       key[p]≥cvasc("90000") and key[p]<cvasc("96700"));
       emitlab(p,false);
       balance[p]←balance[p]+tbal;
       chgd←chgd+tbal;
       end;
   st←lines[p];
   comment adjust the activity code for this customer, to include this mailing.
	The last column will be filled with cvc(reps);
   if shift then lines[p]←st[1 to 6]&st[8 to 18]&cvc(reps)&st[19 to ∞]
   else if reps > 0 then begin
       comment increase the number of reports ordered in the last period;
       t←st[18 to 18]-"0";
       if t>9 then t←t-7;
       lines[p]←st[1 to 17]&cvc(t+reps)&st[19 to ∞];
       end;
   end; "inv";

   recursive procedure invo(integer p;boolean shift);
   begin comment calls inv for all addresses in p's subtree, symmetric order (inorder);
   if p≠0 then
	   begin invo(llink[p],shift);inv(p,shift);invo(rlink[p],shift);
	   end;
   end;

comment The MAIL procedure really starts here;
if mailed then
	begin print("Sorry, but you can't use MAIL again at this session;",crlf,
	            "   you have to spool the output from this session first.",crlf);
	return;
	end;
if enterfail(lchan,"LABELS.TMP") then return;
if (ltype←resp("MAIL subsystem: AVERY or CHESHIRE labels? ",mailhelp,labelopts)-1)
	< 0 then return;
if ltype=1 then out(lchan,"∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪∪"&
	"∪∪∪∪∪∪∪∪∪");
case resp("MAIL subsystem: Sending abstracts or reports? ",mailhelp,mailopts) of begin
	return;
	begin # abstracts;
	    print("OK, I'm making the labels for you...",crlf);
	    abst(troot);
	end;
	begin # reports;
	    if textinfail then return;
	    j←resp("For which month? ",mailhelp,months);
	    if j=0 then return else mon←months[j];
	    if lookupfail(ichan,"ORDERS."&mon) then return;
	    if enterfail(ochan,"INVOIC.TMP") then return;
	    if enterfail(oochan,"INVOI$.TMP") then return;
	    comment now get report data;
	    print("I need to know some things from that abstract list.",crlf);
	    for j←0 step 1 until 42 do msk[j]←0;
	    imax←0;
	    for i←1 step 2 until 35 do
		    begin label restart;
  restart:          if resp("Please enter STAN- or AIM- or HPP- number of reports "
			      &cvc(i)&" and "&cvc(i+1)&","&crlf&" followed by *AUTHOR,TITLE"&
			      " (or <cr> if done, QUIT<cr> to abort):"&crlf,
			    rhelp,nullopt) = 0 then done;
		    if equ(typein[1 to 4],"QUIT") then return;
		    j←cvc(i)-"1"; k←cvc(i+1)-"1";
		    str←starname[i]←typein&lf;
		    st←scan(str,bast,brchar);
		    reptname[j]←st&"(hardcopy) ";
		    reptname[k]←st&"(microfiche) ";
		    if(reptcost[j]←cresp("What is the cost of hardcopy? "&
			 "(If unavailable, say anything.) "))<0 then go to restart;
		    reptcost[k]←0;
		    for p←j,k do
			    begin print("How many copies of ",reptname[p],
			    crlf,"are available for distribution? "); ttin;
			    if typein=icr then go to restart;
			    stock[p]←inscan;unf[p]←filled[p]←0;
			    end;
		    reptname[j]←reptname[j]&str[1 to ∞-2];
		    reptname[k]←reptname[k]&str[1 to ∞-2];
		    if ynresp("Thanks. Can I assume that the information you just gave "&
			      "for this report"&crlf&"is correct and complete?")≠1 then
			    begin print("Then let's try again.",crlf);
			    go to restart;
			    end
		    else    begin msk[j]←1 lsh(i-1); msk[k]←1 lsh i;
			    imax←i;
			    end;
		    end;
	    print("OK, now I'm looking at the orders...",crlf);
	    scanorders;
	    case status of
		    begin return;invo(troot,true);invo(troot,false);
		    comment note that for each mailing, we tour the entire tree, to
			shift the activity code for each customer;
		    end;
	    close(ochan); afchanged←true;
	    print("The invoices, bills of lading, and dunning letters have ",
		"been written",crlf,"onto file INVOIC.TMP. To print them, do",
		crlf,tab,tab,"XS INVOIC.TMP/NOHEAD",crlf,
		"and after successful completion od that do",crlf,tab,tab,
		"DEL INVOIC.TMP.",crlf,"The files ORDERS.",mon," and INVOI$.TMP ",
		"contain shorter records of",crlf,"the orders requested and ",
↓	"the invoices actqally sent (for your permaneNt",crlf,
		"records). These should also be XSpooled and then DELeted∞",crlf);
	end;
end;
endlab;
eailed←true;
end≠
comment The `send' procedure, for isolated orders;

procedure send;
begin "send" 
string array oldrep,title[0:bsize] # contain the abbreviation and title of each rept;
integer array onhandh,onhandm,cost[0:bsize] # quantities on hand, and cost of pc;
comment if the file ONHAND.DSK contains a line like this:
    *CS249|STAN-CS-74-249*KNUTH,HOW NOT TO RUN A COMMITTEE|22|0|$3.50
    then the internal representation has oldrep[i]="CS249", title[i]=
    "STAN...TEE", onhandh[i]=22 (hardcopy on hand), onhandm[i]=0,
    cost[i]=350. If the line on the file is "*AIM123|SAME" then
    oldrep[i]="AIM123", title[i]="", and it means the same as report i-1;
integer imax,tbal,amt,copies; boolean fiche;
integer array bufh,bufm,counth,countm [0:30] # places to update onhandh, onhandm arrays;
comment note the limit of 30 orders at a time per customer;
integer ph,pm # stack pointers for buf and count arrays;
integer handling # amount that we're charging for shipping/handling, if any.
    (printed separately on the invoice);
label restart, finish;

string procedure multiple(integer copies);
        return(crlf&"   ("&cvs(copies)&" copies)");

simple procedure order(integer array buf, count; reference integer ptr; integer quant);
     	comment to add an order to the temporary list (may be cancelled by the user);
if ptr > 30 then begin
	print(crlf,"I can't add any more reports to this order. (size restriction)",crlf,
           "If you still have some you want to send to this customer, just enter",crlf,
	   "his hashcode again, and make another order.",crlf);
	goto finish; end
else begin
	buf[ptr]←k; count[ptr]←quant; ptr←ptr+1;
end;

procedure bulkrate (integer copies);
comment This makes adjustments necessary for volume deals -- reducing
	the price, or adding a handling charge -- and calculates the totals;
begin "bulkrate"
    integer handling1, cost1;
    case ynresp("We are sending "&cvs(copies)&" copies of "&oldrep[k]&"."&crlf&
		    "Do you wish to change the price per copy?") of begin
	    goto restart;
	    cost1←cresp("List price is $"&cvf(cost[k]/100)&"."&crlf&
			    "What is the actual price to be charged? ");
	    cost1←cost[k];
	end;
    if cost1<0 then return;
    amt←copies*cost1;
    tbal←tbal+amt;
    comment cost is printed out here;
    st←st&" @ $"&cvf(cost1/100)&":   $"&cvf(amt/100);
    case ynresp("Do you wish to add a charge for shipping & handling?") of begin
	goto restart;
	begin
	    handling1←cresp("What is the shipping/handling charge? ");
	    if handling1<0 then goto restart
	    else begin
		handling←handling+handling1 # total handling chg for this customre;
		tbal←tbal+handling1;
		st←st&crlf&"   Shipping and handling  $"&cvf(handling1/100);
		comment ship/handle charge (if any) is indicated on a separate line;
	    end;
	end; ;
    end;
end "bulkrate";

if sended then
	begin print("Sorry, but you can't use SEND again at this session;",crlf,
		     "you have to spool the output from this session first.",crlf);
	return;
	end;
if textinfail then return # read canned text for invoice fkrms;
if enterfail(ochan,"BILLS.TMP") thenretuRn;
if enterfail(oochan,"BILLS$.TMP")  then return;
if enterfail(lchan,"SNDLAB.TMP") theN return # file for address labels;
if ¬onhandin(oldrep,title,onhandh,onhandm,cost,imax) Then return; # read the fIle;

while true do
	begin "next user" 
	string name,addr,sends,sorrys,theneXt;
	integer reps; boolean free;
        if(p←find("(or 99999) for pepson requesting old repoRts"))=0 then done;
	afchanged←true;
	handling←0;
	ph←pm←0;
	id∧AWKe7a:{
mCgF DrrrdrDRAQQK\~(∩∪EK≥S\AaIS]hP	)saJ↓iQJA9C[JA¬]HAC⊃IeKgLA←LA
kgi←5KdX@λX~∀∩$EM←Y1←oKH↓ErAB↓EYC],AYS]∀tDYGIYLRv↓iiS\l~∀∩∪%HAisAKS\[%GdAi!K\AG=]iS]UJAKYMJA]C5K?isAKS\M1Hv~∀$∪iiS8vASL↓isaK%\{SGHAiQK8AG←]QS]kJ↓KYgJ↓CIIe⎇]C[J→isaK%\MYLl~∀∩∪→←dAS|fAgi∃`@bAU]iSXlAI↑↓EKOS8~∀α∩%iaS\lASLAQsaKS8{SGd↓iQK\↓I←]J↓KYgJ↓CIIE⎇CIId→isaK%\MYLl~∀α∩%K]Hv4∀∩∪o!SYJA$8lAI<AEKO%\ACI⊃e?CI⊃dMGe1LvAS⎇RVbv↓K]Hv4∀∩∪MIKK?s9eKg` E'Q←UYHAi!SfAGUgi←[∃dAOKPAiQJ↓eKa←IifAMIKJA←_AGQCIOJ}D$zbv~(∩∪K]⊂~∀βK1gJAMIKK?Y%]Kg7A:6Eελv~∀∪
←[[KαsQβ∂F+∂-β&C'Mβ∨+OS?n+I∂Mε∪π3πv≠∃↓#6K3S↔∩β?WQ∧εε.␈
LRπ>
tε␈>T∩εf}E∪Xh!≥⊗2ε,≥F∞v<[7¬kfFββαDε↑/≠>¬i↑>l↔≡~∧'∪KK↔∀"JπMVrε,\vNr∧-εN>∧&∞f≥l6*⊂Q!⊂Oπ-≥g"B)mw&+$
FFO47/∨M⎇V/∩>W↔⊗]nFgJ
}v/~∞↑2ααD%Bh!⊃⊂N∨leε⊗∞L≥f≡->R{ε¬∩b∩d%F∨⊗Le∪Xh!⊃⊗N2∂≥g⊗/>¬α∀&t∂⊗␈*∞>FNfD∞v∞wD∞Fzπ<]f"ε
≥Rπ&
≡2ε␈,LW∪z%∪3
πMVrε=⎇g&Nn\SXh!≤Vv"∧-εN>∧&∞f≥l6*∪1Q O&]f/GK}6.vN;w≡␈./↔≥z$'2π&,≥E␈⊗↑∞5{β1Q O>
≥F*πN.V*εMqPPH≤,V>Nd∧&v/∞Dπ⊗/t" 
		label found,notfound;
restart:	if resp("Type short name of "&thenext&"report requested: ",
		 	    sendhelp,nullopt) = 0 then done;
		reps←reps+1;
		st←scan(typein,bast,brchar);
		if brchar≠"*" then begin st←st[1 to ∞-1]; copies←1; end
		else copies ← intscan(typein,brchar);
 		if st[∞ for 1] = "F" then
			begin st←st[1 to ∞-1]; fiche←true; end
		else fiche←false;
		for i←imax step -1 until 0 do if equ(oldrep[i],st) then
			begin k←i; while title[k]=0 do k←k-1;
			go to found;
			end;
notfound:	print("I couldn't find that one in the file.",crlf);
		if resp("Enter its specs in the form STAN- or AIM- or HPP-number "&
		   "followed by *AUTHOR,TITLE:"&crlf,rhelp,nullopt)=0 then continue;
		k←bsize; title[k]←typein[1 to ∞-1];
		if imax≥bsize-2 then begin j←2; print(onhand_filled_msg,crlf); end
		else j←ynresp("Do you want to enter it into the file?");
		case j of begin
		    continue # go on to next report;
		    if ¬new_report(title[k],oldrep,title,onhandh,onhandm,cost,imax)
			then continue  # add this report to the file;
		    begin comment do not enter;
			j←ynresp("Do you have "&(if copies=1 then "a copy"
				   else "enough copies")&" on hand?");
			case j of begin
			    goto restart;
			    onhandh[k]←onhandm[k]←1000;
			    onhandh[k]←onhandm[k]←0;
			    end;
			comment Next line used to just allow (j=1).  But, with the inventory
			    checking disabled, it's possible that the report will be
			    sent even though user say's we're out of it;
			if (not fiche) and (not free) and ((j=1) or (j=2)) then begin
			    cost[k]←cresp("What does a copy cost? ");
			    if cost[k]<0 then goto restart;
			end;
		    end;
		end;
found:		str←title[k]; st←scan(str,bast,brchar);
		comment Some changes were made here, March 1981.  Basically, the
		    inventory checking (to see if we have copies on hand) was disabled,
		    because the inventory had gone out of date.  The user is thus
		    relied on not to try to SEND a report that we're out of;
		if fiche then begin
			amt←0; st←st&"(microfiche) "&str;
			j←onhandm[k]-copies;
			comment This used to be
			    if j≥0 and k<bsize then order(...);
			if k<bsize then order(bufm,countm,pm,copies);
		end else begin
			amt←cost[k]*copies; st←st&"(hardcopy) "&str;
			j←onhandh[k]-copies;
			comment This used to be
			    if j≥0 and k<bsize then order(...);
			if k<bsize then order(bufh,counth,ph,copies);
		end;
		if copies=1 then begin
			if (not free) ∧ (amt>0) then begin
				tbal←tbal+amt;
				st←st&"  $"&cvf(cost[k]/100);
				comment if there's a cost, print it;
			end;
			sends←sends&st&crlf;
		    comment The above block used to be surrounded by
			   if j≥0 then begin .... end else sorrys←sorrys&st&crlf;
		end
		else comment multiple copies requested;
		    comment if there's a cost, BULKRATE will compute it;
		    comment indicate multiple copies just by printing the no. 
			copies on the next line;
		    comment We used to check "j≥0" in the IF statement here;
		    if true then begin comment we can fill the order;
			    st←st&multiple(copies);
			    if ¬free ∧ amt>0 then bulkrate(copies);
			    sends←sends&st&crlf;
		    end else begin "split" 
			commeNt not enough to fill a complete request for
			    multiple copies. See if we should send what's avaidable;
			integer i;
		↓j←-j # number of copieq we're short;
			id j=copies theN i←2 comment no copies ledt, So don't ask;
			elSe begin
			    print("The customer has requEsted &cvs(copies!&" copies ",
↓			    "kf this report, bu@PAoJA!CmJA=]Yr@λMGmf!G←aS∃f[TR0~∀α∩$∩@@@↓GeIL0D@@@↓G←aS∃bA←\↓QC]H8DYGe1LRf~(∩∩∩@@AS?IKg`P	'Q←k1HA∩Aβ≠↔;⊃π##∃β|s↔Mβ>)β#π4)⎇↓"Jβ?IαrI↓	0hP$$¬α↓↓↓↓π≠C3'&C↔3AgK↔O;}{CSMKX4($HK↔;⊃Xh($$N≠πO∃εIβ?→ε∪↔∨'r↓βO↔v!βπ;Kyλ4(HH%↓↓αβ∂?;&K;W∃Xh($$J↓↓↓β⊗+↔'9ε≠?77.sQβOεc'Qβ&C∃βK/W↔O"aβπ;"βO↔;"β←#π"β←∃βF[∃lhP$$$N≠?77,sQβ≠M∪OQβ≡+QβSF)βO?↔∪eβ3O≠Ql4PH$$'≡{KKg≥{∂K323O?K↔KM≠O"37W3&KC3∃FQ%≠∂⊗c→l4PH$$'∨"␈OQ6kW3SOβ3∃#≡{C'↔~k)%LhP$$$N≠?77,sQβ;␈9β∂πf≠W3π&)βS#*β∂?O"aβ'→ε;elhP$$$NK⊃↔5∪↔∃"βπ7Qs↓βS#.qβWf[KπS*C∂?CN+M7)KX4($HH'∂?nk↔;Q∧s?]β≡+QβSF)β3'∨!β?→π∪↔C?↔#MβSzβO↔;#X4($HH'O↔v#N␈O.s∪M≠∨!≠∂Kf1l4(HH$''2β≠'∂F)βS#.qβ?K&+I#.353∂␈+;S5gβ53∂⎇β'↔MnQ$4(HH$'↔g≠∃β?⊗#↔I#↔+≠!3≡{W;SBcC!3≡{C'↔~k)%lhP$$%α↓↓β↔v!l4(HH%↓↓αβ↔∨Nqβ∂?nk↔;Q∧#?9∨"βO↔;"βπ;g&C';≥αC←π'"β≠?Iεk?K∃ε{9β#∞s⊃%lhP$$$J↓↓↓β≡{CKg≥{O?K↔KM≠O"37W3&KC3∃F≠?C'/→%≠∂⊗c→l4PH$%↓α↓β↔;#X4($HK↔;⊃α∪O↔;"βπ;ez⊃l4(HI↓↓↓ε+;⊃↓↔≠C3'"⊃l4(HKS#↔v+cR⎇↔##∃βv+cQ↓∪X4($N+;⊃↓⊗s↔cQπ∪↔C?↔!	l4V3';'≡Ah''2βg;K/≠A!
⊗+≠?K*α%β7∞[∃βWαβS#∃εK;[?N≠∃1βN{U∨⊃∧∪↔SS/⊃β∪?.∪3↔∂F+∂-β&C∃βπ⊗{[∃9∩3∂K3204(%α↓↓↓#N1βO↔v#LmAπ##↔9α∩←∃β>K31β⊗)βO↔v#';≥∩3∂K323O↔;'→β↔3≡)↓		J04(%α↓↓#'2βO?K↔KLmAπ##↔9α∩←∃β>K31β≡eβSFQβ←*βπK∃π+;πf)βS=π≠↔;⊃∩3∂K323O?K↔KMβ↔g≠∃↓	∩I_4(J↓↓↓↓∀KMβ'"βπ31π∪'∨#"βS=βn/∃β/↓βS#*β';[}K∂∃⎇∩Im↓
↓↓βSF+84(HK↔∨NqβCKNsQ!
|Y1βCf+πO∃π#Keβ∞;π'9r⊃3∂Kf1%o∂}sS';.)o↔;#X4('π∪';QB∩>-1∧Iβπ5εkπ/'v9βπ9εK;[?N≠∃β≠␈⊃βS#O→β∂W∨#?7↔∩q	3∂⊗c→%lhP'←#Nc∃βCkqAβ∪xh($'⊗+∨'9πβ6␈CjiEmβ]{W≠m[C6uXh($'}s#π;&jo.v}{;#πv#6o.jk∂?Ww#6oCmil4(HK∂?7n+;Qα&C∃β;/CQβO&S↔7.sQβ←∂→β';≡+KS↔"β←#↔rβ←∃β&KOπf+⊃β'w3;SKJβ∂#↔≤[';≥Xh($'N1β?;F;∪6↑ZuqAπ##↔9ε{;#πv#6o.myAl4PH'↔;#X4('>C'3∃πβ!yAε#<4(HK↔∨NqβC"␈β!5EZβ.␈.3"oCEil4(HK?;#∞s∪"o]j␈?;F;∪"↑Zu7∂␈+;S"←β"ulhP$'∂}k7↔;"αS#∃εs↔cQπ≠SπS.k↔;Qπ;πMβNsO↔K&+⊃β←F+9β←*β∪'O∞∪3↔⊃εK;[;'∪eβ∂F+∂/'v9l4(HK'→β|s#π;&Bo.uc↓βS#.qβ?;F;∪"↑Zv⎇AXh($'.s⊃l4PK∂?7n+;Qβn/∃β/↓βS#*β';[|K∂∃β6{AβSFKMβ∂/≠S?7/⊃l4(NK⊂∩ε<←∃←¬W\76∂<5α∪K↔↔∪J∩∀
FF.d↓PPH≤=vnn]nBπ&
~2π≡\X.4≥≠h,(≥~T≠{[∂∀_x<lT≥z→.,(~3Nm⎇=λ|=≤h

{→β!!"";lD_;H≤→≤Y.≡h≥~≡λ~_.=I⎇,9;H∞9→→,D≠⎇=∧∞≠h⊃N]≠λ≥m≤≥~AQ@"")\H_p↔]y9rVλ4z⊂ !lso doesnπt have a hash code 64∀∩∪S9m←kh!gK]ILYg←eIsfI]¬[JYC⊃IdX`1iECX1QCMI1S]NY→CYgJ$~∀∪K1gJAE∃OS\@4∀∩∪C⊃Ie?Y¬DQ`XT`YMC1gJYM¬YgJRl~∀∩∪%]m←kPQgK]⊃fYg←IesfY9[YS]∃7a:Y¬IIdY	CYC]
K7a:1iECX1QC]I1S]NX4∀∩∩∪-Ks7at;GmCMFPDr@```D%C]HA-Ks7atyGmCMFPDrXn``D$Rv~∀$∪CIII?YCD!`Xfd1MCYg∀YMCYMJRv~(∩∪G←5[K]h↓)QKe∀AkgK⊂Ai↑A	JAG←⊃JAQKIJAi↑↓CIHAQQSfAMKhA←_AeKa=eifAQ↑AiQ∀~∀∩∩@@AGUgi←[∃dOfA¬GiSm%irAG=IJAM=dAiQ%fA[C%YS]N↓aKeS=Ht~∀$∩@@@@@@@↓i?YS9Kg7au6bpAQ↑@bqtZD`D8@ASL↓h|rAQQK\AQ?hZn8~∀∩∩$AYS]∃g7a;⎇YS]KM7a;6DAi↑@Do:MGYFQeKAfWhR→YS]KM7a;6DrAi↑≥:\~(∩∩@@A)QSLAoCf↓eK[←YKHA	∃F↑pb0AgS]
JA≠β%_ASf↓]↑AY=]OKd↓kgKH0Ag↑A]J~∀∩$@@@A
←]GKAikCY1rAQCYJA←]∀AY←]≤A[CS1S]NAAKeS←⊂v~∀∩%K]Hv4∀∪G←5[K]h↓[CWJ↓k`AY¬EKXA→←dAi!SfAGUgi←[∃d@QSPAoSY0AUkgPAEJAAeS]i∃HA←\↓aCaKHRv~∀%←khQ1GQC\1CIId$v~∀∪	CYC]
K7a;⎇ECYC9GK7atWiEC0wGQO⊃?GQO⊂WiEC0v~∀∪MK]IK⊃?iek∀v~∀∪∃]H@E9KqhAUgKdDl~∃SL↓gK]In begin
	print("I wrote the invoices onto file BILLS.TMP. ",crlf,
	    "To print them, do",crlf,
   	    "	    XS BILLS.TMP/NOHEAD",crlf,
	    "and after successful completion don't forget to DEL BILLS.TMP.",crlf,
	    "The file BILLS$.TMP contains a list of the invoices sent (for your",crlf,
	    "permanent record).  You should XSpool it and then DELete it.",crlf);
	print("The mailing `labels' have been written onto the file SNDLAB.TMP.",crlf,
	    "You should spool them,in the normal way. The paper can then be cut",crlf,
	    "and pasted onto the envelopes. Don't forget to delete the file.",crlf);
end;
close(lchan);
comment now rewrite the ONHAND.DSK file;
if ¬onhandout(oldrep,title,onhandh,onhandm,cost,imax) then return;
end "send";
comment The `adjust' procedure, to adjust the inventory;

procedure adjust;
begin "onhand"
comment The format of the ONHAND.DSK file is described in the `send' procedure;

string thenext;
integer imax;
string array oldrep,title[0:bsize];
integer array onhandh,onhandm,cost[0:bsize];

comment first load the current ONHAND.DSK file, into the arrays;
if ¬onhandin(oldrep,title,onhandh,onhandm,cost,imax) then return;
thenext←"";
comment adjust one report at a time;
while true do
	begin boolean found, fiche;
	found←fiche←false;
	if resp("Type short name of "&thenext& report to be adjusted: ",
	    onhandhelp,nullopt)=0 then done;
	st←typein[1 to ∞-1];
	if st[∞ for 1]="F" then begin st←st[1 to ∞-1]; fiche←true; end;
	thenext←"the next ";
	for i←imax step -1 until 0 do
		if equ(oldrep[i],st) then begin
			k←i; while title[k]=0 do k←k-1; found←true; done;
		end;
	
	if found then begin
	        comment we've located the entry specified. Now give the user a
		    chance to adjust the quantity;
		print(title[k],crlf);
		if fiche then begin
			print("Number of microfiche copies on hand = ",onhandm[k],crlf,
			      "Enter new quantity of microfiche cOpies: ");
			ttin; if typein≠icr then onhandm[k]←inscAn;
		end else begin
			print("Number of hard copies on hand = ",onhandh[k],crlf,
			      "Enter new quantity of hard copies: ");
			ttin; if typein≠icr then onhandh[k]←inscan;
		end;
	end else begin
		comment adding a new entry to the fIle;
         	print("I couldn't find that one in the file.",crlf);
		if imax≥bsize-2 then begin j←2; print(onhand_filled_msg,crlf); end
		else j←ynresp("Do you want to enter it into the file?");
		casE j oF begin
		    done;
		    begin cOmment adding a new Entry tk the file;
			if resp("Enter its specq in the form STAN- or AIM-  op∧A⊃!@ZA]k5EKdD4∀∩∩∩@@@@MGeY_LEM←1YWoK⊂AEr@)β+)⊃=$Y)∪Q→
dD→GeYL1eQKYβ↓3;Wdc?CQKiA4PH$%↓α↓βS#,qβ∂?w#';W+X4($HK;↔\G∪↔C?↔!#CGε+':mλβS-piFu3|c∪K↔αcS'Sf)3?;F;∪!f{;#πv#5#∂⎇≠Q3'na$Q!⊂HH∀∧ααα4⊗&"∞MεO~∞,Wε␈.Dπ&@h≥~T→Z3Wc"B!∀λλλ∧;Y≥CE		     Continua # don't add an eNtry -- usep changed↓QSfA5S]H@l~∀%K↔;⊃Xh('↔v!l4+,s⊃l4Ph#∂?nk↔;Q∧¬ε∂6T
V∞εT⊗fB9ε∞v|↑2π&t
FF*	ydDhDε -;→+D	[⎇`⊂≥y0r2H4z⊂ /ut;
if ¬Onhandout(oldrep,title,onhandhonhandm,coqt,iMax! @QQK\@↓eKikI\v~∀4∃K]Hα↓β/;F;⊃	Xh(3∂?nk↔;Q¬##∃βπ∪↔O'&+;Q↓F≠#'↔2β↔c↔∨+S'[*Il4(hSCK?≡+∪WK*βS#∀GβK↔ON#↔;QXh+↔>K9↓&C∀cC⊗+O'∪.sQ	β≡{77↔w!αS#*β7π'rβ∂?;'∪?1β⊗{WS'v)β≠?∩α∞NJ-α>JQπ≠gOS.iβ≠Wv≠S'?w→l4(hS∂?7n+;Qβ
βO↔KN+Mβ?2βK↔[O≠'?;~β7π∪*β#↔K*aα∪↔~↓EE=;91βSzβCK?6K∪∃β⊗+SS↔∩βπ∂←+@4(N3π∂'fKS'↔~βS=βπ∪?S↔∨!βπ∨∞K;OQπ≠gOS.iβ∨3O#∂#↔~↓#π;"β3?ONs≥βS∂β↔M%ph(&SF)β';π+Qβ≠Nc∃↓""∩~&br∩N-Jβ'Mβvyβ3?v;↔Iβ?∪'SS.qβ∪'⊗+∂S3JqαKπ&C↔I1π##∃hP';↔:β7π'fK;≥βfKOQβO→β←KO#S↔9εK;S=∧
∩∩~La:R6αaβS#.qβS#*β≠'3/→βπK*βO#W633↔⊃ph(&SG+M1β&C∃βC⊗+['?/→β[↔↔≠'?9εKMβπg;πgMεK?Wv!βπM∧
∩∩~La:
.βX4(4V≠?77.sQαπrβ↔cS⊗	β∂#.≠-β≠␈⊃βπ;␈##↔Iπ+O↔Iπ;'31εCπCC.qβπW&{7πSN≠π33JaβO'v≠∃βSF)β?W'βWP4PK≠'3*↓"ε∩$2&1:$jA%β>K31β⊗)α⊗:$*K↔⊃εQβSF)βOS∂∪Q1β&CWMβf{∂/'v9β?W"βπ31ε{S#↔⊂h('[/∪O'?w→β?→π##∃βπ∪?∨K∞il4+N1β↔;&+K≠πNa#??}≠#π9b∩ε∩∩4J1:Rm↓	%β&C↔9β⊗+∨'8hP'CKNsQ!
≡{7↔?v)β↔3≡)β7W∨!β∃π+O';:βS#∃πβK?∨⊗59	f≠K3→KX4('⊗+SWKsX4+↔v!l4(hS∂?7n+;Qβ6KKOQε#↔S↔⊗k';∃εK→β←*;K∃β⊗+C↔π&K;≥lhSπ∂←+A↓
ε;↔Qβ&C∃β'wβWQβ↔+≠≠↔∩βπ∨πNql4+>C'3∃π#KW∃ε#=β.;'9↓↔∪↔O∂∞s;';:⊂4)↓α↓β∂#∃{';∂G∪]l4R↓↓↓βN1β∂#∪k3→β&C↔9β⊗+∨'9εc?∨W≡+∩␈≠∞cO∃mπ∪↔C↔∂#';≡}3π3O+Yβ∪?v)↓K/≠∂π;vK;≥	Zβ↔;⊂hQ↓↓↓ε+3O∃εK→β∂G⊃u	m∩βS#↔rβ↔∨Nq↓C∂∪π5	h('∂}k7↔;"β≠?Wv!β¬βεKπ7/#↔I↓jiβ'∪.sS'≠Jβ'QlhP'OS∃{';∂G;1mhP'←#Nc∃βO'⊃u	↓∩β∪=β≡CJ␈3␈↓#OS∩Il4(NK→β↔∂)#OS∃YEβSz↓Nu1∀∩ε
	JβS#↔rβ↔∨Nq4(J↓↓↓βf{∨WO."␈SK.)mβK/β↔πSNs≡␈S↔+∃mβ&{;∃↓↔∪↔O∂∞s;';:⊃mβ↔v 4('.cO∃β⊗+∨'9πβK';"A
W;⊗+∂?∨vKk↔⊃πβπKπn+S↔Iε{9αJ,qβ∂?nkπ;⊃R↓	3O'⊃3∂Kf1%mβ⊗+SWKsYβ↔;#X4)↓α↓β↔;"↓Cπ⊗5	lhS↔;⊃α∪K↔O≡;;'v9	l4Ph+∂?nk↔;Qεs?]β≡+QβWαβS#∃∧b>≥β6K3∃1π;#'∂BβK↔∂␈∪∪Mβ&C∃β∂}k7π;'→β'O∨+↔⊃lhS'→/∪↔C↔∂#';≥π##↔9ε∪↔∨'r↓∂K.S∃αdz≥	hQ↓↓↓ε≠?77.sQβ?ε+;';:β'9α∀	β7?&)β←?r;Qβ∂⊗+πS∃π##∃β6K3∃1π≠=β←*β∪=βO!β#↔⊗)l4)α↓↓β∂}k7↔;"α≠'K∨!1β∂F+∂-β&yβO↔*βS#π"βS#∃εc?≥β6K3∃β&{↔O9?!βπ3⊗+π∪eε+c'O#X4)↓α↓β3?}[WA#⊗≠#π9b∩2>≥u"6A	f33π≥KX4)↓α↓β'→+≠3π:βS#↔rβ↔∨Nq↓OF{W3⊃ε∪∃β'rβπ∂←+Aλ4PKCK'w!!
SF)β≠'f)α2>8rR6Aε3K↔∞#eβ↔FKOSMαCC?O≡K3eεMβSF)βK↔∨+3Qβ}1β¬	f≠K3→`h(%↓α↓↓OO≠S↔5ε≠KπOBI9↓αN{UβOF{W3⊃ε+'S#/⊃βKWrβS#∃πβK?∨⊗5β'rα
ε∞]*Aβ7}#∃1	f≠K3→`h(%↓α↓↓	#&yβK↔&yβS#*βSKπw≠π∂SN{;Mβ⊗+∂?K&+⊃β' file), or delete",crlf,
	    "the file LOG.TMP before starting up the program.",crlf2);
	return;
    end "should be in backup"
    else begin comment backup doesn't already exist;
	 if enterfail(bchan,"LOG.TMP") then return; close(bchan);
         comment close the channel, and just leave the empty file around;
    end;
end "create LOG";

comment now, open the file in read-alter mode, by doing a LOOKUP then an ENTER;
if lookupfail(bchan,"LOG.TMP") then return # e.g., asked for BACKUP, but file doesn't exist;
enter(bchan,"LOG.TMP",flag);
if flag then begin print("whoa, I can't open the LOG file.",crlf); return; end;

if repeating then begin "check for directory page"
	str←input(bchan,bfflf);
	if equ(str[1 to 10],"COMMENT ⊗ ") then 
		while brchar≠iff do stt←input(bchan,bff) comment skip directory page;
	else useti(bchan,1) # comment reset to start of file;
end "check for directory page";

comment at this point we have entered the output file. Leave it open for writing,
	to ensure that no-one else breaks thru;
comment next, get the input;
if lookupfail(ichan,"ADDFIL.DSK") then return;
print("Hello! Please wait a minute while I read in the address file....",crlf);
addfilin;
comment the channel `ichan' will be used by other parts of the program, for input;
recd←fixd←chgd←calrecd←0;
linecnt←charcnt←0;
while true do begin 
case resp(crlf&"  CSREPORT system:  What can I do for you? ",cshelp,csopts) of
		begin done;
		update;
		orders;
		receive;
		mail;
		send;
		adjust;
		end;
end;
if afchanged and ynresp("May I record all of today's transactions permanently"&
	" on file ADDFIL.DSK?")=1 then
	begin
	addfilout # write everything out to ADDFIL.TMP;
	comment now, shuffle the files, saving a backup;
	comment SAIL requires thatwe do a lookup before a rename;
	comment new protection set to '277 (delete protect, no read/write by other users);
	commeNt fiRst, Delete the previous backup, if one exists;
	clkse(ichan); lookup(ichan,"ADDFIL.BKP",flag); if ¬flag then
		id renamefail(i@
QC\Y9kYXX@YMYC≤RAiQ∃\AeKQkeL@AIKY∃iJA←1HAmKIcS←\l~∀∪S_AY←←-kaMC%XQSG!C\XE¬		
∪0]	'⊗λRAiQ∃\AeKQke\v4∀∪SL↓eK]C5KMCS0QSGQ¬\XEβ⊃	
∪_9¬↔ D0Ndnn1MYCN$AiQK8AeKiUe\@F↓←YH@9	'⊗@d@]¬↔@v~∀∪%LAeK9C[KM¬SXQ←=←GQC8XEβ	⊃
∪_]⊃'⊗DX≤dnnY→YCNR↓iQK\↓eKikI\@F@9)≠ @d@]	',v~∀∪∃]H~∃∃YgJAAeS]h E≥↑A
QC]O∃fA[C⊃JAi↑↓β		
%_]	',AiQSLAiS[∀\DYGIYLRv4∀~∃G=[[K]PAαAG!C]OJ↓[CIJ↓QKeJ0Aβae%X@br``XAg<AiQCPAiQJ↓Y←NA→SYJA%fAgCYKHAS8ACYX↓GCgKL\~∀@@A∪h↓kgKH↓i↑Ag¬mKHA=]YrA%LAoJ↓QCHA	KK\AIk]]S9NAS\↓ECGWU`A[←⊃J@QR9J\XA1←Okg∃HRv~)EKOS8@EgCYJAY←≤D~∀∪
Y←gJ!EGQC8Rv@~(∪Y←←-k`QE
QC\X	%!∨I(]→∨≤DYMY¬NRv@4∀∪SLMYC≤AiQK8~∀∩∪%LAeK9C[KM¬SXQE
QC\Y9kYXX@YMYC≤RAiQ∃\AeKQke\@AIKY∃iJAaIKlAm∃egS←8v~∀∪%LAY←=WkaM¬SXQE
QC\X	→∨∞]Q≠ DR↓iQK\↓eKikI\v~∀%SLAe∃]C[K→CSXQ	GQC\0E%!=%(]→=∞DXN@nnYM1CNRAQQK\AIKike8@FA→=∞])≠@@2A%∃!∨%(9→∨∞v4∃K]HEgCm∀AY←Nλv~∀∩4∃SLAIKGHW
CYeK
HWCELQMSq⊂RWGQ≥H|`AQQK\~)aeS]PQGeY_XE'+5≠β%2↓←LAi=ICrOLAMS]¬]GSC0AieC9gCGi%←]ft4∀@@@HDYGYLQeK
H↑b`@RXDAIKGKSYKHAS8AaCs5K]if0~∀@@@HDY
mLQG¬YeKG⊂↑b``$XDA←_AoQS
PAoCLAMe←4AeKg%IK]iLA←LA
CYSM=e]SB8~∀@@@HDY
mLQM%qH↑b@`RXD↓oCfAMkEie¬GiKH↓Me←Z↓CGG←U]ifA⊃kJAi<ACIUUgi[K9ifA←H~∀∩@@AIK1KiS←9fAMe=ZAiQ∀A[CS1S]NA1Sgh\4∀@@@HDYGYLQGQ≥H↑b`@RXDA9KnAG!CeOKLAoKe∀AgK]PA←kh↓←\AS9m←SG∃f\DY
eYLRl~∀~∃∃]H@EQQJ1aIKgSI∃]hDv4∀→G←[5K]hAQQJAaI←OeC4AgiCIifAQ∃eJ@QMKifAMieS]≤AG←]MiC]iLXAS]
YkIS9NA⊃1!fRv4∀~∃i¬E>NbDv~∃Y→>Nbdl~∃MM|Nbhv4∀~∃g∃iM←e5ChP`0dR@F↓M←e[¬hAgQ=kYHA¬YoCsLAEJAIKike9KHAi<AiQSLASLA
QC]O∃Hv~∀4∃i?G¬YXP`0E	β)∀DRv~)I?hA5←H@fD@V@bl~∃[>!i?hA⊃Sl@fDRA[←⊂@bd@,bv~∃⊃CiK?
mfQH$LD@D→[←]i!g7[:_D@DM
mfPbdlhVAPAISlbdRv4∃S]m=SGKI¬iK?[=]iQgm[:MGYfQHR→GmfPDrlhV↓hAISX@bdRl~∀~∃5iCaKla;>A
mgSp E∂∨	5∨λDRlA[iCAK6c;|@NbnFAgKPAi↑A5CWJAQQJACAaiJA
CYXv4∀~∃]UYYg>≤`vAM=dAS>DAgiK@@bAk9iSX@D`AI↑↓]kYYM?]kY1fM]k1Yfv@4∃]kY1g?]k1Yg6b↓i↑@lPa:v~(~∃EY¬]Wg>λ@@@@@@@@@@@@@@@@@@@@@@@@Dv~∃	YC]WM?EYC9WfME1C]Wfl~∀~∃
gQKYA>E⊃K1Y↑XAQQSfA%fAs←UdAMe%K]IYdAπ'%∃!∨%(↓gsgi∃Z\~∃QQKeJ↓CeJAMSpAW%]IfA=LAiQ%]OfA$ACZAAe←Oe¬[[KH↓i↑AI<AM←d↓s←jt4∀∪+!⊃β)
∪1←←VA¬hAC]⊂AaKe!CafA
QC]O∀AiQJ↓[CSY%]NAY%gh\~(∪∨%	∃$∪
S1JACo¬rAC]dA←eI∃efAi!ChAQ¬mJAE∃K\Ae∃GKSm∃H~∀∩%M←dA∧AOSm∃\A[←9iPOf↓YSgh8~∀∪%∃π∪-∀∪%KG=eHAa¬s[K]QfAeK
KSmK⊂XA←d↓CIUkMhACG
←k]iL\~∀∪5β∪_∪AeKaCIJA[C%YS]N↓YCEK1fAC]⊂←←dA%]m←S
KfAC9H~∀∩%ESYYLA←LA1CIS]≤AM←d↓KmKee←]JAQQChA!CfA←IIKeK⊂~∀∩∪IKa←eQfAMe=ZABA≥SmK\↓[←]i OfAY%gh\~(∪'≥⊂∪!eKACeJA%]m←S
KfAM=dASg=YCiK⊂AECG,[←eI∃dAeKEkKgiLX~∀∩%S]GYUIS]N↓←eIKIfAMe=ZAaK=aYJA9←hA←8AiQJ↓[CSY%]NAY%gh\~(∪β	∃U'(@A
QC]O∀AiQJ↓eKG←IIfAS8AiQJ↓S]mK9i←er↓YSgh0Ai↑AIKMYK
h~∀∩%GQC]≥KfAS8AiQJ↓]k[E∃dA←L↓G←aS∃fA←\↓QC]H8~∃/Q∃\A∩A¬gVXA↓/QCh↓GC\A$AI↑A→←dAs=j}NX↓Ukgh↓isaJ↓iQJA→Segh↓iQeK∀AYKiQKef~)←LA←9JA←L↓iQKg∀AMk]
iS←]LAC]H↓QShAα≠πKKN∨∃α∀*RVJr↓s∂Irq↓"g␈)β∂πrβπ3Oxh+Sgε)β7?⊗)βS#∞qβS#⊗+∃β3/#S↔K~β'→βN{Uβ←∞sQβSzq%αSF)βWO-⊃∨Mβn;Wπbaβ←#L≠ 4+≡{;SπNsMβ7␈∪∃β'v3?K7∂#'?9bβ'Mβ6K3∃α∀*B>J"rRbR\">
∩≥~Ju9∪X4(4V3';∪F+3AFz∩%βπjβ∨?'v9βS=π#Ceβ&yβ'∪.sS'≠Jβπ9β∞#∪K↔∨≠↔∃β6{Iβg␈)9αSOβ∃βSF(4+≠O3∃7∂FKπ∂&+Iβ#∂≠!β∂|#∃β'2βg?Uε[;?]εKQ1β␈⊃βSgε)↓eeKIeβ'2βS#∀hSπ∪∪⊗+OO↔*β'Mβv{Qβ?rβ?WIεkπ'3Ns≥β3O≠Qβπv!β;?"β↔'v9β';≡+KS↔"β';Szβ'Q8hR'	βN{Uβ∪}q∨Qβ↑s?]β&C∃β#∂≠!β∂}#∃1β'KC∃αEBbbaε;⊃αJβ←'3bβSKeε	β;πn)βO↔∂∪∂ 4V{→βSF)β←#}c∃β≠Nc∃9αN1βg?*βSgC*β+WO"↓s∂Irβ;?]bα%β←Nc1β∨zβ?9β&yβO?n+S#'v84+↔g≠∃9α≡K;∂∃εCπO!ε≠?∪↔~βO?7/#'7↔~β∂#πv;∃1βN{UβOF{W3⊃ε#?Wf+∂#↔≡YβS#(h+π∪'∪↔OO.)β;πn)α%β6K;⊃βNqβ∂π≡)β'QεKMβSF)β←K}s≥βC/∪O?9r⊃l4(hS≠';&C↔3A∃y
%β∞iβSKNK;≥β&yβ'∪.sS'≠Jβπ9β∞#∪K↔∨≠↔∃β6{Iβg␈)9αSOβ∃βSF)β;πn(4+?∩βπ;eπβπKQε{→βSF)β;πn)1βπv!α%β>K31β≡C?]βN{Uβπfaβ;πn)β3'v+Mβ'rβS#∃ε3'3∃h+←#N≠!β∂}sSπ'rβS#π"βO↔G.+;∂∃ε{→β∂FKπ∂&+KM↓FK;∂3.#';≥ε∪3π;ZβOCπ≡+Mβ'ph+S#*β7'∪&c∃β?2βS#∃π≠↔GW.s∂∃1εK→βg␈)βWO*βS#↔jI9α;␈#∃βSFQα%π;'31π≠↔πK≡@4+?vceβSF)β≠'↔≠Qβ3Ns∃β?2β↔π∂Bβπ∪∪⊗+OMβ}qβS#*β7π'fK;≥βfKOQ9∪X4(4V≠?∪↔F+3B⎇∃#gC∃βc∂Iyπ#=βK.S↔∂Qπ##'Mε+;SKJβπ;⊃ε33WOBβ'Qmε{IβSOβ∃α
f≠Iyβ6{H4+v{K7πbβ↔;S↔I1α→f≠Iyβ6{IβSF)β≠K.)β3'∨!1α9f≠Iyβ6{IβSF)α>:∩β3'O"aα5s∨⊃yβ≠␈⊂4+SF)αεJ∧	β3'∨!1α¬f≠Iyβ6{IβSF)ββπ/#?7π&K
≥βfKOQ1∧⊃s∂Irβ≠?Iπ##∃βεWS?nS'hS7'∂⊗{≠'∂' list.";

updhelp←"The UPDATE routine should be used to make all changes to the
address file, since editing with E is risky. To insert a new entry,
type INS and follow instructions. To modify an existing entry, type
MOD and see what happens.  To delete an entry, type DEL (but don't delete
anybody who has orders outstanding on some ORDERS file -- it's best to
delete only after MAILing all orders).  To simply look at an entry,
type LOOK.  Type only <cr> when you want to quit updating.";

ordhelp1←"The ORDERS subsystem is used to record orders received from a
given month's mailing list. The information you enter is recorded in
the file ORDERS.XXX where XXX is JAN,FEB, ..., or DEC.";

ordhelp2←"In ADD mode, you can add new orders to the file. In CHANGE, you
modify a previous entry for a prticular customer, adding or deleting
reports from his list.  You identify a person by his hashcode,
and then say which reports he has ordered.  For example, if he 
wants reports 1,9,A, and G, you can type 19AG or 1AG9, etc.";

mailhelp←"The MAIL subsystem is used to prepare mailing labels and/or
invoices for monthly report distribution.  Two kinds of mailing labels
are presently provided for: AVERY (34 characters wide, one printed
at a time) and CHESHIRE (34 characters wide and three printed at once).
When mailing an abstract list, type ABS<cr> and I will prepare the file
LABELS.TMP containing mailing labels for everyone on the mailing list
except codes N, M, or A.  When mailing invoices and reports, type
REP<cr> and give the necessary information about the relevant month's
reports.  The ORDERS.XXX file for that month will be used to specify
all orders, and the activity records for all customers are shifted
left one position unless you request otherwise.";

acthelp←"Each addressee has activity codes representing the number of orders
he made during the last 12 mailing periods. If you type Y<cr>, the
present mailing is considered a new mailing period.  If you type
N<cr> or <cr>, the present mailing is considered to be combined with
the previous mailing period.";

sendhelp←"Give the short name of a report requested, e.g. CS287 or AIM239 or HPP772,
followed by F if it is microfiche, e.g. CS287F.  But if no more 
reports are requested by this customer, just type <cr>.";

splithelp←"If you type Y, I will send all the copies of this report which we have
on hand, with a note mentioning that we don't have the rest. If you type N
I won't fill any of the order (i.e., as if we were completely out of it),
and I'll send a sorry letter saying so.";

onhandhelp←"The ADJUST routine should be used to make all changes to the
inventory file, since editing with E is risky. To change the quantity
for a report, just enter its short name, e.g. CS287 or AIM 239F
or HPP772. You will be prompted for the new total of copies on hand.
If no more reports are to be updated, just type <cr>.";

namehelp1←"I want to know whether this report can also be referenced by a different
name. For example, some CS Reports also have AI Memo numbers.  If this 
report has such an alternate name, type YES, otherwise type NO or <cr>.";

namehelp2←"Type the alternate name for this report.";

yesnohelp←"Answer YES,SIR<cr> or NO,SIR<cr> or some abbreviation.";

rhelp←"At this point I need to know the names and numbers of the reports,
in order to identify them meaningfully on the invoices to
be written. Here are three examples of the form I want you to type:
STAN-CS-76-562*KNUTH,TRABB PARDO,EARLY DEVEL OF PROG LANGUAGES
AIM-282*TAYLOR,SYNTHESIS OF MANIPULATOR CONTROL PROGRAMS(THESIS)
HPP-77-5*STEFIK,MARTIN,A REVIEW OF KNWLDG-BASED PROBLEM SOLVING
Note that there should be an asterisk (and no space) between the report
number and the author name(s). The title has to be abbreviated so that
everything fits on one line, even when I substitute the word `(microfiche) '
for the asterisk. If the report is a thesis, follow the title by `(THESIS)'.";

onhand_filled_msg←"Whoops! The ONHAND.DSK file is as big as it can get, so I won't
be able to enter this report into the file.  To increase the table size 
one may simply recompile CSREPT with bsize defined larger.";
comment Set breaks, open channels, call main procedure, end gracefully;

setbreak(btt,lf,null,"ISK") # for ttin, translates lower case to upper;
setbreak(bfflf,ff&lf,null,"IA") # for scanning lines of character files;
setbreak(bff,ff,null,"IA") # for scanning pages quickly but carefully;
setbreak(blf,lf,null,"IA") # for scanning lines within a page;
setbreak(babs,"|",null,"IS") # for separating substrings delimited by |;
setbreak(bast,"*",null,"IS") # for separating substrings delimited by *;
setbreak(bpar,")",null,"IA") # for substrings ending with );
setbreak(bsp," "&tab,null,"IS") # for scanning to blank or tab;
setbreak(bch," "&tab,null,"XR") # for scanning past blanks and tabs;
setbreak(blfdollar,"$"&lf,null,"IS") # for scanning for price or eol;

eof←0;
open(ichan←getchan,"DSK",0,19,0,450,brchar,eof) # channel for character and addfil input;
open(ochan←getchan,"DSK",0,0,19,0,0,eof) # channel for character output;
open(oochan←getchan,"DSK",0,0,19,0,0,eof) # for backup invoices;
open(ooochan←getchan,"DSK",0,0,19,0,0,eof) # for addfil output;
open(lchan←getchan,"DSK",0,0,19,0,0,eof) # channel for mailing label output;
open(bchan←getchan,"DSK",0,2,1,450,brchar,eof) # for LOG file;

if ¬inuse("DIALOG.TMP") then begin

setprint("DIALOG.TMP","B");

mailed←sended←afchanged←false;

the_president;

print(crlf,"See you later. Be sure to xspool a copy of DIALOG.TMP,",crlf,
       "which records what happened today.");

setprint(null,"N"); comment closes the dialog file;
close(bchan) # close the LOG file (which is robust in case of system crash);

close(ichan);
close(ochan); close(oochan); close (ooochan); close(lchan) # just in case I forgot;

end;
end "report";