PROGRAM RECIPE; (* ** PROGRAM TITLE THE RECIPE SYSTEM ** Version PAS-1.2 translated from ** the BASIC version into Pascal. ** ** WRITTEN BY: Ray Penley ** DATE WRITTEN: 23 FEB 1980 / last modified: 28 FEB 80 ** WRITTEN FOR: Computer hobbyists ** ** PROGRAM SUMMARY: ** ** The recipe system stores recipes and retrives them ** 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. ** ** INPUT AND OUTPUT FILES: ** RCPDAT.XXX and RCPDAT.YYY ** - the DATA and the backup files ** RECIPE.MST - the statistics file ** DUMMY.$$$ - see Procedure InputRecipe for use. ** ** ORIGINAL PROGRAM: ** T.G.LEWIS, 'THE MIND APPLIANCE' ** HAYDEN BOOK COMPANY **) CONST str_len = 73; (* max length of all strings + one *) EOS = '|'; (* End of String marker *) Master = 'RECIPE.MST'; Tab20 = 20 ; Tab15 = 15 ; on = true; off = false; (* !!!!! IMPLEMENTATION DEPENDENCY !!!!! *) (***** PASCAL/Z ver 2.0 *****) INPUT = 0; TYPE string = packed array[1..str_len] of char; string2 = packed array[1..2] of char; string14 = packed array[1..14] of char; datatype = record MR, (* MaxRecords *) CR : integer; (* Curr_Rcds *) F1, (* current_ID *) F2, (* backup_ID *) date : string14(* last_update *) end; VAR a_RAY : packed array[1..5] of string; data :datatype; Bell, command :char; Last_update :string14; Curr_Rcds, (* No. of current active records *) Hash, (* Computed Index value of Recipe *) Last, MaxRecords, (* Maximum records allowed *) TTY (* width of terminal/CRT *) :integer; End_of_File, (* End of File flag *) End_of_Text, (* End of Text flag *) adding_recipies, (* flag = true when adding recipies *) switch, error, done, yflag : boolean; (* FID. File Identifier *) current_ID, (* Current file ID *) backup_ID :string14; (* Back up file ID *) (* FCB. File descriptors *) fa, fb :TEXT; stats :FILE of datatype; (*----------------------------------------------* * INPUT/OUTPUT ROUTINES * *----------------------------------------------*) (*----------------------------------------------*) (* 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 : string ); (** Returns: End_of_Text = true if attempt is made to exceed the input buffer length. End_of_File = true if EOF INBUFF = input string ***) VAR CH : CHAR; ix, length : integer; begin length := 0; End_of_Text := FALSE; WHILE NOT EOF(fa) AND (CH <> EOS) DO begin If length < str_len then begin READ(fa, CH ); length := length +1; INBUFF [length] := CH end(* If *) ELSE (*** error ***) begin error := true; End_of_Text := TRUE end(* else *) end(* WHILE *); If length >= last then last:=length Else REPEAT INBUFF[ last ] := EOS; last := last -1 UNTIL last=length; (*** !!! SET FLAG !!! ***) End_of_File := EOF(fa); end(*---of GetLine---*); Procedure PUTLINE((* VAR fx : TEXT; *) VAR this :string ); VAR CH : char; pos : integer; begin pos := 0; REPEAT pos := pos +1; CH := this[ pos ]; If CH <> EOS then Write(fb, CH) UNTIL (CH = EOS) OR (pos = str_len); Write(fb, EOS ) (* Mark the End of String *) end(*---of PUTLINE---*); Procedure PUT_RECORD((* VAR fx : TEXT; *) VAR Index : integer ); VAR jx : integer; begin Writeln(fb, Index:5); For jx:=1 to 5 do PUTLINE((* fb, *) a_RAY[jx] ); end(*---of PUT_RECORD---*); Procedure GET_RECORD((* VAR fx : TEXT; *) VAR Index : integer ); VAR JJ : integer; begin READLN (fa, Index); FOR JJ := 1 to 5 DO GETLINE((* fa, *) a_RAY[JJ] ); end(*---of GET_RECORD---*); (*----------------------------------------------*) (* CONSOLE I/O *) (*----------------------------------------------*) Procedure PRINT((* VAR fx : TEXT; *) VAR this : string ); (* Print the string 'this' until EOS *) VAR CH : CHAR; pos : integer; begin pos := 0; REPEAT pos := pos +1; CH := this[ pos ]; If CH <> EOS then Write(CH) UNTIL (CH = EOS) OR (pos = str_len); Writeln end(*---of PRINT---*); Procedure SCAN((* VAR fx : TEXT; *) VAR INBUFF : String ; count : integer ); (* SCAN Version 1.1 * Enter with: count = maximum # chars allowed. Returns: INBUFF = input string EOS = End of string marker Flags: error = false - good input = true if buffer length exceeded If invalid ASCII char detected. Valid Alphanumeric chars are: between the space - CHR(32) to the tilde - CHR(126) GLOBAL str_len = << default for string length >> EOS = '|'; error : boolean string : packed array[1..str_len] of char *) VAR InChar : char; length : integer; begin error := false; For length:=1 to str_len do INBUFF[ length ]:= EOS; length := 0; REPEAT If length < count then(* get valid inputs *) begin READ( InChar ); If InChar IN [' ' .. '~'] then begin (* Increment length and store InChar *) length := length +1; INBUFF[length] := InChar end(* if *) ELSE begin Writeln(' Alphanumerics only -'); error:=TRUE end(* else *) end(* If *) ELSE (* ERROR *) begin (* RESET EndOfLine (EOLN) *) READLN(INBUFF[count]); Writeln('Maximum of', count:4, ' characters please!'); error:=TRUE end(* ELSE *) UNTIL EOLN(INPUT) OR error; end(*---of SCAN11---*); (*----------------------------------------------* * UTILITY ROUTINES * *----------------------------------------------*) Procedure QUIRY; (* YES/NO INPUT MODULE Returns: yflag =TRUE FOR ''Y' or 'y' INPUT =FALSE FOR 'N' or 'n' INPUT GLOBAL yflag : boolean; *) VAR Ans : char; error : boolean; begin error := true; yflag := false; REPEAT error := false; READ(Ans); If (Ans = 'Y') OR (Ans = 'y') then yflag := true Else If (Ans <> 'N') AND (Ans <> 'n') then begin Writeln(BELL, 'Please answer ''Y'' or ''N'' '); error := true end Until NOT error end(*---of QUIRY---*); 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; VAR dummy : char; begin skip(4); Write('Type return to continue:'); READ(dummy); end; Procedure BREAK; begin CLEAR; SKIP(5); end; Procedure Pstring(picture : string2; count : integer ); VAR ix : integer; begin FOR ix:=1 to count DO Write( picture ); Writeln; end(*---of Pstring---*); Procedure ShowRecipe; VAR JJ : integer; begin FOR JJ := 1 to 5 DO PRINT(a_RAY[JJ]) ; Writeln end(*--of ShowRecipe--*); Procedure Display_One(VAR Index : integer); begin Writeln; Writeln( 'Recipe #', Index:5 ); Writeln; Pstring( '- ', 20); Writeln; ShowRecipe; skip(4) end; (*----------------------------------------------* * ADD MODULE * *----------------------------------------------*) 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; begin REPEAT Writeln; Write('Enter Choice (1 to', X2:2, ') '); READ(ix); 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) 2. Oats' ); Writeln( ' ':Tab15, '3. Rice 4. Corn' ); Writeln( ' ':Tab15, '5. Macaroni 6. Noodles' ); Writeln( ' ':Tab15, '7. Spaghetti 8. ', Msg1 ); F := quiry(8); Writeln; Writeln( ' ':Tab15, 'Protein' ); Writeln; Writeln( ' ':Tab15, '1. Beef 2. Poultry' ); Writeln( ' ':Tab15, '3. Fish 4. Eggs' ); Writeln( ' ':Tab15, '5. Beans 6. Nuts' ); Writeln( ' ':Tab15, '7. ', Msg1 ); P := quiry(7); BREAK; Writeln; Writeln( ' ':Tab15, 'Dairy' ); Writeln; Writeln( ' ':Tab15, '1. Milk 2. Cheese' ); Writeln( ' ':Tab15, '3. Cottage Cheese 4. Cream' ); Writeln( ' ':Tab15, '5. Sour Cream 6. ', Msg1 ); D := quiry(6); Writeln; Writeln( ' ':Tab15, 'Fruits and Vegetables' ); Writeln; Writeln( ' ':Tab15, '1. Citrus 2. Melon' ); Writeln( ' ':Tab15, '3. Juices 4. Greens' ); Writeln( ' ':Tab15, '5. Yellows & Reds' ); Writeln( ' ':Tab15, '6. ', Msg1 ); V := quiry(6); (****************************************** * Compute the index value by assigning * * a weight to each digit in the set. * *******************************************) I := 252*F + 36*P + 6*D + V -295 end; Procedure InputRecipe; LABEL 2399; (*---EXIT---*) VAR state : (absent, done, adding) ; ix, jx : integer; temp : string14; Line : string; Procedure Correct; begin REPEAT BREAK; Write(bell); Writeln(' ':(TTY DIV 2) -10, 'HERE IS YOUR RECIPE'); Writeln; ShowRecipe; Writeln; Writeln('Are there any corrections to be made '); QUIRY; If yflag then begin BREAK; Writeln('Enter return if correct or Reenter the line'); Writeln; For ix:=1 to 5 do begin PRINT(a_RAY[ix]); SCAN(Line, str_len -1); If Line[1] <> ' ' then a_RAY[ix] := Line end end(* If *) Until yflag=false; end(*---of Correct---*); Procedure QUEST; begin Pause; BREAK; Write('Do you want to ADD recipies? ' ); QUIRY; CLEAR; end; begin(*---InputRecipe---*) QUEST; If yflag=false then (* EXIT *) goto 2399; adding_recipies := true ; state := adding ; (* OPEN file backup_ID for WRITE assign fb *) REWRITE(backup_ID, fb); (* OPEN file current_ID for READ assign fa *) RESET(current_ID, fa); If NOT EOF(fa) then begin(* COPY current to back_up *) ix := 0 ; While ix < Curr_Rcds do begin ix := ix +1; GET_RECORD((* fa, *) HASH); PUT_RECORD((* fb, *) HASH); end(* while *) end(* IF *); (*---Input/Enter additional recipies until done---*) (*---or curr_records > Max_Records allowed ---*) REPEAT If Curr_Rcds > MaxRecords then state := done Else(* we can add more date *) begin 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 Write('>'); SCAN( a_RAY[jx], str_len -1 ); end;(* For *) Correct(* if required *); Curr_Rcds := Curr_Rcds +1; PUT_RECORD((* fb, *) HASH); QUEST; If yflag=false then state := done; end;(* else *) UNTIL state<>adding; (*---------------------------------------* * *** trick *** * * close previous file ID assigned * * FCB fb and fix CP/M directory entry * *---------------------------------------*) REWRITE('DUMMY.$$$', fb); (* 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--*) 2399: (* EXIT *); 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) ; ix, index : integer; Procedure DUMP; (********************************* * OUTPUT all Recipes from file * **********************************) begin REPEAT If ix > Curr_Rcds then state := absent Else begin ix := ix +1; GET_RECORD((* fa, *) HASH); Display_One(HASH); Pause end(* else *) UNTIL state<>searching; end(*--of DUMP--*); Procedure FIND; (************************************* * Lookup recipes from file * **************************************) VAR Index : integer; begin CLEAR; InputFeatures(Index); REPEAT If ix > Curr_Rcds then state := absent Else begin GET_RECORD((* fa, *) HASH); If HASH=Index then state := found Else ix := ix +1 end(* else *); Until state<>searching; If state=found then begin CLEAR; Display_One(HASH); end; end(*--of Lookup--*); begin(*---File_Scan---*) Pause; state := absent; If adding_recipies then (* read new stats *) OPEN_MASTER; (* OPEN file current_ID for READ assign fa *) RESET(current_ID, fa); If NOT EOF(fa) then begin state := searching ; ix := 1 ; If Curr_rcds=0 then state := absent Else begin CASE command of 'O', 'o': DUMP; 'F', 'f': FIND end(* case *) end(* else *) end(* IF *); If state=absent then begin BREAK; Writeln('That''s all the Recipes on File'); end; Pause; end(*---of File_Scan---*); (*--------------------------------------*) (* INITIALIZATION *) (*--------------------------------------*) Procedure INIT1; (* byte count/record = (chars/line + overhead/line) times No. of lines *) begin BELL := CHR(7) ; TTY := 72 ; last := str_len ; MaxRecords := 50 ;(* 360 times 50 = 18000 bytes *) 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; 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 '); READLN(last_update) end; (*----------------------------------------------* * MAIN PROGRAM * *----------------------------------------------*) BEGIN INIT1; CLEAR; Pstring( '**', (TTY DIV 2)); Writeln; Writeln( ' ':22, 'The Recipe System'); Writeln; Pstring( '**', (TTY DIV 2)); INIT2; done := false; WHILE NOT(done) DO begin CLEAR; Pstring( '**', (TTY DIV 2)); 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'); switch := on; WHILE switch(* is on *) do begin switch := off; Writeln; Write(' ':(Tab15), 'Enter choice ' ); READ( command ); CASE command of 'I', 'i': InputRecipe; 'O', 'o', 'F', 'f': File_Scan; 'S', 's': done := true; ELSE: begin Write(BELL); switch := on end end(* case *) end(* while switch is on *) end(* while not done *) end(*---of Program Recipe---*). .