home *** CD-ROM | disk | FTP | other *** search
/ ftp.update.uu.se / ftp.update.uu.se.2014.03.zip / ftp.update.uu.se / pub / rainbow / msdos / decus / RB139 / wut312sc.lzh / WUPART.PAS < prev    next >
Pascal/Delphi Source File  |  1988-12-15  |  39KB  |  965 lines

  1. {** file wupart.pas ⌐ Copyright 1986 Anthony G. Camas, all rights reserved **}
  2. { RepartitionDisk - Repartitions the hard disk. }
  3. overlay procedure RepartitionDisk;
  4.  
  5. Const
  6.   { The following represent the standard positions in the OSN block for the
  7.     three Rainbow operating systems.  In theory, each partition is defined
  8.     with an index into the OSN area, and the name stored there determines
  9.     which operating system the partition uses.  In practice, this is nearly
  10.     unworkable, and all the operating systems search for their partitions
  11.     by finding those which use a specific OSN index.  We will take our cue
  12.     from that practice, and use fixed OSN index values for determining which
  13.     operating system a partition uses.  The names we display in the editing
  14.     phase will still come from the OSN block, but we'll use these indexes
  15.     to determine how to initialize the partitions, to determine the maximum
  16.     size of a partition, etc. }
  17.   CPMIndex = 1;
  18.   MSDOSIndex = 2;
  19.   CCPMIndex = 3;
  20.  
  21. Type
  22.   PartitionData = Record
  23.     Tracks   :Integer;  { Number of tracks devoted to partition }
  24.     OS       :Integer;  { Index of operating system in OSN block }
  25.     Descrip  :Str80;    { Description line for partition to show on screen }
  26.   End;
  27.  
  28. Var
  29.   DiskCapacity          :Integer; { In tracks }
  30.   AllocDifference       :Integer; { Diff between capacity and amount actually
  31.                                     allocated, in tracks.  + means allocation is
  32.                                     over capacity, - means allocation is under }
  33.   Partition             :Array [1..30] of PartitionData;
  34.   TempDPD               :SectorBlock;
  35.   TempPartition,
  36.   DeletedPartition      :PartitionData;
  37.   Trk                   :Integer;
  38.   Sec                   :Byte;
  39.   LastOS                :Integer;
  40.   LastCPMUnit           :Integer;
  41.   NumberOfPartitions    :Integer;
  42.   Current               :Integer;
  43.   I, J, K               :Integer;
  44.   DisplayNeeded         :Boolean;
  45.   DoKeyPressed          :Boolean;
  46.   SomethingDeleted      :Boolean;
  47.   ErrorExists           :Boolean;
  48.   ErrorMessage          :String [45];
  49.   MaxMSDOSSize          :Integer;
  50.  
  51.   {sub}function MinSize (OS :Integer) :Integer;
  52.   { Return the minimum size (in tracks) allowed for an entry (partition or
  53.     gap), given the operating system code }
  54.   begin
  55.     if OS = -1 then MinSize := 1 else MinSize := 128;
  56.   end {MinSize};
  57.  
  58.   {sub}function MaxSize (OS :Integer) :Integer;
  59.   { Return the maximum size (in tracks) allowed for an entry (partition or
  60.     gap), given the operating system code }
  61.   begin
  62.     if OS = -1 then MaxSize := DiskCapacity
  63.     else if OS = MSDOSIndex then MaxSize := MaxMSDOSSize
  64.     else MaxSize := 1024;
  65.   end {MaxSize};
  66.  
  67.   {sub}procedure FormDescrip (Var Part :PartitionData);
  68.   var
  69.     Trks  :String [4];
  70.     MB    :String [5];
  71.     MBVal :Real;
  72.   begin
  73.     MBVal := Part.Tracks * 8.0 / 1024.0;
  74.     Str (Part.Tracks:4, Trks);
  75.     Str (MBVal:5:3, MB);
  76.     Part.Descrip := Concat (Trks, ' (', MB, ') ');
  77.     if Part.OS = -1 then
  78.       Part.Descrip := Concat (Part.Descrip, '----- GAP ----- ')
  79.     else
  80.       Part.Descrip := Concat (Part.Descrip, OSNBlock.OSN.Entry[Part.OS]);
  81.   end {FormDescrip};
  82.  
  83.   {sub}procedure ReadySummaryLine;
  84.   begin
  85.     FVRow := 22; FVColumn := 2; FVAttribute := AtNormal;
  86.     WriteFast
  87. ('                                                                             ');
  88.     FVRow := 22; FVColumn := 5;
  89.   end {ReadySummaryLine};
  90.  
  91.   {sub}procedure PrintSummary;
  92.   var
  93.     Temp :String [10];
  94.     Line :String [80];
  95.   begin
  96.     ReadySummaryLine;
  97.     Str (DiskCapacity, Temp);
  98.     Line := Concat ('Disk capacity is ', Temp, ' tracks (');
  99.     Str ((DiskCapacity * 8.0 / 1024.0):5:3, Temp);
  100.     Line := Concat (Line, Temp, 'MB).  ');
  101.     WriteFast (Line);
  102.     if AllocDifference = 0 then
  103.       WriteFast ('All tracks allocated.')
  104.     else if AllocDifference < 0 then
  105.     begin
  106.       Str ((-AllocDifference), Temp);
  107.       Line := Concat (Temp, ' track');
  108.       if AllocDifference < -1 then Line := Concat (Line, 's');
  109.       FVAttribute := AtBold;
  110.       WriteFast (Concat (Line, ' not allocated.'));
  111.     end
  112.     else
  113.     begin
  114.       Str (AllocDifference, Temp);
  115.       Line := Concat (Temp, ' track');
  116.       if AllocDifference > 1 then Line := Concat (Line, 's');
  117.       FVAttribute := AtBlinkBold;
  118.       WriteFast (Concat ('OVER capacity by ', Line, '!'));
  119.     end;
  120.   end {PrintSummary};
  121.  
  122.   {sub}procedure PaintScreen;
  123.   begin
  124.     DrawOutline;
  125.     StartFastVideo (5, 5, AtDefault, 79, 23);
  126.     FVRow := 2; FVAttribute := AtBold;
  127.     Center ('WUTIL Partition Editor: Press HELP key for instructions');
  128.     FVRow := 4; FVAttribute := AtNormal;
  129.     WriteFastLn
  130.      (' #  Tracks (MB)  Operating System   #  Tracks (MB)  Operating System');
  131.     WriteLn;
  132.     For I := 1 to 15 do
  133.       WriteLn (I:2, '.                                ', (I+15):2, '.');
  134.     PrintSummary;
  135.   end {PaintScreen};
  136.  
  137.   {sub}procedure DisplayItem (Index :Integer; Attribute :Integer);
  138.   begin
  139.     If Index < 16 then
  140.       StartFastVideo ( 9, (Index+5),  Attribute, 79, 23)
  141.     else
  142.       StartFastVideo (44, (Index-10), Attribute, 79, 23);
  143.     If Index > NumberOfPartitions then
  144.       WriteFastLn ('                             ')
  145.     else
  146.       WriteFastLn (Partition[Index].Descrip);
  147.   end {DisplayItem};
  148.  
  149. begin {RepartitionDisk}
  150.   { Read current disk information, exit (with message) if we can't }
  151.   if not ReadMajorBlocks then Exit;
  152.  
  153.   WITH HOMBlock.HOM DO K := FirstAltTrack + NumAltTracks;
  154.   NumberOfPartitions := 0;
  155.   I := 1;
  156.   while (I <= DPDBlock.DPD.EntryCount) AND (NumberOfPartitions < 30) DO
  157.   begin
  158.     NumberOfPartitions := NumberOfPartitions + 1;
  159.     WITH DPDBlock.DPD.Entry[I] DO WITH Partition[NumberOfPartitions] DO
  160.     begin
  161.       If FirstTrack <> K then
  162.       begin
  163.         Tracks := FirstTrack - K;
  164.         OS := -1;
  165.         K := FirstTrack;
  166.       end
  167.       else
  168.       begin
  169.         Tracks := LastTrack - FirstTrack + 1;
  170.         OS := OSNIndex;
  171.         I := I + 1;
  172.         K := LastTrack + 1;
  173.       end;
  174.     end;
  175.     FormDescrip (Partition[NumberOfPartitions]);
  176.   end;
  177.  
  178.   { We get out of the loop above when we either fill the partition array,
  179.     process all the DPD partition entries, or both.  If we have not processed
  180.     all DPD partitions yet, this means we filled our internal array, and this
  181.     is an error (see above). }
  182.   if I <= DPDBlock.DPD.EntryCount then
  183.   begin
  184.     DrawOutline;
  185.     StartFastVideo (3, 10, AtBold, 78, 23);
  186.     Center ('Your disk contains more than 30 partitions or unallocated gaps!');
  187.     WriteLn;
  188.     Center ('Sorry, but WUTIL can handle a maximum of 30 partitions+gaps');
  189.     Center ('You cannot use the WUTIL Partition Editor');
  190.     FVRow := 22;
  191.     Center ('Press any key to continue');  
  192.     I := RawChar;
  193.     Exit;
  194.   end;
  195.  
  196.   { There may be a gap at the end of the defined partitions.  If there is
  197.     still room in the partition array for another entry, and if we have not
  198.     reached the correct ending track, create one more entry for a "gap" }
  199.   WITH HOMBlock.HOM DO
  200.   If (NumberOfPartitions < 30) and (K < (MaintCylinder * Surfaces)) then
  201.   begin
  202.     NumberOfPartitions := NumberOfPartitions + 1;
  203.     WITH Partition[NumberOfPartitions] DO
  204.     begin
  205.       Tracks := (MaintCylinder * Surfaces) - K;
  206.       OS := -1;
  207.     end;
  208.     FormDescrip (Partition[NumberOfPartitions]);
  209.   end;
  210.  
  211.   { Next, determine the index of the last operating system defined in the OSN
  212.     block.  NOTE that we assume position 0 in the OSN table is not used (it
  213.     is not by any current rainbow partitioning software, including ours). }
  214.   I := 1;
  215.   While ORD (OSNBlock.OSN.Entry[I][0]) <> 0 do I := I + 1;
  216.   LastOS := I - 1;
  217.  
  218.   { Determine total capacity of disk in tracks.  Then add up the sizes of all
  219.     the partitions currently on the disk and compute the difference, if any,
  220.     between this and the total capacity we determined. }
  221.   WITH HOMBlock.HOM DO DiskCapacity := (MaintCylinder * Surfaces) -
  222.                                        (FirstAltTrack + NumAltTracks);
  223.   AllocDifference := 0;
  224.   For I := 1 to NumberOfPartitions do AllocDifference :=
  225.                                       AllocDifference + Partition[I].Tracks;
  226.   AllocDifference := AllocDifference - DiskCapacity;
  227.  
  228.   { Determine the maximum size of an MS-DOS partition.  This is 8MB in versions
  229.     of MS-DOS prior to 3.0, (almost) 32MB in versions 3.0 and after. }
  230.   If MSDOSVersion < 3 then MaxMSDOSSize := 1024 else MaxMSDOSSize := 4095;
  231.  
  232.   { Now we can draw the screen and fill in the initial things that will always
  233.     be there. }
  234.  
  235.   PaintScreen;
  236.  
  237.   { Indicate we will need to display the current list of partitions the first
  238.     time into our loop, and initialize other things. }
  239.  
  240.   Current := 1;
  241.   DisplayNeeded := True;
  242.   DoKeyPressed := False;
  243.   SomethingDeleted := False;
  244.  
  245.   { We are now ready to enter the main loop of the partition editor.  In it,
  246.     we will display new data if necessary and then wait for a key to tell us
  247.     what to do next. }
  248.  
  249.   Repeat {Until DoKeyPressed}
  250.     { Main loop:  First, see if we should display everything.  If so, do it. }
  251.     if DisplayNeeded then
  252.     begin
  253.       for I := 1 to 30 do
  254.       begin
  255.         if I = Current then DisplayItem (I, AtReverse)
  256.                        else DisplayItem (I, AtNormal);
  257.         DisplayItem (I, FVAttribute);
  258.       end;
  259.  
  260.       DisplayNeeded := False;
  261.     end;
  262.  
  263.     { Now wait for a character saying what the user wants us to do }
  264.     I := (RawChar AND CapsLockMask);
  265.  
  266.     { See what character we got and dispatch accordingly }
  267.     if (I = UpArrowKeyCode) and (Current > 1) then
  268.     begin
  269.       Current := Current - 1;
  270.       DisplayNeeded := True;
  271.     end
  272.     else if (I = DownArrowKeyCode) and (Current < Min (NumberOfPartitions + 1,
  273.                                                        30)) then
  274.     begin
  275.       Current := Current + 1;
  276.       DisplayNeeded := True;
  277.     end
  278.     else if (I = Shift+UpArrowKeyCode) and (Current > 1)
  279.         and (Current <= NumberOfPartitions) then
  280.     begin
  281.       TempPartition := Partition[Current-1];
  282.       Partition[Current-1] := Partition[Current];
  283.       Partition[Current] := TempPartition;
  284.       Current := Current - 1;
  285.       DisplayNeeded := True;
  286.     end
  287.     else if (I = Shift+DownArrowKeyCode) and (Current < NumberOfPartitions) then
  288.     begin
  289.       TempPartition := Partition[Current+1];
  290.       Partition[Current+1] := Partition[Current];
  291.       Partition[Current] := TempPartition;
  292.       Current := Current + 1;
  293.       DisplayNeeded := True;
  294.     end
  295.     else if (I = LeftArrowKeyCode) and (Current <= NumberOfPartitions)
  296.         and (Partition[Current].Tracks > MinSize(Partition[Current].OS)) then
  297.     begin
  298.       { Since the largest legal MS-DOS partition size for 12-bit FATS is 1024
  299.         tracks and the smallest legal MS-DOS partition size for 16-bit FATS is
  300.         1028 tracks, we have to make sure that no MS-DOS partition can be
  301.         created with the size of either 1025, 1026, or 1027 tracks }
  302.       if (Partition[Current].OS = MSDOSIndex) and (Partition[Current].Tracks
  303.           = 1028) then J := 4 else J := 1;
  304.       Partition[Current].Tracks := Partition[Current].Tracks - J;
  305.       FormDescrip(Partition[Current]);
  306.       AllocDifference := AllocDifference - J;
  307.       DisplayNeeded := True;
  308.       PrintSummary;
  309.     end
  310.     else if (I = RightArrowKeyCode) and (Current <= NumberOfPartitions)
  311.         and (Partition[Current].Tracks < MaxSize(Partition[Current].OS)) then
  312.     begin
  313.       if (Partition[Current].OS = MSDOSIndex) and (Partition[Current].Tracks
  314.           = 1024) then J := 4 else J := 1;
  315.       Partition[Current].Tracks := Partition[Current].Tracks + J;
  316.       FormDescrip(Partition[Current]);
  317.       AllocDifference := AllocDifference + J;
  318.       DisplayNeeded := True;
  319.       PrintSummary;
  320.     end
  321.  
  322.     else if (I = Shift+LeftArrowKeyCode) and (Current <= NumberOfPartitions)
  323.         and (Partition[Current].Tracks > MinSize(Partition[Current].OS)) then
  324.     begin
  325.       J := Min (10, Partition[Current].Tracks-MinSize(Partition[Current].OS));
  326.       Partition[Current].Tracks := Partition[Current].Tracks - J;
  327.       if (Partition[Current].OS = MSDOSIndex) and (Partition[Current].Tracks
  328.           > 1024) and (Partition[Current].Tracks < 1028) then
  329.       begin
  330.         J := J + Partition[Current].Tracks - 1024;
  331.         Partition[Current].Tracks := 1024;
  332.       end;
  333.       FormDescrip(Partition[Current]);
  334.       AllocDifference := AllocDifference - J;
  335.       DisplayNeeded := True;
  336.       PrintSummary;
  337.     end
  338.     else if (I = Shift+RightArrowKeyCode) and (Current <= NumberOfPartitions)
  339.         and (Partition[Current].Tracks < MaxSize(Partition[Current].OS)) then
  340.     begin
  341.       J := Min (10, MaxSize(Partition[Current].OS)-Partition[Current].Tracks);
  342.       Partition[Current].Tracks := Partition[Current].Tracks + J;
  343.       if (Partition[Current].OS = MSDOSIndex) and (Partition[Current].Tracks
  344.           > 1024) and (Partition[Current].Tracks < 1028) then
  345.       begin
  346.         J := J - Partition[Current].Tracks + 1028;
  347.         Partition[Current].Tracks := 1028;
  348.       end;
  349.       FormDescrip(Partition[Current]);
  350.       AllocDifference := AllocDifference + J;
  351.       DisplayNeeded := True;
  352.       PrintSummary;
  353.     end
  354.     else if (I = Ctrl+LeftArrowKeyCode) and (Current <= NumberOfPartitions)
  355.         and (Partition[Current].Tracks > MinSize(Partition[Current].OS)) then
  356.     begin
  357.       J := Min (100, Partition[Current].Tracks-MinSize(Partition[Current].OS));
  358.       Partition[Current].Tracks := Partition[Current].Tracks - J;
  359.       if (Partition[Current].OS = MSDOSIndex) and (Partition[Current].Tracks
  360.           > 1024) and (Partition[Current].Tracks < 1028) then
  361.       begin
  362.         J := J + Partition[Current].Tracks - 1024;
  363.         Partition[Current].Tracks := 1024;
  364.       end;
  365.       FormDescrip(Partition[Current]);
  366.       AllocDifference := AllocDifference - J;
  367.       DisplayNeeded := True;
  368.       PrintSummary;
  369.     end
  370.     else if (I = Ctrl+RightArrowKeyCode) and (Current <= NumberOfPartitions)
  371.         and (Partition[Current].Tracks < MaxSize(Partition[Current].OS)) then
  372.     begin
  373.       J := Min (100, MaxSize(Partition[Current].OS)-Partition[Current].Tracks);
  374.       Partition[Current].Tracks := Partition[Current].Tracks + J;
  375.       if (Partition[Current].OS = MSDOSIndex) and (Partition[Current].Tracks
  376.           > 1024) and (Partition[Current].Tracks < 1028) then
  377.       begin
  378.         J := J - Partition[Current].Tracks + 1028;
  379.         Partition[Current].Tracks := 1028;
  380.       end;
  381.       FormDescrip(Partition[Current]);
  382.       AllocDifference := AllocDifference + J;
  383.       DisplayNeeded := True;
  384.       PrintSummary;
  385.     end
  386.     else if (I = SelectKeyCode) and (Current <= NumberOfPartitions) then
  387.     begin
  388.       J := Partition[Current].OS + 1;
  389.       { When cycling through the operating system choices with the SELECT
  390.         key, we do not want to do that in the sequence of the OS indices, which
  391.         is 'GAP  CP/M  MS-DOS  CCP/M'.  Since mostly MS-DOS partitions are
  392.         used nowadays we want this sequence: 'GAP  MS-DOS  CP/M  CCP/M'.  To
  393.         achieve this, we have to manipulate the OS index pointer a little bit }
  394.       if J = 0 then J := 2
  395.       else if J = 3 then J := 1
  396.       else if J = 2 then J := 3
  397.       else if J > LastOS then J := -1;
  398.       Partition[Current].OS := J;
  399.       { Check that the partition is not now too small for its new operating
  400.         system.  If so, then increase its size to the minimum. }
  401.       K := MinSize (J);
  402.       If Partition[Current].Tracks < K then
  403.       begin
  404.         AllocDifference := AllocDifference + K - Partition[Current].Tracks;
  405.         PrintSummary;
  406.         Partition[Current].Tracks := K;
  407.       end;
  408.       { Now a similar check that the partition is not now too big for its
  409.         new operating system.  If so, decrease its size to the maximum. }
  410.       K := MaxSize (J);
  411.       If Partition[Current].Tracks > K then
  412.       begin
  413.         AllocDifference := AllocDifference + K - Partition[Current].Tracks;
  414.         PrintSummary;
  415.         Partition[Current].Tracks := K;
  416.       end;
  417.       { And a check for MS-DOS partitions to not be 1025, 1026, or 1027 tracks
  418.         in size }
  419.       if (Partition[Current].OS = MSDOSIndex) and (Partition[Current].Tracks
  420.           > 1024) and (Partition[Current].Tracks < 1028) then
  421.       begin
  422.         AllocDifference := AllocDifference + 1024 - Partition[Current].Tracks;
  423.         PrintSummary;
  424.         Partition[Current].Tracks := 1024;
  425.       end;
  426.       FormDescrip(Partition[Current]);
  427.       DisplayNeeded := True;
  428.     end
  429.     else if (I = RemoveKeyCode) and (Current <= NumberOfPartitions) then
  430.     begin
  431.       SomethingDeleted := True;
  432.       DeletedPartition := Partition[Current];
  433.       AllocDifference := AllocDifference - DeletedPartition.Tracks;
  434.       NumberOfPartitions := NumberOfPartitions - 1;
  435.       for J := Current to NumberOfPartitions do
  436.         Partition[J] := Partition[J+1];
  437.       DisplayNeeded := True;
  438.       PrintSummary;
  439.     end
  440.     else if (I = InsertHereKeyCode) and (NumberOfPartitions < 30) then
  441.     begin
  442.       for J := NumberOfPartitions downto Current do
  443.         Partition[J+1] := Partition[J];
  444.       NumberOfPartitions := NumberOfPartitions + 1;
  445.       { Form the size of the partition we will add.  Try and make it equal to
  446.         the size of the current remaining space on the disk (if there is any).
  447.         But in any case, it cannot be smaller than one track, nor can it be
  448.         larger than the maximum allowed. }
  449.       Partition[Current].Tracks := Min (MaxSize(-1),
  450.                                         Max (1, (-AllocDifference)));
  451.       Partition[Current].OS := -1;
  452.       FormDescrip(Partition[Current]);
  453.       AllocDifference := AllocDifference + Partition[Current].Tracks;
  454.       DisplayNeeded := True;
  455.       PrintSummary;
  456.     end
  457.  
  458.     else if (I = Shift+InsertHereKeyCode) and (NumberOfPartitions < 30)
  459.         and (SomethingDeleted) then
  460.     begin
  461.       for J := NumberOfPartitions downto Current do
  462.         Partition[J+1] := Partition[J];
  463.       NumberOfPartitions := NumberOfPartitions + 1;
  464.       Partition[Current] := DeletedPartition;
  465.       FormDescrip(Partition[Current]);
  466.       AllocDifference := AllocDifference + Partition[Current].Tracks;
  467.       DisplayNeeded := True;
  468.       PrintSummary;
  469.     end
  470.     else if (I = AddtnlOptionsKeyCode) and (NumberOfPartitions > 0) then
  471.     begin
  472.       ReadySummaryLine;
  473.       FVAttribute := AtNormal; WriteFast ('Press a key:  best ');
  474.       FVAttribute := AtBold; WriteFast ('F');
  475.       FVAttribute := AtNormal; WriteFast ('it current; ');
  476.       FVAttribute := AtBold; WriteFast ('B');
  477.       FVAttribute := AtNormal; WriteFast ('alance all');
  478.       J := (RawChar AND CapsLockMask AND (NOT Shift));
  479.       if      ((J = Ord ('F')) or (J = Ord ('f')))
  480.           and (Current <= NumberOfPartitions) then
  481.       begin
  482.         AllocDifference := AllocDifference - Partition[Current].Tracks;
  483.         Partition[Current].Tracks := Min (MaxSize(Partition[Current].OS),
  484.                                           Max (MinSize(Partition[Current].OS),
  485.                                                (-AllocDifference)));
  486.         { Again, make sure our MS-DOS partition is not 1024, 1025, 1026 tracks 
  487.           large }
  488.         if (Partition[Current].OS = MSDOSIndex) and (Partition[Current].Tracks
  489.             > 1024) and (Partition[Current].Tracks < 1028) then
  490.             Partition[Current].Tracks := 1024;
  491.         FormDescrip(Partition[Current]);
  492.         AllocDifference := AllocDifference + Partition[Current].Tracks;
  493.         DisplayNeeded := True;
  494.       end
  495.       else if (J = Ord ('B')) or (J = Ord ('b')) then
  496.       begin
  497.         AllocDifference := 0;
  498.         I := DiskCapacity DIV NumberOfPartitions;
  499.         J := DiskCapacity MOD NumberOfPartitions;
  500.         For K := 1 to NumberOfPartitions do
  501.         begin
  502.           If J = 0 then Partition[K].Tracks := I else
  503.           begin
  504.             Partition[K].Tracks := I + 1;
  505.             J := J - 1;
  506.           end;
  507.           { The above can leave us with something which is too big or too
  508.             small for its operating system.  In a perfect world, we could
  509.             figure out an intelligent algorithm to do something clever here
  510.             to save the day, but unfortunately, this is not a perfect world.
  511.             So if the result is too big or too small, we'll adjust it to
  512.             bring it in line.  Yucky to be sure, but what else can we do? }
  513.           If Partition[K].Tracks < MinSize(Partition[K].OS) then
  514.           begin
  515.             AllocDifference := AllocDifference + MinSize(Partition[K].OS)
  516.                                                - Partition[K].Tracks;
  517.             Partition[K].Tracks := MinSize(Partition[K].OS);
  518.           end
  519.           else if Partition[K].Tracks > MaxSize(Partition[K].OS) then
  520.           begin
  521.             AllocDifference := AllocDifference + MaxSize(Partition[K].OS)
  522.                                                - Partition[K].Tracks;
  523.             Partition[K].Tracks := MaxSize(Partition[K].OS);
  524.           end;
  525.           if (Partition[Current].OS = MSDOSIndex) and (Partition[K].Tracks
  526.               > 1024) and (Partition[K].Tracks < 1028) then
  527.           begin
  528.             AllocDifference := AllocDifference + 1024 - Partition[K].Tracks;
  529.             Partition[K].Tracks := 1024;
  530.           end;
  531.           FormDescrip (Partition[K]);
  532.         end;
  533.         DisplayNeeded := True;
  534.       end
  535.       else Write (^G);
  536.       PrintSummary;
  537.     end
  538.     else if I = MainScreenKeyCode then Exit { Main screen aborts back to menu }
  539.  
  540.     else if I = HelpKeyCode then
  541.     begin
  542.       DrawOutline;
  543.       FVAttribute := AtBold;
  544.       Center ('WUTIL Partition Editor Instructions');
  545.       WriteLn;
  546.       FVAttribute := AtDefault;
  547.       WriteFastLn
  548. ('The partition editor allows you to interactively manipulate the way');
  549.       WriteFastLn
  550. ('your hard disk is partitioned.  After you have set up the partitions');
  551.       WriteFastLn
  552. ('the way you want, you press the DO key to actually perform partitioning.');
  553.       WriteLn;
  554.       WriteFastLn
  555. ('The current partitions are always displayed.  Keys work like this:');
  556.       WriteLn;
  557.       WriteFastLn
  558. ('UP and DOWN arrows select a different line for editing (the "current" line).');
  559.       WriteFastLn
  560. ('RIGHT and LEFT arrows increase or decrease the size of the current partition.');
  561.       WriteFastLn
  562. ('SHIFT or CTRL + RIGHT and LEFT arrows do the same thing, but faster.');
  563.       WriteFastLn
  564. ('SHIFT + UP and DOWN arrows "move" the current partition in that direction.');
  565.       WriteFastLn
  566. ('SELECT changes the current partition''s operating system (cycles choices).');
  567.       WriteFastLn
  568. ('INSERT HERE adds a new partition before the current partition.');
  569.       WriteFastLn
  570. ('REMOVE deletes the current partition and saves its description.');
  571.       WriteFastLn
  572. ('SHIFT + INSERT HERE inserts the saved partition (undeletes).');
  573.       WriteFastLn
  574. ('ADDTNL OPTIONS, then "F" adjusts the current partition size for "best fit".');
  575.       WriteFastLn
  576. ('ADDTNL OPTIONS, then "B" balances all partition sizes (makes same).');
  577.       WriteFastLn
  578. ('When done, press DO to perform the partitioning, or MAIN SCREEN to abort.');
  579.       WriteLn;
  580.       FVAttribute := AtBold;
  581.       WriteFastLn
  582. ('Press any key to return to partitioning editor...');
  583.       J := RawChar;
  584.       PaintScreen;
  585.       DisplayNeeded := True;
  586.     end
  587.  
  588.     else if (I = DoKeyCode) and (NumberOfPartitions > 0)
  589.         and (AllocDifference = 0) then
  590.     begin
  591.       ErrorExists := False;
  592.       DisplayItem (Current, AtNormal);
  593.  
  594.       { Initialize the DPD block we will use to describe the new partitions
  595.         we've formed.  Start with all zeroes, and then add a few fields from
  596.         the current DPD. }
  597.       With TempDPD do For J := 0 to 255 do SectorArray[J] := 0;
  598.       TempDPD.DPD.ID := DPDBlock.DPD.ID;
  599.       TempDPD.DPD.LBN := DPDBlock.DPD.LBN;
  600.       TempDPD.DPD.MaxEntries := DPDBlock.DPD.MaxEntries;
  601.  
  602.       { Build the beginnings of the partition data in the new DPD block.
  603.         For now we'll just fill in the track and operating system information.
  604.         This routine will detect if there are too many partitions (more than
  605.         15) and exit if this happens.  This section will also check that
  606.         each partition defined has no bad sectors within its first four
  607.         allocated tracks.  Again, this will be treated as an error. }
  608.       WITH HOMBlock.HOM DO K := FirstAltTrack + NumAltTracks;
  609.       J := 1;
  610.       While (J <= NumberOfPartitions) and (NOT ErrorExists) do
  611.       With Partition[J] do
  612.       begin
  613.         If OS <> -1 then With TempDPD.DPD do
  614.         begin
  615.           EntryCount := EntryCount + 1;
  616.           If EntryCount > MaxEntries then
  617.           begin
  618.             ErrorExists := True;
  619.             ErrorMessage := 'MORE THAN 15 PARTITIONS DEFINED';
  620.             Current := J;
  621.           end
  622.           else With Entry[EntryCount] do
  623.           begin
  624.             FirstTrack := K;
  625.             LastTrack := K + Tracks - 1;
  626.             OSNIndex := OS;
  627.             Flag := J; { Temporarily save original index for later errors }
  628.             For I := FirstTrack to FirstTrack + 3 do
  629.             If SectorTable[I].Num <> 0 then
  630.             begin
  631.               ErrorExists := True;
  632.               Str (J, ErrorMessage);
  633.               ErrorMessage := Concat
  634.                                 ('BAD SECTOR IN FIRST 4 TRACKS, PARTITION #',
  635.                                  ErrorMessage);
  636.               Current := J; { Point to offending partition }
  637.             end;
  638.           end;
  639.         end;
  640.         K := K + Tracks;
  641.         J := J + 1;
  642.       end;
  643.  
  644.       { If we have not detected an error yet, the next thing to do is go
  645.         through the partitions by operating system and fill in the things
  646.         necessary for the operating systems.  There can be no more than
  647.         four partitions per operating system.  For these purposes, CP/M and
  648.         CCP/M count as one operating system, since CCP/M can access CP/M
  649.         partitions.  We'll check all this as we go.  ** NOTE THAT THIS CODE
  650.         RELIES ON THE FACT THE THE STANDARD OSN INDEX FOR CP/M IS LESS THAN
  651.         THAT FOR CCP/M.  IF THIS CHANGES FOR SOME REASON (NEXT TO IMPOSSIBLE),
  652.         THIS CODE WILL NEED MODIFICATION **}
  653.       If NOT ErrorExists then
  654.       begin
  655.         J := 0;
  656.         LastCPMUnit := 0;
  657.         While (J < LastOS) and (Not ErrorExists) do
  658.         begin
  659.           J := J + 1;
  660.           K := 0;
  661.           For I := 1 to TempDPD.DPD.EntryCount do With TempDPD.DPD.Entry[I] do
  662.           if (OSNIndex = J) and (Not ErrorExists) then
  663.           begin
  664.             K := K + 1;
  665.             LogicalUnit := K + 4;
  666.             If J = CPMIndex then
  667.             begin
  668.               LastCPMUnit := K;
  669.               Name := 'CPM-x   ';
  670.               Name[4] := Chr(Ord('0')+K);
  671.             end
  672.             else if J = CCPMIndex then
  673.             begin
  674.               Name := 'CCPM-x  ';
  675.               Name[5] := Chr(Ord('0')+K);
  676.               LogicalUnit := LogicalUnit + LastCPMUnit;
  677.             end
  678.             else if J = MSDOSIndex then
  679.             begin
  680.               Name := 'MSDOS-x ';
  681.               Name[6] := Chr(Ord('0')+K);
  682.             end
  683.             else
  684.             begin
  685.               Name := 'DISKnn  ';
  686.               Name[4] := Chr(Ord('0')+(I div 10));
  687.               Name[5] := Chr(Ord('0')+(I mod 10));
  688.             end;
  689.             If LogicalUnit > 8 then
  690.             begin
  691.               ErrorExists := True;
  692.               if (J = CPMIndex) or (J = CCPMIndex) then
  693.                 ErrorMessage := 'MORE THAN 4 PARTITIONS ARE CP/M OR CCP/M'
  694.               else
  695.                 ErrorMessage := Concat ('MORE THAN 4 PARTITIONS ARE ',
  696.                                         OSNBlock.OSN.Entry[J]);
  697.               Current := Flag; { Point at offending entry }
  698.             end;
  699.             Flag := 0; { Reset flag - no longer need original index }
  700.           end;
  701.         end;
  702.       end;
  703.  
  704.       { We've done everything we expect to be doing here.  If we found an
  705.         error, print the message, wait for acknowledgement, and then return
  706.         to the loop.  If there's no error, indicate that the DO key was pressed
  707.         so we can leave the loop. }
  708.       If ErrorExists then
  709.       begin
  710.         { "Current" now points to the "offending" line in our partitions on
  711.           the screen.  Redisplay that line, blinking and bolded, so the user
  712.           can see which line we're complaining about.  Then indicate that
  713.           redisplay will be needed, so that when we re-enter the main loop,
  714.           we'll restore normal display characteristics. }
  715.         DisplayItem (Current, AtBlinkBold);
  716.         DisplayNeeded := True;
  717.  
  718.         { Now print the error message on the summary line and wait for the
  719.           user to acknowledge it; then reprint the summary line and go back
  720.           to the main loop }
  721.         ReadySummaryLine;
  722.         FVAttribute := AtBold;
  723.         Write (^G);
  724.         WriteFast (Concat (ErrorMessage, ' -- Press any key to continue'));
  725.         K := RawChar;
  726.         PrintSummary;
  727.       end
  728.       else DoKeyPressed := True;
  729.     end
  730.     else Write (^G);
  731.   Until DoKeyPressed;
  732.  
  733.   { If we get here, then we've determined that it's all right to actually
  734.     perform the repartitioning.  We have a good portion of the partition data
  735.     already built in the TempDPD block.  Now we have to confirm that the user
  736.     actually wants to build the partitions, and determine which partitions
  737.     need to be physically reformatted. }
  738.  
  739.   DrawOutline;
  740.   DrawBox (4, 4, 77, 11, AtBlinkBold);
  741.   FVRow := 4; FVAttribute := AtDefault;
  742.   Write (^G^G);
  743.   Center (' WARNING ');
  744.   FVAttribute := AtBold;
  745.   Center ('You are about to initiate repartitioning of your hard disk.');
  746.   Center ('This action may result in the LOSS of all or some of your');
  747.   Center ('data on the hard disk.  You should not attempt this operation');
  748.   Center ('unless you do not want any of your hard disk data or you have');
  749.   Center ('backed up whatever you wish to save.  Do not assume that any');
  750.   Center ('data will be saved.  If you lose anything, it will be YOUR FAULT!');
  751.   FVRow := 14;
  752.   Center ('Press RESUME to continue');
  753.   Center ('or Press MAIN SCREEN to cancel repartitioning');
  754.   Repeat
  755.     I := (RawChar and CapsLockMask);
  756.     If I = MainScreenKeyCode then Exit
  757.     else if I <> ResumeKeyCode then Write (^G);
  758.   Until I = ResumeKeyCode;
  759.  
  760.   DrawBox (4, 14, 77, 18, AtBlinkBold);
  761.   FVRow := 14; FVAttribute := AtDefault;
  762.   Write (^G^G);
  763.   Center (' SECOND WARNING ');
  764.   FVAttribute := AtBold;
  765.   Center ('If you press RESUME now, you will start repartitioning.');
  766.   Center ('There will be no turning back.');
  767.   Center (' Have you backed up everything you intend to save? ');
  768.   FVRow := 21;  FVAttribute := AtBold;
  769.   Center ('Press RESUME to begin repartitioning');
  770.   Center ('or Press MAIN SCREEN to cancel and return to main menu');
  771.   Repeat
  772.     I := (RawChar and CapsLockMask);
  773.     If I = MainScreenKeyCode then Exit
  774.     else if I <> ResumeKeyCode then Write (^G);
  775.   Until I = ResumeKeyCode;
  776.  
  777.   { If we get here, we have confirmed (twice) that repartitioning is desired.
  778.     Disallow EXIT from this point forward, and then go to it! }
  779.  
  780.   EXITAllowed := False;
  781.  
  782.   { In the process of repartitioning, we might have created a "gap" somewhere
  783.     (where there are no partitions) containing bad sectors.  Since these
  784.     sectors will never be used, we want to see if any of them have alternate
  785.     sectors assigned, and if they do, get rid of them (no need to waste
  786.     alternate sectors on these. }
  787.  
  788.   For I := 0 to (HOMBlock.HOM.NumAltTracks * 16) - 1 do
  789.   With ASTVector[I] do
  790.   If Sector <> 0 then
  791.   With TempDPD.DPD do
  792.   begin
  793.     J := 0;
  794.     Repeat J := J + 1
  795.     Until (J > EntryCount) or (Track <= Entry[J].LastTrack);
  796.     { Here J is either greater than the number of partitions if the track
  797.       specified by this AST entry is greater than the last allocated track,
  798.       or else it points at the entry which might be the correct one.  This
  799.       track is therefore within a gap if either J is greater than the number
  800.       of partitions (EntryCount) or if the track is before the first track of
  801.       the partition pointed to by J. }
  802.     If (J > EntryCount) or (Track < Entry[J].FirstTrack) then
  803.     begin
  804.       Track := 0;
  805.       Sector := 0;
  806.     end;
  807.   end;
  808.  
  809.   With TempDPD.DPD do
  810.   begin
  811.     For I := 1 to EntryCount do With Entry[I] do
  812.     begin
  813.       { MAIN PARTITION BUILDING CODE FOR ONE PARTITION (INDEX I). }
  814.       { See if there's an old partition with the same (or similar enough)
  815.         parameters to this one.  If so, we can probably leave it alone, but
  816.         we'll give the user the option.  The idea of the code below is to
  817.         set the value of "Flag" to "FF" (hex) if the partition must
  818.         be initialized, or to something else if maybe it doesn't.  If we think
  819.         the partition is similar to a partition defined previously, we'll set
  820.         this flag value equal to the index in the old DPD block of the similar
  821.         partition.  We will assume a partition is "similar to" a previous
  822.         partition if they have the same starting and ending tracks and they
  823.         are either both MS-DOS parititions or both NOT MS-DOS partitions. }
  824.       Flag := $FF; { Initially, assume must be initialized }
  825.       For J := 1 to DPDBlock.DPD.EntryCount do
  826.       if (FirstTrack = DPDBlock.DPD.Entry[J].FirstTrack) and
  827.          (LastTrack  = DPDBlock.DPD.Entry[J].LastTrack) then
  828.       begin
  829.         if OSNIndex = MSDOSIndex then
  830.         begin
  831.           if OSNIndex = DPDBlock.DPD.Entry[J].OSNIndex then Flag := J;
  832.         end
  833.         else
  834.         begin
  835.           if DPDBlock.DPD.Entry[J].OSNIndex <> MSDOSIndex then Flag := J;
  836.         end;
  837.       end;
  838.       If Flag <> $FF then
  839.       begin
  840.         { If this partition is "similar to" a previous partition, but the
  841.           name and logical unit field in the temporary DPD block are 0,
  842.           then it's an operating system WUTIL doesn't know about (like VENIX).
  843.           In this case we'll keep the original DPD block entries for name and
  844.           logical unit. }
  845.         if LogicalUnit = 0 then
  846.         begin
  847.           Name := DPDBlock.DPD.Entry[Flag].Name;
  848.           LogicalUnit := DPDBlock.DPD.Entry[Flag].LogicalUnit;
  849.         end;
  850.         DrawOutline;
  851.         StartFastVideo (10, 5, AtBold, 79, 23);
  852.         Center ('-- Partition Initialization Option --');
  853.         FVRow := 8;
  854.         WriteFastLn ('One of your new partitions appears to be similar to a');
  855.         WriteFastLn ('partition previously defined for this disk:');
  856.         WriteLn;
  857.         WriteFastLn (Concat ('New partition: ', Name, ' (drive ',
  858.                              Chr (LogicalUnit+Ord('A')-1), ':)'));
  859.         WriteFastLn (Concat ('Old partition: ', DPDBlock.DPD.Entry[Flag].Name,
  860.                              ' (drive ',
  861.                              Chr (DPDBlock.DPD.Entry[Flag].LogicalUnit+
  862.                                   Ord('A')-1),
  863.                              ':)'));
  864.         WriteLn;
  865.         FVAttribute := AtNormal;
  866.         WriteFastLn ('You can re-initialize this partition if you wish.');
  867.         WriteFastLn ('Or, you can leave the partition with the data it had');
  868.         WriteFastLn ('before repartitioning.');
  869.         WriteLn;
  870.         FVAttribute := AtBold;
  871.         WriteFastLn
  872.           ('Press RESUME to LEAVE THE PARTITION AS IS (don''t re-initialize)');
  873.         WriteFastLn
  874.           ('Press ADDTNL OPTIONS to re-initialize the partition anyway');
  875.         FVAttribute := AtBlinkBold;
  876.         WriteFastLn
  877.           ('BE SURE TO PRESS THE RIGHT KEY!  You will not get another chance.');
  878.         Repeat
  879.           K := (RawChar and CapsLockMask);
  880.           If (K <> ResumeKeyCode) and (K <> AddtnlOptionsKeyCode) then
  881.             Write (^G);
  882.         Until (K = ResumeKeyCode) or (K = AddtnlOptionsKeyCode);
  883.         If K = ResumeKeyCode then
  884.           Flag := $F0  { Correct flag value for "initialized" partition }
  885.         else
  886.           Flag := $FF; { Value for "non-existent" -- will cause init }
  887.       end;
  888.       { Now we have the flag set to indicate whether or not we should
  889.         initialize this partition.  If it is $F0 (hex), then the partition
  890.         is already initialized.  If it is anything else, we'll initialize it. }
  891.       If Flag <> $F0 then
  892.       begin
  893.         DrawOutline;
  894.         FVRow := 12; FVAttribute := AtBlink;
  895.         Center (Concat ('Initializing partition ', Name, ' ...please wait'));
  896.         If OSNIndex = MSDOSIndex then
  897.         begin
  898.           FormatMSDOSPartition (FirstTrack, LastTrack, Name);
  899.           FATSectors := NumFATSectors;
  900.         end
  901.         else
  902.         begin
  903.           FormatCPMPartition (FirstTrack, LastTrack, Name);
  904.           NumPASBlocks := 1;
  905.         end;
  906.         Flag := $F0; { Indicate the partition is now initialized }
  907.       end;
  908.     end;
  909.   end;
  910.  
  911.   { Finally, update the DPD and AST blocks with the correct information, as
  912.     modified.  }
  913.   DrawOutline;
  914.   FVRow := 12; FVAttribute := AtBlink;
  915.   Center ('Updating partitioning and alternate sector information on hard disk');
  916.   { Write AST blocks first }
  917.   WriteASTBlocks;
  918.   { Finally, write the new DPD block }
  919.   With HOMBlock.HOM.DPDLocation do
  920.     WriteMajorBlock (Track, Sector, TempDPD);
  921.  
  922.   { The last thing we need to check is to see if the auto boot partition makes
  923.     sense in the context of what we have changed.  If it doesn't, then we'll
  924.     remove auto-boot and update the HOM block. }
  925.   With HOMBlock.HOM do If (BootTrack <> 0) and (AutoBoot <> $FF) then
  926.   With TempDPD.DPD do
  927.   Begin
  928.     I := 0;
  929.     Repeat I := I + 1
  930.     Until (I > EntryCount) or
  931.           ((Entry[I].FirstTrack = BootTrack) and
  932.            (Entry[I].LogicalUnit = AutoBoot));
  933.     If I > EntryCount {we didn't find a matching partition} then
  934.     begin
  935.       BootTrack := 0;  AutoBoot := $FF;
  936.       WriteMajorBlock (0, 2, HOMBlock);
  937.     end;
  938.   end;
  939.  
  940.   { We've finished.  Tell the guy. }
  941.  
  942.   DrawOutline;
  943.   FVRow := 10; FVAttribute := AtBold;
  944.   Center ('Re-partitioning has been completed');
  945.   WriteLn;
  946.   if TwoDrives and (CurrentDrive = 2) then
  947.   begin
  948.     CurrentDrive := 1;
  949.     if ReadMajorBlocks then
  950.     begin
  951.       With HOMBlock.HOM do ManualSeek(0,StepRate);
  952.       CurrentDrive :=2;
  953.     end;
  954.   end;
  955.   FVAttribute := AtDefault;
  956.   Center ('Now that you have modified the way your disk is partitioned, you');
  957.   Center ('will not be able to exit this program.');
  958.   WriteLn;
  959.   FVAttribute := AtBold;
  960.   Center ('To reboot and use the new partitions, press SET-UP and then CTRL/SET-UP');
  961.   Center ('or press any other key to return to the WUTIL main menu');
  962.   SetUpExit := True;
  963.   I := RawChar;
  964. end {RepartitionDisk};
  965.