home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / pascal / library / dos / install / inifile / ini.pas next >
Encoding:
Pascal/Delphi Source File  |  1992-05-02  |  20.0 KB  |  748 lines

  1. Unit Ini;
  2.  
  3. {* An interface to .INI files, created by Max Maischein    02.05.1992 *}
  4.  
  5. {* The code is for Turbo Pascal 6.0 ( and above ), TPW-users might    *}
  6. {* use it too, but you should have a look at the Windows-API-         *}
  7. {* Functions Write/ReadProfileString and Write/                       *}
  8. {* ReadPrivateProfileString, which do mainly the same as TConfigFile. *}
  9.  
  10. {* This code is freeware, you are allowed to use it in your own       *}
  11. {* applications, but if you make interesting changes or detect any    *}
  12. {* bugs, send them to me rather than redistributing the fix on your   *}
  13. {* own. And please don't change anything in this text !               *}
  14.  
  15. {* The only thing I ask of you is that the programmers notice stays   *}
  16. {* in place. I don't think, that this is too much !                   *}
  17.  
  18. {* There are five procedures. UpperCase and BruteSearch are string    *}
  19. {* procedures needed by the code, but are very unsophisticated. I     *}
  20. {* wrote them, as I don't want to distribute all my units. Use OPro's *}
  21. {* Strings unit, assembler ports, and a Boyer-Moore-Search for better *}
  22. {* results.                                                           *}
  23. {* The same applies for CurrentDate and CurrentTime, they construct   *}
  24. {* strings containing the date and time. Just look in the .INI file,  *}
  25. {* after you called Flush. ;-)                                        *}
  26.  
  27. {* Now to the Tech-Stuff :                                            *}
  28.  
  29. {* The interface is programmed in pseudo-OOP, i.e., it could have     *}
  30. {* been done as well in procedural coding, but as everybody today is  *}
  31. {* deep into OOP, why not ;-)                                         *}
  32.  
  33. {* The unit declares the types TConfigFile and PConfigFile, their     *}
  34. {* names are selfexplanatory and according to Borlands naming         *}
  35. {* strategy.                                                          *}
  36.  
  37. (*
  38.  
  39. The object TConfigFile looks like this :
  40.  
  41.       TConfigFile = Object
  42.  
  43.                       Changed : Boolean;
  44.         This is the flag, if the items in the file were changed.
  45.         This flag will be set by SetItem, CreateGroup and EraseGroup.
  46.         To avoid saving the changes to the file, you have to set the
  47.         flag to false directly.
  48.  
  49.                       TheSize : Word;
  50.         After the above assumption, a word will suffice to hold the
  51.         size of the .INI file.
  52.  
  53.                       TheBuffer : Pointer;
  54.         This is the buffer, that holds the current version of the
  55.         .INI file. As all changes will be made in memory, the file
  56.         won't be updated until you call Flush or Done. The size
  57.         of the block allocated to TheBuffer is TheSize.
  58.  
  59.                       TheName : String;
  60.         This is the DOS file name of the .INI file.
  61.  
  62.                       TheApp : String;
  63.         This is the name of your application, it will be inserted
  64.         by about into the date-time-stamp-line.
  65.  
  66.                       Group : String;
  67.         This is the name of the current group. Please do not change
  68.         it directly, as there is other data related to it.
  69.  
  70.                       GroupStart : Word;
  71.         This is the zero based offset of the current goup in the
  72.         .INI file.
  73.  
  74.                       GroupSize : Word;
  75.         This is the size in bytes of the current group.
  76.  
  77.                       Constructor Init( FileName : String; ApplicationName : String; AGroup : String );
  78.         This is the constructor. You should call it immediately
  79.         with the allocation of the Object. Init allocates a buffer
  80.         big enough to hold the whole .INI file, and initializes
  81.         some variables.
  82.         - FileName is the DOS file name of the .INI file. The .INI
  83.         file must exist, no error checking is done.
  84.         - ApplicationName is the name of your application. It will be
  85.         inserted in the date-time-stamp.
  86.         - AGroup specifies the group, that will be set first with
  87.         SetGroup. If this group does not exist, it will be created.
  88.  
  89.                       Function SetGroup( NewGroup : String ) : Boolean;
  90.         - Sets Group to NewGroup. All variables will be searched and
  91.         set only local to this group. This prevents your program from
  92.         overwriting the variables of other programs in the WIN.INI,
  93.         and faciliates different setups for different users.
  94.         If NewGroup is empty, the group spans the whole file. This
  95.         can be very dangerous, but has its uses sometimmes ...
  96.  
  97.                       Procedure CreateGroup( NewGroup : String );
  98.         - Creates a new group with the name NewGroup, and sets
  99.         the current group to it.
  100.  
  101.                       Procedure EraseGroup( GroupName : String );
  102.         - Erases a whole group, including any variables contained
  103.         in it. After a call to this procedure, Group is undefined.
  104.         Make sure, that after each EraseGroup a new group is set !
  105.  
  106.                       Function GetItem( ItemName : String ) : String;
  107.         Just like GetEnv, this returns the value of the variable
  108.         ItemName. If the variable is undefined in the current group,
  109.         the result is an empty string.
  110.  
  111.                       Procedure SetItem( ItemName, Value : String );
  112.         Sets the variable ItemName in the current group to the value
  113.         Value. If the variable was previously undefined, it is created,
  114.         if it had another value, it is overwritten, if value is empty,
  115.         the variable is erased from the file.
  116.  
  117.                       Procedure Flush( CreateBackup : Boolean );
  118.         Writes the current version of the .INI file out on disk.
  119.         A backup ( .BAK ) will onyl be created, if CreateBackup
  120.         is true.
  121.  
  122.                       Destructor Done( CreateBackup : Boolean );
  123.         Frees all memory allocated by TConfigFile, and calls Flush.
  124.  
  125.                       Private
  126.  
  127.                       Procedure About;
  128.         This procedure takes care of my message being inserted
  129.         into the .INI file, and inserts the date-time-stamp of
  130.         your application. Don't leave it out, please !
  131.  
  132.                     End;
  133.  
  134. *)
  135.  
  136. {* Caveats :                                                          *}
  137.  
  138. {* The unit assumes, that enough memory will be available to hold the *}
  139. {* entire .INI file in memory, and that the size if the .INI file     *}
  140. {* will be less than 65535 bytes.                                     *}
  141.  
  142. {* Memory requirements when changing .INI file will be at least twice *}
  143. {* the size of the old .INI file.                                     *}
  144.  
  145. {* No changes to the .INI file will actually be made until the        *}
  146. {* destructor Done was called. A backup file will only be created,    *}
  147. {* if specified with the Destructor Done.                             *}
  148.  
  149. Interface
  150.  
  151. Uses DOS
  152.      ;
  153.  
  154. Type TConfigFile = Object
  155.  
  156.                       Changed : Boolean; { True, if items in file were changed }
  157.  
  158.                       TheSize : Word;  { After the above assumption ... }
  159.                       TheBuffer : Pointer; { Size is same as TheSize }
  160.                       TheName : String; { The file name of the .INI file }
  161.  
  162.                       TheApp : String;
  163.  
  164.                       Group : String;
  165.                       GroupStart : Word;
  166.                       GroupSize : Word;
  167.  
  168.                       Constructor Init( FileName : String; ApplicationName : String; AGroup : String );
  169.  
  170.                       Function SetGroup( NewGroup : String ) : Boolean;
  171.                       Procedure CreateGroup( NewGroup : String );
  172.                       Procedure EraseGroup( GroupName : String );
  173.  
  174.                       Function GetItem( ItemName : String ) : String;
  175.                       Procedure SetItem( ItemName, Value : String );
  176.  
  177.                       Procedure Flush( CreateBackup : Boolean );
  178.  
  179.                       Destructor Done( CreateBackup : Boolean );
  180.  
  181.                       Private
  182.  
  183.                       Procedure About;
  184.                     End;
  185.  
  186.      PConfigFile = ^TConfigFile;
  187.  
  188. {* DISCLAIMER:                                                        *}
  189. {* In no event shall I, Max Maischein, be liable for any damage to    *}
  190. {* your software or hardware. By using it in your program, you agree  *}
  191. {* to the above terms.                                                *}
  192.  
  193. Implementation
  194. {$DEFINE Debug }         { define these, if you have to debug something }
  195. {$DEFINE DUMPinput }
  196. {$DEFINE DUMPoutput}
  197.  
  198. {****************************************************************}
  199. { The following functions are q&d replacements from other units. }
  200. { use OPro's String-unit, or something like this for better performance }
  201.  
  202. Function UpperCase( Var S : String ) : String;
  203. { Convert string to all uppercase letters }
  204. Var SLen : Byte absolute S;
  205.     Result : String;
  206.     RLen : Byte absolute Result;
  207.     I : Byte;
  208. Begin
  209.  
  210.   RLen := SLen;
  211.  
  212.   For I := 1 to SLen do
  213.     Result[ I ] := UpCase( S[ I ]);
  214.  
  215.   UpperCase := Result;
  216. End;
  217.  
  218. { Search a string in a 64K buffer. Very unsophisticated. }
  219. { Use a Boyer-Moore-Search or port to assembler. }
  220. { Returns 0 if string was not found. }
  221.  
  222. Function BruteSearch( Var Buffer; BufSize : Word; Target : String ) : Word;
  223. Type TSearch = Array[ 0..65534 ] of Char;
  224. Var I : Word;
  225.     J : Byte;
  226.  
  227.     P : TSearch absolute Buffer;
  228.  
  229.     Ch : Char;
  230.  
  231.     Found : Boolean;
  232. Begin
  233.  
  234.   If Target = ''
  235.     then BruteSearch := 0
  236.     else
  237.       Begin
  238.  
  239.         Target := UpperCase( Target );
  240.  
  241.         I := 0;
  242.         Ch := Target[ 1 ];
  243.  
  244.         Repeat
  245.  
  246.           While ( UpCase( P[ I ]) <> Ch ) and ( I < BufSize ) do
  247.               Inc( I );
  248.  
  249.           Found := False;
  250.  
  251.           If UpCase( P[ I ] ) = Ch
  252.             then
  253.               Begin
  254.  
  255.                 J := 1;
  256.  
  257.                 Found := True;
  258.  
  259.                 While Found and ( J < Length( Target )) do
  260.                   Begin
  261.  
  262.                     Inc( J );
  263.                     Found := ( UpCase( P[ I + J -1 ]) = Target[ J ]);
  264.                   End;
  265.  
  266.                 If not Found
  267.                   then Inc( I );
  268.  
  269.               End
  270.             else I := BufSize;
  271.  
  272.         Until Found or ( I = BufSize );
  273.  
  274.         If I = BufSize
  275.           then BruteSearch := 0
  276.           else BruteSearch := Succ( I );
  277.       End;
  278. End;
  279.  
  280. CONST
  281. Months : Array[ 1..12 ] Of String[ 9 ] =
  282.   ( 'January',
  283.     'February',
  284.     'March',
  285.     'April',
  286.     'May',
  287.     'June',
  288.     'July',
  289.     'August',
  290.     'September',
  291.     'October',
  292.     'November',
  293.     'December' );
  294.  
  295. DayOfWeek : Array[ 0..7 ] Of String[ 9 ] =
  296.   ( 'Sunday',
  297.     'Monday',
  298.     'Tuesday',
  299.     'Wednesday',
  300.     'Thursday',
  301.     'Friday',
  302.     'Saturday',
  303.     'Sunday' );
  304.  
  305. Function Ordinal( W : Word ) : String;
  306. Var Internal : String[ 7 ];
  307. Begin
  308.  
  309.   Str( W,Internal );
  310.   If W > 3 Then Internal := Internal + 'th' Else
  311.   If W = 1 Then Internal := Internal + 'st' Else
  312.   If W = 2 Then Internal := Internal + 'nd' Else
  313.   If W = 3 Then Internal := Internal + 'rd';
  314.   Ordinal := Internal;
  315. End;
  316.  
  317. Function CurrentDate : String;
  318. Var Year, Month, Day, DOW : Word;
  319.     S : String[ 4 ];
  320. Begin
  321.  
  322.   GetDate( Year, Month, Day, DOW );
  323.  
  324.   Str( Year, S );
  325.  
  326.   CurrentDate := DayOfWeek[ DOW ] + ', the '+ Ordinal( Day ) +' of '+Months[ Month ] + ' ' + S;
  327. End;
  328.  
  329. Function CurrentTime : String;
  330. Var Hour, Minute, Second, HS : Word;
  331.     S : String[ 2 ];
  332.     Q : String[ 2 ];
  333.     Internal : String[ 8 ];
  334. Begin
  335.  
  336.   GetTime( Hour, Minute, Second, HS );
  337.   Str( Hour : 2, Q );
  338.   If Hour < 10 then Q[ 1 ] := '0';
  339.   Str( Minute : 2, S );
  340.   If Minute < 10 then S[ 1 ] := '0';
  341.   CurrentTime := Q + ':' + S;
  342. End;
  343.  
  344. {****************************************************************}
  345. Const CopyRight : Array[ 1..95 ] of Char =
  346.   '; Ini Version 1.0 -- A freeware interface to .INI files. Created by Max Maischein 2:249/6.17.'#$0D#$0A;
  347.  
  348. Type TSearch = Array[ 1..65535 ] of Char;
  349.      PSearch = ^TSearch;
  350.  
  351. {$IFDEF Debug}
  352. Procedure WriteChar( Ch : Char );
  353. Begin
  354.  
  355.   If Ch < ' '
  356.     then Write('#',Ord( Ch ))
  357.     else Write( Ch );
  358. End;
  359.  
  360. Procedure Dump( P1 : Pointer; S1 : Word; P2 : Pointer; S2 : Word );
  361. Var I : Word;
  362.     P : PSearch;
  363. Begin
  364.  
  365.   P := P1;
  366.  
  367.   WriteLn( 'Contents of P1( ',S1,' ) :' );
  368.   For I := 1 to S1 do
  369.     WriteChar( P^[ I ]);
  370.   WriteLn;
  371.  
  372.   P := P2;
  373.  
  374.   WriteLn( 'Contents of P2( ',S2,' ) :' );
  375.   For I := 1 to S2 do
  376.     WriteChar( P^[ I ]);
  377.   ReadlN;
  378. End;
  379. {$ENDIF}
  380.  
  381. Constructor TConfigFile.Init;
  382. Var P : PSearch absolute TheBuffer;
  383.     TheFile : File;
  384.  
  385. Begin
  386.  
  387.   TheName := FileName;
  388.  
  389.   Assign( TheFile, FileName );
  390.   Reset( TheFile,1 );
  391.  
  392.   TheSize := FileSize( TheFile );
  393.   GetMem( TheBuffer, TheSize ); { Get enough memory to hold the entire File }
  394.   BlockRead( TheFile, TheBuffer^, TheSize );
  395.  
  396.   Close( TheFile );
  397.  
  398.   {$IFDEF DumpInput}
  399.   WriteLn( 'Input read from file ',FileName );
  400.   Dump( TheBuffer, TheSize, nil, 0 );
  401.   {$ENDIF}
  402.  
  403.   If P^[ TheSize ] = ^Z
  404.     then Dec( TheSize );
  405.  
  406.   GroupSize := 0;
  407.   GroupStart := 0;
  408.  
  409.   If not SetGroup( AGroup )
  410.     then CreateGroup( AGroup );
  411.  
  412.   Changed := False;
  413.  
  414.   TheApp := ApplicationName;
  415.  
  416. End;
  417.  
  418. Procedure TConfigFile.Flush;
  419. Var TheFile : File;
  420. Begin
  421.  
  422.   About;
  423.  
  424.   {$IFDEF DUMPoutput}
  425.     WriteLn( 'Writing out :' );
  426.     Dump( TheBuffer, TheSize, nil, 0 );
  427.   {$ENDIF}
  428.  
  429.   Assign( TheFile, TheName );
  430.  
  431.   If CreateBackup
  432.     then
  433.       Begin
  434.  
  435.         Rename( TheFile, Copy( TheName, 1, Byte( TheName[ 0 ] ) -3)+'BAK' );
  436.         Assign( TheFile, TheName );
  437.       End;
  438.  
  439.   ReWrite( TheFile, 1 );
  440.  
  441.   BlockWrite( TheFile, TheBuffer^, TheSize );
  442.  
  443.   Close( TheFile );
  444.  
  445.   Changed := False;
  446. End;
  447.  
  448. Destructor TConfigFile.Done;
  449. Begin
  450.  
  451.   If Changed
  452.     then Flush( CreateBackup );
  453.  
  454.   If TheBuffer <> nil
  455.     then FreeMem( TheBuffer, TheSize );
  456. End;
  457.  
  458. Function TConfigFile.SetGroup;
  459.  
  460. Var Table1, Table2 : Array[ Char ] of Byte;
  461.     Found : Boolean;
  462.     MyPos : Word;
  463.  
  464.     P : PSearch;
  465.     S : String;
  466.  
  467.     Size : Byte;
  468.  
  469.     Search : String;
  470.  
  471. Begin
  472.  
  473.   If NewGroup = ''
  474.     then
  475.       Begin
  476.  
  477.         GroupStart := 0;
  478.         GroupSize := TheSize;
  479.  
  480.         SetGroup := True;
  481.  
  482.         Exit; { could be better, but ;-) }
  483.       End;
  484.  
  485.   If NewGroup[ 1 ] <> '['
  486.     then NewGroup := '[' + NewGroup;
  487.  
  488.   If NewGroup[ Byte( NewGroup[ 0 ])] <> ']'
  489.     then NewGroup := NewGroup + ']';
  490.  
  491.   Search := NewGroup;
  492.  
  493.   MyPos := BruteSearch( TheBuffer^, TheSize, Search );
  494.  
  495.   If MyPos <> 0
  496.     then
  497.       Begin
  498.  
  499.         GroupStart := MyPos-1;
  500.         Group := NewGroup;
  501.  
  502.         MyPos := Length( NewGroup )+2;
  503.         P := TheBuffer;
  504.  
  505.         GroupSize := 0;
  506.         Repeat
  507.  
  508.           Inc( GroupSize );
  509.         Until ( GroupStart + GroupSize = TheSize ) or ( P^[ GroupStart + GroupSize + 1 ] = '[');
  510.  
  511.         SetGroup := True;
  512.       End
  513.     else
  514.       SetGroup := False;
  515. End;
  516.  
  517. Procedure TConfigFile.CreateGroup;
  518. Var NewSize : Word;
  519.  
  520.     NewBuffer : Pointer;
  521.     P : PSearch absolute NewBuffer;
  522.  
  523. Begin
  524.  
  525.   If NewGroup[ 1 ] <> '['
  526.     then NewGroup := '[' + NewGroup;
  527.  
  528.   If NewGroup[ Byte( NewGroup[ 0 ])] <> ']'
  529.     then NewGroup := NewGroup + ']';
  530.  
  531.   NewSize := TheSize;
  532.  
  533.   Inc( NewSize, Length( NewGroup ) + 2 );
  534.   GetMem( NewBuffer, NewSize );
  535.  
  536.   Move( TheBuffer^, NewBuffer^, TheSize );
  537.   Move( NewGroup[ 1 ], P^[ TheSize+1 ], Byte( NewGroup[ 0 ] ));
  538.   P^[ Pred( NewSize )] := #$0D;
  539.   P^[ NewSize ] := #$0A;
  540.  
  541.   FreeMem( TheBuffer, TheSize );
  542.   TheBuffer := NewBuffer;
  543.   TheSize := NewSize;
  544.  
  545.   If SetGroup( NewGroup ) then ;
  546.  
  547.   Changed := True;
  548.  
  549. End;
  550.  
  551. Procedure TConfigFile.EraseGroup;
  552. Var P : PSearch absolute TheBuffer;
  553.     NewBuffer : Pointer;
  554.     Q : PSearch absolute NewBuffer;
  555.     NewSize : Word;
  556. Begin
  557.  
  558.   If SetGroup( GroupName )
  559.     then
  560.       Begin
  561.  
  562.         NewSize := TheSize - GroupSize;
  563.         GetMem( NewBuffer, NewSize );
  564.  
  565.         Move( TheBuffer^, NewBuffer^, GroupStart );
  566.         Move( P^[ GroupStart + GroupSize ], Q^[ GroupStart ], TheSize - GroupStart - GroupSize +1 );
  567.  
  568.         If SetGroup( '' )
  569.           then;
  570.  
  571.         Freemem( TheBuffer, TheSize );
  572.         TheBuffer := NewBuffer;
  573.         TheSize := NewSize;
  574.  
  575.         Changed := True;
  576.       End;
  577. End;
  578.  
  579. Function TConfigFile.GetItem;
  580. Var Table1, Table2 : Array[ Char ] of Byte;
  581.     Found : Boolean;
  582.     MyPos : Word;
  583.  
  584.     P : PSearch absolute TheBuffer;
  585.     Result : String;
  586.  
  587.     I : Byte;
  588.  
  589.     Search : String;
  590. Begin
  591.  
  592.   If ItemName[ Byte( ItemName[ 0 ])] <> '='
  593.     then ItemName := ItemName + '=';
  594.  
  595.   MyPos := BruteSearch( P^[ GroupStart+1 ], GroupSize, UpperCase( ItemName ));
  596.  
  597.   Result := '';
  598.  
  599.   If MyPos <> 0
  600.     then
  601.       Begin
  602.         Inc( MyPos, Byte( ItemName[ 0 ]));
  603.  
  604.         Result[ 0 ] := #255;
  605.         Move( P^[ GroupStart+MyPos ], Result[ 1 ], 255 );
  606.  
  607.         Result[ 0 ] := Char( Pred( Pos( #$0D,Result )));
  608.         GetItem := Result;
  609.  
  610.       End
  611.     else
  612.       GetItem := '';
  613. End;
  614.  
  615. Procedure TConfigFile.SetItem;
  616. Var NewBuffer : Pointer;
  617.     P,Q : PSearch;
  618.     NewSize : Word;
  619.     NewString : String;
  620.  
  621.     Table1, Table2 : Array[ Char ] of Byte;
  622.  
  623.     Pos : Word;
  624.  
  625.     Search : String;
  626.  
  627. Begin
  628.  
  629.   If ItemName[ Byte( ItemName[ 0 ])] <> '='
  630.     then ItemName := ItemName + '=';
  631.  
  632.   P := TheBuffer;
  633.  
  634.   If GetItem( ItemName ) <> ''
  635.     then { remove old Item + Value }
  636.       Begin
  637.  
  638.         NewString := ItemName + GetItem( ItemName );
  639.         NewSize := TheSize - Byte( NewString[ 0 ]) - 2;
  640.         GetMem( NewBuffer, NewSize );
  641.  
  642.         Search := ItemName;
  643.  
  644.         P := TheBuffer;
  645.         Pos := BruteSearch( P^[ GroupStart ], GroupSize, UpperCase( ItemName ));
  646.         Dec( Pos );
  647.  
  648.         Q := NewBuffer;
  649.  
  650.         Move( TheBuffer^, NewBuffer^, GroupStart + Pos );
  651.         Move( P^[ GroupStart + Pos +
  652.           Byte( NewString[ 0 ]) +1], Q^[ GroupStart + Pos-1 ], TheSize - GroupStart - Pos - Byte( NewString[ 0 ] ) + 1 );
  653.  
  654.         FreeMem( TheBuffer, TheSize );
  655.         TheBuffer := NewBuffer;
  656.         TheSize := NewSize;
  657.  
  658.         Dec( GroupSize, Byte( NewString[ 0 ]) +2 );
  659.       End;
  660.  
  661.   If Value <> ''
  662.     then
  663.       Begin
  664.  
  665.         NewString := ItemName + Value + #$0D#$0A;
  666.  
  667.         NewSize := TheSize + Byte( NewString[ 0 ]);
  668.         GetMem( NewBuffer, NewSize );
  669.  
  670.         P := NewBuffer;
  671.         Q := TheBuffer;
  672.         Move( TheBuffer^, NewBuffer^, GroupStart + GroupSize );
  673.         Move( NewString[ 1 ], P^[ GroupStart + GroupSize +1 ], Byte( NewString[ 0 ]));
  674.         Move( Q^[ GroupStart + GroupSize+1 ], P^[ 1 + GroupStart + GroupSize + Byte( NewString[ 0 ])],
  675.           TheSize - ( GroupStart + GroupSize ));
  676.  
  677.         Inc( GroupSize, Byte( NewString[ 0 ]));
  678.  
  679.         FreeMem( TheBuffer, TheSize );
  680.         TheSize := NewSize;
  681.         TheBuffer := NewBuffer;
  682.  
  683.     End;
  684.  
  685.   Changed := True;
  686. End;
  687.  
  688. Procedure TConfigFile.About;
  689. Var NewBuffer : Pointer;
  690.     NewSize : Word;
  691.     P : PSearch absolute NewBuffer;
  692.     Q : PSearch absolute TheBuffer;
  693.  
  694.     AboutMsg : String;
  695.  
  696.     M : Word;
  697.     Pos : Word;
  698.  
  699. Begin
  700.  
  701.   AboutMsg := '; Last modification by '+TheApp+' to this file was on '+CurrentDate+' at '+CurrentTime+'h.'#$0D#$0A;
  702.  
  703.   M := BruteSearch( TheBuffer^, TheSize, CopyRight );
  704.   If M <> 0
  705.     then
  706.       Begin
  707.  
  708.         Move( Q^[ M + SizeOf( CopyRight )], Q^[ M ], TheSize - M - SizeOf( CopyRight ) +1 );
  709.  
  710.         Dec( TheSize, SizeOf( CopyRight ));
  711.       End;
  712.  
  713.   M := BruteSearch( TheBuffer^, TheSize, Copy( AboutMSG, 1, 32 ));
  714.   If M <> 0
  715.     then
  716.       Begin
  717.  
  718.         Pos := 0;
  719.         Repeat
  720.  
  721.           Inc( Pos );
  722.         Until Q^[ M + Pos ] = #$0A;
  723.  
  724.         Inc( Pos );
  725.  
  726.         Move( Q^[ M + Pos ], Q^[ M ], TheSize - M - Pos + 1 );
  727.  
  728.         Dec( TheSize, Pos );
  729.       End;
  730.  
  731.   NewSize := TheSize;
  732.  
  733.   Inc( NewSize, SizeOf( CopyRight ));
  734.   Inc( NewSize, Byte( AboutMsg[ 0 ]));
  735.  
  736.   GetMem( NewBuffer, NewSize );
  737.  
  738.   Move( CopyRight, NewBuffer^, SizeOf( CopyRight ));
  739.   Move( AboutMsg[ 1 ], P^[ SizeOf( CopyRight )+1], Byte( AboutMsg[ 0 ]));
  740.   Move( TheBuffer^, P^[ Byte( AboutMsg[ 0 ] )+ 1 + SizeOf( CopyRight )], TheSize );
  741.  
  742.   FreeMem( TheBuffer, TheSize );
  743.   TheSize := NewSize;
  744.   TheBuffer := NewBuffer;
  745. End;
  746.  
  747. End.
  748.