home *** CD-ROM | disk | FTP | other *** search
/ Oakland CPM Archive / oakcpm.iso / sigm / vol148 / student2.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1984-04-29  |  21.5 KB  |  848 lines

  1. PROGRAM Student;
  2.  
  3. {---------------------------------------------------------------}
  4. { PROGRAM TITLE:    STUDENT    version 2.0            }
  5. { WRITTEN BY:        Raymond E. Penley            }
  6. { DATE WRITTEN:        Dec 18, 1982                }
  7. {                                }
  8. { INPUT/OUTPUT FILES:           *** ACCESS METHOD ***    }
  9. {   STUDENT.NDX - Misc data        <sequential>        }
  10. {   STUDENT.DAT - Name & Address    <direct (random)>    }
  11. {   STUDENT.GDS    - Grade data        <sequential>        }
  12. {                                }
  13. { COMMANDS:                            }
  14. {  New student    - Adds a new entry if file not filled.        }
  15. {  Find        - Searches & displays a student            }
  16. {  Change    - Allows changes on address/grades        }
  17. {  List        - Displays data for all students        }
  18. {  Quit        - Terminate program/close all files        }
  19. {                                }
  20. { SUMMARY:                            }
  21. { Writes a name & address file and a grade file on all students.}
  22. { Also a file of misc. data; # of rcds on file and date file    }
  23. { was last updated.                        }
  24. {                                }
  25. { 01/29/83 -- EXTENSIVE ERROR CORRECTING ADDED:            }
  26. { 04/02/83 -- added vlength() by D. Cortesi            }
  27. {                                }
  28. { NOTES:                            }
  29. {  utility procedures from the Pascal/Z User's Group        }
  30. {  Library diskette.                        }
  31. {---------------------------------------------------------------}
  32.  
  33.  
  34. CONST
  35.   MaxStudents = 200; { determines maximum # of data records in file }
  36.   enter = 'ENTER NEW DATA OR PRESS RETURN TO KEEP PRESENT DATA';
  37.   escape = 27;        { ASCII escape character }
  38.  
  39. TYPE
  40.  
  41.    {-------------------------------------------------------------}
  42.    { create a binary search tree in memory to hold our index     }
  43.    {-------------------------------------------------------------}
  44.  
  45.    link = ^ip;            { pointer to the binary tree }
  46.    ip = RECORD            { the binary tree record }
  47.       item : integer;    { KEY FIELD. student's id number      }
  48.       rcd  : integer;    {            data file record pointer      }
  49.       left,right: link    {            pointers to left/right nodes }
  50.     END;
  51.  
  52.   byte        = 0..255;
  53.   charset    = SET OF CHAR;
  54.   strng2    = string 2;
  55.   strng5    = string 5;
  56.   strng20    = string 20;
  57.   strng        = string 20;
  58.  
  59.  
  60.   {-------------------------------------------------------------}
  61.   { sturec - identifies the data to be stored for each student    }
  62.   {-------------------------------------------------------------}
  63.  
  64.   sturec  = RECORD              { field, type, length        }
  65.            id    : integer;    { id,     n,  5     <KEY FIELD>    }
  66.            name,        { name,   c, 20            }
  67.            street,        { street, c, 20            }
  68.            city   : strng20;{ city,   c, 20            }
  69.            state  : strng2;    { state,  c,  2            }
  70.            zipcode: strng5    { zipcode,c,  5            }
  71.         END;
  72.   { total number of bytes = 77 per record.
  73.     72 bytes + 1 for each string }
  74.  
  75.  
  76.   {-------------------------------------------------------------}
  77.   { Allow for ten grades and the student ID. Please note that    }
  78.   { this may be changed to suit your particular requirements.    }
  79.   { NOTE: the enumerated type has been setup such that        }
  80.   {       ORD(exam1) = 1.                    }
  81.   {-------------------------------------------------------------}
  82.  
  83.   gradetype = ( id,    { id field is link between all data files }
  84.         exam1,    { 1st exam grade              }
  85.         exam2,    { 2nd exam grade              }
  86.         exam3,    { 3rd exam grade              }
  87.         exam4,    { 4th exam grade              }
  88.         exam5,    { 5th exam grade              }
  89.         exam6,    { 6th exam grade              }
  90.         exam7,    { 7th exam grade              }
  91.         quiz1,    { quiz 1                   }
  92.         quiz2,    { quiz 2                   }
  93.         final);    { final grade                   }
  94.  
  95.   {-------------------------------------------------------------}
  96.   gradestore    = array [gradetype] of integer;
  97.  
  98.   {-------------------------------------------------------------}
  99.   StuGds    = array [1..MaxStudents] of gradestore;
  100.  
  101.   {-------------------------------------------------------------}
  102.   FTYPE        = FILE OF StuRec;
  103.  
  104.   string0    = string 0;
  105.   string255    = string 255;
  106.  
  107. VAR
  108.   bell        : char;        { console bell }
  109.   command    : char;        { command character }
  110.   console    : TEXT;        { direct output to console }
  111.   date        : packed array [1..8] of char; { date of last update }
  112.   g        : gradetype;
  113.   Grades    : StuGds;
  114.   ioresult    : boolean;
  115.   listhead    : link;
  116.   more        : boolean;    { done processing flag }
  117.   R        : integer;    { record pointer }
  118.   rof        : integer;    { total Records On File }
  119.   stucount    : integer;    { # of students in class }
  120.   Student    : StuRec;    { A single student }
  121.   StuFile    : FTYPE;    { name & address file }
  122.   taken        : integer;    { # of tests taken thus far }
  123.   updated    : boolean;    { flag for updated items }
  124.  
  125. {$R-}{ range checking OFF }
  126.  
  127. {-----------------------------------------------}
  128. {         GENERAL PURPOSE UTILITIES             }
  129. {-----------------------------------------------}
  130.  
  131. {$iPRIMS.PZ }
  132.  
  133.  
  134. { pause - allows prgm to stop until ready to continue }
  135.  
  136. PROCEDURE pause;
  137. VAR    ch : char;
  138. BEGIN
  139.    writeln;
  140.    write ('Press any key to continue ');
  141.    keyin(ch); writeln
  142. END{ pause };
  143.  
  144.  
  145. { ClearScreen - simple routine to clear the console device }
  146.  
  147. PROCEDURE ClearScreen;
  148. VAR    i: 1..25;
  149. BEGIN
  150.    FOR i:=1 TO 25 DO writeln
  151. END{ ClearScreen };
  152.  
  153.  
  154. { Q - prints a text message and accepts only the characters    }
  155. { passed via goodchars. Returns the result in uppercase.    }
  156. {                                }
  157. { Q ( 'Enter "A", "B", or "C" -> ', ['A','B','C'], command );    }
  158. {                                }
  159. { REQUIRES:                            }
  160. {    procedure keyin();external;                }
  161. {    function toupper():char;external;            }
  162.  
  163. PROCEDURE Q ( message: string255; goodchars: charset; VAR ch: char );
  164. CONST    bell = 7;        { ASCII bell char }
  165. VAR    tch: char;        { temp char       }
  166. BEGIN
  167.    write( message );
  168.    REPEAT
  169.      keyin(tch);
  170.      ch := toupper(tch);
  171.      IF ch IN goodchars
  172.     THEN
  173.        writeln (tch)
  174.     ELSE
  175.        write (chr(bell))
  176.    UNTIL ch in goodchars
  177. END{ Q };
  178.  
  179.  
  180. { readint - input of an integer value between lower..upper.    }
  181. {    returns integer value and true if valid integer        }
  182. {    else returns a zero value and false.            }
  183. { REQUIRES:                            }
  184. {    function ivalue():integer;                }
  185. {    function vlength():integer;external;            }
  186.     
  187. FUNCTION readint ( VAR i: integer; lower,upper: integer ): boolean;
  188. VAR    answer: strng20;
  189. BEGIN
  190.       readint := true;
  191.       readln(answer);
  192.       IF vlength(answer) > 0 THEN BEGIN
  193.      i := ivalue(answer,1);
  194.      IF (i < lower) OR (upper < i ) THEN {do it again}
  195.         readint := false;
  196.       END
  197. END{ readint };
  198.  
  199.  
  200. {-----------------------------------------------}
  201. {        PROGRAM SPECIFIC UTILITIES             }
  202. {-----------------------------------------------}
  203.  
  204. { gde - converts an integer to the enumerated type gradetype }
  205.  
  206. FUNCTION gde ( exam: integer ): gradetype;
  207. BEGIN
  208.      CASE exam OF
  209.     0: gde := id;
  210.     1: gde := exam1;
  211.     2: gde := exam2;
  212.     3: gde := exam3;
  213.     4: gde := exam4;
  214.     5: gde := exam5;
  215.     6: gde := exam6;
  216.     7: gde := exam7;
  217.     8: gde := quiz1;
  218.     9: gde := quiz2;
  219.        10: gde := final
  220.      END
  221. END{ gde };
  222.  
  223. {$R+}{ RANGE CHECKING ON }
  224.  
  225.  
  226. { insert - adds a node to the binary search tree, preserving the ordering }
  227.  
  228. PROCEDURE insert( VAR node: link; ident, R: integer );
  229. BEGIN
  230.   IF node=nil THEN BEGIN
  231.      new(node);        { create a new storage location }
  232.      WITH node^ DO BEGIN
  233.         left := nil;
  234.         right := nil;
  235.         item := ident;    { store the student's id }
  236.         rcd := R    { store the location record pointer }
  237.      END{with}
  238.   END
  239.   ELSE
  240.      WITH node^ DO
  241.         IF ident<item THEN
  242.            insert ( left,ident,R )
  243.         ELSE IF ident>item THEN
  244.            insert ( right,ident,R )
  245.         ELSE
  246.        { DUPLICATE ENTRY }{ not handled }
  247. END{ insert };
  248.  
  249.  
  250. { search - returns a pointer to a node in the tree containing }
  251. {  the given data, or nil if there is no such node.          }
  252.  
  253. FUNCTION search ( node: link; ident: integer ): link;
  254. BEGIN
  255.   IF node=nil THEN
  256.      search := nil
  257.   ELSE
  258.      WITH node^ DO
  259.         IF ident<item THEN
  260.            search := search(left,ident)
  261.         ELSE IF ident>item THEN
  262.            search := search(right,ident)
  263.         ELSE
  264.            search := node
  265. END{ search };
  266.  
  267.  
  268.  
  269. PROCEDURE ListRange ( VAR first, last: integer );
  270. { RETURNS:                               }
  271. {   one value        -first = last                   }
  272. {   all values        -first = lower bound, last = highest bound }
  273. {   a range of values    -first/last = entered values           }
  274. { ENTER with first = lower bound; last = uppermost bound.       }
  275. VAR
  276.     tch: char;
  277.     t1,t2: integer;
  278. BEGIN
  279.   t1 := first;
  280.   t2 := last;
  281.   writeln;
  282.   Q( 'ENTER LIST RANGE: A(ll, O(ne, R(ange ->', ['A','O','R'], tch );
  283.   CASE tch of
  284.      'A':
  285.     BEGIN
  286.       first := t1;
  287.       last := t2
  288.     END
  289.      'O':
  290.     REPEAT
  291.       write ( 'WHICH ONE? '); readln(first);
  292.       last := first;
  293.     UNTIL (first<=t2) or (first>=t1);
  294.      'R':
  295.     REPEAT
  296.        write ( 'Enter lower bound ->'); readln(first);
  297.        write ( 'Enter upper bound ->'); readln(last)
  298.     UNTIL first <= last
  299.   end{CASE}
  300. END{ ListRange };
  301.  
  302.  
  303.  
  304. { fread - reads the address file and sets the global record pointer }
  305.  
  306. PROCEDURE fread ( VAR StuFile: FTYPE; VAR node: link );
  307. BEGIN
  308.    R := node^.rcd;        { returns the record pointer. }
  309.    read ( StuFile:R, student )    { read student record R. }
  310. END{ fread };
  311.  
  312.  
  313.  
  314. PROCEDURE ChangeAddress ( VAR student: sturec; VAR goodstatus: boolean );
  315. LABEL
  316.    1;    { early exit }
  317. CONST
  318.    ok = true;
  319. VAR
  320.    answer: strng20;
  321.    i     : integer;
  322.    node  : link;
  323.    valid : boolean;
  324.  
  325.     PROCEDURE disp ( message, value: string255 );
  326.     BEGIN
  327.        writeln;
  328.        IF vlength(value) > 0 THEN BEGIN
  329.           writeln ( message, value );
  330.           write ( ' ':19 )
  331.        END
  332.        ELSE
  333.           write ( message );
  334.     END{ disp };
  335.  
  336. BEGIN {ChangeAddress}
  337.    goodstatus := ok;
  338.    IF command = 'C' THEN BEGIN
  339.       writeln;
  340.       writeln ( enter )
  341.    END;    
  342.    writeln;writeln;
  343.    WITH student DO BEGIN
  344.     IF id=0
  345.        THEN
  346.           setlength ( answer,0 )
  347.        ELSE
  348.           STR ( id,answer );
  349.  
  350.         { NOTE:
  351.         do not allow ID to be changed after initial input }
  352.     IF command = 'N' THEN BEGIN { adding New records }
  353.        REPEAT
  354.           disp ( 'ID Number      ... ', answer )
  355.        UNTIL readint ( id,1,9999 );
  356.  
  357.        node := search ( listhead,id );    { id already on file? }
  358.        IF node<>nil THEN BEGIN { already on file }
  359.           fread ( StuFile, node );    { read record for display }
  360.           ClearScreen;
  361.           writeln ( bell, id, ' already on file!');
  362.           goodstatus := not ok;
  363.           {EXIT}goto 1
  364.        END
  365.     END{IF command='N'...}
  366.     ELSE
  367.        writeln ( 'ID Number      ... ', answer );
  368.  
  369.     disp ( 'Name           ... ', name ); readln(answer);
  370.     IF vlength(answer)>0 THEN
  371.        name := answer;
  372.  
  373.     disp ( 'Street Address ... ', street ); readln(answer);
  374.     IF vlength(answer)>0 THEN
  375.        street := answer;
  376.  
  377.     disp ( 'City           ... ', city ); readln(answer);
  378.     IF vlength(answer)>0 THEN
  379.        city := answer;
  380.  
  381.     disp ( 'State          ... ', state ); readln(answer);
  382.     IF vlength(answer)>0 THEN BEGIN
  383.        state[1] := toupper ( answer[1] );
  384.        state[2] := toupper ( answer[2] );
  385.        setlength ( state,2 )
  386.     END;
  387.  
  388.     REPEAT
  389.        valid := true;
  390.        disp ( 'Zip code       ... ', zipcode ); readln(answer);
  391.        IF vlength(answer)>0 THEN BEGIN
  392.           zipcode := '     ';        { insure no garbage in answer }
  393.           IF isdigit(answer[1]) THEN { good chance is digit }
  394.              FOR i:=1 TO 5 DO
  395.             zipcode[i] := answer[i]
  396.           ELSE BEGIN
  397.          write(bell); valid := false
  398.           END
  399.        END
  400.     UNTIL valid;
  401.    END;
  402.    updated := true;
  403. 1:{early exit}
  404. END{ ChangeAddress };
  405.  
  406.  
  407.  
  408. PROCEDURE ChangeGrades ( VAR student: sturec );
  409. {NOTE:
  410.     record pointer must be set before entry to ChangeGrades() }
  411. CONST
  412.    low = 0;    { lowest grade acceptable }
  413.    high = 110;    { highest grade acceptable <wow!>}
  414. VAR
  415.    answer    : strng20;
  416.    first,last    : gradetype;
  417.    lower,upper    : integer;
  418. BEGIN
  419.    lower := 1;
  420.    upper := taken;
  421.    ListRange ( lower,upper );
  422.    first := gde(lower);
  423.    last := gde(upper);
  424.    writeln;
  425.    writeln ( enter );
  426.    writeln;writeln;
  427.    writeln ( 'STUDENT: ', student.name );
  428.    writeln;
  429.    FOR g:=first TO last DO BEGIN
  430.       REPEAT
  431.      write ( ord(g):3, grades[R,g]:6, ' ?' )
  432.       UNTIL readint ( grades[R,g],low,high )
  433.    END{FOR g}
  434. END{ ChangeGrades };
  435.  
  436.  
  437.  
  438. PROCEDURE display ( VAR output: TEXT; VAR student: sturec );
  439. {NOTE:
  440.     record pointer must be set before entry to display() }
  441. CONST
  442.    width = 35;
  443. BEGIN
  444.    writeln ( output );
  445.    writeln ( output );
  446.    WITH student DO BEGIN
  447.     writeln ( output, 'STUDENT ID: ', id:1 );
  448.     writeln ( output, name, ' ':width-vlength(name), street );
  449.     writeln ( output, ' ':width, city, ', ', state, ' ', zipcode );
  450.     writeln ( output, 'GRADES');
  451.     writeln ( output, ' < first half year >< second half year >');
  452.     FOR g:=exam1 TO gde(taken) DO
  453.        write ( output, grades[R,g]:4 );
  454.     writeln ( output );
  455.     writeln ( output );
  456.     writeln ( output )
  457.    END
  458. END{ display };
  459.  
  460.  
  461.  
  462. PROCEDURE MODIFY;
  463. VAR
  464.     node       : link;
  465.     ch       : char;
  466.     goodstatus : boolean;
  467. BEGIN
  468.    IF command='N' THEN { arrived here from ADD }
  469.       command := 'C'   { ... switch to CHANGE. }
  470.    ELSE BEGIN
  471.       writeln;
  472.       REPEAT
  473.      write ('Enter student id number ... ')
  474.       UNTIL readint ( student.id,1,9999 )
  475.    END;
  476.  
  477.    node := search ( listhead,student.id );
  478.    IF node<>nil THEN BEGIN
  479.       fread ( StuFile, node );
  480.       CASE command of
  481.     'C':
  482.        BEGIN {CHANGE}
  483.           writeln;
  484.           Q( 'Do you wish to change A(ddress, or G(rades? <escape=quit> ',
  485.             [chr(escape),'A','G'], ch );
  486.           if ord(ch)<>escape then begin
  487.              CASE ch of
  488.             'A':
  489.                ChangeAddress ( student,goodstatus );
  490.             'G':
  491.                ChangeGrades ( student )
  492.              END{CASE};
  493.              display ( console,student );
  494.              if ch='A' THEN { update address file }
  495.             write ( StuFile:R, student )
  496.           end
  497.        END{ CHANGE };
  498.     'F':
  499.        BEGIN {FIND}
  500.           display ( console,student )
  501.        END{ FIND }
  502.       END{CASE}
  503.    END
  504.    ELSE
  505.       writeln ( bell, student.id:1,' not on file!')
  506. END{ MODIFY };
  507.  
  508.  
  509.  
  510. PROCEDURE ADD;
  511. VAR
  512.     goodstatus: boolean;
  513. BEGIN
  514.    IF rof >= MaxStudents THEN
  515.       writeln ( 'Sorry can''t add file is full.' )
  516.    ELSE BEGIN { OK to add more records }
  517.       R := rof + 1;        { temp set record pointer }
  518.       WITH student DO BEGIN { initialize all fields to zero }
  519.      id := 0;
  520.      setlength ( name,0 );
  521.      setlength ( street,0 );
  522.      setlength ( city,0 );
  523.      setlength ( state,0 );
  524.      setlength ( zipcode,0 )
  525.       END;
  526.       writeln;
  527.       writeln ( 'RECORD #', R:1 );
  528.       ChangeAddress ( student,goodstatus );
  529.       display ( console, student );
  530.  
  531.       IF goodstatus THEN BEGIN
  532.      grades[R,id] := student.id;    { update grades matrix }
  533.      insert ( listhead,student.id,R );
  534.      write ( StuFile:R, student );    { update address file }
  535.      updated := true;        { flag we updated the file }
  536.      rof := R;            { increment records on file }
  537.      stucount := rof;        { and student count         }
  538.      { move right into edit mode...change address/grades }
  539.      MODIFY
  540.       END{IF goodstatus then...};
  541.       pause
  542.    END{ELSE}
  543. END{ ADD };
  544.  
  545.  
  546.  
  547. { list - lists ALL records on file }
  548.  
  549. PROCEDURE LIST;
  550. VAR
  551.     output : TEXT;
  552.  
  553.     { printlist - writes the entire tree recursively }
  554.     PROCEDURE PrintList ( node: link );
  555.     LABEL    1;
  556.     BEGIN
  557.        IF node<>nil THEN
  558.           WITH node^ DO BEGIN
  559.              PrintList (left);
  560.          fread ( StuFile, node ); { read address file }
  561.          display ( output, student );
  562.          { test the keyboard and abort on any keypress }
  563.          IF conchar<>0 THEN BEGIN {ABORT}
  564.             writeln;writeln(chr(7);'ABORTED');goto 1
  565.          END;
  566.          IF command<>'P' THEN pause;
  567.              PrintList ( right )
  568.           END{with};
  569.     1:{abort}
  570.     END{ PrintList };
  571.  
  572. BEGIN
  573.    writeln;
  574.    Q('Output to C(onsole or P(rinter? <escape=quit> ',
  575.         [chr(escape),'C','P'], command );
  576.    IF ord(command)=escape THEN
  577.       {all done}
  578.    ELSE BEGIN 
  579.       CASE command OF
  580.      'P': { direct output to the list device }
  581.         REWRITE( 'LST:',output );
  582.      'C': { direct output to the console device }
  583.           REWRITE( 'CON:',output )
  584.       end{CASE};
  585.       PrintList(listhead)
  586.    END
  587. END{ LIST }{ CLOSE(output); };
  588.  
  589.  
  590.  
  591. { report - generates totals and prints a formatted report on each student }
  592.  
  593. PROCEDURE report;
  594. LABEL
  595.     1; {abort}
  596. CONST
  597.     fw = 6;
  598. TYPE
  599.     atype = (avg,total);
  600. VAR
  601.     a,rr     : integer;
  602.     aborted     : boolean;
  603.     accum     : array [avg..total,gradetype] of integer;
  604.     first,g,
  605.     last     : gradetype;
  606.     output     : TEXT;
  607.  
  608.  
  609.    PROCEDURE PrintClass ( node: link );
  610.    LABEL
  611.      2; {abort}
  612.    BEGIN
  613.       IF node<>nil THEN
  614.          WITH node^ DO BEGIN
  615.             PrintClass (left);
  616.         R := rcd;
  617.         rr := rr + 1;
  618.         { output line consists of: line #, student id #, grades }
  619.         write ( output, rr:3, grades[R,id]:5, '  ' );
  620.         a := 0;                   { "a" = grade accumulator }
  621.         FOR g:=first TO last DO BEGIN
  622.            write ( output,grades[R,g]:fw );
  623.                a := a + grades[R,g];
  624.            accum[total,g] := accum[total,g] + grades[R,g]
  625.         END{FOR g};
  626.         { print the rounded average of this student's grades }
  627.         writeln (output, '  ', round(a/taken):fw );
  628.  
  629.         { test the keyboard and abort on any keypress }
  630.         IF conchar<>0 THEN BEGIN {ABORT}
  631.            aborted := true;
  632.            writeln;writeln(chr(7);'ABORTED');goto 2
  633.         END;
  634.             PrintClass ( right )
  635.          END{with};
  636.    2:{abort}
  637.    END{ PrintClass };
  638.  
  639. BEGIN{ report }
  640.    writeln;
  641.    Q('Output to C(onsole or P(rinter? <escape=quit> ',
  642.         [chr(escape),'C','P'], command );
  643.    IF ord(command)=escape THEN
  644.       goto 1; {all done}
  645.    CASE command OF
  646.       'P': { direct output to the list device }
  647.          REWRITE( 'LST:',output );
  648.       'C': { direct output to the console device }
  649.          REWRITE( 'CON:',output )
  650.    END{CASE};
  651.  
  652.    first := exam1;    { first = 1st exam grade, last = last exam taken }
  653.    last := gde(taken);
  654.  
  655. {REPORT LINE 1}
  656.    writeln ( output );
  657.    write(output,' STUDENT  ');
  658.    FOR g:=first TO last DO
  659.       IF ord(g)=1
  660.      then write(output,' EXAMS')
  661.      else write(output,'      ');
  662.    writeln(output,'  AVERAGE');
  663.  
  664. {REPORT LINE 2}
  665.    write ( output,'========  ' );
  666.    FOR g:=first TO last DO BEGIN
  667.       write( output,ord(g):fw );
  668.       accum[total,g] := 0    { zero total accumulator }
  669.    END;
  670.    writeln ( output,'  =======' );
  671.  
  672. {REPORT LINE 3...n}
  673.    rr := 0;
  674.    aborted := false;
  675.    PrintClass(listhead);
  676.    if aborted then goto 1;
  677.    write ( output,'          ' );{ 10 spaces }
  678.    FOR g:=first TO last DO BEGIN
  679.     { compute the average for all the student's grades }
  680.     accum[avg,g] := accum[total,g] DIV stucount;
  681.     { underline each column }
  682.     write(output,'   ---');
  683.    END;
  684.    writeln (output);
  685.  
  686. {REPORT SUMMATION LINE...}
  687.    writeln ( ' CLASS' );
  688.    write   ( ' AVERAGE: ');
  689.    FOR g:=first TO last DO
  690.       write ( output,accum[avg,g]:fw );
  691.    writeln ( output );
  692.    writeln ( output );
  693. 1:{abort}
  694. END{ report }{ CLOSE(output); };
  695.  
  696.  
  697.  
  698. PROCEDURE STATS;
  699. VAR
  700.     answer    : strng20;
  701.     valid    : boolean;
  702. BEGIN
  703.    writeln;
  704.    writeln ( 'MAX STUDENTS ALLOWED ... ', MaxStudents:3 );
  705.    writeln ( 'NUMBER OF STUDENTS ..... ', stucount:3 );
  706.    REPEAT
  707.     write  ('NUMBER OF TESTS ........ ', taken:3,' ?' );
  708.     readln ( answer );
  709.     IF vlength(answer)>0 THEN
  710.        taken := ivalue( answer,1 );
  711.     valid := (taken>=0)
  712.    UNTIL valid
  713. END{ STATS };
  714.  
  715.  
  716.  
  717. PROCEDURE fclose;
  718. VAR
  719.     StuGrades: FILE OF gradestore;    { grade data on each student }
  720.     StuNdx   : TEXT;        { index file }
  721. BEGIN
  722.    { OPEN 'STUDENT.NDX' for WRITE assign StuNdx }
  723.    rewrite('STUDENT.NDX',StuNdx);
  724.    writeln ( StuNdx, rof );
  725.    writeln ( StuNdx, date );
  726.    writeln ( StuNdx, stucount ); { # of students in class }
  727.    writeln ( StuNdx, taken );    { # of tests taken thus far }
  728.  
  729.    { OPEN 'STUDENT.GDS' for WRITE assign StuGrades }
  730.    rewrite('STUDENT.GDS',StuGrades);
  731.    FOR R:=1 TO rof DO
  732.       write ( StuGrades, grades[R] )
  733. END{ fclose }{ CLOSE(StuNdx); CLOSE(StuGrades); };
  734.  
  735.  
  736.  
  737. PROCEDURE Initialize;
  738. VAR
  739.     i,rr    : integer;
  740.     ch    : char;
  741.     StuGrades: FILE OF gradestore;    { grade data on each student }
  742.     StuNdx   : TEXT;        { index file }
  743. BEGIN
  744.    ClearScreen;
  745.    writeln ( ' ':32, 'STUDENT SYSTEM');
  746.    writeln;
  747.    writeln;
  748.    bell := chr(7);
  749.    listhead := nil;    { make the list empty }
  750.    updated := false;    { say file has not been updated }
  751.    
  752.    { insure that all cells in grades matrix are 0 }
  753.    FOR g:=id TO final DO
  754.       grades[1,g] := 0;
  755.    FOR rr:=2 TO MaxStudents DO
  756.       grades[rr] := grades[1];
  757.  
  758.    { OPEN 'CON:' for WRITE assign console }        {open files=1}
  759.    rewrite('CON:',console);
  760.  
  761.    { OPEN 'STUDENT.NDX' for READ assign StuNdx }    {open files=2}
  762.    reset('STUDENT.NDX',StuNdx);
  763.  
  764.    IF eof(StuNdx) THEN BEGIN {create all files}
  765.     writeln ( 'Please standby while I create data files ...' );
  766.        { OPEN 'STUDENT.NDX' for WRITE assign StuNdx }    {open files=2}
  767.     rewrite('STUDENT.NDX',StuNdx);
  768.        { OPEN 'STUDENT.DAT' for WRITE assign StuFile }    {open files=3}
  769.        rewrite('STUDENT.DAT',StuFile);
  770.        { OPEN 'STUDENT.GDS' for WRITE assign StuGrades }    {open files=4}
  771.        rewrite('STUDENT.GDS',StuGrades);
  772.  
  773.     rof := 0;
  774.     stucount := 0;
  775.     taken := 10;    { setup to 10 then can lower at any time }
  776.     date := 'MM/DD/YY'
  777.    END
  778.    ELSE BEGIN { finish opening files and read record count }
  779.        { OPEN 'STUDENT.DAT' for READ assign StuFile }    {open files=3}
  780.     reset('STUDENT.DAT',StuFile);
  781.        { OPEN 'STUDENT.GDS' for READ assign StuGrades }    {open files=4}
  782.     reset('STUDENT.GDS',StuGrades);
  783.  
  784.     readln ( StuNdx, rof );
  785.     readln ( StuNdx, date );
  786.     readln ( StuNdx, stucount );    { # of students in class }
  787.     readln ( StuNdx, taken );    { # of tests taken thus far }
  788.     writeln;
  789.     FOR rr:=1 TO rof DO BEGIN
  790.        write( chr(13), 'RECORD #', rr:1 );
  791.        read ( StuGrades, grades[rr] );
  792.        read ( StuFile:rr,student );    { create the B-tree in memory }
  793.        insert ( listhead,student.id,rr ){ INDEX ON student.id         }
  794.     END;
  795.     writeln
  796.    END;
  797.  
  798.    IF rof>0 THEN BEGIN
  799.       writeln;
  800.       writeln ( 'There are ',rof:1,' records on file as of ', date )
  801.    END;
  802.    writeln;
  803.    write ( 'ENTER TODAY''S DATE <MM/DD/YY>  ->');
  804.    keyin(ch);
  805.    if ord(ch)=13 then
  806.       {accept date given}
  807.    else begin
  808.       date[1] := ch;
  809.       write(ch);
  810.       FOR i:=2 TO 8 DO BEGIN
  811.          IF (i=3) or (i=6)
  812.         THEN ch := '/'
  813.         ELSE keyin(ch);
  814.           write(ch);
  815.           date[i] := ch
  816.       END;
  817.    end;
  818.    writeln
  819. END{ Initialize }{ CLOSE(StuNdx); CLOSE(StuGrades); };        {open files=2}
  820.  
  821.  
  822.  
  823. BEGIN    (*** MAIN PROGRAM ***)
  824.    Initialize;
  825.    more := true;
  826.    WHILE more DO BEGIN
  827.     writeln;
  828.     Q('N(ew student, F(ind, C(hange, R(eport, L(ist, S(tats, Q(uit ...?',
  829.         ['N','C','F','R','L','S','Q'], command );
  830.     CASE command of
  831.        'N':
  832.           ADD;
  833.        'C','F':
  834.           MODIFY;
  835.        'R':
  836.           report;
  837.        'L':
  838.           LIST;
  839.        'S':
  840.           STATS;
  841.        'Q':
  842.           more := false
  843.     end{CASE}
  844.    END{while};
  845.    IF updated THEN fclose
  846. END.
  847.  
  848.