{++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ** PROGRAM TITLE THE RECIPE SYSTEM ** ** Translated by: Raymond E. Penley from the BASIC ** version into Pascal. ** ** DATE WRITTEN: 23 FEB 1980 ** ** WRITTEN FOR: Computer hobbyists ** ** PROGRAM SUMMARY: ** ** The recipe system stores recipes and retrives recipies ** by means of a numeric key that represents the foods ** used in the meal. Foods are divided into four ** categories according to their nutritional value. ** For more comments see the original program. ** ** INPUT AND OUTPUT FILES: ** RCPDAT.XXX and RCPDAT.YYY ** - the DATA and the backup files ** RCPDAT.MST - the statistics file ** ** MODIFICATION RECORD: ** 28 Feb 80 - ** 2 Jun 80 -Rewritten for Pascal/Z v 3.0 ** 8 Jun 80 -Rewrote SCAN ** ** ORIGINAL PROGRAM: ** T.G.LEWIS, 'THE MIND APPLIANCE' ** HAYDEN BOOK COMPANY ** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++} PROGRAM RECIPE; CONST default = 80; (* Default length for strings *) str_len = 73; (* Length of a recipe line plus one char *) StrMax = 255; (* Max Length of strings allowed *) EOS = '|'; (* End of String marker *) Master = 'RCPDAT.MST'; Tab20 = 20 ; Tab15 = 15 ; INPUT = 0; (***** PASCAL/Z ver 3.n *****) TYPE ALFA = STRING 10 ; BYTE = 0..255; LINE = string default; Mstring = string 255 ; DataType = record MR, (* MaxRecords *) CR : integer; (* Curr_Rcds *) F1, (* current_ID *) F2, (* backup_ID *) date : string 14 (* last_update *) end; S$0 = STRING 0 ; { zero length string } S$255 = STRING 255 ; { max string length } VAR adding_recipies, (* adding recipies state flag *) comanding, (* Command mode flag *) done (* Program execution flag *) : boolean; bell, (* ASCII bell char *) ch, command : char; data : datatype; End_of_File, (* End of File flag *) End_of_Text (* End of Text flag *) : boolean; error_flag : BYTE; CRT_width, (* Width of video display *) Curr_Rcds, (* No. of current active records *) Hash, (* Computed Index value of Recipe *) ix, (* global indexer *) Last, (* length of last line read *) MaxRecords, (* Maximum records allowed *) TTY_width (* Width of teletype device *) : integer; Last_update : string 14; (* date of last file update *) matrix : packed array[1..5] of LINE; (* File Identifiers *) current_ID, (* Current file ID *) backup_ID :string 14; (* Back up file ID *) (* File descriptor *) stats :FILE of datatype; {$C- [ctrl-c checking OFF]} {$F- [floating point error checking OFF]} {$M- [integer mult & divd checking OFF]} (*---Required for Pascal/Z supplied string functions---*) FUNCTION LENGTH(X: S$255): INTEGER; EXTERNAL; PROCEDURE SETLENGTH(VAR X :S$0; Y :INTEGER); EXTERNAL; (*----------------------------------------------*) (* DISK I/O *) (*----------------------------------------------*) Procedure OPEN_MASTER; begin (* OPEN file RECIPE.MST for READ assign stats *) RESET(master, stats); READ(stats, data ); with data do begin MaxRecords := MR; Curr_Rcds := CR; current_ID := F1; backup_ID := F2; last_update := date end(* with *) end; Procedure UPDATE_MASTER; begin (* OPEN file RECIPE.MST for WRITE assign stats *) REWRITE(master, stats); with data do begin MR := MaxRecords; CR := Curr_Rcds; F1 := current_ID ; F2 := backup_ID ; date := last_update end(* with *); WRITE(stats, data ) end; Procedure GETLINE(VAR fx : TEXT; VAR INBUFF : LINE ); { This Procedure gets a line of text from a disk file. Returns: End_of_Text = true if the input buffer length exceeded. End_of_File = true if EOF INBUFF = input buffer } VAR CH : CHAR; ix, length : integer; begin length := 0; End_of_Text := FALSE; SETLENGTH(INBUFF,0); WHILE NOT EOF(fx) AND (CH <> EOS) DO begin If length < str_len then begin(* valid *) READ(fx, CH ); length := SUCC(length); APPEND(INBUFF,CH) end(* If *) ELSE End_of_Text := TRUE; end(* WHILE *); If length >= last then last:=length Else REPEAT APPEND(INBUFF,EOS); last := PRED(last) UNTIL last=length; End_of_File := EOF(fx) end(*---of GetLine---*); Procedure PUTLINE( VAR fx : TEXT; VAR this : LINE ); { This Procedure puts a line of text to a disk file } VAR CH : char; pos : integer; begin pos := 0; REPEAT pos := SUCC(pos); CH := this[ pos ]; If CH <> EOS then Write(fx, CH) UNTIL (CH = EOS) OR (pos = str_len); Write(fx, EOS) (* Mark the End of String *) end(*---of PUTLINE---*); Procedure PUT_RECORD( VAR fx : TEXT; VAR Index : integer ); VAR jx : integer; begin Writeln(fx, Index:5); For jx:=1 to 5 do PUTLINE(fx,matrix[jx] ); end(*---of PUT_RECORD---*); Procedure GET_RECORD(VAR fx : TEXT; VAR Index : integer ); VAR JJ : integer; begin READLN (fx, Index); FOR JJ := 1 to 5 DO GETLINE(fx,matrix[JJ]); end(*---of GET_RECORD---*); (*----------------------------------------------*) (* CONSOLE I/O *) (*----------------------------------------------*) Procedure KEYIN(VAR CIX : char); EXTERNAL; (*---Single char input directly from keyboard---*) Procedure PRINT(this : Mstring); (* Print the string 'this' until EOS *) VAR CH : CHAR; pos : integer; begin pos := 0; REPEAT pos := SUCC(pos); CH := this[ pos ]; If CH <> EOS then Write(CH) UNTIL (CH = EOS) OR (pos = str_len); Writeln end(*---of PRINT---*); Procedure SCAN( VAR Arg_string : LINE ; count : integer ; VAR status : BYTE ); (*----------------------------------------------*) (* version: 3.1 /8 JUN 80/ by R.E.Penley *) (*----------------------------------------------* ** Scan will scan your input line and return: STATUS: 0 -OK, valid inputs 1 -an attempt was made to exceed "count" characters - so I truncated the string at count chars for you. 2 -an invalid character was detected. You figure out what to do with it! LENGTH(arg string) = 0 means a null string input. ** Valid Alphanumeric chars are the ASCII char set starting at the space [ CHR(32) ] and ending at the tilde [ CHR(126) ]. *----------------------------------------------* GLOBAL StrMax = 255; BYTE = 0..255; LINE = STRING Default; *----------------------------------------------*) VAR loop : (scanning, found, notfound); ix : 1..StrMax; begin { return status = 0 if no errors detected. } status := 0; { return status = 1 if requested length is exceeded } If LENGTH(arg_string) > count then begin status := 1; SETLENGTH(arg_string,count) end; loop := scanning; ix := 1; While (loop=scanning) do { return status = 2 if any invalid chars found } begin If ix > LENGTH(arg_string) then loop := notfound{excellent - no invalid chars} Else If arg_string[ix] IN [' '..'~'] then{good show - keep going} ix := SUCC(ix) Else begin loop := found{invalid char}; status := 2 end end{while} End(*---of SCAN 3.1---*); (*----------------------------------------------*) (* UTILITY ROUTINES *) (*----------------------------------------------*) Function YORN : boolean ; { YES/NO INPUT MODULE Returns: TRUE FOR 'Y' or 'y' INPUT FALSE FOR 'N' or 'n' INPUT } VAR ans : ALFA; valid : boolean; begin REPEAT valid := true; READ(ans); CASE ans[1] of 'Y','y': YORN := true; 'N','n': YORN := false; Else: begin valid := false; Writeln(BELL, 'Please answer ''Y'' or ''N'' ') end end{case} Until valid{response} End(*---of YORN---*); Procedure CLEAR; (* Device dependent procedure *) begin Write( CHR(26) ) end; Procedure SKIP(L1 : integer); VAR ix : integer; begin FOR ix:=1 to L1 do Writeln end; Procedure PAUSE; CONST sign = 'Type return to continue:'; VAR dummy : char; begin SKIP(4); Write(sign); Readln(dummy) end; Procedure BREAK; begin CLEAR; SKIP(5) end; Procedure DRAW(picture : Mstring; count : integer ); { Draw a picture count times } VAR ix : integer; begin FOR ix:=1 to count DO Write( picture ); Writeln end(*---of DRAW---*); Procedure ShowRecipe; VAR JJ : integer; begin FOR JJ := 1 to 5 DO PRINT(matrix[JJ]) ; Writeln end(*--of ShowRecipe--*); Procedure Display_One(VAR Index : integer); begin Writeln; Writeln( 'Recipe #', Index:5 ); Writeln; DRAW( '- ', 20); Writeln; ShowRecipe; skip(4) end(*---of Display_One---*); (*----------------------------------------------* * ADD MODULE * *----------------------------------------------*) {$C+ [ctrl-c checking ON]} Procedure InputFeatures(VAR I : integer); (****************************************** * Input Features of Recipe * ******************************************) (* RETURNS: Hash value computed for various choices **) CONST Msg1 = 'None of these' ; VAR F, D, V, P :integer; Function QUIRY(X2 : integer) : integer; VAR ix : integer; cix : char; begin REPEAT Writeln; Write('Enter Choice (1 to', X2:2, ') '); KEYIN(cix);write(cix); ix := (ORD(cix) - ORD('0')) UNTIL (ix>=1) AND (ix<=X2) ; QUIRY := ix end; begin Writeln; Writeln( ' Enter number of choice :'); Writeln; Writeln( ' ':Tab15, 'Fibre Foods' ); Writeln; Writeln( ' ':Tab15, '1. Bread (flour)'); Writeln( ' ':Tab15, '2. Oats' ); Writeln( ' ':Tab15, '3. Rice'); Writeln( ' ':Tab15, '4. Corn' ); Writeln( ' ':Tab15, '5. Macaroni'); Writeln( ' ':Tab15, '6. Noodles' ); Writeln( ' ':Tab15, '7. Spaghetti'); Writeln( ' ':Tab15, '8. ', Msg1 ); F := QUIRY(8); BREAK; Writeln; Writeln( ' ':Tab15, 'Protein' ); Writeln; Writeln( ' ':Tab15, '1. Beef'); Writeln( ' ':Tab15, '2. Poultry' ); Writeln( ' ':Tab15, '3. Fish'); Writeln( ' ':Tab15, '4. Eggs' ); Writeln( ' ':Tab15, '5. Beans'); Writeln( ' ':Tab15, '6. Nuts' ); Writeln( ' ':Tab15, '7. ', Msg1 ); P := QUIRY(7); BREAK; Writeln; Writeln( ' ':Tab15, 'Dairy' ); Writeln; Writeln( ' ':Tab15, '1. Milk'); Writeln( ' ':Tab15, '2. Cheese' ); Writeln( ' ':Tab15, '3. Cottage Cheese'); Writeln( ' ':Tab15, '4. Cream' ); Writeln( ' ':Tab15, '5. Sour Cream'); Writeln( ' ':Tab15, '6. ', Msg1 ); D := QUIRY(6); BREAK; Writeln; Writeln( ' ':Tab15, 'Fruits and Vegetables' ); Writeln; Writeln( ' ':Tab15, '1. Citrus'); Writeln( ' ':Tab15, '2. Melon' ); Writeln( ' ':Tab15, '3. Juices'); Writeln( ' ':Tab15, '4. Greens' ); Writeln( ' ':Tab15, '5. Yellows & Reds' ); Writeln( ' ':Tab15, '6. ', Msg1 ); V := QUIRY(6); CLEAR; {*****************************************} { Compute the index value by assigning } { a weight to each digit in the set. } {*****************************************} I := 252*F + 36*P + 6*D + V - 295 {******************************************} end{of InputFeatures}; Procedure InputRecipe; (*---------------------------------------* * Input individual recipies * *---------------------------------------*) LABEL 99; (*---EXIT---*) CONST prompt = '>'; VAR state : (absent, done, adding) ; ix, jx : integer; temp : STRING 14; One_Line : LINE; YES : boolean; (* File descriptors *) current, backup : TEXT; PROCEDURE CORRECT; CONST question = 'Are there any corrections to be made'; msg1 = 'Enter return if correct or Reenter the line'; begin REPEAT BREAK; Writeln(bell,' ':(TTY_width DIV 2) -10, 'HERE IS YOUR RECIPE'); Writeln; ShowRecipe; Writeln; Writeln(question); YES := YORN; If YES then begin BREAK; Writeln(msg1); Writeln; For ix:=1 to 5 do begin REPEAT PRINT(matrix[ix]); SETLENGTH(one_line,0); READLN(one_Line); SCAN(one_Line, str_len - 1, error_flag); If (LENGTH(one_Line) > 0) AND (error_flag=0) then begin APPEND(one_Line,EOS); matrix[ix] := one_Line end; If error_flag IN [1,2] then CASE error_flag of 1: writeln('Invalid length, please reinput'); 2: writeln('Alpha numerics only, please reinput') End{case} Until error_flag=0; end{for} end(* If *) Until not YES end(*---of Correct---*); Function adding_desired : boolean ; CONST addquest = 'Do you want to ADD recipies? '; begin PAUSE; BREAK; Write(addquest); adding_desired := YORN; CLEAR end; begin(*---InputRecipe---*) If not adding_desired then{EXIT}goto 99; adding_recipies := true ; state := adding ; (* OPEN file backup_ID for WRITE assign backup *) REWRITE(backup_ID, backup); (* OPEN file current_ID for READ assign current *) RESET(current_ID, current); {$C- [ctrl-c checking OFF]} If NOT EOF(current) then begin(* COPY current to back_up *) ix := 0 ; While ix < Curr_Rcds do begin ix := SUCC(ix); GET_RECORD(current,hash); PUT_RECORD(backup,hash) end(* while *) end(* COPY current to back_up *); {$C+ [ctrl-c checking ON]} (*---Input/Enter additional recipies until done---*) (*---or curr_records > Max_Records allowed ---*) REPEAT If Curr_Rcds > MaxRecords then state := done Else begin(*---add more recipies---*) Writeln('Identify Recipe with features. First '); InputFeatures(HASH); BREAK; Writeln('Now Enter 5 lines of the recipe'); Writeln; For jx := 1 to 5 DO begin REPEAT write(prompt); SETLENGTH(one_line,0); READLN(one_line); SCAN(one_Line, str_len - 1, error_flag); If error_flag IN [1,2] then CASE error_flag of 1: writeln('Invalid length, please reinput'); 2: writeln('Alpha numerics only, please reinput') End{case} Until error_flag=0; APPEND(one_Line,EOS); matrix[jx] := one_Line end{For}; Correct(* if required *); Curr_Rcds := SUCC(Curr_Rcds); PUT_RECORD(backup,hash); If not adding_desired then state := done; end(*---add more recipies---*) UNTIL state<>adding; (*--------------------------------------------*) (* SWAP file ID`s *) (* Back Up file is now the Current file *) (*--------------------------------------------*) temp := backup_ID; backup_ID := current_ID; current_ID := temp; UPDATE_MASTER;(*--status file--*) 99:(* Come here if do not desire to add *) End{*--of InputRecipe--*}; (*--------------------------------------*) (* DUMP/FIND MODULE *) (*--------------------------------------*) PROCEDURE FILE_SCAN ; (* GLOBAL MaxRecords = maximum allowed records Curr_Rcds = # of recipes in file *) VAR state : (absent, found, searching) ; Rcds, index : integer; fa : TEXT; (* FCB. File descriptor *) Procedure DUMP; (**********************************) (* OUTPUT all Recipes from file *) (**********************************) begin REPEAT If Rcds > Curr_Rcds then state := absent Else begin Rcds := SUCC(Rcds); GET_RECORD(fa,hash); Display_One(hash); PAUSE end(* else *) UNTIL state<>searching end(*--of DUMP--*); Procedure FIND; (************************************) (* Lookup recipes from file *) (************************************) begin {$C- [ctrl-c checking OFF]} InputFeatures(Index); REPEAT If Rcds > Curr_Rcds then state := absent Else begin Rcds := SUCC(Rcds); GET_RECORD(fa,hash); If HASH=Index then begin CLEAR; Display_One(hash); PAUSE end end(* else *) Until state<>searching end(*--of Lookup--*); {$C+ [ctrl-c checking ON]} begin(*---File_Scan---*) CLEAR; state := absent; If adding_recipies then{read in new stats} OPEN_MASTER; (* OPEN file current_ID for READ assign fa *) RESET(current_ID, fa); If NOT EOF(fa) then If Curr_rcds=0 then state := absent Else begin state := searching ; Rcds := 1 ; CASE command of 'O', 'o': DUMP; 'F', 'f': FIND End{case commmand of} end(* else *); If state=absent then begin BREAK; Writeln('That''s all the Recipes on File') end; PAUSE end(*---of File_Scan---*); (*--------------------------------------*) (* INITIALIZATION *) (*--------------------------------------*) Procedure INIT1; begin bell := CHR(7) ; CRT_width := 80 ; TTY_width := 72 ; last := str_len ; MaxRecords := 75 ; (* maximum number of records = # BYTES per Record times # of records # BYTES per record = # chars per line + overhead per line times # of lines. ***) Curr_Rcds := 0 ; Last_Update := 'YY/MM/DD '; current_ID := 'RCPDAT.XXX '; backup_ID := 'RCPDAT.YYY '; adding_recipies := false; end; Procedure INIT2; begin (* OPEN file `RECIPE.MST` for READ assign stats *) RESET(master, stats); If EOF(stats) then(* not found *) (* OPEN file `RECIPE.MST` for WRITE assign stats *) UPDATE_MASTER Else begin(* READ in data record *) READ(stats, data ); with data do begin MaxRecords := MR; Curr_Rcds := CR; current_ID := F1; backup_ID := F2; last_update := date end(* with *) end(* READ in data record *); SKIP(5); Writeln('Last update of Recipe data file was ', last_update); Writeln('File currently consists of ', Curr_Rcds:4, ' Recipies'); Writeln; Write('Please enter todays date '); last_update := ' ';{<<<=== 14 spaces required ===} For ix:=1 to 8 do begin if (ix=3) or (ix=6) then ch := '/' else KEYIN(ch); write(ch); last_update[ix] := ch end{for}; writeln end(*--of INIT2---*); (*----------------------------------------------* * MAIN PROGRAM * *----------------------------------------------*) BEGIN INIT1; (* start the initialization process here *) CLEAR; DRAW('************',TTY_width DIV 12); Writeln; Writeln( ' ':22, 'The Recipe System'); Writeln; DRAW('************',TTY_width DIV 12); INIT2; (* finish init now *) { Now execute the program until done } done := false; While not done do begin CLEAR; DRAW('************',TTY_width DIV 12); SKIP(3); Writeln( ' ':Tab15, 'Select One of the following:'); Writeln; Writeln( ' ':Tab20, 'I(nput Recipes'); Writeln( ' ':Tab20, 'O(utput all Recipes'); Writeln( ' ':Tab20, 'F(ind a Recipe'); Writeln( ' ':Tab20, 'S(top'); comanding := true; WHILE comanding do begin comanding := false; Writeln; Write(' ':(Tab15), 'Enter choice ' ); KEYIN(command);write(command); CASE command of 'I', 'i': InputRecipe; 'O', 'o', 'F', 'f': File_Scan; 'S', 's': done := true; Else: begin Write(BELL); comanding := true end End{ case } end{while comanding} end{ while not done } End{---of Program Recipe---}. .