home *** CD-ROM | disk | FTP | other *** search
/ Power Programming / powerprogramming1994.iso / progtool / turbopas / sfmsrc.arc / SFMSCRN.INC < prev    next >
Text File  |  1987-06-12  |  37KB  |  1,407 lines

  1. {                    Super File Manager
  2.  
  3.                         SFMSCRN.INC
  4.  
  5.                      by David Steiner
  6.                         2035 J Apt. 6
  7.                         Lincoln, NE
  8. }
  9.  
  10. procedure SetCursorType;
  11.   {
  12.   Sets default colors, cursor shape according to mode
  13.     and makes sure the current mode is 80 column text.
  14.   }
  15. var
  16.   Regs : reg_T;
  17. begin
  18.   with Regs do
  19.   begin
  20.     AH := $0F;           { BIOS Video function $0F - Get Current Video Mode }
  21.     Intr( $10, Regs );
  22.     case AL of
  23.       BW80,
  24.        C80 : { Text mode OK };
  25.       BW40 : TextMode( BW80 );    { Make sure we have an 80 column text mode }
  26.        C40 : TextMode(  C80 );
  27.        $07 : CursorNum := $0C0D;  { Set Monochrome cursor attribute, mode OK }
  28.        else  TextMode( BW80 );    { Must be graphics, set to BW80 }
  29.     end;
  30.     Color := (AL in [C40,C80]);
  31.   end;
  32. end;
  33.  
  34. procedure CursorON;
  35. var
  36.   Regs : reg_T;
  37. begin
  38.   with Regs do
  39.   begin
  40.     AH := $01;                { BIOS Video function $01 - Set Cursor Shape }
  41.     AL := $00;
  42.     CX := CursorNum;
  43.     Intr( $10, Regs );
  44.   end;
  45. end;
  46.  
  47. procedure CursorOFF;
  48. var
  49.   Regs : reg_T;
  50. begin
  51.   with Regs do
  52.   begin
  53.     AH := $01;                { BIOS Video function $01 - Set Cursor Shape }
  54.     AL := $00;
  55.     CX := $1000;
  56.     Intr( $10, Regs );
  57.   end;
  58. end;
  59.  
  60. function Cstr( num : real; wid, dec : integer ) : str80;
  61.   {
  62.   Basically the same as Turbo's str procedure but is a function.
  63.   }
  64. var
  65.   tstr : str80;
  66. begin
  67.   if wid <> 0 then
  68.     str( num:wid:dec, tstr )
  69.   else
  70.     str( round( num ), tstr );
  71.   Cstr := tstr;
  72. end;
  73.  
  74. procedure AbortProgram( s1, s2, s3, s4 : str80 );
  75.   {
  76.   Basically allows an orderly exit from the program.  Was put in
  77.     during the early stages of the program so I'd know where problems
  78.     were.  Now it is just there for those few situations that SFM
  79.     can't handle (e.g. a damaged FAT).
  80.     Also required so that when an error does occur the interrupt
  81.     handlers can be reset to their original values.
  82.   }
  83. begin
  84.   textcolor( LightGray );
  85.   textbackground( Black );
  86.   window( 1, 1, 80, 25 );
  87.   clrscr;
  88.   gotoxy( 1, 7 );
  89.   writeln( 'An error not handled by this program has occured.' );
  90.   writeln;
  91.   writeln( '   The information below gives the name of the procedure' );
  92.   writeln( '   that decided to stop execution of the program and the' );
  93.   writeln( '   error that caused termination.' );
  94.   writeln;
  95.   writeln( '   ', s1 );
  96.   writeln( '   ', s2 );
  97.   writeln( '   ', s3 );
  98.   writeln( '   ', s4 );
  99.   CursorON;
  100.   Int24OFF;
  101.   Int10OFF;
  102.   {$I-}
  103.   chdir( SavedPath );
  104.   {$I+}
  105.   Noise(  250, 200 );
  106.   Noise(  500, 100 );
  107.   Noise( 1000, 200 );
  108.   Halt;
  109. end;
  110.  
  111. procedure AbortOnError( ErrNum, ErrAddr : integer );
  112.   {
  113.   We trap these run-time errors so that we can shut off all
  114.     of the interrupt handlers we created before exiting.
  115.     If we don't do this they stay active while we are in
  116.     the Turbo interactive editor environment.
  117.   }
  118. var
  119.   tstr : str80;
  120. begin
  121.   release( HeapStart );
  122.   tstr  := '';
  123.   case Hi( ErrNum ) of
  124.     0  : tstr := 'A User Break (^C)';
  125.     1  : tstr := 'An I/O error';
  126.     2  : tstr := 'A Run-Time error';
  127.     3  : tstr := 'A Program error';
  128.     else tstr := 'A type ' + Cstr( Lo( ErrNum ), 0, 0 ) + 'error';
  129.   end;
  130.   AbortProgram( 'AbortOnError:',
  131.                 '   ' + tstr + ' has occured.',
  132.                 '   Error Number: $' + copy(HexStr(Lo(ErrNum)),3,2),
  133.                 '        Address: $' + HexStr( ErrAddr ) );
  134. end;
  135.  
  136. function MemoryAvail : real;
  137.   {
  138.   Return the amount of memory free as a real number of
  139.     bytes, rather than an integer number of paragraphs.
  140.     It also takes into account the Minimum amount of stack
  141.     space defined in sfmVARS.inc.
  142.   }
  143. var
  144.   MA : real;
  145. begin
  146.   MA := MaxAvail;
  147.   if MA < 0 then MA := MA + 65536.0;
  148.   MA := MA * 16.0;
  149.   MA := MA - MinStack;
  150.   MemoryAvail := MA;
  151. end;
  152.  
  153. function KeyBoard : char;
  154.   {
  155.   Waits for a key to be pressed and sets the global variable
  156.     funckey if it was an extended key code.
  157.   }
  158. var
  159.   ch : char;
  160. begin
  161.   funckey := false;
  162.   read( kbd, ch );
  163.   if keypressed and (ch = #27) then
  164.   begin
  165.     read( kbd, ch );
  166.     funckey := true;
  167.   end;
  168.   KeyBoard := ch;
  169. end;
  170.  
  171. function KeyboardNorm : char;
  172.   {
  173.   Uses the Keyboard routine above but turns the cursor on
  174.     first and won't pass on extended key codes.
  175.   }
  176. var
  177.   ch : char;
  178. begin
  179.   CursorON;
  180.   repeat
  181.     ch := KeyBoard
  182.   until not funckey;
  183.   CursorOFF;
  184.   KeyboardNorm := ch;
  185. end;
  186.  
  187. function YorN( ans : boolean ) : boolean;
  188.   {
  189.   Function requests yes or no answers in a nice standardized way.
  190.   }
  191. const
  192.   YN   : array[false..true] of string[3] = ( 'No', 'Yes' );
  193. var
  194.   ch   : char;
  195.   x, y : integer;
  196. begin
  197.   Disp( NATTR, '? ' );
  198.   x := wherex;
  199.   y := wherey;
  200.   repeat
  201.     gotoxy( x, y );
  202.     clreol;
  203.     Disp( HATTR, YN[ans] );
  204.     ch := KeyBoardNorm;
  205.     case upcase(ch) of
  206.       ' ',
  207.       '+' : ans := not ans;
  208.       'Y' : ans := true;
  209.       'N' : ans := false;
  210.     end;
  211.   until ch = #13;
  212.   YorN := ans;
  213. end;
  214.  
  215. function Continue : boolean;
  216. begin
  217.   writeln;
  218.   Disp( NATTR, ' Continue with next file' );
  219.   Noise( 1000, 100 );
  220.   Continue := YorN( false );
  221. end;
  222.  
  223. function TryAgain : boolean;
  224. begin
  225.   writeln;
  226.   Disp( NATTR, ' Try again' );
  227.   Noise( 500, 100 );
  228.   TryAgain := YorN( false );
  229. end;
  230.  
  231. procedure wait;
  232.   {
  233.   Present press any key and a small beep to promp the user.
  234.   }
  235. var
  236.   ch : char;
  237. begin
  238.   Disp( NATTR, 'PRESS ANY KEY' );
  239.   Noise( 1000, 100 );
  240.   CursorON;
  241.   ch := KeyBoard;
  242.   CursorOFF;
  243. end;
  244.  
  245. function SelectFloppy( drv : integer ) : integer;
  246.   {
  247.   Selects either floppy drive A or B.
  248.   }
  249. var
  250.   ch   : char;
  251.   x, y : integer;
  252. begin
  253.   x := wherex;
  254.   y := wherey;
  255.   repeat
  256.     gotoxy( x, y );
  257.     clreol;
  258.     Disp( HATTR, char( drv + 64 ) );
  259.     ch := KeyboardNorm;
  260.     case upcase(ch) of
  261.       ' ',
  262.       '+' : drv := 3 - drv;
  263.       'A' : drv := 1;
  264.       'B' : drv := 2;
  265.     end;
  266.   until ch in [#13,#27];
  267.   if ch = #27 then
  268.     SelectFloppy := 0
  269.   else
  270.     SelectFloppy := drv;
  271. end;
  272.  
  273. function CharValid( ch : char ) : boolean;
  274.   {
  275.   Determines if a character is a valid DOS file name character.
  276.   }
  277. const
  278.   ValChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&()_-~{}"';
  279. var
  280.   val : boolean;
  281. begin
  282.   val := (not funckey) and ( pos( ch, ValChars) <> 0 );
  283.   if not val then
  284.     Noise( 1000, 10 );
  285.   CharValid := val;
  286. end;
  287.  
  288. function GetLine( maxlen : integer ) : str80;
  289.   {
  290.   A nice non-breakable input routine.
  291.   }
  292. var
  293.   tstr : str80;
  294.   ch   : char;
  295.   len  : integer;
  296. begin
  297.   tstr := '';
  298.   repeat
  299.     ch  := KeyBoardNorm;
  300.     len := ord( tstr[0] );
  301.     case ch of
  302.       #13 : {};
  303.       #8  : if len > 0 then
  304.             begin
  305.               tstr := copy( tstr, 1, len - 1 );
  306.               write( ch, ' ', ch );
  307.             end;
  308.       else if (len < maxlen) then
  309.            begin
  310.              tstr := tstr + ch;
  311.              Disp( HATTR, ch );
  312.            end
  313.            else Noise( 500, 10 );
  314.     end;
  315.   until ch = #13;
  316.   GetLine := tstr;
  317. end;
  318.  
  319. Procedure WriteScreen;
  320.   {
  321.   Writes out boxes for windows once.  After that we just assume
  322.     that nothing can mess up our pretty little screens.
  323.     (Actually, I don't think much can)
  324.   }
  325. var
  326.   i    : integer;
  327.   tstr : str80;
  328. begin
  329.   window( 1, 1, 80, 25 );
  330.   clrscr;
  331.   fillchar( tstr, sizeof( str80 ), horzlin );
  332.   tstr[ 0] := #80;
  333.   tstr[ 1] := corn1;  tstr[40] := int1;  tstr[41] := int1; tstr[80] := corn2;
  334.   Display( 1, 1, BNATTR, tstr );
  335.   tstr[ 1] := tleft;  tstr[40] := int2;  tstr[41] := int2; tstr[80] := trght;
  336.   Display( 1, 3, BNATTR, tstr );
  337.   tstr[ 1] := corn3;  tstr[40] := int3;  tstr[41] := int3; tstr[80] := corn4;
  338.   Display( 1, 21, BNATTR, tstr );
  339.   fillchar( tstr[1], sizeof(str80)-1, ' ' );
  340.   tstr[1]  := vertlin;   tstr[40] := vertlin;
  341.   tstr[41] := vertlin;   tstr[80] := vertlin;
  342.   Display( 1, 2, BNATTR, tstr );
  343.   for i := 4 to 20 do
  344.     Display( 1, i, BNATTR, tstr );
  345. end;
  346.  
  347. procedure Colors;
  348.   {
  349.   Sets all the display colors according to the flag Color.
  350.   }
  351. begin
  352.   case Color of
  353.     true  : begin
  354.               PATTR    := Yellow;
  355.               NATTR    := White;
  356.               HATTR    := LightRed;
  357.               HATTR2   := LightMagenta;
  358.               BNATTR   := Blue;
  359.               BHATTR   := Blue * 16 + LightMagenta  {LightMagenta};
  360.               MATTR[1] := (LightGray * 16) + Red;
  361.               MATTR[2] := (Red       * 16) + White;
  362.             end;
  363.     false : begin
  364.               PATTR    := White;
  365.               NATTR    := LightGray;
  366.               HATTR    := White;
  367.               HATTR2   := White;
  368.               BNATTR   := LightGray;
  369.               BHATTR   := 16 * lightgray{White};
  370.               MATTR[1] := LightGray * 16;
  371.               MATTR[2] := LightGray * 16;
  372.             end;
  373.   end;
  374. end;
  375.  
  376. function EntrySize( E : Entry_T ) : real;
  377.   {
  378.   Calculates the file size of the entry passed in.
  379.   }
  380. var
  381.   i    : integer;
  382.   word : array[0..1] of real;
  383. begin
  384.   for i := 0 to 1 do
  385.   begin
  386.     if E.Size[i] < 0 then word[i] := E.Size[i] + 65536.0
  387.     else word[i] := E.Size[i];
  388.   end;
  389.   EntrySize := 65536.0 * word[1] + word[0];
  390. end;
  391.  
  392. function EntryTime( E : Entry_T ) : str6;
  393.   {
  394.   Returns a five character time string;
  395.     Time field is in the following word format
  396.               [hhhhhmmmmmmsssss]
  397.   }
  398. var
  399.   hrs, mins : str6;
  400. begin
  401.   str(  (E.Time            SHR 11):2, hrs );
  402.   str( ((E.Time AND $07FF) SHR 5 ):2, mins );
  403.   if mins[1] = ' ' then mins[1] := '0';
  404.   EntryTime := hrs + ':' + mins;
  405. end;
  406.  
  407. function EntryDate( E : Entry_T ) : str6;
  408.   {
  409.   Returns the date in a scrunched 6 character string;
  410.     Date field is in the following word format
  411.               [yyyyyyymmmmddddd]
  412.   }
  413. var
  414.   i             : integer;
  415.   temp, d, m, y : str6;
  416. begin
  417.   str( ((E.Date AND $01FF) SHR 5 ):2, m );
  418.   str(  (E.Date AND $001F        ):2, d );
  419.   str( ((E.Date SHR 9 )  +  1980 ):4, y );
  420.   temp := m + d + y[3] + y[4];
  421.   for i := 1 to 6 do
  422.     if temp[i] = ' ' then temp[i] := '0';
  423.   EntryDate := temp;
  424. end;
  425.  
  426. function EntryAttr( E : Entry_T ) : str6;
  427.   {
  428.   Returns a 4 character string for the file's attributes or
  429.     the »DEL string if it was deleted.
  430.     The volume and directory attributes are left out since they
  431.     will be represented by the (VOL) or <DIR> strings in place
  432.     of a file size.
  433.   }
  434. const
  435.   dstr = '»DEL';
  436. var
  437.   temp    : str6;
  438.   i, mask : integer;
  439. begin
  440.   if E.Name[1] = DelChar then
  441.     temp := dstr
  442.   else
  443.   begin
  444.     mask := Abit;          { Mask corresponds to the bit associated with  }
  445.     temp := 'ADVSHR';      {   attributes.  See the constants Abit - Rbit }
  446.     for i := 1 to 6 do     {   defined in sfmVARS.inc                     }
  447.     begin
  448.       if (E.Attr AND mask) = 0 then
  449.         temp[i] := ' ';
  450.       mask := mask SHR 1;
  451.     end;
  452.     delete( temp, 2, 2 );  { Remove the V and D attribute characters }
  453.   end;
  454.   EntryAttr := temp;
  455. end;
  456.  
  457. procedure WriteEntry( M : boolean; E : Entry_T );
  458.   {
  459.   Writes the entry specified at the current cursor postion.
  460.   }
  461. const
  462.   dstr = '<DIR> ';
  463.   vstr = '(VOL) ';
  464. var
  465.   tstr     : str80;
  466.   i, attr  : integer;
  467.   r        : real;
  468. begin
  469.   if M then attr := HATTR else attr := NATTR;
  470.   if E.Name[1] = NulChar then
  471.     Disp( attr, '  unused entry' )
  472.   else
  473.   begin
  474.     tstr := '  ';
  475.     move( E.Name[1], tstr[3], 11 );
  476.     tstr[0] := #13;
  477.     if tstr[3] = DelChar then tstr[3] := '?';
  478.     if (E.Attr AND Vbit) = 0 then insert( ' ', tstr, 11 )
  479.     else tstr := tstr + ' ';
  480.     Disp( attr, tstr );
  481.     r := EntrySize( E );
  482.     if (r <= 500) then r := KiloByte;
  483.  
  484.     if (E.Attr AND Dbit) <> 0 then
  485.       Disp( attr, dstr )
  486.     else if (E.Attr AND Vbit) <> 0 then
  487.       Disp( attr, vstr )
  488.     else
  489.       Disp( attr, Cstr( r / KiloByte,4,0 ) + 'K ' );
  490.  
  491.     Disp( attr, EntryDate(E)+' '+EntryTime(E)+' '+EntryAttr(E) );
  492.   end;
  493.   clreol;
  494. end;
  495.  
  496. function ConvertName( E : Entry_T ) : str80;
  497.   {
  498.   Provides the name of an entry as a 12 character or less string.
  499.   }
  500. var
  501.   tstr : str80;
  502.   i    : integer;
  503. begin
  504.   move( E.Name, tstr[1], 11 );
  505.   tstr[0] := #11;
  506.   insert( '.', tstr, 9 );
  507.  
  508.   while (tstr[ord(tstr[0])] = ' ') do
  509.     tstr[0] := char( ord(tstr[0]) - 1 );
  510.  
  511.   i := 8;
  512.   while (tstr[i] = ' ') and (i <> 0) do
  513.   begin
  514.     delete( tstr, i, 1 );
  515.     i := i - 1;
  516.   end;
  517.  
  518.   if ( (E.Attr AND Vbit) <> 0 ) or ( tstr[ord(tstr[0])] = '.' ) then
  519.     delete( tstr, pos( '.', tstr ), 1 );
  520.  
  521.   if tstr[1] = DelChar then
  522.     tstr[1] := '?';
  523.  
  524.   ConvertName := tstr;
  525. end;
  526.  
  527. function CheckMask( w, i : integer ) : boolean;
  528.   {
  529.   Checks the Entry[w][i] against the current mask string
  530.     to determine if it should be displayed or not.
  531.   }
  532. var
  533.   j     : integer;
  534.   match : boolean;
  535. begin
  536.   match := true;
  537.   j := 0;
  538.   repeat
  539.     j := j + 1;
  540.     if ConvMask[w][j] <> '?' then
  541.       if ConvMask[w][j] <> Entry[w][i].Name[j] then match := false;
  542.   until (j=11) or not match;
  543.   CheckMask := match;
  544. end;
  545.  
  546. function NextEntry( w, i : integer ) : integer;
  547.   {
  548.   Given the current entry, NextEntry returns the next entry
  549.     that is in the current mask.
  550.   }
  551. var
  552.   found : boolean;
  553. begin
  554.   if i = MaxEntry[w] then
  555.     NextEntry := 0
  556.   else
  557.   begin
  558.     if ShowAll then
  559.       NextEntry := i + 1
  560.     else
  561.     begin
  562.       found := false;
  563.       while (i < MaxEntry[w]) and not found do
  564.       begin
  565.         i := i + 1;
  566.         found := not ( Entry[w][i].Name[1] in [DelChar,NulChar] ) and
  567.                      ( ( Entry[w][i].Attr AND Vbit ) = 0 );
  568.         if found then
  569.           found := CheckMask( w, i );
  570.       end;
  571.       if found then NextEntry := i else NextEntry := 0;
  572.     end;
  573.   end;
  574. end;
  575.  
  576. function LastEntry( w, i : integer ) : integer;
  577.   {
  578.   Same as NextEntry but in the other direction.
  579.   }
  580. var
  581.   found : boolean;
  582. begin
  583.   if i = 1 then
  584.     LastEntry := 0
  585.   else
  586.   begin
  587.     if ShowAll then
  588.       LastEntry := i - 1
  589.     else
  590.     begin
  591.       found := false;
  592.       while (i > 1) and not found do
  593.       begin
  594.         i := i - 1;
  595.         found := not( Entry[w][i].Name[1] in [DelChar,NulChar] ) and
  596.                     ( ( Entry[w][i].Attr AND Vbit) = 0 );
  597.         if found then
  598.           found := CheckMask( w, i );
  599.       end;
  600.       if found then LastEntry := i else LastEntry := 0;
  601.     end;
  602.   end;
  603. end;
  604.  
  605. function TallySizes( w : integer ) : real;
  606.   {
  607.   Totals the sizes of all undeleted files in the directory.
  608.     This is a byte count of their directory entry size, not
  609.     the actual space used on disk.
  610.   }
  611. var
  612.   total : real;
  613.   i     : integer;
  614. begin
  615.   total := 0.0;
  616.   for i := 1 to MaxEntry[w] do
  617.     if not(Entry[w][i].Name[1] in [DelChar,NulChar]) then
  618.       total := total + EntrySize( Entry[w][i] );
  619.   TallySizes := total;
  620. end;
  621.  
  622. procedure Wind( w : integer );
  623.   {
  624.   Sets the window constants and Turbo's window to one
  625.     of the three windows used.
  626.   }
  627. begin
  628.   case w of
  629.     1 : begin X1 := 2;  X2 := 39;  Y1 := 4;  Y2 := 20;  end;
  630.     2 : begin X1 := 42; X2 := 79;  Y1 := 4;  Y2 := 20;  end;
  631.     3 : begin X1 := 2;  X2 := 79;  Y1 := 22; Y2 := 25;  end;
  632.   end;
  633.   window( X1, Y1, X2, Y2 );
  634. end;
  635.  
  636. procedure WriteSizes( w : integer; flag : boolean );
  637.   {
  638.   Write the space used by the directory and the amount of free
  639.     space on disk at the bottom of the window.
  640.     If flag is false then redraw the line at the bottom of the window.
  641.   }
  642. var
  643.   tstr : str80;
  644.   i    : integer;
  645. begin
  646.   Wind( w );
  647.   if not flag then
  648.   begin
  649.     fillchar( tstr[0], sizeof( str80 ), horzlin );
  650.     tstr[0] := #38;
  651.     Display( X1, Y2+1, BNATTR, tstr );
  652.   end
  653.   else
  654.   begin
  655.     Display( X1+1,  Y2+1, BNATTR, lbrk );
  656.     Display( X1+17, Y2+1, BNATTR, rbrk );
  657.     Display( X1+23, Y2+1, BNATTR, lbrk );
  658.     Display( X1+36, Y2+1, BNATTR, rbrk );
  659.     Display(X1+2 ,Y2+1,BHATTR,'DirSize ='+Cstr(DirSize[w]/KiloByte,5,0)+'K');
  660.     Display(X1+24,Y2+1,BHATTR,'Free ='+Cstr(DiskFree[w]/KiloByte,5,0)+'K');
  661.   end;
  662. end;
  663.  
  664. procedure WriteMask( w : integer; flag : boolean );
  665.   {
  666.   Similar to WriteSizes but writes the mask at the top of the
  667.     screen unless it happens to be '*.*'.
  668.   }
  669. var
  670.   tstr : str80;
  671.   i    : integer;
  672. begin
  673.   Wind( w );
  674.   fillchar( tstr, sizeof(str80), horzlin );
  675.   tstr[0] := #38;
  676.   Display( X1, Y1-1, BNATTR, tstr );
  677.   if flag then
  678.   begin
  679.     if ConvMask[w] <> '???????????' then
  680.     begin
  681.       Display( X1+1, Y1-1, BNATTR, lbrk );
  682.       Display( X1+ord(Mask[w][0])+9, Y1-1, BNATTR, rbrk);
  683.       Display( X1+2, Y1-1, BHATTR, 'Mask = ' + Mask[w] );
  684.     end;
  685.   end;
  686. end;
  687.  
  688. procedure WriteWindow( w : integer );
  689.   {
  690.   Rewrites the window specified and calls the routines
  691.     above to write the sizes and current mask.
  692.   }
  693. var
  694.   tstr  : str80;
  695.   x,i,j : integer;
  696. begin
  697.   Wind( w );
  698.   fillchar( tstr, sizeof(str80), ' ' );
  699.   tstr[0] := #38;
  700.   Display( X1, Y1-2, HATTR2, tstr );
  701.   if loaded[w] then
  702.   begin
  703.     tstr := Path[w];
  704.     if ord( tstr[0] ) > 38 then tstr := copy( tstr, ord(tstr[0])-37, 38 );
  705.     x := 19 - ( ord(tstr[0]) div 2 );
  706.     Display( X1+x, Y1-2, HATTR2, tstr );
  707.   end;
  708.   if HelpScreen[w] or not loaded[w] then
  709.   begin
  710.     WriteMask( w, false );
  711.     WriteSizes( w, false );
  712.   end
  713.   else
  714.   begin
  715.     WriteMask( w, not ShowAll );
  716.     i := TopEntry[w];
  717.     j := 1;
  718.     while (i <> 0) and (j <= WindowLen) do
  719.     begin
  720.       gotoxy( 1, j );
  721.       WriteEntry( Marked[w][i], Entry[w][i] );
  722.       i := NextEntry( w, i );
  723.       j := j + 1;
  724.     end;
  725.     for i := j to WindowLen do
  726.     begin
  727.       gotoxy( 1, i );
  728.       clreol;
  729.     end;
  730.     WriteSizes( w, true );
  731.     gotoxy( 1, CurLin[w] );
  732.     Disp( PATTR, ' ' + PtrChar );
  733.   end;
  734. end;
  735.  
  736. procedure MarkAll( w : integer );
  737.   {
  738.   Mark all files except those that can't be marked.
  739.     (e.g. directories or deleted files can't be marked)
  740.   }
  741. var
  742.   i : integer;
  743. begin
  744.   i := NextEntry( w, 0 );
  745.   repeat
  746.     if (Entry[w][i].Attr AND Dbit) = 0 then
  747.       Marked[w][i] := true
  748.     else
  749.       Marked[w][i] := false;
  750.     i := NextEntry( w, i );
  751.   until i = 0;
  752.   WriteWindow( w );
  753. end;
  754.  
  755. procedure ClearMarks( w : integer );
  756. begin
  757.   fillchar( Marked[w], sizeof(MarkedArr_T), 0 );
  758.   WriteWindow( w );
  759. end;
  760.  
  761. procedure MarkEntry( w : integer );
  762. begin
  763.   if CurEntry[w] <> 0 then
  764.   begin
  765.     if (Entry[w][CurEntry[w]].Attr AND Dbit) = 0 then
  766.     begin
  767.       Marked[w][CurEntry[w]] := true;
  768.       gotoxy( 1, CurLin[w] );
  769.       WriteEntry( true, Entry[w][CurEntry[w]] );
  770.     end;
  771.   end;
  772. end;
  773.  
  774. procedure UnMarkEntry( w : integer );
  775. begin
  776.   if CurEntry[w] <> 0 then
  777.   begin
  778.     Marked[w][CurEntry[w]] := false;
  779.     gotoxy( 1, CurLin[w] );
  780.     WriteEntry( false, Entry[w][CurEntry[w]] );
  781.   end;
  782. end;
  783.  
  784. procedure HomeKey( w : integer );
  785. begin
  786.   CurLin[w] := 1;
  787.   CurEntry[w] := NextEntry( w, 0 );
  788.   TopEntry[w] := CurEntry[w];
  789.   WriteWindow( w );
  790. end;
  791.  
  792. procedure EndKey( w : integer );
  793. var
  794.   i, j : integer;
  795. begin
  796.   if CurEntry[w] <> 0 then
  797.   begin
  798.     j := 0;
  799.     i := MaxEntry[w] + 1;
  800.     TopEntry[w] := 0;
  801.     CurEntry[w] := LastEntry( w, i );
  802.     repeat
  803.       i := LastEntry( w, i );
  804.       if i <> 0 then
  805.       begin
  806.         TopEntry[w] := i;
  807.         j := j + 1;
  808.       end;
  809.     until (j = WindowLen) or (i = 0);
  810.     CurLin[w] := j;
  811.     WriteWindow( w );
  812.   end;
  813. end;
  814.  
  815. procedure UpKey( w : integer );
  816. var
  817.   i : integer;
  818. begin
  819.   if CurEntry[w] <> 0 then
  820.   begin
  821.     i := LastEntry( w, CurEntry[w] );
  822.     if i <> 0 then
  823.     begin
  824.       if CurLin[w] <> 1 then
  825.         CurLin[w] := CurLin[w] - 1
  826.       else
  827.       begin
  828.         TopEntry[w] := i;
  829.         Display( X1, Y1, PATTR, '  ' );
  830.         gotoxy( 1, 1 );
  831.         insline;
  832.         WriteEntry( Marked[w][i], Entry[w][i] );
  833.       end;
  834.       CurEntry[w] := i;
  835.     end;
  836.   end;
  837. end;
  838.  
  839. procedure DownKey( w : integer );
  840. var
  841.   i : integer;
  842. begin
  843.   if CurEntry[w] <> 0 then
  844.   begin
  845.     i := NextEntry( w, CurEntry[w] );
  846.     if i <> 0 then
  847.     begin
  848.       if CurLin[w] <> WindowLen then
  849.         CurLin[w] := CurLin[w] + 1
  850.       else
  851.       begin
  852.         TopEntry[w] := NextEntry( w, TopEntry[w] );
  853.         gotoxy( 1, WindowLen );
  854.         WriteEntry( Marked[w][CurEntry[w]], Entry[w][CurEntry[w]] );
  855.         writeln;
  856.         WriteEntry( Marked[w][i], Entry[w][i] );
  857.       end;
  858.       CurEntry[w] := i;
  859.     end;
  860.   end;
  861. end;
  862.  
  863. procedure PgUp( w : integer );
  864. var
  865.   i, j : integer;
  866. begin
  867.   if CurEntry[w] <>  0 then
  868.   begin
  869.     j := 0;
  870.     i := TopEntry[w];
  871.     repeat
  872.       i := LastEntry( w, i );
  873.       if i <> 0 then
  874.       begin
  875.         j := j + 1;
  876.         TopEntry[w] := i;
  877.         CurEntry[w] := LastEntry( w, CurEntry[w] );
  878.       end;
  879.     until (i = 0) or (j = WindowLen);
  880.     if i = 0 then HomeKey( w )
  881.     else WriteWindow( w );
  882.   end;
  883. end;
  884.  
  885. procedure PgDown( w : integer );
  886. var
  887.   i, j : integer;
  888. begin
  889.   if CurEntry[w] <> 0 then
  890.   begin
  891.     i := CurEntry[w];
  892.     j := 0;
  893.     repeat
  894.       i := NextEntry( w, i );
  895.       if i <> 0 then
  896.       begin
  897.         j := j + 1;
  898.         CurEntry[w] := i;
  899.         TopEntry[w] := NextEntry( w, TopEntry[w] )
  900.       end;
  901.     until (j = WindowLen) or (i = 0);
  902.     if i <> 0 then
  903.     begin
  904.       j := CurLin[w];
  905.       while (j <> WindowLen) and (i <> 0) do
  906.       begin
  907.         j := j + 1;
  908.         i := NextEntry( w, i );
  909.       end;
  910.     end;
  911.     if i = 0 then EndKey( w )
  912.     else WriteWindow( w );
  913.   end;
  914. end;
  915.  
  916. procedure MoveEntry( w : integer );
  917.   {
  918.   With this procedure we need to rewrite each of the screen control
  919.     procedures since we aren't just moving the pointer, we're moving
  920.     files around too.  For this reason there are several procedures
  921.     local to MoveEntry with the same names as used from the main menus.
  922.   }
  923. var
  924.   tEntry : Entry_T;
  925.   i      : integer;
  926.  
  927.   procedure Exchange( i, j : integer );  { Local to MoveEntry }
  928.   begin
  929.     tEntry:= Entry[w][i];
  930.     Entry[w][i] := Entry[w][j];
  931.     Entry[w][j] := tEntry;
  932.   end;
  933.  
  934.   procedure UpKey;                       { Local to MoveEntry }
  935.   begin
  936.     if CurEntry[w] >  1 then
  937.     begin
  938.       gotoxy( 1, CurLin[w] );
  939.       WriteEntry( false, Entry[w][CurEntry[w]-1] );
  940.       if CurLin[w] <> 1 then
  941.         CurLin[w] := CurLin[w] - 1
  942.       else
  943.       begin
  944.         insline;
  945.         TopEntry[w] := TopEntry[w] - 1;
  946.       end;
  947.       gotoxy( 1, CurLin[w] );
  948.       WriteEntry( true, Entry[w][CurEntry[w]] );
  949.       Exchange( CurEntry[w], CurEntry[w]-1 );
  950.       CurEntry[w] := CurEntry[w] - 1;
  951.     end;
  952.   end;
  953.  
  954.   procedure DownKey;                        { Local to MoveEntry }
  955.   begin
  956.     if CurEntry[w] < MaxEntry[w] then
  957.     begin
  958.       gotoxy( 1, CurLin[w] );
  959.       WriteEntry( false, Entry[w][CurEntry[w]+1] );
  960.       if CurLin[w] <> WindowLen then
  961.         CurLin[w] := CurLin[w] + 1
  962.       else
  963.       begin
  964.         writeln;
  965.         TopEntry[w] := TopEntry[w] + 1;
  966.       end;
  967.       gotoxy( 1, CurLin[w] );
  968.       WriteEntry( true, Entry[w][CurEntry[w]] );
  969.       Exchange( CurEntry[w], CurEntry[w]+1 );
  970.       CurEntry[w] := CurEntry[w] + 1;
  971.     end;
  972.   end;
  973.  
  974.   procedure MoveHome;                  { Local to MoveEntry }
  975.   begin
  976.     if CurEntry[w] > 1 then
  977.     begin
  978.       tEntry := Entry[w][CurEntry[w]];
  979.       for i := CurEntry[w] downto 2 do
  980.         Entry[w][i] := Entry[w][i-1];
  981.       Entry[w][1] := tEntry;
  982.       HomeKey( w );
  983.       gotoxy( 1, CurLin[w] );
  984.       WriteEntry( true, Entry[w][CurEntry[w]] );
  985.     end;
  986.   end;
  987.  
  988.   procedure MoveEnd;                { Local to MoveEntry }
  989.   begin
  990.     if CurEntry[w] < MaxEntry[w] then
  991.     begin
  992.       tEntry := Entry[w][CurEntry[w]];
  993.       for i := CurEntry[w] to MaxEntry[w]-1 do
  994.         Entry[w][i] := Entry[w][i+1];
  995.       Entry[w][MaxEntry[w]] := tEntry;
  996.       EndKey( w );
  997.       gotoxy( 1, CurLin[w] );
  998.       WriteEntry( true, Entry[w][CurEntry[w]] );
  999.     end;
  1000.   end;
  1001.  
  1002.   procedure PgUp;                  { Local to MoveEntry }
  1003.   begin
  1004.     if CurEntry[w] <> 1 then
  1005.     begin
  1006.       if TopEntry[w] - WindowLen < 1 then
  1007.         MoveHome
  1008.       else
  1009.       begin
  1010.         tEntry := Entry[w][CurEntry[w]];
  1011.         for i := CurEntry[w] downto CurEntry[w] - WindowLen + 1 do
  1012.           Entry[w][i] := Entry[w][i-1];
  1013.         CurEntry[w] := CurEntry[w] - WindowLen;
  1014.         TopEntry[w] := TopEntry[w] - WindowLen;
  1015.         Entry[w][CurEntry[w]] := tEntry;
  1016.         WriteWindow( w );
  1017.         gotoxy( 1, CurLin[w] );
  1018.         WriteEntry( true, Entry[w][CurEntry[w]] );
  1019.       end;
  1020.     end;
  1021.   end;
  1022.  
  1023.   procedure PgDown;                 { Local to MoveEntry }
  1024.   begin
  1025.     if CurEntry[w] <> MaxEntry[w] then
  1026.     begin
  1027.       if TopEntry[w] + (2 * WindowLen) > MaxEntry[w] then
  1028.         MoveEnd
  1029.       else
  1030.       begin
  1031.         tEntry := Entry[w][CurEntry[w]];
  1032.         for i := CurEntry[w] to CurEntry[w]+WindowLen-1 do
  1033.           Entry[w][i] := Entry[w][i+1];
  1034.         CurEntry[w] := CurEntry[w] + WindowLen;
  1035.         TopEntry[w] := TopEntry[w] + WindowLen;
  1036.         Entry[w][CurEntry[w]] := tEntry;
  1037.         WriteWindow( w );
  1038.         gotoxy( 1, CurLin[w] );
  1039.         WriteEntry( true, Entry[w][CurEntry[w]] );
  1040.       end;
  1041.     end;
  1042.   end;
  1043.  
  1044. var                             { Actual start of MoveEntry }
  1045.   ch : char;
  1046.   tstr : str80;
  1047. begin
  1048.   if CurEntry[w] <> 0 then
  1049.   begin
  1050.     Wind( 3 );
  1051.     clrscr;
  1052.     writeln;
  1053.     tstr := ConvertName( Entry[w][CurEntry[w]] );
  1054.     Disp( NATTR, ' Moving file ' );
  1055.     Disp( HATTR, tstr );
  1056.     Disp( NATTR, ', press F10 when in position.' );
  1057.     Wind( w );
  1058.     gotoxy( 1, CurLin[w] );
  1059.     WriteEntry( true, Entry[w][CurEntry[w]] );
  1060.     repeat
  1061.       gotoxy( 1, CurLin[w] );
  1062.       Display( x1, y1+CurLin[w]-1, PATTR, ' '+PtrChar );
  1063.       CursorON;
  1064.       ch := Keyboard;
  1065.       CursorOFF;
  1066.       case ch of
  1067.         #72 : UpKey;
  1068.         #80 : DownKey;
  1069.         #73 : PgUp;
  1070.         #81 : PgDown;
  1071.         #71 : MoveHome;
  1072.         #79 : MoveEnd;
  1073.       end;
  1074.     until (funckey and (ch = #68)) or (ch = #13);  { Done when F10 is pressed }
  1075.     gotoxy( 1, CurLin[w] );
  1076.     WriteEntry( false, Entry[w][CurEntry[w]] );
  1077.     Saved[w] := false;
  1078.   end;
  1079. end;
  1080.  
  1081. procedure MaxFileMessage;
  1082. begin
  1083.   Wind( 3 );
  1084.   clrscr;
  1085.   writeln;
  1086.   Disp( NATTR, ' Warning: ' );
  1087.   Disp( HATTR, 'Directory exceeds file limit, menu 2 save option disabled' );
  1088.   writeln;
  1089.   gotoxy( 11, wherey );
  1090.   wait;
  1091. end;
  1092.  
  1093. procedure DupPathMessage;
  1094. begin
  1095.   writeln;
  1096.   Disp( NATTR, ' Error: ' );
  1097.   Disp( HATTR, 'Windows must have different paths.' );
  1098.   writeln;
  1099.   gotoxy( 9, wherey );
  1100.   wait;
  1101. end;
  1102.  
  1103. procedure GetColor;
  1104.   {
  1105.   Startup screen and prompt for Color override.  Why, you ask, do
  1106.     I allow the user to specify whether or not they have a color
  1107.     system when I have already read their hardware configuration?
  1108.     Well, because those poor souls with monochrome monitors and CGA
  1109.     cards wouldn't get a very good display if I didn't.
  1110.   }
  1111. var
  1112.   MA : real;
  1113. begin
  1114.   x1 := 10;
  1115.   y1 := 3;
  1116.   textbackground( Black );
  1117.   clrscr;
  1118.   window( x1, y1, 80, 25 );   { Use light text colors so they will show }
  1119.   gotoxy( 1, 1 );             {   on all systems.                       }
  1120.   textcolor( HATTR2 );
  1121.   writeln( '                 - Super File Manager '+ version +' -' );
  1122.   writeln;
  1123.   textcolor( NATTR );
  1124.   writeln( '                      David Steiner' );
  1125.   writeln( '                      2035 J Apt. 6' );
  1126.   writeln( '                      Lincoln, NE  68510' );
  1127.   writeln( '                      (402) 475-0601' );
  1128.   writeln( '                      June 1, 1987' );
  1129.   writeln;
  1130.   textcolor( PATTR );
  1131.   writeln( '   Capitol PC User Group 1987 Software Programming Contest' );
  1132.   textcolor( HATTR2 );
  1133.   writeln;
  1134.   writeln( 'Permission is granted for Capital PC and other not for profit' );
  1135.   writeln( 'organizations to publish the source and executable portions of' );
  1136.   writeln( 'this program.' );
  1137.   writeln;
  1138.   writeln;
  1139.   textcolor( NATTR );
  1140.   MA := MemoryAvail;
  1141.   write  ( '            Copy buffer =' + Cstr( MA, 7, 0 ) );
  1142.   writeln( ' bytes  ( ' + Cstr( MA/KiloByte, 0, 0 ) + 'K )' );
  1143.   writeln;
  1144.   writeln;
  1145.   textcolor( NATTR );
  1146.   write  ( '                Is this a color system' );
  1147.   color := YorN( color );
  1148. end;
  1149.  
  1150. procedure WriteHelp1;
  1151.   {
  1152.   Screen shown on right side when program started up.
  1153.   }
  1154. begin
  1155.   Wind( 2 );
  1156.   clrscr;
  1157.   Display( X1, Y1-2, HATTR2,'     - Super File Manager '+version+' -' );
  1158. {               |--------------------------------------|  }
  1159.   writeln;
  1160.   Disp( PATTR,  '          Standard Features:' );            writeln;
  1161.   writeln;
  1162.   Disp( NATTR,  '      Mark files to be managed' );          writeln;
  1163.   Disp( NATTR,  '       Copy, delete, rename...' );          writeln;
  1164.   Disp( NATTR,  '     Create, remove directories' );         writeln;
  1165.   writeln;
  1166.   Disp( PATTR,  '        Outstanding Features:' );           writeln;
  1167.   writeln;
  1168.   Disp( NATTR,  '     Mask files being displayed' );         writeln;
  1169.   Disp( NATTR,  '        Reorder directories' );             writeln;
  1170.   Disp( NATTR,  '     Move files without copying' );         writeln;
  1171.   Disp( NATTR,  '  Full memory usage for copy buffer' );     writeln;
  1172.   Disp( NATTR,  '   Change/clear disks during copy' );       writeln;
  1173.   writeln;
  1174.   Disp( HATTR2, '       ( Press F2 for help )' );
  1175. end;
  1176.  
  1177. procedure HelpWindow( var w : integer; helpw : integer );
  1178.   {
  1179.   Display help when asked for.  Uses enough logic to always
  1180.     open the window on the unused side even if the key for
  1181.     the other side was entered.
  1182.   }
  1183. begin
  1184.   HelpScreen[helpw] := not HelpScreen[helpw];
  1185.   if not HelpScreen[helpw] then
  1186.   begin
  1187.     if Loaded[helpw] then WriteWindow( helpw )
  1188.     else HelpScreen[helpw] := true;
  1189.   end
  1190.   else
  1191.   begin
  1192.     if not loaded[3-helpw] then     { If a window is not used then put help }
  1193.     begin                           {   there by default.                   }
  1194.       HelpScreen[helpw] := false;
  1195.       helpw := 3 - helpw;
  1196.       HelpScreen[helpw] := true;
  1197.     end
  1198.     else if HelpScreen[3-helpw] then
  1199.     begin
  1200.       HelpScreen[3-helpw] := false;
  1201.       WriteWindow( 3-helpw );
  1202.     end;
  1203.     if helpw = w then w := 3 - w;
  1204.   end;
  1205.   if HelpScreen[helpw] then
  1206.   begin
  1207.     WriteWindow( helpw );
  1208.     Display( x1, 2, HATTR2, '     - Super File Manager ' + version + ' -' );
  1209.     clrscr;
  1210. {                 |--------------------------------------|  }
  1211.     writeln;
  1212.     Disp( PATTR,  '               Help!!' );                 writeln;
  1213.     writeln;
  1214.     Disp( NATTR,  '     F1,F2: This help window' );          writeln;
  1215.     Disp( NATTR,  '     F3,F4: Load subdirectory' );         writeln;
  1216.     Disp( NATTR,  '     F5,F6: Load path entered' );         writeln;
  1217.     Disp( NATTR,  '     F7,F8: Select command ' );           writeln;
  1218.     Disp( NATTR,  '        F9: Mark file' );                 writeln;
  1219.     Disp( NATTR,  '       F10: Remove mark' );               writeln;
  1220.     Disp( NATTR,  '       Del: Delete file or directory' );  writeln;
  1221.     writeln;
  1222.     Disp( NATTR,  '   Cursor keys: Move file pointer' );     writeln;
  1223.     writeln;
  1224.     Disp( NATTR,  '  Shift-Cursor keys: Select command' );   writeln;
  1225.     writeln;
  1226.     Disp( NATTR,  '       RETURN: Execute command' );
  1227.   end;
  1228. end;
  1229.  
  1230. procedure Menu2Window( w : integer );
  1231. begin
  1232.   HelpScreen[3-w] := true;
  1233.   WriteWindow( 3-w );
  1234.   clrscr;
  1235.   Display( X1, Y1-2, HATTR2, '     - Advanced Functions Menu -' );
  1236. {               |--------------------------------------|  }
  1237.   writeln;
  1238.   Disp( NATTR,  ' Changes are not made directly to the' );      writeln;
  1239.   Disp( NATTR,  '  disk, you must Update any changes.' );       writeln;
  1240.   writeln;
  1241.   Disp( HATTR,  ' Do not change disks when using these' );      writeln;
  1242.   Disp( HATTR,  '  functions.  Updating the wrong one' );       writeln;
  1243.   Disp( HATTR,  '     may result in a loss of data.' );         writeln;
  1244.   writeln;
  1245.   Disp( NATTR,  '        F7,F8: Select command' );              writeln;
  1246.   Disp( NATTR,  '           F9: Pick up file' );                writeln;
  1247.   Disp( NATTR,  '          F10: Drop file' );                   writeln;
  1248.   writeln;
  1249.   Disp( NATTR,  '    Cursor keys: Move file pointer' );         writeln;
  1250.   Disp( NATTR,  '  Shift-Cursor keys: Select command' );        writeln;
  1251.   writeln;
  1252.   Disp( NATTR,  '        RETURN: Execute command' );            writeln;
  1253. end;
  1254.  
  1255. procedure CopyInfo( w : integer );
  1256.   {
  1257.   Show the amount of space required to store the marked
  1258.     files on any disks that we currently have information for.
  1259.   }
  1260. const
  1261.   fits : array[false..true] of str10 = (' Won''t Fit','  Will Fit');
  1262. var
  1263.   CLsize                : array[1..2] of integer;
  1264.   dsize, dskfr          : array[1..2] of real;
  1265.   size, tempsize, tempR : real;
  1266.   i, j, k               : integer;
  1267.   drivech               : char;
  1268. begin
  1269.   with DiskTable[w]^ do
  1270.     CLsize[w] := SECTORSIZE * (CLUSTERSIZE+1);
  1271.   dskfr[w] := DiskFree[w];
  1272.  
  1273.   drivech := #00;
  1274.   if loaded[3-w] and (Drive[w] <> Drive[3-w]) then
  1275.   begin
  1276.     drivech := Path[3-w][1];
  1277.     with DiskTable[3-w]^ do
  1278.       CLsize[3-w] := SECTORSIZE * (CLUSTERSIZE+1);
  1279.     dskfr[3-w] := DiskFree[3-w];
  1280.   end
  1281.   else
  1282.     CLsize[3-w] := CLsize[w];
  1283.  
  1284.   for j := 1 to 2 do dsize[j] := 0;
  1285.   k := 0;
  1286.   i := NextEntry( w, 0 );
  1287.   while (i <> 0) do
  1288.   begin
  1289.     if Marked[w][i] then
  1290.     begin
  1291.       k := k + 1;
  1292.       tempsize := EntrySize( Entry[w][i] );
  1293.       size := size + tempsize;
  1294.       for j := 1 to 2 do
  1295.       begin
  1296.         tempR := tempsize / CLsize[j];
  1297.         if frac( tempR ) <> 0.0 then tempR := trunc( tempR ) + 1
  1298.         else tempR := trunc( tempR );
  1299.         dsize[j] := dsize[j] + ( tempR * CLsize[j] );
  1300.       end;
  1301.     end;
  1302.     i := NextEntry( w, i );
  1303.   end;
  1304.   if k <> 0 then
  1305.   begin
  1306.     Wind( 3 );
  1307.     clrscr;
  1308.     Disp( NATTR, ' Total size of' );
  1309.     Disp( HATTR, Cstr( k, 3, 0 ) );
  1310.     Disp( NATTR, ' marked file(s)   =' + Cstr( size, 8, 0 ) + ' bytes' );
  1311.     writeln;
  1312.     Disp( NATTR, '    Disk space required ');
  1313.     i := wherex;
  1314.     Disp( NATTR, 'on drive ' + Path[w][1] + ' =' );
  1315.     Disp( HATTR, Cstr( dsize[w], 8, 0 ) + '         ('
  1316.                + Cstr( round(dsize[w] / KiloByte),5, 0 ) + 'K )' );
  1317.     Disp( NATTR, fits[ (dsize[w] <= dskfr[w]) ] );
  1318.     writeln;
  1319.     if drivech <> #00 then
  1320.     begin
  1321.       gotoxy( i, wherey );
  1322.       Disp( NATTR, 'on drive ' + drivech + ' =' );
  1323.       Disp( HATTR, Cstr( dsize[3-w], 8, 0 ) + '         ('
  1324.                  + Cstr( round(dsize[3-w] / KiloByte),5,0) + 'K )' );
  1325.       Disp( NATTR, fits[ (dsize[3-w] <= dskfr[3-w]) ] );
  1326.     end;
  1327.     writeln;
  1328.     gotoxy( 25, wherey );
  1329.     wait;
  1330.   end;
  1331. end;
  1332.  
  1333. procedure TechInfo( w : integer );
  1334.   {
  1335.   Show specific information about the current disk.
  1336.   }
  1337. var
  1338.   tstr  : str80;
  1339.   tempR : real;
  1340.   i     : integer;
  1341. begin
  1342.   WriteWindow( 3-w );
  1343.   clrscr;
  1344.   Display( x1, 2, HATTR2, '    - Disk Technical Information -' );
  1345.   {               |--------------------------------------|  }
  1346.   writeln;
  1347.   with DiskTable[w]^ do
  1348.   begin
  1349.     Disp( NATTR,  '           Bytes per sector = ' + Cstr(SECTORSIZE,0,0) );
  1350.     writeln;
  1351.     Disp( NATTR,  '        Sectors per cluster = ' + Cstr(CLUSTERSIZE+1,0,0) );
  1352.     writeln;
  1353.     Disp( NATTR,  '     Total clusters on disk = ' + Cstr(MAXCLUSTER-1,0,0) );
  1354.     writeln;
  1355.     writeln;
  1356.     tempR := 1.0 * SECTORSIZE * (CLUSTERSIZE+1) * (MAXCLUSTER-1);
  1357.     if tempR > KiloByte * KiloByte then
  1358.       tstr := Cstr( tempR / (KiloByte * KiloByte),0,0 ) + ' Meg'
  1359.     else
  1360.       tstr := Cstr( tempR / KiloByte,0,0 ) + 'K';
  1361.     Disp( PATTR,  ' Total disk storage (bytes) = ' + tstr );
  1362.     writeln;
  1363.     writeln;
  1364.     writeln;
  1365.     Disp( NATTR,  ' Sectors used by DOS bootstrap = '+Cstr(BOOTSIZE,0,0) );
  1366.     writeln;
  1367.     Disp( NATTR,  '          Number of FAT copies = '+Cstr(NFATS,0,0) );
  1368.     writeln;
  1369.     Disp( NATTR,  '          Sectors per FAT copy = '+Cstr(FATSIZE,0,0) );
  1370.     writeln;
  1371.     Disp( NATTR,  '   Max files in root directory = '+Cstr(ROOTENTRIES,0,0) );
  1372.     writeln;
  1373.     i := DATASECTOR - ROOTSECTOR;
  1374.     Disp( NATTR,  '      Sectors occupied by root = '+Cstr(i,0,0) );
  1375.     writeln;
  1376.     writeln;
  1377.     i := i + BOOTSIZE + NFATS * FATSIZE;
  1378.     Disp( PATTR,  '     Total sectors used by DOS = '+Cstr(i,0,0) );
  1379.     writeln;
  1380.     if (Drive[w] <> 1) and (DiskTable[w]^.DRIVE2 = 0) then
  1381.       tstr := 'a RAM DISK (format specifications not valid).'
  1382.     else
  1383.     begin
  1384.       case DiskTable[w]^.FATATTR of
  1385.         $FF : tstr := 'double sided, 8 sectored and has 40 tracks.';
  1386.         $FE : tstr := 'single sided, 8 sectored and has 40 tracks.';
  1387.         $FD : tstr := 'double sided, 9 sectored and has 40 tracks.';
  1388.         $FC : tstr := 'single sided, 9 sectored and has 40 tracks.';
  1389.         $FB : tstr := 'double sided, 8 sectored and has 80 tracks.';
  1390.         $F9 : tstr := 'double sided, 15 (or 9) sectored and has 80 tracks.';
  1391.         $F8 : tstr := 'a fixed disk (format specifications not shown).';
  1392.         else  tstr := 'an unknown type of device.';
  1393.       end;
  1394.     end;
  1395.     Wind( 3 );
  1396.     clrscr;
  1397.     writeln;
  1398.     Disp( NATTR, ' Drive ' + Path[w][1] + ' is ' + tstr );
  1399.   end;
  1400.   writeln;
  1401.   writeln;
  1402.   gotoxy( 20, wherey );
  1403.   wait;
  1404.   Menu2Window( w );
  1405. end;
  1406.  
  1407.