home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 1999 February / DPPCPRO0299.ISO / February / Delphi / Install / DATA.Z / SYSUTILS.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1996-06-09  |  132.9 KB  |  4,388 lines

  1.  
  2. {*******************************************************}
  3. {                                                       }
  4. {       Delphi Runtime Library                          }
  5. {       System Utilities Unit                           }
  6. {                                                       }
  7. {       Copyright (C) 1995,96 Borland International     }
  8. {                                                       }
  9. {*******************************************************}
  10.  
  11. unit SysUtils;
  12.  
  13. {$H+}
  14.  
  15. interface
  16.  
  17. uses Windows;
  18.  
  19. const
  20.  
  21. { File open modes }
  22.  
  23.   fmOpenRead       = $0000;
  24.   fmOpenWrite      = $0001;
  25.   fmOpenReadWrite  = $0002;
  26.   fmShareCompat    = $0000;
  27.   fmShareExclusive = $0010;
  28.   fmShareDenyWrite = $0020;
  29.   fmShareDenyRead  = $0030;
  30.   fmShareDenyNone  = $0040;
  31.  
  32. { File attribute constants }
  33.  
  34.   faReadOnly  = $00000001;
  35.   faHidden    = $00000002;
  36.   faSysFile   = $00000004;
  37.   faVolumeID  = $00000008;
  38.   faDirectory = $00000010;
  39.   faArchive   = $00000020;
  40.   faAnyFile   = $0000003F;
  41.  
  42. { File mode magic numbers }
  43.  
  44.   fmClosed = $D7B0;
  45.   fmInput  = $D7B1;
  46.   fmOutput = $D7B2;
  47.   fmInOut  = $D7B3;
  48.  
  49. { Seconds and milliseconds per day }
  50.  
  51.   SecsPerDay = 24 * 60 * 60;
  52.   MSecsPerDay = SecsPerDay * 1000;
  53.  
  54. { Days between 1/1/0001 and 12/31/1899 }
  55.  
  56.   DateDelta = 693594;
  57.  
  58. type
  59.  
  60. { Type conversion records }
  61.  
  62.   WordRec = packed record
  63.     Lo, Hi: Byte;
  64.   end;
  65.  
  66.   LongRec = packed record
  67.     Lo, Hi: Word;
  68.   end;
  69.  
  70.   TMethod = record
  71.     Code, Data: Pointer;
  72.   end;
  73.  
  74. { General arrays }
  75.  
  76.   PByteArray = ^TByteArray;
  77.   TByteArray = array[0..32767] of Byte;
  78.  
  79.   PWordArray = ^TWordArray;
  80.   TWordArray = array[0..16383] of Word;
  81.  
  82. { Generic procedure pointer }
  83.  
  84.   TProcedure = procedure;
  85.  
  86. { Generic filename type }
  87.  
  88.   TFileName = string;
  89.  
  90. { Search record used by FindFirst, FindNext, and FindClose }
  91.  
  92.   TSearchRec = record
  93.     Time: Integer;
  94.     Size: Integer;
  95.     Attr: Integer;
  96.     Name: TFileName;
  97.     ExcludeAttr: Integer;
  98.     FindHandle: THandle;
  99.     FindData: TWin32FindData;
  100.   end;
  101.  
  102. { Typed-file and untyped-file record }
  103.  
  104.   TFileRec = record
  105.     Handle: Integer;
  106.     Mode: Integer;
  107.     RecSize: Cardinal;
  108.     Private: array[1..28] of Byte;
  109.     UserData: array[1..32] of Byte;
  110.     Name: array[0..259] of Char;
  111.   end;
  112.  
  113. { Text file record structure used for Text files }
  114.  
  115.   PTextBuf = ^TTextBuf;
  116.   TTextBuf = array[0..127] of Char;
  117.   TTextRec = record
  118.     Handle: Integer;
  119.     Mode: Integer;
  120.     BufSize: Cardinal;
  121.     BufPos: Cardinal;
  122.     BufEnd: Cardinal;
  123.     BufPtr: PChar;
  124.     OpenFunc: Pointer;
  125.     InOutFunc: Pointer;
  126.     FlushFunc: Pointer;
  127.     CloseFunc: Pointer;
  128.     UserData: array[1..32] of Byte;
  129.     Name: array[0..259] of Char;
  130.     Buffer: TTextBuf;
  131.   end;
  132.  
  133. { FloatToText, FloatToTextFmt, TextToFloat, and FloatToDecimal type codes }
  134.  
  135.   TFloatValue = (fvExtended, fvCurrency);
  136.  
  137. { FloatToText format codes }
  138.  
  139.   TFloatFormat = (ffGeneral, ffExponent, ffFixed, ffNumber, ffCurrency);
  140.  
  141. { FloatToDecimal result record }
  142.  
  143.   TFloatRec = packed record
  144.     Exponent: Smallint;
  145.     Negative: Boolean;
  146.     Digits: array[0..20] of Char;
  147.   end;
  148.  
  149. { Date and time record }
  150.  
  151.   TTimeStamp = record
  152.     Time: Integer;      { Number of milliseconds since midnight }
  153.     Date: Integer;      { One plus number of days since 1/1/0001 }
  154.   end;
  155.  
  156. { Exceptions }
  157.  
  158.   Exception = class(TObject)
  159.   private
  160.     FMessage: string;
  161.     FHelpContext: Integer;
  162.   public
  163.     constructor Create(const Msg: string);
  164.     constructor CreateFmt(const Msg: string; const Args: array of const);
  165.     constructor CreateRes(Ident: Integer);
  166.     constructor CreateResFmt(Ident: Integer; const Args: array of const);
  167.     constructor CreateHelp(const Msg: string; AHelpContext: Integer);
  168.     constructor CreateFmtHelp(const Msg: string; const Args: array of const;
  169.       AHelpContext: Integer);
  170.     constructor CreateResHelp(Ident: Integer; AHelpContext: Integer);
  171.     constructor CreateResFmtHelp(Ident: Integer; const Args: array of const;
  172.       AHelpContext: Integer);
  173.     property HelpContext: Integer read FHelpContext write FHelpContext;
  174.     property Message: string read FMessage write FMessage;
  175.   end;
  176.  
  177.   ExceptClass = class of Exception;
  178.  
  179.   EAbort = class(Exception);
  180.  
  181.   EOutOfMemory = class(Exception)
  182.   public
  183.     destructor Destroy; override;
  184.     procedure FreeInstance; override;
  185.   end;
  186.  
  187.   EInOutError = class(Exception)
  188.   public
  189.     ErrorCode: Integer;
  190.   end;
  191.  
  192.   EIntError = class(Exception);
  193.   EDivByZero = class(EIntError);
  194.   ERangeError = class(EIntError);
  195.   EIntOverflow = class(EIntError);
  196.  
  197.   EMathError = class(Exception);
  198.   EInvalidOp = class(EMathError);
  199.   EZeroDivide = class(EMathError);
  200.   EOverflow = class(EMathError);
  201.   EUnderflow = class(EMathError);
  202.  
  203.   EInvalidPointer = class(Exception);
  204.  
  205.   EInvalidCast = class(Exception);
  206.  
  207.   EConvertError = class(Exception);
  208.  
  209.   EAccessViolation = class(Exception);
  210.   EPrivilege = class(Exception);
  211.   EStackOverflow = class(Exception);
  212.   EControlC = class(Exception);
  213.  
  214.   EVariantError = class(Exception);
  215.  
  216.   EPropReadOnly = class(Exception);
  217.   EPropWriteOnly = class(Exception);
  218.  
  219.   EExternalException = class(Exception)
  220.   public
  221.     ExceptionRecord: PExceptionRecord;
  222.   end;
  223.  
  224. const
  225.  
  226. { Empty string and null string pointer. These constants are provided for
  227.   backwards compatibility only. }
  228.  
  229.   EmptyStr: string = '';
  230.   NullStr: PString = @EmptyStr;
  231.  
  232. { Win32 platform identifier.  This will be one of the following values:
  233.  
  234.     VER_PLATFORM_WIN32s
  235.     VER_PLATFORM_WIN32_WINDOWS
  236.     VER_PLATFORM_WIN32_NT
  237.  
  238.   See WINDOWS.PAS for the numerical values. }
  239.  
  240.   Win32Platform: Integer = 0;
  241.  
  242. { Currency and date/time formatting options
  243.  
  244.   The initial values of these variables are fetched from the system registry
  245.   using the GetLocaleInfo function in the Win32 API. The description of each
  246.   variable specifies the LOCALE_XXXX constant used to fetch the initial
  247.   value.
  248.  
  249.   CurrencyString - Defines the currency symbol used in floating-point to
  250.   decimal conversions. The initial value is fetched from LOCALE_SCURRENCY.
  251.  
  252.   CurrencyFormat - Defines the currency symbol placement and separation
  253.   used in floating-point to decimal conversions. Possible values are:
  254.  
  255.     0 = '$1'
  256.     1 = '1$'
  257.     2 = '$ 1'
  258.     3 = '1 $'
  259.  
  260.   The initial value is fetched from LOCALE_ICURRENCY.
  261.  
  262.   NegCurrFormat - Defines the currency format for used in floating-point to
  263.   decimal conversions of negative numbers. Possible values are:
  264.  
  265.     0 = '($1)'      4 = '(1$)'      8 = '-1 $'      12 = '$ -1'
  266.     1 = '-$1'       5 = '-1$'       9 = '-$ 1'      13 = '1- $'
  267.     2 = '$-1'       6 = '1-$'      10 = '1 $-'      14 = '($ 1)'
  268.     3 = '$1-'       7 = '1$-'      11 = '$ 1-'      15 = '(1 $)'
  269.  
  270.   The initial value is fetched from LOCALE_INEGCURR.
  271.  
  272.   ThousandSeparator - The character used to separate thousands in numbers
  273.   with more than three digits to the left of the decimal separator. The
  274.   initial value is fetched from LOCALE_STHOUSAND.
  275.  
  276.   DecimalSeparator - The character used to separate the integer part from
  277.   the fractional part of a number. The initial value is fetched from
  278.   LOCALE_SDECIMAL.
  279.  
  280.   CurrencyDecimals - The number of digits to the right of the decimal point
  281.   in a currency amount. The initial value is fetched from LOCALE_ICURRDIGITS.
  282.  
  283.   DateSeparator - The character used to separate the year, month, and day
  284.   parts of a date value. The initial value is fetched from LOCATE_SDATE.
  285.  
  286.   ShortDateFormat - The format string used to convert a date value to a
  287.   short string suitable for editing. For a complete description of date and
  288.   time format strings, refer to the documentation for the FormatDate
  289.   function. The short date format should only use the date separator
  290.   character and the  m, mm, d, dd, yy, and yyyy format specifiers. The
  291.   initial value is fetched from LOCALE_SSHORTDATE.
  292.  
  293.   LongDateFormat - The format string used to convert a date value to a long
  294.   string suitable for display but not for editing. For a complete description
  295.   of date and time format strings, refer to the documentation for the
  296.   FormatDate function. The initial value is fetched from LOCALE_SLONGDATE.
  297.  
  298.   TimeSeparator - The character used to separate the hour, minute, and
  299.   second parts of a time value. The initial value is fetched from
  300.   LOCALE_STIME.
  301.  
  302.   TimeAMString - The suffix string used for time values between 00:00 and
  303.   11:59 in 12-hour clock format. The initial value is fetched from
  304.   LOCALE_S1159.
  305.  
  306.   TimePMString - The suffix string used for time values between 12:00 and
  307.   23:59 in 12-hour clock format. The initial value is fetched from
  308.   LOCALE_S2359.
  309.  
  310.   ShortTimeFormat - The format string used to convert a time value to a
  311.   short string with only hours and minutes. The default value is computed
  312.   from LOCALE_ITIME and LOCALE_ITLZERO.
  313.  
  314.   LongTimeFormat - The format string used to convert a time value to a long
  315.   string with hours, minutes, and seconds. The default value is computed
  316.   from LOCALE_ITIME and LOCALE_ITLZERO.
  317.  
  318.   ShortMonthNames - Array of strings containing short month names. The mmm
  319.   format specifier in a format string passed to FormatDate causes a short
  320.   month name to be substituted. The default values are fecthed from the
  321.   LOCALE_SABBREVMONTHNAME system locale entries.
  322.  
  323.   LongMonthNames - Array of strings containing long month names. The mmmm
  324.   format specifier in a format string passed to FormatDate causes a long
  325.   month name to be substituted. The default values are fecthed from the
  326.   LOCALE_SMONTHNAME system locale entries.
  327.  
  328.   ShortDayNames - Array of strings containing short day names. The ddd
  329.   format specifier in a format string passed to FormatDate causes a short
  330.   day name to be substituted. The default values are fecthed from the
  331.   LOCALE_SABBREVDAYNAME system locale entries.
  332.  
  333.   LongDayNames - Array of strings containing long day names. The dddd
  334.   format specifier in a format string passed to FormatDate causes a long
  335.   day name to be substituted. The default values are fecthed from the
  336.   LOCALE_SDAYNAME system locale entries. }
  337.  
  338. var
  339.   CurrencyString: string;
  340.   CurrencyFormat: Byte;
  341.   NegCurrFormat: Byte;
  342.   ThousandSeparator: Char;
  343.   DecimalSeparator: Char;
  344.   CurrencyDecimals: Byte;
  345.   DateSeparator: Char;
  346.   ShortDateFormat: string;
  347.   LongDateFormat: string;
  348.   TimeSeparator: Char;
  349.   TimeAMString: string;
  350.   TimePMString: string;
  351.   ShortTimeFormat: string;
  352.   LongTimeFormat: string;
  353.   ShortMonthNames: array[1..12] of string;
  354.   LongMonthNames: array[1..12] of string;
  355.   ShortDayNames: array[1..7] of string;
  356.   LongDayNames: array[1..7] of string;
  357.  
  358. { Memory management routines }
  359.  
  360. { AllocMem allocates a block of the given size on the heap. Each byte in
  361.   the allocated buffer is set to zero. To dispose the buffer, use the
  362.   FreeMem standard procedure. }
  363.  
  364. function AllocMem(Size: Cardinal): Pointer;
  365.  
  366. { Exit procedure handling }
  367.  
  368. { AddExitProc adds the given procedure to the run-time library's exit
  369.   procedure list. When an application terminates, its exit procedures are
  370.   executed in reverse order of definition, i.e. the last procedure passed
  371.   to AddExitProc is the first one to get executed upon termination. }
  372.  
  373. procedure AddExitProc(Proc: TProcedure);
  374.  
  375. { String handling routines }
  376.  
  377. { NewStr allocates a string on the heap. NewStr is provided for backwards
  378.   compatibility only. }
  379.  
  380. function NewStr(const S: string): PString;
  381.  
  382. { DisposeStr disposes a string pointer that was previously allocated using
  383.   NewStr. DisposeStr is provided for backwards compatibility only. }
  384.  
  385. procedure DisposeStr(P: PString);
  386.  
  387. { AssignStr assigns a new dynamically allocated string to the given string
  388.   pointer. AssignStr is provided for backwards compatibility only. }
  389.  
  390. procedure AssignStr(var P: PString; const S: string);
  391.  
  392. { AppendStr appends S to the end of Dest. AppendStr is provided for
  393.   backwards compatibility only. Use "Dest := Dest + S" instead. }
  394.  
  395. procedure AppendStr(var Dest: string; const S: string);
  396.  
  397. { UpperCase converts all ASCII characters in the given string to upper case.
  398.   The conversion affects only 7-bit ASCII characters between 'a' and 'z'. To
  399.   convert 8-bit international characters, use AnsiUpperCase. }
  400.  
  401. function UpperCase(const S: string): string;
  402.  
  403. { UpperCase converts all ASCII characters in the given string to lower case.
  404.   The conversion affects only 7-bit ASCII characters between 'A' and 'Z'. To
  405.   convert 8-bit international characters, use AnsiLowerCase. }
  406.  
  407. function LowerCase(const S: string): string;
  408.  
  409. { CompareStr compares S1 to S2, with case-sensitivity. The return value is
  410.   less than 0 if S1 < S2, 0 if S1 = S2, or greater than 0 if S1 > S2. The
  411.   compare operation is based on the 8-bit ordinal value of each character
  412.   and is not affected by the current Windows locale. }
  413.  
  414. function CompareStr(const S1, S2: string): Integer;
  415.  
  416. { CompareText compares S1 to S2, without case-sensitivity. The return value
  417.   is the same as for CompareStr. The compare operation is based on the 8-bit
  418.   ordinal value of each character, after converting 'a'..'z' to 'A'..'Z',
  419.   and is not affected by the current Windows locale. }
  420.  
  421. function CompareText(const S1, S2: string): Integer;
  422.  
  423. { AnsiUpperCase converts all characters in the given string to upper case.
  424.   The conversion uses the current Windows locale. }
  425.  
  426. function AnsiUpperCase(const S: string): string;
  427.  
  428. { AnsiLowerCase converts all characters in the given string to lower case.
  429.   The conversion uses the current Windows locale. }
  430.  
  431. function AnsiLowerCase(const S: string): string;
  432.  
  433. { AnsiCompareStr compares S1 to S2, with case-sensitivity. The compare
  434.   operation is controlled by the current Windows locale. The return value
  435.   is the same as for CompareStr. }
  436.  
  437. function AnsiCompareStr(const S1, S2: string): Integer;
  438.  
  439. { AnsiCompareText compares S1 to S2, without case-sensitivity. The compare
  440.   operation is controlled by the current Windows locale. The return value
  441.   is the same as for CompareStr. }
  442.  
  443. function AnsiCompareText(const S1, S2: string): Integer;
  444.  
  445. { Trim trims leading and trailing spaces and control characters from the
  446.   given string. }
  447.  
  448. function Trim(const S: string): string;
  449.  
  450. { TrimLeft trims leading spaces and control characters from the given
  451.   string. }
  452.  
  453. function TrimLeft(const S: string): string;
  454.  
  455. { TrimRight trims trailing spaces and control characters from the given
  456.   string. }
  457.  
  458. function TrimRight(const S: string): string;
  459.  
  460. { QuotedStr returns the given string as a quoted string. A single quote
  461.   character is inserted at the beginning and the end of the string, and
  462.   for each single quote character in the string, another one is added. }
  463.  
  464. function QuotedStr(const S: string): string;
  465.  
  466. { AdjustLineBreaks adjusts all line breaks in the given string to be true
  467.   CR/LF sequences. The function changes any CR characters not followed by
  468.   a LF and any LF characters not preceded by a CR into CR/LF pairs. }
  469.  
  470. function AdjustLineBreaks(const S: string): string;
  471.  
  472. { IsValidIdent returns true if the given string is a valid identifier. An
  473.   identifier is defined as a character from the set ['A'..'Z', 'a'..'z', '_']
  474.   followed by zero or more characters from the set ['A'..'Z', 'a'..'z',
  475.   '0..'9', '_']. }
  476.  
  477. function IsValidIdent(const Ident: string): Boolean;
  478.  
  479. { IntToStr converts the given value to its decimal string representation. }
  480.  
  481. function IntToStr(Value: Integer): string;
  482.  
  483. { IntToHex converts the given value to a hexadecimal string representation
  484.   with the minimum number of digits specified. }
  485.  
  486. function IntToHex(Value: Integer; Digits: Integer): string;
  487.  
  488. { StrToInt converts the given string to an integer value. If the string
  489.   doesn't contain a valid value, an EConvertError exception is raised. }
  490.  
  491. function StrToInt(const S: string): Integer;
  492.  
  493. { StrToIntDef converts the given string to an integer value. If the string
  494.   doesn't contain a valid value, the value given by Default is returned. }
  495.  
  496. function StrToIntDef(const S: string; Default: Integer): Integer;
  497.  
  498. { LoadStr loads the string resource given by Ident from the application's
  499.   executable file. If the string resource does not exist, an empty string
  500.   is returned. }
  501.  
  502. function LoadStr(Ident: Integer): string;
  503.  
  504. { LoadStr loads the string resource given by Ident from the application's
  505.   executable file, and uses it as the format string in a call to the
  506.   Format function with the given arguments. }
  507.  
  508. function FmtLoadStr(Ident: Integer; const Args: array of const): string;
  509.  
  510. { File management routines }
  511.  
  512. { FileOpen opens the specified file using the specified access mode. The
  513.   access mode value is constructed by OR-ing one of the fmOpenXXXX constants
  514.   with one of the fmShareXXXX constants. If the return value is positive,
  515.   the function was successful and the value is the file handle of the opened
  516.   file. A return value of -1 indicates that an error occurred. }
  517.  
  518. function FileOpen(const FileName: string; Mode: Integer): Integer;
  519.  
  520. { FileCreate creates a new file by the specified name. If the return value
  521.   is positive, the function was successful and the value is the file handle
  522.   of the new file. A return value of -1 indicates that an error occurred. }
  523.  
  524. function FileCreate(const FileName: string): Integer;
  525.  
  526. { FileRead reads Count bytes from the file given by Handle into the buffer
  527.   specified by Buffer. The return value is the number of bytes actually
  528.   read; it is less than Count if the end of the file was reached. The return
  529.   value is -1 if an error occurred. }
  530.  
  531. function FileRead(Handle: Integer; var Buffer; Count: Integer): Integer;
  532.  
  533. { FileWrite writes Count bytes to the file given by Handle from the buffer
  534.   specified by Buffer. The return value is the number of bytes actually
  535.   written, or -1 if an error occurred. }
  536.  
  537. function FileWrite(Handle: Integer; const Buffer; Count: Integer): Integer;
  538.  
  539. { FileSeek changes the current position of the file given by Handle to be
  540.   Offset bytes relative to the point given by Origin. Origin = 0 means that
  541.   Offset is relative to the beginning of the file, Origin = 1 means that
  542.   Offset is relative to the current position, and Origin = 2 means that
  543.   Offset is relative to the end of the file. The return value is the new
  544.   current position, relative to the beginning of the file, or -1 if an error
  545.   occurred. }
  546.  
  547. function FileSeek(Handle, Offset, Origin: Integer): Integer;
  548.  
  549. { FileClose closes the specified file. }
  550.  
  551. procedure FileClose(Handle: Integer);
  552.  
  553. { FileAge returns the date-and-time stamp of the specified file. The return
  554.   value can be converted to a TDateTime value using the FileDateToDateTime
  555.   function. The return value is -1 if the file does not exist. }
  556.  
  557. function FileAge(const FileName: string): Integer;
  558.  
  559. { FileExists returns a boolean value that indicates whether the specified
  560.   file exists. }
  561.  
  562. function FileExists(const FileName: string): Boolean;
  563.  
  564. { FindFirst searches the directory given by Path for the first entry that
  565.   matches the filename given by Path and the attributes given by Attr. The
  566.   result is returned in the search record given by SearchRec. The return
  567.   value is zero if the function was successful. Otherwise the return value
  568.   is a Windows error code. FindFirst is typically used in conjunction with
  569.   FindNext and FindClose as follows:
  570.  
  571.     Result := FindFirst(Path, Attr, SearchRec);
  572.     while Result = 0 do
  573.     begin
  574.       ProcessSearchRec(SearchRec);
  575.       Result := FindNext(SearchRec);
  576.     end;
  577.     FindClose(SearchRec);
  578.  
  579.   where ProcessSearchRec represents user-defined code that processes the
  580.   information in a search record. }
  581.  
  582. function FindFirst(const Path: string; Attr: Integer;
  583.   var F: TSearchRec): Integer;
  584.  
  585. { FindNext returs the next entry that matches the name and attributes
  586.   specified in a previous call to FindFirst. The search record must be one
  587.   that was passed to FindFirst. The return value is zero if the function was
  588.   successful. Otherwise the return value is a Windows error code. }
  589.  
  590. function FindNext(var F: TSearchRec): Integer;
  591.  
  592. { FindClose terminates a FindFirst/FindNext sequence. FindClose does nothing
  593.   in the 16-bit version of Windows, but is required in the 32-bit version,
  594.   so for maximum portability every FindFirst/FindNext sequence should end
  595.   with a call to FindClose. }
  596.  
  597. procedure FindClose(var F: TSearchRec);
  598.  
  599. { FileGetDate returns the DOS date-and-time stamp of the file given by
  600.   Handle. The return value is -1 if the handle is invalid. The
  601.   FileDateToDateTime function can be used to convert the returned value to
  602.   a TDateTime value. }
  603.  
  604. function FileGetDate(Handle: Integer): Integer;
  605.  
  606. { FileSetDate sets the DOS date-and-time stamp of the file given by Handle
  607.   to the value given by Age. The DateTimeToFileDate function can be used to
  608.   convert a TDateTime value to a DOS date-and-time stamp. The return value
  609.   is zero if the function was successful. Otherwise the return value is a
  610.   Windows error code. }
  611.  
  612. function FileSetDate(Handle: Integer; Age: Integer): Integer;
  613.  
  614. { FileGetAttr returns the file attributes of the file given by FileName. The
  615.   attributes can be examined by AND-ing with the faXXXX constants defined
  616.   above. A return value of -1 indicates that an error occurred. }
  617.  
  618. function FileGetAttr(const FileName: string): Integer;
  619.  
  620. { FileSetAttr sets the file attributes of the file given by FileName to the
  621.   value given by Attr. The attribute value is formed by OR-ing the
  622.   appropriate faXXXX constants. The return value is zero if the function was
  623.   successful. Otherwise the return value is a Windows error code. }
  624.  
  625. function FileSetAttr(const FileName: string; Attr: Integer): Integer;
  626.  
  627. { DeleteFile deletes the file given by FileName. The return value is True if
  628.   the file was successfully deleted, or False if an error occurred. }
  629.  
  630. function DeleteFile(const FileName: string): Boolean;
  631.  
  632. { RenameFile renames the file given by OldName to the name given by NewName.
  633.   The return value is True if the file was successfully renamed, or False if
  634.   an error occurred. }
  635.  
  636. function RenameFile(const OldName, NewName: string): Boolean;
  637.  
  638. { ChangeFileExt changes the extension of a filename. FileName specifies a
  639.   filename with or without an extension, and Extension specifies the new
  640.   extension for the filename. The new extension can be a an empty string or
  641.   a period followed by up to three characters. }
  642.  
  643. function ChangeFileExt(const FileName, Extension: string): string;
  644.  
  645. { ExtractFilePath extracts the drive and directory parts of the given
  646.   filename. The resulting string is the rightmost characters of FileName,
  647.   up to and including the colon or backslash that separates the path
  648.   information from the name and extension. The resulting string is empty
  649.   if FileName contains no drive and directory parts. }
  650.  
  651. function ExtractFilePath(const FileName: string): string;
  652.  
  653. { ExtractFileDir extracts the drive and directory parts of the given
  654.   filename. The resulting string is a directory name suitable for passing
  655.   to SetCurrentDir, CreateDir, etc. The resulting string is empty if
  656.   FileName contains no drive and directory parts. }
  657.  
  658. function ExtractFileDir(const FileName: string): string;
  659.  
  660. { ExtractFileDrive extracts the drive part of the given filename.  For
  661.   filenames with drive letters, the resulting string is '<drive>:'.
  662.   For filenames with a UNC path, the resulting string is in the form
  663.   '\\<servername>\<sharename>'.  If the given path contains neither
  664.   style of filename, the result is an empty string. }
  665.  
  666. function ExtractFileDrive(const FileName: string): string;
  667.  
  668. { ExtractFileName extracts the name and extension parts of the given
  669.   filename. The resulting string is the leftmost characters of FileName,
  670.   starting with the first character after the colon or backslash that
  671.   separates the path information from the name and extension. The resulting
  672.   string is equal to FileName if FileName contains no drive and directory
  673.   parts. }
  674.  
  675. function ExtractFileName(const FileName: string): string;
  676.  
  677. { ExtractFileExt extracts the extension part of the given filename. The
  678.   resulting string includes the period character that separates the name
  679.   and extension parts. The resulting string is empty if the given filename
  680.   has no extension. }
  681.  
  682. function ExtractFileExt(const FileName: string): string;
  683.  
  684. { ExpandFileName expands the given filename to a fully qualified filename.
  685.   The resulting string consists of a drive letter, a colon, a root relative
  686.   directory path, and a filename. Embedded '.' and '..' directory references
  687.   are removed. }
  688.  
  689. function ExpandFileName(const FileName: string): string;
  690.  
  691. { ExpandUNCFileName expands the given filename to a fully qualified filename.
  692.   This function is the same as ExpandFileName except that it will return the
  693.   drive portion of the filename in the format '\\<servername>\<sharename> if
  694.   that drive is actually a network resource instead of a local resource.
  695.   Like ExpandFileName, embedded '.' and '..' directory references are
  696.   removed. }
  697.  
  698. function ExpandUNCFileName(const FileName: string): string;
  699.  
  700. { FileSearch searches for the file given by Name in the list of directories
  701.   given by DirList. The directory paths in DirList must be separated by
  702.   semicolons. The search always starts with the current directory of the
  703.   current drive. The returned value is a concatenation of one of the
  704.   directory paths and the filename, or an empty string if the file could not
  705.   be located. }
  706.  
  707. function FileSearch(const Name, DirList: string): string;
  708.  
  709. { DiskFree returns the number of free bytes on the specified drive number,
  710.   where 0 = Current, 1 = A, 2 = B, etc. DiskFree returns -1 if the drive
  711.   number is invalid. }
  712.  
  713. function DiskFree(Drive: Byte): Integer;
  714.  
  715. { DiskSize returns the size in bytes of the specified drive number, where
  716.   0 = Current, 1 = A, 2 = B, etc. DiskSize returns -1 if the drive number
  717.   is invalid. }
  718.  
  719. function DiskSize(Drive: Byte): Integer;
  720.  
  721. { FileDateToDateTime converts a DOS date-and-time value to a TDateTime
  722.   value. The FileAge, FileGetDate, and FileSetDate routines operate on DOS
  723.   date-and-time values, and the Time field of a TSearchRec used by the
  724.   FindFirst and FindNext functions contains a DOS date-and-time value. }
  725.  
  726. function FileDateToDateTime(FileDate: Integer): TDateTime;
  727.  
  728. { DateTimeToFileDate converts a TDateTime value to a DOS date-and-time
  729.   value. The FileAge, FileGetDate, and FileSetDate routines operate on DOS
  730.   date-and-time values, and the Time field of a TSearchRec used by the
  731.   FindFirst and FindNext functions contains a DOS date-and-time value. }
  732.  
  733. function DateTimeToFileDate(DateTime: TDateTime): Integer;
  734.  
  735. { GetCurrentDir returns the current directory. }
  736.  
  737. function GetCurrentDir: string;
  738.  
  739. { SetCurrentDir sets the current directory. The return value is True if
  740.   the current directory was successfully changed, or False if an error
  741.   occurred. }
  742.  
  743. function SetCurrentDir(const Dir: string): Boolean;
  744.  
  745. { CreateDir creates a new directory. The return value is True if a new
  746.   directory was successfully created, or False if an error occurred. }
  747.  
  748. function CreateDir(const Dir: string): Boolean;
  749.  
  750. { RemoveDir deletes an existing empty directory. The return value is
  751.   True if the directory was successfully deleted, or False if an error
  752.   occurred. }
  753.  
  754. function RemoveDir(const Dir: string): Boolean;
  755.  
  756. { PChar routines }
  757.  
  758. { StrLen returns the number of characters in Str, not counting the null
  759.   terminator. }
  760.  
  761. function StrLen(Str: PChar): Cardinal;
  762.  
  763. { StrEnd returns a pointer to the null character that terminates Str. }
  764.  
  765. function StrEnd(Str: PChar): PChar;
  766.  
  767. { StrMove copies exactly Count characters from Source to Dest and returns
  768.   Dest. Source and Dest may overlap. }
  769.  
  770. function StrMove(Dest, Source: PChar; Count: Cardinal): PChar;
  771.  
  772. { StrCopy copies Source to Dest and returns Dest. }
  773.  
  774. function StrCopy(Dest, Source: PChar): PChar;
  775.  
  776. { StrECopy copies Source to Dest and returns StrEnd(Dest). }
  777.  
  778. function StrECopy(Dest, Source: PChar): PChar;
  779.  
  780. { StrLCopy copies at most MaxLen characters from Source to Dest and
  781.   returns Dest. }
  782.  
  783. function StrLCopy(Dest, Source: PChar; MaxLen: Cardinal): PChar;
  784.  
  785. { StrPCopy copies the Pascal style string Source into Dest and
  786.   returns Dest. }
  787.  
  788. function StrPCopy(Dest: PChar; const Source: string): PChar;
  789.  
  790. { StrPLCopy copies at most MaxLen characters from the Pascal style string
  791.   Source into Dest and returns Dest. }
  792.  
  793. function StrPLCopy(Dest: PChar; const Source: string;
  794.   MaxLen: Cardinal): PChar;
  795.  
  796. { StrCat appends a copy of Source to the end of Dest and returns Dest. }
  797.  
  798. function StrCat(Dest, Source: PChar): PChar;
  799.  
  800. { StrLCat appends at most MaxLen - StrLen(Dest) characters from Source to
  801.   the end of Dest, and returns Dest. }
  802.  
  803. function StrLCat(Dest, Source: PChar; MaxLen: Cardinal): PChar;
  804.  
  805. { StrComp compares Str1 to Str2. The return value is less than 0 if
  806.   Str1 < Str2, 0 if Str1 = Str2, or greater than 0 if Str1 > Str2. }
  807.  
  808. function StrComp(Str1, Str2: PChar): Integer;
  809.  
  810. { StrIComp compares Str1 to Str2, without case sensitivity. The return
  811.   value is the same as StrComp. }
  812.  
  813. function StrIComp(Str1, Str2: PChar): Integer;
  814.  
  815. { StrLComp compares Str1 to Str2, for a maximum length of MaxLen
  816.   characters. The return value is the same as StrComp. }
  817.  
  818. function StrLComp(Str1, Str2: PChar; MaxLen: Cardinal): Integer;
  819.  
  820. { StrLIComp compares Str1 to Str2, for a maximum length of MaxLen
  821.   characters, without case sensitivity. The return value is the same
  822.   as StrComp. }
  823.  
  824. function StrLIComp(Str1, Str2: PChar; MaxLen: Cardinal): Integer;
  825.  
  826. { StrScan returns a pointer to the first occurrence of Chr in Str. If Chr
  827.   does not occur in Str, StrScan returns NIL. The null terminator is
  828.   considered to be part of the string. }
  829.  
  830. function StrScan(Str: PChar; Chr: Char): PChar;
  831.  
  832. { StrRScan returns a pointer to the last occurrence of Chr in Str. If Chr
  833.   does not occur in Str, StrRScan returns NIL. The null terminator is
  834.   considered to be part of the string. }
  835.  
  836. function StrRScan(Str: PChar; Chr: Char): PChar;
  837.  
  838. { StrPos returns a pointer to the first occurrence of Str2 in Str1. If
  839.   Str2 does not occur in Str1, StrPos returns NIL. }
  840.  
  841. function StrPos(Str1, Str2: PChar): PChar;
  842.  
  843. { StrUpper converts Str to upper case and returns Str. }
  844.  
  845. function StrUpper(Str: PChar): PChar;
  846.  
  847. { StrLower converts Str to lower case and returns Str. }
  848.  
  849. function StrLower(Str: PChar): PChar;
  850.  
  851. { StrPas converts Str to a Pascal style string. This function is provided
  852.   for backwards compatibility only. To convert a null terminated string to
  853.   a Pascal style string, use a type cast or an assignment. }
  854.  
  855. function StrPas(Str: PChar): string;
  856.  
  857. { StrAlloc allocates a buffer of the given size on the heap. The size of
  858.   the allocated buffer is encoded in a four byte header that immediately
  859.   preceeds the buffer. To dispose the buffer, use StrDispose. }
  860.  
  861. function StrAlloc(Size: Cardinal): PChar;
  862.  
  863. { StrBufSize returns the allocated size of the given buffer, not including
  864.   the two byte header. }
  865.  
  866. function StrBufSize(Str: PChar): Cardinal;
  867.  
  868. { StrNew allocates a copy of Str on the heap. If Str is NIL, StrNew returns
  869.   NIL and doesn't allocate any heap space. Otherwise, StrNew makes a
  870.   duplicate of Str, obtaining space with a call to the StrAlloc function,
  871.   and returns a pointer to the duplicated string. To dispose the string,
  872.   use StrDispose. }
  873.  
  874. function StrNew(Str: PChar): PChar;
  875.  
  876. { StrDispose disposes a string that was previously allocated with StrAlloc
  877.   or StrNew. If Str is NIL, StrDispose does nothing. }
  878.  
  879. procedure StrDispose(Str: PChar);
  880.  
  881. { String formatting routines }
  882.  
  883. { The Format routine formats the argument list given by the Args parameter
  884.   using the format string given by the Format parameter.
  885.  
  886.   Format strings contain two types of objects--plain characters and format
  887.   specifiers. Plain characters are copied verbatim to the resulting string.
  888.   Format specifiers fetch arguments from the argument list and apply
  889.   formatting to them.
  890.  
  891.   Format specifiers have the following form:
  892.  
  893.     "%" [index ":"] ["-"] [width] ["." prec] type
  894.  
  895.   A format specifier begins with a % character. After the % come the
  896.   following, in this order:
  897.  
  898.   -  an optional argument index specifier, [index ":"]
  899.   -  an optional left-justification indicator, ["-"]
  900.   -  an optional width specifier, [width]
  901.   -  an optional precision specifier, ["." prec]
  902.   -  the conversion type character, type
  903.  
  904.   The following conversion characters are supported:
  905.  
  906.   d  Decimal. The argument must be an integer value. The value is converted
  907.      to a string of decimal digits. If the format string contains a precision
  908.      specifier, it indicates that the resulting string must contain at least
  909.      the specified number of digits; if the value has less digits, the
  910.      resulting string is left-padded with zeros.
  911.  
  912.   e  Scientific. The argument must be a floating-point value. The value is
  913.      converted to a string of the form "-d.ddd...E+ddd". The resulting
  914.      string starts with a minus sign if the number is negative, and one digit
  915.      always precedes the decimal point. The total number of digits in the
  916.      resulting string (including the one before the decimal point) is given
  917.      by the precision specifer in the format string--a default precision of
  918.      15 is assumed if no precision specifer is present. The "E" exponent
  919.      character in the resulting string is always followed by a plus or minus
  920.      sign and at least three digits.
  921.  
  922.   f  Fixed. The argument must be a floating-point value. The value is
  923.      converted to a string of the form "-ddd.ddd...". The resulting string
  924.      starts with a minus sign if the number is negative. The number of digits
  925.      after the decimal point is given by the precision specifier in the
  926.      format string--a default of 2 decimal digits is assumed if no precision
  927.      specifier is present.
  928.  
  929.   g  General. The argument must be a floating-point value. The value is
  930.      converted to the shortest possible decimal string using fixed or
  931.      scientific format. The number of significant digits in the resulting
  932.      string is given by the precision specifier in the format string--a
  933.      default precision of 15 is assumed if no precision specifier is present.
  934.      Trailing zeros are removed from the resulting string, and a decimal
  935.      point appears only if necessary. The resulting string uses fixed point
  936.      format if the number of digits to the left of the decimal point in the
  937.      value is less than or equal to the specified precision, and if the
  938.      value is greater than or equal to 0.00001. Otherwise the resulting
  939.      string uses scientific format.
  940.  
  941.   n  Number. The argument must be a floating-point value. The value is
  942.      converted to a string of the form "-d,ddd,ddd.ddd...". The "n" format
  943.      corresponds to the "f" format, except that the resulting string
  944.      contains thousand separators.
  945.  
  946.   m  Money. The argument must be a floating-point value. The value is
  947.      converted to a string that represents a currency amount. The conversion
  948.      is controlled by the CurrencyString, CurrencyFormat, NegCurrFormat,
  949.      ThousandSeparator, DecimalSeparator, and CurrencyDecimals global
  950.      variables, all of which are initialized from the Currency Format in
  951.      the International section of the Windows Control Panel. If the format
  952.      string contains a precision specifier, it overrides the value given
  953.      by the CurrencyDecimals global variable.
  954.  
  955.   p  Pointer. The argument must be a pointer value. The value is converted
  956.      to a string of the form "XXXX:YYYY" where XXXX and YYYY are the
  957.      segment and offset parts of the pointer expressed as four hexadecimal
  958.      digits.
  959.  
  960.   s  String. The argument must be a character, a string, or a PChar value.
  961.      The string or character is inserted in place of the format specifier.
  962.      The precision specifier, if present in the format string, specifies the
  963.      maximum length of the resulting string. If the argument is a string
  964.      that is longer than this maximum, the string is truncated.
  965.  
  966.   x  Hexadecimal. The argument must be an integer value. The value is
  967.      converted to a string of hexadecimal digits. If the format string
  968.      contains a precision specifier, it indicates that the resulting string
  969.      must contain at least the specified number of digits; if the value has
  970.      less digits, the resulting string is left-padded with zeros.
  971.  
  972.   Conversion characters may be specified in upper case as well as in lower
  973.   case--both produce the same results.
  974.  
  975.   For all floating-point formats, the actual characters used as decimal and
  976.   thousand separators are obtained from the DecimalSeparator and
  977.   ThousandSeparator global variables.
  978.  
  979.   Index, width, and precision specifiers can be specified directly using
  980.   decimal digit string (for example "%10d"), or indirectly using an asterisk
  981.   charcater (for example "%*.*f"). When using an asterisk, the next argument
  982.   in the argument list (which must be an integer value) becomes the value
  983.   that is actually used. For example "Format('%*.*f', [8, 2, 123.456])" is
  984.   the same as "Format('%8.2f', [123.456])".
  985.  
  986.   A width specifier sets the minimum field width for a conversion. If the
  987.   resulting string is shorter than the minimum field width, it is padded
  988.   with blanks to increase the field width. The default is to right-justify
  989.   the result by adding blanks in front of the value, but if the format
  990.   specifier contains a left-justification indicator (a "-" character
  991.   preceding the width specifier), the result is left-justified by adding
  992.   blanks after the value.
  993.  
  994.   An index specifier sets the current argument list index to the specified
  995.   value. The index of the first argument in the argument list is 0. Using
  996.   index specifiers, it is possible to format the same argument multiple
  997.   times. For example "Format('%d %d %0:d %d', [10, 20])" produces the string
  998.   '10 20 10 20'.
  999.  
  1000.   The Format function can be combined with other formatting functions. For
  1001.   example
  1002.  
  1003.     S := Format('Your total was %s on %s', [
  1004.       FormatFloat('$#,##0.00;;zero', Total),
  1005.       FormatDateTime('mm/dd/yy', Date)]);
  1006.  
  1007.   which uses the FormatFloat and FormatDateTime functions to customize the
  1008.   format beyond what is possible with Format. }
  1009.  
  1010. function Format(const Format: string; const Args: array of const): string;
  1011.  
  1012. { FmtStr formats the argument list given by Args using the format string
  1013.   given by Format into the string variable given by Result. For further
  1014.   details, see the description of the Format function. }
  1015.  
  1016. procedure FmtStr(var Result: string; const Format: string;
  1017.   const Args: array of const);
  1018.  
  1019. { StrFmt formats the argument list given by Args using the format string
  1020.   given by Format into the buffer given by Buffer. It is up to the caller to
  1021.   ensure that Buffer is large enough for the resulting string. The returned
  1022.   value is Buffer. For further details, see the description of the Format
  1023.   function. }
  1024.  
  1025. function StrFmt(Buffer, Format: PChar; const Args: array of const): PChar;
  1026.  
  1027. { StrFmt formats the argument list given by Args using the format string
  1028.   given by Format into the buffer given by Buffer. The resulting string will
  1029.   contain no more than MaxLen characters, not including the null terminator.
  1030.   The returned value is Buffer. For further details, see the description of
  1031.   the Format function. }
  1032.  
  1033. function StrLFmt(Buffer: PChar; MaxLen: Cardinal; Format: PChar;
  1034.   const Args: array of const): PChar;
  1035.  
  1036. { FormatBuf formats the argument list given by Args using the format string
  1037.   given by Format and FmtLen into the buffer given by Buffer and BufLen.
  1038.   The Format parameter is a reference to a buffer containing FmtLen
  1039.   characters, and the Buffer parameter is a reference to a buffer of BufLen
  1040.   characters. The returned value is the number of characters actually stored
  1041.   in Buffer. The returned value is always less than or equal to BufLen. For
  1042.   further details, see the description of the Format function. }
  1043.  
  1044. function FormatBuf(var Buffer; BufLen: Cardinal; const Format;
  1045.   FmtLen: Cardinal; const Args: array of const): Cardinal;
  1046.  
  1047. { Floating point conversion routines }
  1048.  
  1049. { FloatToStr converts the floating-point value given by Value to its string
  1050.   representation. The conversion uses general number format with 15
  1051.   significant digits. For further details, see the description of the
  1052.   FloatToStrF function. }
  1053.  
  1054. function FloatToStr(Value: Extended): string;
  1055.  
  1056. { CurrToStr converts the currency value given by Value to its string
  1057.   representation. The conversion uses general number format. For further
  1058.   details, see the description of the CurrToStrF function. }
  1059.  
  1060. function CurrToStr(Value: Currency): string;
  1061.  
  1062. { FloatToStrF converts the floating-point value given by Value to its string
  1063.   representation. The Format parameter controls the format of the resulting
  1064.   string. The Precision parameter specifies the precision of the given value.
  1065.   It should be 7 or less for values of type Single, 15 or less for values of
  1066.   type Double, and 18 or less for values of type Extended. The meaning of the
  1067.   Digits parameter depends on the particular format selected.
  1068.  
  1069.   The possible values of the Format parameter, and the meaning of each, are
  1070.   described below.
  1071.  
  1072.   ffGeneral - General number format. The value is converted to the shortest
  1073.   possible decimal string using fixed or scientific format. Trailing zeros
  1074.   are removed from the resulting string, and a decimal point appears only
  1075.   if necessary. The resulting string uses fixed point format if the number
  1076.   of digits to the left of the decimal point in the value is less than or
  1077.   equal to the specified precision, and if the value is greater than or
  1078.   equal to 0.00001. Otherwise the resulting string uses scientific format,
  1079.   and the Digits parameter specifies the minimum number of digits in the
  1080.   exponent (between 0 and 4).
  1081.  
  1082.   ffExponent - Scientific format. The value is converted to a string of the
  1083.   form "-d.ddd...E+dddd". The resulting string starts with a minus sign if
  1084.   the number is negative, and one digit always precedes the decimal point.
  1085.   The total number of digits in the resulting string (including the one
  1086.   before the decimal point) is given by the Precision parameter. The "E"
  1087.   exponent character in the resulting string is always followed by a plus
  1088.   or minus sign and up to four digits. The Digits parameter specifies the
  1089.   minimum number of digits in the exponent (between 0 and 4).
  1090.  
  1091.   ffFixed - Fixed point format. The value is converted to a string of the
  1092.   form "-ddd.ddd...". The resulting string starts with a minus sign if the
  1093.   number is negative, and at least one digit always precedes the decimal
  1094.   point. The number of digits after the decimal point is given by the Digits
  1095.   parameter--it must be between 0 and 18. If the number of digits to the
  1096.   left of the decimal point is greater than the specified precision, the
  1097.   resulting value will use scientific format.
  1098.  
  1099.   ffNumber - Number format. The value is converted to a string of the form
  1100.   "-d,ddd,ddd.ddd...". The ffNumber format corresponds to the ffFixed format,
  1101.   except that the resulting string contains thousand separators.
  1102.  
  1103.   ffCurrency - Currency format. The value is converted to a string that
  1104.   represents a currency amount. The conversion is controlled by the
  1105.   CurrencyString, CurrencyFormat, NegCurrFormat, ThousandSeparator, and
  1106.   DecimalSeparator global variables, all of which are initialized from the
  1107.   Currency Format in the International section of the Windows Control Panel.
  1108.   The number of digits after the decimal point is given by the Digits
  1109.   parameter--it must be between 0 and 18.
  1110.  
  1111.   For all formats, the actual characters used as decimal and thousand
  1112.   separators are obtained from the DecimalSeparator and ThousandSeparator
  1113.   global variables.
  1114.  
  1115.   If the given value is a NAN (not-a-number), the resulting string is 'NAN'.
  1116.   If the given value is positive infinity, the resulting string is 'INF'. If
  1117.   the given value is negative infinity, the resulting string is '-INF'. }
  1118.  
  1119. function FloatToStrF(Value: Extended; Format: TFloatFormat;
  1120.   Precision, Digits: Integer): string;
  1121.  
  1122. { CurrToStrF converts the currency value given by Value to its string
  1123.   representation. A call to CurrToStrF corresponds to a call to
  1124.   FloatToStrF with an implied precision of 19 digits. }
  1125.  
  1126. function CurrToStrF(Value: Currency; Format: TFloatFormat;
  1127.   Digits: Integer): string;
  1128.  
  1129. { FloatToText converts the given floating-point value to its decimal
  1130.   representation using the specified format, precision, and digits. The
  1131.   Value parameter must be a variable of type Extended or Currency, as
  1132.   indicated by the ValueType parameter. The resulting string of characters
  1133.   is stored in the given buffer, and the returned value is the number of
  1134.   characters stored. The resulting string is not null-terminated. For
  1135.   further details, see the description of the FloatToStrF function. }
  1136.  
  1137. function FloatToText(Buffer: PChar; const Value; ValueType: TFloatValue;
  1138.   Format: TFloatFormat; Precision, Digits: Integer): Integer;
  1139.  
  1140. { FormatFloat formats the floating-point value given by Value using the
  1141.   format string given by Format. The following format specifiers are
  1142.   supported in the format string:
  1143.  
  1144.   0     Digit placeholder. If the value being formatted has a digit in the
  1145.         position where the '0' appears in the format string, then that digit
  1146.         is copied to the output string. Otherwise, a '0' is stored in that
  1147.         position in the output string.
  1148.  
  1149.   #     Digit placeholder. If the value being formatted has a digit in the
  1150.         position where the '#' appears in the format string, then that digit
  1151.         is copied to the output string. Otherwise, nothing is stored in that
  1152.         position in the output string.
  1153.  
  1154.   .     Decimal point. The first '.' character in the format string
  1155.         determines the location of the decimal separator in the formatted
  1156.         value; any additional '.' characters are ignored. The actual
  1157.         character used as a the decimal separator in the output string is
  1158.         determined by the DecimalSeparator global variable. The default value
  1159.         of DecimalSeparator is specified in the Number Format of the
  1160.         International section in the Windows Control Panel.
  1161.  
  1162.   ,     Thousand separator. If the format string contains one or more ','
  1163.         characters, the output will have thousand separators inserted between
  1164.         each group of three digits to the left of the decimal point. The
  1165.         placement and number of ',' characters in the format string does not
  1166.         affect the output, except to indicate that thousand separators are
  1167.         wanted. The actual character used as a the thousand separator in the
  1168.         output is determined by the ThousandSeparator global variable. The
  1169.         default value of ThousandSeparator is specified in the Number Format
  1170.         of the International section in the Windows Control Panel.
  1171.  
  1172.   E+    Scientific notation. If any of the strings 'E+', 'E-', 'e+', or 'e-'
  1173.   E-    are contained in the format string, the number is formatted using
  1174.   e+    scientific notation. A group of up to four '0' characters can
  1175.   e-    immediately follow the 'E+', 'E-', 'e+', or 'e-' to determine the
  1176.         minimum number of digits in the exponent. The 'E+' and 'e+' formats
  1177.         cause a plus sign to be output for positive exponents and a minus
  1178.         sign to be output for negative exponents. The 'E-' and 'e-' formats
  1179.         output a sign character only for negative exponents.
  1180.  
  1181.   'xx'  Characters enclosed in single or double quotes are output as-is, and
  1182.   "xx"  do not affect formatting.
  1183.  
  1184.   ;     Separates sections for positive, negative, and zero numbers in the
  1185.         format string.
  1186.  
  1187.   The locations of the leftmost '0' before the decimal point in the format
  1188.   string and the rightmost '0' after the decimal point in the format string
  1189.   determine the range of digits that are always present in the output string.
  1190.  
  1191.   The number being formatted is always rounded to as many decimal places as
  1192.   there are digit placeholders ('0' or '#') to the right of the decimal
  1193.   point. If the format string contains no decimal point, the value being
  1194.   formatted is rounded to the nearest whole number.
  1195.  
  1196.   If the number being formatted has more digits to the left of the decimal
  1197.   separator than there are digit placeholders to the left of the '.'
  1198.   character in the format string, the extra digits are output before the
  1199.   first digit placeholder.
  1200.  
  1201.   To allow different formats for positive, negative, and zero values, the
  1202.   format string can contain between one and three sections separated by
  1203.   semicolons.
  1204.  
  1205.   One section - The format string applies to all values.
  1206.  
  1207.   Two sections - The first section applies to positive values and zeros, and
  1208.   the second section applies to negative values.
  1209.  
  1210.   Three sections - The first section applies to positive values, the second
  1211.   applies to negative values, and the third applies to zeros.
  1212.  
  1213.   If the section for negative values or the section for zero values is empty,
  1214.   that is if there is nothing between the semicolons that delimit the
  1215.   section, the section for positive values is used instead.
  1216.  
  1217.   If the section for positive values is empty, or if the entire format string
  1218.   is empty, the value is formatted using general floating-point formatting
  1219.   with 15 significant digits, corresponding to a call to FloatToStrF with
  1220.   the ffGeneral format. General floating-point formatting is also used if
  1221.   the value has more than 18 digits to the left of the decimal point and
  1222.   the format string does not specify scientific notation.
  1223.  
  1224.   The table below shows some sample formats and the results produced when
  1225.   the formats are applied to different values:
  1226.  
  1227.   Format string          1234        -1234       0.5         0
  1228.   -----------------------------------------------------------------------
  1229.                          1234        -1234       0.5         0
  1230.   0                      1234        -1234       1           0
  1231.   0.00                   1234.00     -1234.00    0.50        0.00
  1232.   #.##                   1234        -1234       .5
  1233.   #,##0.00               1,234.00    -1,234.00   0.50        0.00
  1234.   #,##0.00;(#,##0.00)    1,234.00    (1,234.00)  0.50        0.00
  1235.   #,##0.00;;Zero         1,234.00    -1,234.00   0.50        Zero
  1236.   0.000E+00              1.234E+03   -1.234E+03  5.000E-01   0.000E+00
  1237.   #.###E-0               1.234E3     -1.234E3    5E-1        0E0
  1238.   ----------------------------------------------------------------------- }
  1239.  
  1240. function FormatFloat(const Format: string; Value: Extended): string;
  1241.  
  1242. { FormatCurr formats the currency value given by Value using the format
  1243.   string given by Format. For further details, see the description of the
  1244.   FormatFloat function. }
  1245.  
  1246. function FormatCurr(const Format: string; Value: Currency): string;
  1247.  
  1248. { FloatToTextFmt converts the given floating-point value to its decimal
  1249.   representation using the specified format. The Value parameter must be a
  1250.   variable of type Extended or Currency, as indicated by the ValueType
  1251.   parameter. The resulting string of characters is stored in the given
  1252.   buffer, and the returned value is the number of characters stored. The
  1253.   resulting string is not null-terminated. For further details, see the
  1254.   description of the FormatFloat function. }
  1255.  
  1256. function FloatToTextFmt(Buffer: PChar; const Value; ValueType: TFloatValue;
  1257.   Format: PChar): Integer;
  1258.  
  1259. { StrToFloat converts the given string to a floating-point value. The string
  1260.   must consist of an optional sign (+ or -), a string of digits with an
  1261.   optional decimal point, and an optional 'E' or 'e' followed by a signed
  1262.   integer. Leading and trailing blanks in the string are ignored. The
  1263.   DecimalSeparator global variable defines the character that must be used
  1264.   as a decimal point. Thousand separators and currency symbols are not
  1265.   allowed in the string. If the string doesn't contain a valid value, an
  1266.   EConvertError exception is raised. }
  1267.  
  1268. function StrToFloat(const S: string): Extended;
  1269.  
  1270. { StrToCurr converts the given string to a currency value. For further
  1271.   details, see the description of the StrToFloat function. }
  1272.  
  1273. function StrToCurr(const S: string): Currency;
  1274.  
  1275. { TextToFloat converts the null-terminated string given by Buffer to a
  1276.   floating-point value which is returned in the variable given by Value.
  1277.   The Value parameter must be a variable of type Extended or Currency, as
  1278.   indicated by the ValueType parameter. The return value is True if the
  1279.   conversion was successful, or False if the string is not a valid
  1280.   floating-point value. For further details, see the description of the
  1281.   StrToFloat function. }
  1282.  
  1283. function TextToFloat(Buffer: PChar; var Value;
  1284.   ValueType: TFloatValue): Boolean;
  1285.  
  1286. { FloatToDecimal converts a floating-point value to a decimal representation
  1287.   that is suited for further formatting. The Value parameter must be a
  1288.   variable of type Extended or Currency, as indicated by the ValueType
  1289.   parameter. For values of type Extended, the Precision parameter specifies
  1290.   the requested number of significant digits in the result--the allowed range
  1291.   is 1..18. For values of type Currency, the Precision parameter is ignored,
  1292.   and the implied precision of the conversion is 19 digits. The Decimals
  1293.   parameter specifies the requested maximum number of digits to the left of
  1294.   the decimal point in the result. Precision and Decimals together control
  1295.   how the result is rounded. To produce a result that always has a given
  1296.   number of significant digits regardless of the magnitude of the number,
  1297.   specify 9999 for the Decimals parameter. The result of the conversion is
  1298.   stored in the specified TFloatRec record as follows:
  1299.  
  1300.   Exponent - Contains the magnitude of the number, i.e. the number of
  1301.   significant digits to the right of the decimal point. The Exponent field
  1302.   is negative if the absolute value of the number is less than one. If the
  1303.   number is a NAN (not-a-number), Exponent is set to -32768. If the number
  1304.   is INF or -INF (positive or negative infinity), Exponent is set to 32767.
  1305.  
  1306.   Negative - True if the number is negative, False if the number is zero
  1307.   or positive.
  1308.  
  1309.   Digits - Contains up to 18 (for type Extended) or 19 (for type Currency)
  1310.   significant digits followed by a null terminator. The implied decimal
  1311.   point (if any) is not stored in Digits. Trailing zeros are removed, and
  1312.   if the resulting number is zero, NAN, or INF, Digits contains nothing but
  1313.   the null terminator. }
  1314.  
  1315. procedure FloatToDecimal(var Result: TFloatRec; const Value;
  1316.   ValueType: TFloatValue; Precision, Decimals: Integer);
  1317.  
  1318. { Date/time support routines }
  1319.  
  1320. function DateTimeToTimeStamp(DateTime: TDateTime): TTimeStamp;
  1321.  
  1322. function TimeStampToDateTime(const TimeStamp: TTimeStamp): TDateTime;
  1323. function MSecsToTimeStamp(MSecs: Comp): TTimeStamp;
  1324. function TimeStampToMSecs(const TimeStamp: TTimeStamp): Comp;
  1325.  
  1326. { EncodeDate encodes the given year, month, and day into a TDateTime value.
  1327.   The year must be between 1 and 9999, the month must be between 1 and 12,
  1328.   and the day must be between 1 and N, where N is the number of days in the
  1329.   specified month. If the specified values are not within range, an
  1330.   EConvertError exception is raised. The resulting value is the number of
  1331.   days between 12/30/1899 and the given date. }
  1332.  
  1333. function EncodeDate(Year, Month, Day: Word): TDateTime;
  1334.  
  1335. { EncodeTime encodes the given hour, minute, second, and millisecond into a
  1336.   TDateTime value. The hour must be between 0 and 23, the minute must be
  1337.   between 0 and 59, the second must be between 0 and 59, and the millisecond
  1338.   must be between 0 and 999. If the specified values are not within range, an
  1339.   EConvertError exception is raised. The resulting value is a number between
  1340.   0 (inclusive) and 1 (not inclusive) that indicates the fractional part of
  1341.   a day given by the specified time. The value 0 corresponds to midnight,
  1342.   0.5 corresponds to noon, 0.75 corresponds to 6:00 pm, etc. }
  1343.  
  1344. function EncodeTime(Hour, Min, Sec, MSec: Word): TDateTime;
  1345.  
  1346. { DecodeDate decodes the integral (date) part of the given TDateTime value
  1347.   into its corresponding year, month, and day. If the given TDateTime value
  1348.   is less than or equal to zero, the year, month, and day return parameters
  1349.   are all set to zero. }
  1350.  
  1351. procedure DecodeDate(Date: TDateTime; var Year, Month, Day: Word);
  1352.  
  1353. { DecodeTime decodes the fractional (time) part of the given TDateTime value
  1354.   into its corresponding hour, minute, second, and millisecond. }
  1355.  
  1356. procedure DecodeTime(Time: TDateTime; var Hour, Min, Sec, MSec: Word);
  1357.  
  1358. { DayOfWeek returns the day of the week of the given date. The result is an
  1359.   integer between 1 and 7, corresponding to Sunday through Saturday. }
  1360.  
  1361. function DayOfWeek(Date: TDateTime): Integer;
  1362.  
  1363. { Date returns the current date. }
  1364.  
  1365. function Date: TDateTime;
  1366.  
  1367. { Time returns the current time. }
  1368.  
  1369. function Time: TDateTime;
  1370.  
  1371. { Now returns the current date and time, corresponding to Date + Time. }
  1372.  
  1373. function Now: TDateTime;
  1374.  
  1375. { DateToStr converts the date part of the given TDateTime value to a string.
  1376.   The conversion uses the format specified by the ShortDateFormat global
  1377.   variable. }
  1378.  
  1379. function DateToStr(Date: TDateTime): string;
  1380.  
  1381. { TimeToStr converts the time part of the given TDateTime value to a string.
  1382.   The conversion uses the format specified by the LongTimeFormat global
  1383.   variable. }
  1384.  
  1385. function TimeToStr(Time: TDateTime): string;
  1386.  
  1387. { DateTimeToStr converts the given date and time to a string. The resulting
  1388.   string consists of a date and time formatted using the ShortDateFormat and
  1389.   LongTimeFormat global variables. Time information is included in the
  1390.   resulting string only if the fractional part of the given date and time
  1391.   value is non-zero. }
  1392.  
  1393. function DateTimeToStr(DateTime: TDateTime): string;
  1394.  
  1395. { StrToDate converts the given string to a date value. The string must
  1396.   consist of two or three numbers, separated by the character defined by
  1397.   the DateSeparator global variable. The order for month, day, and year is
  1398.   determined by the ShortDateFormat global variable--possible combinations
  1399.   are m/d/y, d/m/y, and y/m/d. If the string contains only two numbers, it
  1400.   is interpreted as a date (m/d or d/m) in the current year. Year values
  1401.   between 0 and 99 are assumed to be in the current century. If the given
  1402.   string does not contain a valid date, an EConvertError exception is
  1403.   raised. }
  1404.  
  1405. function StrToDate(const S: string): TDateTime;
  1406.  
  1407. { StrToTime converts the given string to a time value. The string must
  1408.   consist of two or three numbers, separated by the character defined by
  1409.   the TimeSeparator global variable, optionally followed by an AM or PM
  1410.   indicator. The numbers represent hour, minute, and (optionally) second,
  1411.   in that order. If the time is followed by AM or PM, it is assumed to be
  1412.   in 12-hour clock format. If no AM or PM indicator is included, the time
  1413.   is assumed to be in 24-hour clock format. If the given string does not
  1414.   contain a valid time, an EConvertError exception is raised. }
  1415.  
  1416. function StrToTime(const S: string): TDateTime;
  1417.  
  1418. { StrToDateTime converts the given string to a date and time value. The
  1419.   string must contain a date optionally followed by a time. The date and
  1420.   time parts of the string must follow the formats described for the
  1421.   StrToDate and StrToTime functions. }
  1422.  
  1423. function StrToDateTime(const S: string): TDateTime;
  1424.  
  1425. { FormatDateTime formats the date-and-time value given by DateTime using the
  1426.   format given by Format. The following format specifiers are supported:
  1427.  
  1428.   c       Displays the date using the format given by the ShortDateFormat
  1429.           global variable, followed by the time using the format given by
  1430.           the LongTimeFormat global variable. The time is not displayed if
  1431.           the fractional part of the DateTime value is zero.
  1432.  
  1433.   d       Displays the day as a number without a leading zero (1-31).
  1434.  
  1435.   dd      Displays the day as a number with a leading zero (01-31).
  1436.  
  1437.   ddd     Displays the day as an abbreviation (Sun-Sat) using the strings
  1438.           given by the ShortDayNames global variable.
  1439.  
  1440.   dddd    Displays the day as a full name (Sunday-Saturday) using the strings
  1441.           given by the LongDayNames global variable.
  1442.  
  1443.   ddddd   Displays the date using the format given by the ShortDateFormat
  1444.           global variable.
  1445.  
  1446.   dddddd  Displays the date using the format given by the LongDateFormat
  1447.           global variable.
  1448.  
  1449.   m       Displays the month as a number without a leading zero (1-12). If
  1450.           the m specifier immediately follows an h or hh specifier, the
  1451.           minute rather than the month is displayed.
  1452.  
  1453.   mm      Displays the month as a number with a leading zero (01-12). If
  1454.           the mm specifier immediately follows an h or hh specifier, the
  1455.           minute rather than the month is displayed.
  1456.  
  1457.   mmm     Displays the month as an abbreviation (Jan-Dec) using the strings
  1458.           given by the ShortMonthNames global variable.
  1459.  
  1460.   mmmm    Displays the month as a full name (January-December) using the
  1461.           strings given by the LongMonthNames global variable.
  1462.  
  1463.   yy      Displays the year as a two-digit number (00-99).
  1464.  
  1465.   yyyy    Displays the year as a four-digit number (0000-9999).
  1466.  
  1467.   h       Displays the hour without a leading zero (0-23).
  1468.  
  1469.   hh      Displays the hour with a leading zero (00-23).
  1470.  
  1471.   n       Displays the minute without a leading zero (0-59).
  1472.  
  1473.   nn      Displays the minute with a leading zero (00-59).
  1474.  
  1475.   s       Displays the second without a leading zero (0-59).
  1476.  
  1477.   ss      Displays the second with a leading zero (00-59).
  1478.  
  1479.   t       Displays the time using the format given by the ShortTimeFormat
  1480.           global variable.
  1481.  
  1482.   tt      Displays the time using the format given by the LongTimeFormat
  1483.           global variable.
  1484.  
  1485.   am/pm   Uses the 12-hour clock for the preceding h or hh specifier, and
  1486.           displays 'am' for any hour before noon, and 'pm' for any hour
  1487.           after noon. The am/pm specifier can use lower, upper, or mixed
  1488.           case, and the result is displayed accordingly.
  1489.  
  1490.   a/p     Uses the 12-hour clock for the preceding h or hh specifier, and
  1491.           displays 'a' for any hour before noon, and 'p' for any hour after
  1492.           noon. The a/p specifier can use lower, upper, or mixed case, and
  1493.           the result is displayed accordingly.
  1494.  
  1495.   ampm    Uses the 12-hour clock for the preceding h or hh specifier, and
  1496.           displays the contents of the TimeAMString global variable for any
  1497.           hour before noon, and the contents of the TimePMString global
  1498.           variable for any hour after noon.
  1499.  
  1500.   /       Displays the date separator character given by the DateSeparator
  1501.           global variable.
  1502.  
  1503.   :       Displays the time separator character given by the TimeSeparator
  1504.           global variable.
  1505.  
  1506.   'xx'    Characters enclosed in single or double quotes are displayed as-is,
  1507.   "xx"    and do not affect formatting.
  1508.  
  1509.   Format specifiers may be written in upper case as well as in lower case
  1510.   letters--both produce the same result.
  1511.  
  1512.   If the string given by the Format parameter is empty, the date and time
  1513.   value is formatted as if a 'c' format specifier had been given.
  1514.  
  1515.   The following example:
  1516.  
  1517.     S := FormatDateTime('"The meeting is on" dddd, mmmm d, yyyy, ' +
  1518.       '"at" hh:mm AM/PM', StrToDateTime('2/15/95 10:30am'));
  1519.  
  1520.   assigns 'The meeting is on Wednesday, February 15, 1995 at 10:30 AM' to
  1521.   the string variable S. }
  1522.  
  1523. function FormatDateTime(const Format: string; DateTime: TDateTime): string;
  1524.  
  1525. { DateTimeToString converts the date and time value given by DateTime using
  1526.   the format string given by Format into the string variable given by Result.
  1527.   For further details, see the description of the FormatDateTime function. }
  1528.  
  1529. procedure DateTimeToString(var Result: string; const Format: string;
  1530.   DateTime: TDateTime);
  1531.  
  1532. { System error messages }
  1533.  
  1534. function SysErrorMessage(ErrorCode: Integer): string;
  1535.  
  1536. { Initialization file support }
  1537.  
  1538. function GetLocaleStr(Locale, LocaleType: Integer; const Default: string): string;
  1539. function GetLocaleChar(Locale, LocaleType: Integer; Default: Char): Char;
  1540.  
  1541. { GetFormatSettings resets all date and number format variables to their
  1542.   default values. }
  1543.  
  1544. procedure GetFormatSettings;
  1545.  
  1546. { Exception handling routines }
  1547.  
  1548. function ExceptObject: TObject;
  1549. function ExceptAddr: Pointer;
  1550.  
  1551. procedure ShowException(ExceptObject: TObject; ExceptAddr: Pointer);
  1552.  
  1553. procedure Abort;
  1554.  
  1555. procedure OutOfMemoryError;
  1556.  
  1557. procedure Beep;
  1558.  
  1559. implementation
  1560.  
  1561. {$R SYSUTILS.RES}
  1562.  
  1563. {$I SYSUTILS.INC}
  1564.  
  1565. { Utility routines }
  1566.  
  1567. procedure DivMod(Dividend: Integer; Divisor: Word;
  1568.   var Result, Remainder: Word);
  1569. asm
  1570.         PUSH    EBX
  1571.         MOV     EBX,EDX
  1572.         MOV     EDX,EAX
  1573.         SHR     EDX,16
  1574.         DIV     BX
  1575.         MOV     EBX,Remainder
  1576.         MOV     [ECX],AX
  1577.         MOV     [EBX],DX
  1578.         POP     EBX
  1579. end;
  1580.  
  1581. procedure ConvertError(Ident: Integer);
  1582. begin
  1583.   raise EConvertError.CreateRes(Ident);
  1584. end;
  1585.  
  1586. procedure ConvertErrorFmt(Ident: Integer; const Args: array of const);
  1587. begin
  1588.   raise EConvertError.CreateResFmt(Ident, Args);
  1589. end;
  1590.  
  1591. { Memory management routines }
  1592.  
  1593. function AllocMem(Size: Cardinal): Pointer;
  1594. begin
  1595.   GetMem(Result, Size);
  1596.   FillChar(Result^, Size, 0);
  1597. end;
  1598.  
  1599. { Exit procedure handling }
  1600.  
  1601. type
  1602.   PExitProcInfo = ^TExitProcInfo;
  1603.   TExitProcInfo = record
  1604.     Next: PExitProcInfo;
  1605.     SaveExit: Pointer;
  1606.     Proc: TProcedure;
  1607.   end;
  1608.  
  1609. const
  1610.   ExitProcList: PExitProcInfo = nil;
  1611.  
  1612. procedure DoExitProc; far;
  1613. var
  1614.   P: PExitProcInfo;
  1615.   Proc: TProcedure;
  1616. begin
  1617.   P := ExitProcList;
  1618.   ExitProcList := P^.Next;
  1619.   ExitProc := P^.SaveExit;
  1620.   Proc := P^.Proc;
  1621.   Dispose(P);
  1622.   Proc;
  1623. end;
  1624.  
  1625. procedure AddExitProc(Proc: TProcedure);
  1626. var
  1627.   P: PExitProcInfo;
  1628. begin
  1629.   New(P);
  1630.   P^.Next := ExitProcList;
  1631.   P^.SaveExit := ExitProc;
  1632.   P^.Proc := Proc;
  1633.   ExitProcList := P;
  1634.   ExitProc := @DoExitProc;
  1635. end;
  1636.  
  1637. { String handling routines }
  1638.  
  1639. function NewStr(const S: string): PString;
  1640. begin
  1641.   if S = '' then Result := NullStr else
  1642.   begin
  1643.     New(Result);
  1644.     Result^ := S;
  1645.   end;
  1646. end;
  1647.  
  1648. procedure DisposeStr(P: PString);
  1649. begin
  1650.   if (P <> nil) and (P^ <> '') then Dispose(P);
  1651. end;
  1652.  
  1653. procedure AssignStr(var P: PString; const S: string);
  1654. var
  1655.   Temp: PString;
  1656. begin
  1657.   Temp := P;
  1658.   P := NewStr(S);
  1659.   DisposeStr(Temp);
  1660. end;
  1661.  
  1662. procedure AppendStr(var Dest: string; const S: string);
  1663. begin
  1664.   Dest := Dest + S;
  1665. end;
  1666.  
  1667. function UpperCase(const S: string): string;
  1668. var
  1669.   Ch: Char;
  1670.   L: Integer;
  1671.   Source, Dest: PChar;
  1672. begin
  1673.   L := Length(S);
  1674.   SetLength(Result, L);
  1675.   Source := Pointer(S);
  1676.   Dest := Pointer(Result);
  1677.   while L <> 0 do
  1678.   begin
  1679.     Ch := Source^;
  1680.     if (Ch >= 'a') and (Ch <= 'z') then Dec(Ch, 32);
  1681.     Dest^ := Ch;
  1682.     Inc(Source);
  1683.     Inc(Dest);
  1684.     Dec(L);
  1685.   end;
  1686. end;
  1687.  
  1688. function LowerCase(const S: string): string;
  1689. var
  1690.   Ch: Char;
  1691.   L: Integer;
  1692.   Source, Dest: PChar;
  1693. begin
  1694.   L := Length(S);
  1695.   SetLength(Result, L);
  1696.   Source := Pointer(S);
  1697.   Dest := Pointer(Result);
  1698.   while L <> 0 do
  1699.   begin
  1700.     Ch := Source^;
  1701.     if (Ch >= 'A') and (Ch <= 'Z') then Inc(Ch, 32);
  1702.     Dest^ := Ch;
  1703.     Inc(Source);
  1704.     Inc(Dest);
  1705.     Dec(L);
  1706.   end;
  1707. end;
  1708.  
  1709. function CompareStr(const S1, S2: string): Integer; assembler;
  1710. asm
  1711.         PUSH    ESI
  1712.         PUSH    EDI
  1713.         MOV     ESI,EAX
  1714.         MOV     EDI,EDX
  1715.         OR      EAX,EAX
  1716.         JE      @@1
  1717.         MOV     EAX,[EAX-4]
  1718. @@1:    OR      EDX,EDX
  1719.         JE      @@2
  1720.         MOV     EDX,[EDX-4]
  1721. @@2:    MOV     ECX,EAX
  1722.         CMP     ECX,EDX
  1723.         JBE     @@3
  1724.         MOV     ECX,EDX
  1725. @@3:    CMP     ECX,ECX
  1726.         REPE    CMPSB
  1727.         JE      @@4
  1728.         MOVZX   EAX,BYTE PTR [ESI-1]
  1729.         MOVZX   EDX,BYTE PTR [EDI-1]
  1730. @@4:    SUB     EAX,EDX
  1731.         POP     EDI
  1732.         POP     ESI
  1733. end;
  1734.  
  1735. function CompareText(const S1, S2: string): Integer; assembler;
  1736. asm
  1737.         PUSH    ESI
  1738.         PUSH    EDI
  1739.         PUSH    EBX
  1740.         MOV     ESI,EAX
  1741.         MOV     EDI,EDX
  1742.         OR      EAX,EAX
  1743.         JE      @@0
  1744.         MOV     EAX,[EAX-4]
  1745. @@0:    OR      EDX,EDX
  1746.         JE      @@1
  1747.         MOV     EDX,[EDX-4]
  1748. @@1:    MOV     ECX,EAX
  1749.         CMP     ECX,EDX
  1750.         JBE     @@2
  1751.         MOV     ECX,EDX
  1752. @@2:    CMP     ECX,ECX
  1753. @@3:    REPE    CMPSB
  1754.         JE      @@6
  1755.         MOV     BL,BYTE PTR [ESI-1]
  1756.         CMP     BL,'a'
  1757.         JB      @@4
  1758.         CMP     BL,'z'
  1759.         JA      @@4
  1760.         SUB     BL,20H
  1761. @@4:    MOV     BH,BYTE PTR [EDI-1]
  1762.         CMP     BH,'a'
  1763.         JB      @@5
  1764.         CMP     BH,'z'
  1765.         JA      @@5
  1766.         SUB     BH,20H
  1767. @@5:    CMP     BL,BH
  1768.         JE      @@3
  1769.         MOVZX   EAX,BL
  1770.         MOVZX   EDX,BH
  1771. @@6:    SUB     EAX,EDX
  1772.         POP     EBX
  1773.         POP     EDI
  1774.         POP     ESI
  1775. end;
  1776.  
  1777. function AnsiUpperCase(const S: string): string;
  1778. var
  1779.   Len: Integer;
  1780. begin
  1781.   Len := Length(S);
  1782.   SetString(Result, PChar(S), Len);
  1783.   CharUpperBuff(Pointer(Result), Len);
  1784. end;
  1785.  
  1786. function AnsiLowerCase(const S: string): string;
  1787. var
  1788.   Len: Integer;
  1789. begin
  1790.   Len := Length(S);
  1791.   SetString(Result, PChar(S), Len);
  1792.   CharLowerBuff(Pointer(Result), Len);
  1793. end;
  1794.  
  1795. function AnsiCompareStr(const S1, S2: string): Integer;
  1796. begin
  1797.   Result := CompareString(LOCALE_USER_DEFAULT, 0, PChar(S1), Length(S1),
  1798.     PChar(S2), Length(S2)) - 2;
  1799. end;
  1800.  
  1801. function AnsiCompareText(const S1, S2: string): Integer;
  1802. begin
  1803.   Result := CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE,
  1804.     PChar(S1), Length(S1), PChar(S2), Length(S2)) - 2;
  1805. end;
  1806.  
  1807. function Trim(const S: string): string;
  1808. var
  1809.   I, L: Integer;
  1810. begin
  1811.   L := Length(S);
  1812.   I := 1;
  1813.   while (I <= L) and (S[I] <= ' ') do Inc(I);
  1814.   if I > L then Result := '' else
  1815.   begin
  1816.     while S[L] <= ' ' do Dec(L);
  1817.     Result := Copy(S, I, L - I + 1);
  1818.   end;
  1819. end;
  1820.  
  1821. function TrimLeft(const S: string): string;
  1822. var
  1823.   I, L: Integer;
  1824. begin
  1825.   L := Length(S);
  1826.   I := 1;
  1827.   while (I <= L) and (S[I] <= ' ') do Inc(I);
  1828.   Result := Copy(S, I, Maxint);
  1829. end;
  1830.  
  1831. function TrimRight(const S: string): string;
  1832. var
  1833.   I: Integer;
  1834. begin
  1835.   I := Length(S);
  1836.   while (I > 0) and (S[I] <= ' ') do Dec(I);
  1837.   Result := Copy(S, 1, I);
  1838. end;
  1839.  
  1840. function QuotedStr(const S: string): string;
  1841. var
  1842.   I: Integer;
  1843. begin
  1844.   Result := S;
  1845.   for I := Length(Result) downto 1 do
  1846.     if Result[I] = '''' then Insert('''', Result, I);
  1847.   Result := '''' + Result + '''';
  1848. end;
  1849.  
  1850. function AdjustLineBreaks(const S: string): string;
  1851. var
  1852.   Source, SourceEnd, Dest: PChar;
  1853.   Extra: Integer;
  1854. begin
  1855.   Source := Pointer(S);
  1856.   SourceEnd := Source + Length(S);
  1857.   Extra := 0;
  1858.   while Source < SourceEnd do
  1859.   begin
  1860.     case Source^ of
  1861.       #10:
  1862.         Inc(Extra);
  1863.       #13:
  1864.         if Source[1] = #10 then Inc(Source) else Inc(Extra);
  1865.     end;
  1866.     Inc(Source);
  1867.   end;
  1868.   if Extra = 0 then Result := S else
  1869.   begin
  1870.     Source := Pointer(S);
  1871.     SetString(Result, nil, SourceEnd - Source + Extra);
  1872.     Dest := Pointer(Result);
  1873.     while Source < SourceEnd do
  1874.       case Source^ of
  1875.         #10:
  1876.           begin
  1877.             Dest^ := #13;
  1878.             Inc(Dest);
  1879.             Dest^ := #10;
  1880.             Inc(Dest);
  1881.             Inc(Source);
  1882.           end;
  1883.         #13:
  1884.           begin
  1885.             Dest^ := #13;
  1886.             Inc(Dest);
  1887.             Dest^ := #10;
  1888.             Inc(Dest);
  1889.             Inc(Source);
  1890.             if Source^ = #10 then Inc(Source);
  1891.           end;
  1892.       else
  1893.         Dest^ := Source^;
  1894.         Inc(Dest);
  1895.         Inc(Source);
  1896.       end;
  1897.   end;
  1898. end;
  1899.  
  1900. function IsValidIdent(const Ident: string): Boolean;
  1901. const
  1902.   Alpha = ['A'..'Z', 'a'..'z', '_'];
  1903.   AlphaNumeric = Alpha + ['0'..'9'];
  1904. var
  1905.   I: Integer;
  1906. begin
  1907.   Result := False;
  1908.   if (Length(Ident) = 0) or not (Ident[1] in Alpha) then Exit;
  1909.   for I := 2 to Length(Ident) do if not (Ident[I] in AlphaNumeric) then Exit;
  1910.   Result := True;
  1911. end;
  1912.  
  1913. function IntToStr(Value: Integer): string;
  1914. begin
  1915.   FmtStr(Result, '%d', [Value]);
  1916. end;
  1917.  
  1918. function IntToHex(Value: Integer; Digits: Integer): string;
  1919. begin
  1920.   FmtStr(Result, '%.*x', [Digits, Value]);
  1921. end;
  1922.  
  1923. function StrToInt(const S: string): Integer;
  1924. var
  1925.   E: Integer;
  1926. begin
  1927.   Val(S, Result, E);
  1928.   if E <> 0 then ConvertErrorFmt(SInvalidInteger, [S]);
  1929. end;
  1930.  
  1931. function StrToIntDef(const S: string; Default: Integer): Integer;
  1932. var
  1933.   E: Integer;
  1934. begin
  1935.   Val(S, Result, E);
  1936.   if E <> 0 then Result := Default;
  1937. end;
  1938.  
  1939. function LoadStr(Ident: Integer): string;
  1940. var
  1941.   Buffer: array[0..1023] of Char;
  1942. begin
  1943.   SetString(Result, Buffer, LoadString(HInstance, Ident, Buffer,
  1944.     SizeOf(Buffer)));
  1945. end;
  1946.  
  1947. function FmtLoadStr(Ident: Integer; const Args: array of const): string;
  1948. begin
  1949.   FmtStr(Result, LoadStr(Ident), Args);
  1950. end;
  1951.  
  1952. { File management routines }
  1953.  
  1954. function FileOpen(const FileName: string; Mode: Integer): Integer;
  1955. const
  1956.   AccessMode: array[0..2] of Integer = (
  1957.     GENERIC_READ,
  1958.     GENERIC_WRITE,
  1959.     GENERIC_READ or GENERIC_WRITE);
  1960.   ShareMode: array[0..4] of Integer = (
  1961.     0,
  1962.     0,
  1963.     FILE_SHARE_READ,
  1964.     FILE_SHARE_WRITE,
  1965.     FILE_SHARE_READ or FILE_SHARE_WRITE);
  1966. begin
  1967.   Result := CreateFile(PChar(FileName), AccessMode[Mode and 3],
  1968.     ShareMode[(Mode and $F0) shr 4], nil, OPEN_EXISTING,
  1969.     FILE_ATTRIBUTE_NORMAL, 0);
  1970. end;
  1971.  
  1972. function FileCreate(const FileName: string): Integer;
  1973. begin
  1974.   Result := CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE,
  1975.     0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  1976. end;
  1977.  
  1978. function FileRead(Handle: Integer; var Buffer; Count: Integer): Integer;
  1979. begin
  1980.   if not ReadFile(Handle, Buffer, Count, Result, nil) then Result := -1;
  1981. end;
  1982.  
  1983. function FileWrite(Handle: Integer; const Buffer; Count: Integer): Integer;
  1984. begin
  1985.   if not WriteFile(Handle, Buffer, Count, Result, nil) then Result := -1;
  1986. end;
  1987.  
  1988. function FileSeek(Handle, Offset, Origin: Integer): Integer;
  1989. begin
  1990.   Result := SetFilePointer(Handle, Offset, nil, Origin);
  1991. end;
  1992.  
  1993. procedure FileClose(Handle: Integer);
  1994. begin
  1995.   CloseHandle(Handle);
  1996. end;
  1997.  
  1998. function FileAge(const FileName: string): Integer;
  1999. var
  2000.   Handle: THandle;
  2001.   FindData: TWin32FindData;
  2002.   LocalFileTime: TFileTime;
  2003. begin
  2004.   Handle := FindFirstFile(PChar(FileName), FindData);
  2005.   if Handle <> INVALID_HANDLE_VALUE then
  2006.   begin
  2007.     Windows.FindClose(Handle);
  2008.     if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
  2009.     begin
  2010.       FileTimeToLocalFileTime(FindData.ftLastWriteTime, LocalFileTime);
  2011.       if FileTimeToDosDateTime(LocalFileTime, LongRec(Result).Hi,
  2012.         LongRec(Result).Lo) then Exit;
  2013.     end;
  2014.   end;
  2015.   Result := -1;
  2016. end;
  2017.  
  2018. function FileExists(const FileName: string): Boolean;
  2019. begin
  2020.   Result := FileAge(FileName) <> -1;
  2021. end;
  2022.  
  2023. function FileGetDate(Handle: Integer): Integer;
  2024. var
  2025.   FileTime, LocalFileTime: TFileTime;
  2026. begin
  2027.   if GetFileTime(Handle, nil, nil, @FileTime) and
  2028.     FileTimeToLocalFileTime(FileTime, LocalFileTime) and
  2029.     FileTimeToDosDateTime(LocalFileTime, LongRec(Result).Hi,
  2030.       LongRec(Result).Lo) then Exit;
  2031.   Result := -1;
  2032. end;
  2033.  
  2034. function FileSetDate(Handle: Integer; Age: Integer): Integer;
  2035. var
  2036.   LocalFileTime, FileTime: TFileTime;
  2037. begin
  2038.   Result := 0;
  2039.   if DosDateTimeToFileTime(LongRec(Age).Hi, LongRec(Age).Lo, LocalFileTime) and
  2040.     LocalFileTimeToFileTime(LocalFileTime, FileTime) and
  2041.     SetFileTime(Handle, nil, nil, @FileTime) then Exit;
  2042.   Result := GetLastError;
  2043. end;
  2044.  
  2045. function FileGetAttr(const FileName: string): Integer;
  2046. begin
  2047.   Result := GetFileAttributes(PChar(FileName));
  2048. end;
  2049.  
  2050. function FileSetAttr(const FileName: string; Attr: Integer): Integer;
  2051. begin
  2052.   Result := 0;
  2053.   if not SetFileAttributes(PChar(FileName), Attr) then
  2054.     Result := GetLastError;
  2055. end;
  2056.  
  2057. function FindMatchingFile(var F: TSearchRec): Integer;
  2058. var
  2059.   LocalFileTime: TFileTime;
  2060. begin
  2061.   with F do
  2062.   begin
  2063.     while FindData.dwFileAttributes and ExcludeAttr <> 0 do
  2064.       if not FindNextFile(FindHandle, FindData) then
  2065.       begin
  2066.         Result := GetLastError;
  2067.         Exit;
  2068.       end;
  2069.     FileTimeToLocalFileTime(FindData.ftLastWriteTime, LocalFileTime);
  2070.     FileTimeToDosDateTime(LocalFileTime, LongRec(Time).Hi,
  2071.       LongRec(Time).Lo);
  2072.     Size := FindData.nFileSizeLow;
  2073.     Attr := FindData.dwFileAttributes;
  2074.     Name := FindData.cFileName;
  2075.   end;
  2076.   Result := 0;
  2077. end;
  2078.  
  2079. function FindFirst(const Path: string; Attr: Integer;
  2080.   var F: TSearchRec): Integer;
  2081. const
  2082.   faSpecial = faHidden or faSysFile or faVolumeID or faDirectory;
  2083. begin
  2084.   F.ExcludeAttr := not Attr and faSpecial;
  2085.   F.FindHandle := FindFirstFile(PChar(Path), F.FindData);
  2086.   if F.FindHandle <> INVALID_HANDLE_VALUE then
  2087.   begin
  2088.     Result := FindMatchingFile(F);
  2089.     if Result <> 0 then FindClose(F);
  2090.   end else
  2091.     Result := GetLastError;
  2092. end;
  2093.  
  2094. function FindNext(var F: TSearchRec): Integer;
  2095. begin
  2096.   if FindNextFile(F.FindHandle, F.FindData) then
  2097.     Result := FindMatchingFile(F) else
  2098.     Result := GetLastError;
  2099. end;
  2100.  
  2101. procedure FindClose(var F: TSearchRec);
  2102. begin
  2103.   if F.FindHandle <> INVALID_HANDLE_VALUE then
  2104.     Windows.FindClose(F.FindHandle);
  2105. end;
  2106.  
  2107. function DeleteFile(const FileName: string): Boolean;
  2108. begin
  2109.   Result := Windows.DeleteFile(PChar(FileName));
  2110. end;
  2111.  
  2112. function RenameFile(const OldName, NewName: string): Boolean;
  2113. begin
  2114.   Result := MoveFile(PChar(OldName), PChar(NewName));
  2115. end;
  2116.  
  2117. function ChangeFileExt(const FileName, Extension: string): string;
  2118. var
  2119.   I: Integer;
  2120. begin
  2121.   I := Length(FileName);
  2122.   while (I > 0) and not (FileName[I] in ['.', '\', ':']) do Dec(I);
  2123.   if (I = 0) or (FileName[I] <> '.') then I := MaxInt;
  2124.   Result := Copy(FileName, 1, I - 1) + Extension;
  2125. end;
  2126.  
  2127. function ExtractFilePath(const FileName: string): string;
  2128. var
  2129.   I: Integer;
  2130. begin
  2131.   I := Length(FileName);
  2132.   while (I > 0) and not (FileName[I] in ['\', ':']) do Dec(I);
  2133.   Result := Copy(FileName, 1, I);
  2134. end;
  2135.  
  2136. function ExtractFileDir(const FileName: string): string;
  2137. var
  2138.   I: Integer;
  2139. begin
  2140.   I := Length(FileName);
  2141.   while (I > 0) and not (FileName[I] in ['\', ':']) do Dec(I);
  2142.   if (I > 1) and (FileName[I] = '\') and
  2143.     not (FileName[I - 1] in ['\', ':']) then Dec(I);
  2144.   Result := Copy(FileName, 1, I);
  2145. end;
  2146.  
  2147. function ExtractFileDrive(const FileName: string): string;
  2148. var
  2149.   I, J: Integer;
  2150. begin
  2151.   if (Length(FileName) >= 2) and (FileName[2] = ':') then
  2152.     Result := Copy(FileName, 1, 2)
  2153.   else if (Length(FileName) >= 2) and (FileName[1] = '\') and
  2154.     (FileName[2] = '\') then
  2155.   begin
  2156.     J := 0;
  2157.     I := 3;
  2158.     While (I < Length(FileName)) and (J < 2) do
  2159.     begin
  2160.       if FileName[I] = '\' then Inc(J);
  2161.       if J < 2 then Inc(I);
  2162.     end;
  2163.     if FileName[I] = '\' then Dec(I);
  2164.     Result := Copy(FileName, 1, I);
  2165.   end else Result := '';
  2166. end;
  2167.  
  2168. function ExtractFileName(const FileName: string): string;
  2169. var
  2170.   I: Integer;
  2171. begin
  2172.   I := Length(FileName);
  2173.   while (I > 0) and not (FileName[I] in ['\', ':']) do Dec(I);
  2174.   Result := Copy(FileName, I + 1, 255);
  2175. end;
  2176.  
  2177. function ExtractFileExt(const FileName: string): string;
  2178. var
  2179.   I: Integer;
  2180. begin
  2181.   I := Length(FileName);
  2182.   while (I > 0) and not (FileName[I] in ['.', '\', ':']) do Dec(I);
  2183.   if (I > 0) and (FileName[I] = '.') then
  2184.     Result := Copy(FileName, I, 255) else
  2185.     Result := '';
  2186. end;
  2187.  
  2188. function ExpandFileName(const FileName: string): string;
  2189. var
  2190.   FName: PChar;
  2191.   Buffer: array[0..MAX_PATH - 1] of Char;
  2192. begin
  2193.   SetString(Result, Buffer, GetFullPathName(PChar(FileName), SizeOf(Buffer),
  2194.     Buffer, FName));
  2195. end;
  2196.  
  2197. function GetUniversalName(const FileName: string): string;
  2198. type
  2199.   PNetResourceArray = ^TNetResourceArray;
  2200.   TNetResourceArray = array[0..MaxInt div SizeOf(TNetResource) - 1] of TNetResource;
  2201. var
  2202.   I, Count, BufSize, Size, NetResult: Integer;
  2203.   Drive: Char;
  2204.   NetHandle: THandle;
  2205.   NetResources: PNetResourceArray;
  2206.   RemoteNameInfo: array[0..1023] of Byte;
  2207. begin
  2208.   Result := FileName;
  2209.   if Win32Platform <> VER_PLATFORM_WIN32_WINDOWS then
  2210.   begin
  2211.     Size := SizeOf(RemoteNameInfo);
  2212.     if WNetGetUniversalName(PChar(FileName), UNIVERSAL_NAME_INFO_LEVEL,
  2213.       @RemoteNameInfo, Size) <> NO_ERROR then Exit;
  2214.     Result := PRemoteNameInfo(@RemoteNameInfo).lpUniversalName;
  2215.   end else
  2216.   begin
  2217.   { The following works around a bug in WNetGetUniversalName under Windows 95 }
  2218.     Drive := UpCase(FileName[1]);
  2219.     if (Drive < 'A') or (Drive > 'Z') or (Length(FileName) < 3) or
  2220.       (FileName[2] <> ':') or (FileName[3] <> '\') then
  2221.       Exit;
  2222.     if WNetOpenEnum(RESOURCE_CONNECTED, RESOURCETYPE_DISK, 0, nil,
  2223.       NetHandle) <> NO_ERROR then Exit;
  2224.     try
  2225.       BufSize := 50 * SizeOf(TNetResource);
  2226.       GetMem(NetResources, BufSize);
  2227.       try
  2228.         while True do
  2229.         begin
  2230.           Count := -1;
  2231.           Size := BufSize;
  2232.           NetResult := WNetEnumResource(NetHandle, Count, NetResources, Size);
  2233.           if NetResult = ERROR_MORE_DATA then
  2234.           begin
  2235.             BufSize := Size;
  2236.             ReallocMem(NetResources, BufSize);
  2237.             Continue;
  2238.           end;
  2239.           if NetResult <> NO_ERROR then Exit;
  2240.           for I := 0 to Count - 1 do
  2241.             with NetResources^[I] do
  2242.               if (lpLocalName <> nil) and (Drive = UpCase(lpLocalName[0])) then
  2243.               begin
  2244.                 Result := lpRemoteName + Copy(FileName, 3, Length(FileName) - 2);
  2245.                 Exit;
  2246.               end;
  2247.         end;
  2248.       finally
  2249.         FreeMem(NetResources, BufSize);
  2250.       end;
  2251.     finally
  2252.       WNetCloseEnum(NetHandle);
  2253.     end;
  2254.   end;
  2255. end;
  2256.  
  2257. function ExpandUNCFileName(const FileName: string): string;
  2258. begin
  2259.   { First get the local resource version of the file name }
  2260.   Result := ExpandFileName(FileName);
  2261.   if (Length(Result) >= 3) and (Result[2] = ':') and (Upcase(Result[1]) >= 'A')
  2262.     and (Upcase(Result[1]) <= 'Z') then
  2263.     Result := GetUniversalName(Result);
  2264. end;
  2265.  
  2266. function FileSearch(const Name, DirList: string): string;
  2267. var
  2268.   I, P, L: Integer;
  2269. begin
  2270.   Result := Name;
  2271.   P := 1;
  2272.   L := Length(DirList);
  2273.   while True do
  2274.   begin
  2275.     if FileExists(Result) then Exit;
  2276.     while (P <= L) and (DirList[P] = ';') do Inc(P);
  2277.     if P > L then Break;
  2278.     I := P;
  2279.     while (P <= L) and (DirList[P] <> ';') do Inc(P);
  2280.     Result := Copy(DirList, I, P - I);
  2281.     if not (DirList[P - 1] in [':', '\']) then Result := Result + '\';
  2282.     Result := Result + Name;
  2283.   end;
  2284.   Result := '';
  2285. end;
  2286.  
  2287. function DiskFree(Drive: Byte): Integer;
  2288. var
  2289.   RootPath: array[0..4] of Char;
  2290.   RootPtr: PChar;
  2291.   SectorsPerCluster,
  2292.   BytesPerSector,
  2293.   FreeClusters,
  2294.   TotalClusters: Integer;
  2295. begin
  2296.   RootPtr := nil;
  2297.   if Drive > 0 then
  2298.   begin
  2299.     StrCopy(RootPath, 'A:\');
  2300.     RootPath[0] := Char(Drive + $40);
  2301.     RootPtr := RootPath;
  2302.   end;
  2303.   if GetDiskFreeSpace(RootPtr, SectorsPerCluster, BytesPerSector,
  2304.     FreeClusters, TotalClusters) then
  2305.     Result := SectorsPerCluster * BytesPerSector * FreeClusters
  2306.   else Result := -1;
  2307. end;
  2308.  
  2309. function DiskSize(Drive: Byte): Integer;
  2310. var
  2311.   RootPath: array[0..4] of Char;
  2312.   RootPtr: PChar;
  2313.   SectorsPerCluster,
  2314.   BytesPerSector,
  2315.   FreeClusters,
  2316.   TotalClusters: Integer;
  2317. begin
  2318.   RootPtr := nil;
  2319.   if Drive > 0 then
  2320.   begin
  2321.     StrCopy(RootPath, 'A:\');
  2322.     RootPath[0] := Char(Drive + $40);
  2323.     RootPtr := RootPath;
  2324.   end;
  2325.   if GetDiskFreeSpace(RootPtr, SectorsPerCluster, BytesPerSector,
  2326.     FreeClusters, TotalClusters) then
  2327.     Result := SectorsPerCluster * BytesPerSector * TotalClusters
  2328.   else Result := -1;
  2329. end;
  2330.  
  2331. function FileDateToDateTime(FileDate: Integer): TDateTime;
  2332. begin
  2333.   Result :=
  2334.     EncodeDate(
  2335.       LongRec(FileDate).Hi shr 9 + 1980,
  2336.       LongRec(FileDate).Hi shr 5 and 15,
  2337.       LongRec(FileDate).Hi and 31) +
  2338.     EncodeTime(
  2339.       LongRec(FileDate).Lo shr 11,
  2340.       LongRec(FileDate).Lo shr 5 and 63,
  2341.       LongRec(FileDate).Lo and 31 shl 1, 0);
  2342. end;
  2343.  
  2344. function DateTimeToFileDate(DateTime: TDateTime): Integer;
  2345. var
  2346.   Year, Month, Day, Hour, Min, Sec, MSec: Word;
  2347. begin
  2348.   DecodeDate(DateTime, Year, Month, Day);
  2349.   if (Year < 1980) or (Year > 2099) then Result := 0 else
  2350.   begin
  2351.     DecodeTime(DateTime, Hour, Min, Sec, MSec);
  2352.     LongRec(Result).Lo := (Sec shr 1) or (Min shl 5) or (Hour shl 11);
  2353.     LongRec(Result).Hi := Day or (Month shl 5) or ((Year - 1980) shl 9);
  2354.   end;
  2355. end;
  2356.  
  2357. function GetCurrentDir: string;
  2358. var
  2359.   Buffer: array[0..MAX_PATH - 1] of Char;
  2360. begin
  2361.   SetString(Result, Buffer, GetCurrentDirectory(SizeOf(Buffer), Buffer));
  2362. end;
  2363.  
  2364. function SetCurrentDir(const Dir: string): Boolean;
  2365. begin
  2366.   Result := SetCurrentDirectory(PChar(Dir));
  2367. end;
  2368.  
  2369. function CreateDir(const Dir: string): Boolean;
  2370. begin
  2371.   Result := CreateDirectory(PChar(Dir), nil);
  2372. end;
  2373.  
  2374. function RemoveDir(const Dir: string): Boolean;
  2375. begin
  2376.   Result := RemoveDirectory(PChar(Dir));
  2377. end;
  2378.  
  2379. { PChar routines }
  2380.  
  2381. function StrLen(Str: PChar): Cardinal; assembler;
  2382. asm
  2383.         MOV     EDX,EDI
  2384.         MOV     EDI,EAX
  2385.         MOV     ECX,0FFFFFFFFH
  2386.         XOR     AL,AL
  2387.         REPNE   SCASB
  2388.         MOV     EAX,0FFFFFFFEH
  2389.         SUB     EAX,ECX
  2390.         MOV     EDI,EDX
  2391. end;
  2392.  
  2393. function StrEnd(Str: PChar): PChar; assembler;
  2394. asm
  2395.         MOV     EDX,EDI
  2396.         MOV     EDI,EAX
  2397.         MOV     ECX,0FFFFFFFFH
  2398.         XOR     AL,AL
  2399.         REPNE   SCASB
  2400.         LEA     EAX,[EDI-1]
  2401.         MOV     EDI,EDX
  2402. end;
  2403.  
  2404. function StrMove(Dest, Source: PChar; Count: Cardinal): PChar; assembler;
  2405. asm
  2406.         PUSH    ESI
  2407.         PUSH    EDI
  2408.         MOV     ESI,EDX
  2409.         MOV     EDI,EAX
  2410.         MOV     EDX,ECX
  2411.         CMP     EDI,ESI
  2412.         JG      @@1
  2413.         JE      @@2
  2414.         SHR     ECX,2
  2415.         REP     MOVSD
  2416.         MOV     ECX,EDX
  2417.         AND     ECX,3
  2418.         REP     MOVSB
  2419.         JMP     @@2
  2420. @@1:    LEA     ESI,[ESI+ECX-1]
  2421.         LEA     EDI,[EDI+ECX-1]
  2422.         AND     ECX,3
  2423.         STD
  2424.         REP     MOVSB
  2425.         SUB     ESI,3
  2426.         SUB     EDI,3
  2427.         MOV     ECX,EDX
  2428.         SHR     ECX,2
  2429.         REP     MOVSD
  2430.         CLD
  2431. @@2:    POP     EDI
  2432.         POP     ESI
  2433. end;
  2434.  
  2435. function StrCopy(Dest, Source: PChar): PChar; assembler;
  2436. asm
  2437.         PUSH    EDI
  2438.         PUSH    ESI
  2439.         MOV     ESI,EAX
  2440.         MOV     EDI,EDX
  2441.         MOV     ECX,0FFFFFFFFH
  2442.         XOR     AL,AL
  2443.         REPNE   SCASB
  2444.         NOT     ECX
  2445.         MOV     EDI,ESI
  2446.         MOV     ESI,EDX
  2447.         MOV     EDX,ECX
  2448.         MOV     EAX,EDI
  2449.         SHR     ECX,2
  2450.         REP     MOVSD
  2451.         MOV     ECX,EDX
  2452.         AND     ECX,3
  2453.         REP     MOVSB
  2454.         POP     ESI
  2455.         POP     EDI
  2456. end;
  2457.  
  2458. function StrECopy(Dest, Source: PChar): PChar; assembler;
  2459. asm
  2460.         PUSH    EDI
  2461.         PUSH    ESI
  2462.         MOV     ESI,EAX
  2463.         MOV     EDI,EDX
  2464.         MOV     ECX,0FFFFFFFFH
  2465.         XOR     AL,AL
  2466.         REPNE   SCASB
  2467.         NOT     ECX
  2468.         MOV     EDI,ESI
  2469.         MOV     ESI,EDX
  2470.         MOV     EDX,ECX
  2471.         SHR     ECX,2
  2472.         REP     MOVSD
  2473.         MOV     ECX,EDX
  2474.         AND     ECX,3
  2475.         REP     MOVSB
  2476.         LEA     EAX,[EDI-1]
  2477.         POP     ESI
  2478.         POP     EDI
  2479. end;
  2480.  
  2481. function StrLCopy(Dest, Source: PChar; MaxLen: Cardinal): PChar; assembler;
  2482. asm
  2483.         PUSH    EDI
  2484.         PUSH    ESI
  2485.         PUSH    EBX
  2486.         MOV     ESI,EAX
  2487.         MOV     EDI,EDX
  2488.         MOV     EBX,ECX
  2489.         XOR     AL,AL
  2490.         TEST    ECX,ECX
  2491.         JZ      @@1
  2492.         REPNE   SCASB
  2493.         JNE     @@1
  2494.         INC     ECX
  2495. @@1:    SUB     EBX,ECX
  2496.         MOV     EDI,ESI
  2497.         MOV     ESI,EDX
  2498.         MOV     EDX,EDI
  2499.         MOV     ECX,EBX
  2500.         SHR     ECX,2
  2501.         REP     MOVSD
  2502.         MOV     ECX,EBX
  2503.         AND     ECX,3
  2504.         REP     MOVSB
  2505.         STOSB
  2506.         MOV     EAX,EDX
  2507.         POP     EBX
  2508.         POP     ESI
  2509.         POP     EDI
  2510. end;
  2511.  
  2512. function StrPCopy(Dest: PChar; const Source: string): PChar;
  2513. begin
  2514.   Result := StrLCopy(Dest, PChar(Source), 255);
  2515. end;
  2516.  
  2517. function StrPLCopy(Dest: PChar; const Source: string;
  2518.   MaxLen: Cardinal): PChar;
  2519. begin
  2520.   Result := StrLCopy(Dest, PChar(Source), MaxLen);
  2521. end;
  2522.  
  2523. function StrCat(Dest, Source: PChar): PChar;
  2524. begin
  2525.   StrCopy(StrEnd(Dest), Source);
  2526.   Result := Dest;
  2527. end;
  2528.  
  2529. function StrLCat(Dest, Source: PChar; MaxLen: Cardinal): PChar; assembler;
  2530. asm
  2531.         PUSH    EDI
  2532.         PUSH    ESI
  2533.         PUSH    EBX
  2534.         MOV     EDI,Dest
  2535.         MOV     ESI,Source
  2536.         MOV     EBX,MaxLen
  2537.         CALL    StrEnd
  2538.         MOV     ECX,EDI
  2539.         ADD     ECX,EBX
  2540.         SUB     ECX,EAX
  2541.         JBE     @@1
  2542.         MOV     EDX,ESI
  2543.         CALL    StrLCopy
  2544. @@1:    MOV     EAX,EDI
  2545.         POP     EBX
  2546.         POP     ESI
  2547.         POP     EDI
  2548. end;
  2549.  
  2550. function StrComp(Str1, Str2: PChar): Integer; assembler;
  2551. asm
  2552.         PUSH    EDI
  2553.         PUSH    ESI
  2554.         MOV     EDI,EDX
  2555.         MOV     ESI,EAX
  2556.         MOV     ECX,0FFFFFFFFH
  2557.         XOR     EAX,EAX
  2558.         REPNE   SCASB
  2559.         NOT     ECX
  2560.         MOV     EDI,EDX
  2561.         XOR     EDX,EDX
  2562.         REPE    CMPSB
  2563.         MOV     AL,[ESI-1]
  2564.         MOV     DL,[EDI-1]
  2565.         SUB     EAX,EDX
  2566.         POP     ESI
  2567.         POP     EDI
  2568. end;
  2569.  
  2570. function StrIComp(Str1, Str2: PChar): Integer; assembler;
  2571. asm
  2572.         PUSH    EDI
  2573.         PUSH    ESI
  2574.         MOV     EDI,EDX
  2575.         MOV     ESI,EAX
  2576.         MOV     ECX,0FFFFFFFFH
  2577.         XOR     EAX,EAX
  2578.         REPNE   SCASB
  2579.         NOT     ECX
  2580.         MOV     EDI,EDX
  2581.         XOR     EDX,EDX
  2582. @@1:    REPE    CMPSB
  2583.         JE      @@4
  2584.         MOV     AL,[ESI-1]
  2585.         CMP     AL,'a'
  2586.         JB      @@2
  2587.         CMP     AL,'z'
  2588.         JA      @@2
  2589.         SUB     AL,20H
  2590. @@2:    MOV     DL,[EDI-1]
  2591.         CMP     DL,'a'
  2592.         JB      @@3
  2593.         CMP     DL,'z'
  2594.         JA      @@3
  2595.         SUB     DL,20H
  2596. @@3:    SUB     EAX,EDX
  2597.         JE      @@1
  2598. @@4:    POP     ESI
  2599.         POP     EDI
  2600. end;
  2601.  
  2602. function StrLComp(Str1, Str2: PChar; MaxLen: Cardinal): Integer; assembler;
  2603. asm
  2604.         PUSH    EDI
  2605.         PUSH    ESI
  2606.         PUSH    EBX
  2607.         MOV     EDI,EDX
  2608.         MOV     ESI,EAX
  2609.         MOV     EBX,ECX
  2610.         XOR     EAX,EAX
  2611.         OR      ECX,ECX
  2612.         JE      @@1
  2613.         REPNE   SCASB
  2614.         SUB     EBX,ECX
  2615.         MOV     ECX,EBX
  2616.         MOV     EDI,EDX
  2617.         XOR     EDX,EDX
  2618.         REPE    CMPSB
  2619.         MOV     AL,[ESI-1]
  2620.         MOV     DL,[EDI-1]
  2621.         SUB     EAX,EDX
  2622. @@1:    POP     EBX
  2623.         POP     ESI
  2624.         POP     EDI
  2625. end;
  2626.  
  2627. function StrLIComp(Str1, Str2: PChar; MaxLen: Cardinal): Integer; assembler;
  2628. asm
  2629.         PUSH    EDI
  2630.         PUSH    ESI
  2631.         PUSH    EBX
  2632.         MOV     EDI,EDX
  2633.         MOV     ESI,EAX
  2634.         MOV     EBX,ECX
  2635.         XOR     EAX,EAX
  2636.         OR      ECX,ECX
  2637.         JE      @@4
  2638.         REPNE   SCASB
  2639.         SUB     EBX,ECX
  2640.         MOV     ECX,EBX
  2641.         MOV     EDI,EDX
  2642.         XOR     EDX,EDX
  2643. @@1:    REPE    CMPSB
  2644.         JE      @@4
  2645.         MOV     AL,[ESI-1]
  2646.         CMP     AL,'a'
  2647.         JB      @@2
  2648.         CMP     AL,'z'
  2649.         JA      @@2
  2650.         SUB     AL,20H
  2651. @@2:    MOV     DL,[EDI-1]
  2652.         CMP     DL,'a'
  2653.         JB      @@3
  2654.         CMP     DL,'z'
  2655.         JA      @@3
  2656.         SUB     DL,20H
  2657. @@3:    SUB     EAX,EDX
  2658.         JE      @@1
  2659. @@4:    POP     EBX
  2660.         POP     ESI
  2661.         POP     EDI
  2662. end;
  2663.  
  2664. function StrScan(Str: PChar; Chr: Char): PChar; assembler;
  2665. asm
  2666.         PUSH    EDI
  2667.         PUSH    EAX
  2668.         MOV     EDI,Str
  2669.         MOV     ECX,0FFFFFFFFH
  2670.         XOR     AL,AL
  2671.         REPNE   SCASB
  2672.         NOT     ECX
  2673.         POP     EDI
  2674.         MOV     AL,Chr
  2675.         REPNE   SCASB
  2676.         MOV     EAX,0
  2677.         JNE     @@1
  2678.         MOV     EAX,EDI
  2679.         DEC     EAX
  2680. @@1:    POP     EDI
  2681. end;
  2682.  
  2683. function StrRScan(Str: PChar; Chr: Char): PChar; assembler;
  2684. asm
  2685.         PUSH    EDI
  2686.         MOV     EDI,Str
  2687.         MOV     ECX,0FFFFFFFFH
  2688.         XOR     AL,AL
  2689.         REPNE   SCASB
  2690.         NOT     ECX
  2691.         STD
  2692.         DEC     EDI
  2693.         MOV     AL,Chr
  2694.         REPNE   SCASB
  2695.         MOV     EAX,0
  2696.         JNE     @@1
  2697.         MOV     EAX,EDI
  2698.         INC     EAX
  2699. @@1:    CLD
  2700.         POP     EDI
  2701. end;
  2702.  
  2703. function StrPos(Str1, Str2: PChar): PChar; assembler;
  2704. asm
  2705.         PUSH    EDI
  2706.         PUSH    ESI
  2707.         PUSH    EBX
  2708.         MOV     EBX,EAX
  2709.         MOV     EDI,EDX
  2710.         XOR     AL,AL
  2711.         MOV     ECX,0FFFFFFFFH
  2712.         REPNE   SCASB
  2713.         NOT     ECX
  2714.         DEC     ECX
  2715.         JE      @@2
  2716.         MOV     ESI,ECX
  2717.         MOV     EDI,EBX
  2718.         MOV     ECX,0FFFFFFFFH
  2719.         REPNE   SCASB
  2720.         NOT     ECX
  2721.         SUB     ECX,ESI
  2722.         JBE     @@2
  2723.         MOV     EDI,EBX
  2724.         LEA     EBX,[ESI-1]
  2725. @@1:    MOV     ESI,EDX
  2726.         LODSB
  2727.         REPNE   SCASB
  2728.         JNE     @@2
  2729.         MOV     EAX,ECX
  2730.         PUSH    EDI
  2731.         MOV     ECX,EBX
  2732.         REPE    CMPSB
  2733.         POP     EDI
  2734.         MOV     ECX,EAX
  2735.         JNE     @@1
  2736.         LEA     EAX,[EDI-1]
  2737.         JMP     @@3
  2738. @@2:    XOR     EAX,EAX
  2739. @@3:    POP     EBX
  2740.         POP     ESI
  2741.         POP     EDI
  2742. end;
  2743.  
  2744. function StrUpper(Str: PChar): PChar; assembler;
  2745. asm
  2746.         PUSH    ESI
  2747.         MOV     ESI,Str
  2748.         MOV     EDX,Str
  2749. @@1:    LODSB
  2750.         OR      AL,AL
  2751.         JE      @@2
  2752.         CMP     AL,'a'
  2753.         JB      @@1
  2754.         CMP     AL,'z'
  2755.         JA      @@1
  2756.         SUB     AL,20H
  2757.         MOV     [ESI-1],AL
  2758.         JMP     @@1
  2759. @@2:    XCHG    EAX,EDX
  2760.         POP     ESI
  2761. end;
  2762.  
  2763. function StrLower(Str: PChar): PChar; assembler;
  2764. asm
  2765.         PUSH    ESI
  2766.         MOV     ESI,Str
  2767.         MOV     EDX,Str
  2768. @@1:    LODSB
  2769.         OR      AL,AL
  2770.         JE      @@2
  2771.         CMP     AL,'A'
  2772.         JB      @@1
  2773.         CMP     AL,'Z'
  2774.         JA      @@1
  2775.         ADD     AL,20H
  2776.         MOV     [ESI-1],AL
  2777.         JMP     @@1
  2778. @@2:    XCHG    EAX,EDX
  2779.         POP     ESI
  2780. end;
  2781.  
  2782. function StrPas(Str: PChar): string;
  2783. begin
  2784.   Result := Str;
  2785. end;
  2786.  
  2787. function StrAlloc(Size: Cardinal): PChar;
  2788. begin
  2789.   Inc(Size, SizeOf(Cardinal));
  2790.   GetMem(Result, Size);
  2791.   Cardinal(Pointer(Result)^) := Size;
  2792.   Inc(Result, SizeOf(Cardinal));
  2793. end;
  2794.  
  2795. function StrBufSize(Str: PChar): Cardinal;
  2796. begin
  2797.   Dec(Str, SizeOf(Cardinal));
  2798.   Result := Cardinal(Pointer(Str)^) - SizeOf(Cardinal);
  2799. end;
  2800.  
  2801. function StrNew(Str: PChar): PChar;
  2802. var
  2803.   Size: Cardinal;
  2804. begin
  2805.   if Str = nil then Result := nil else
  2806.   begin
  2807.     Size := StrLen(Str) + 1;
  2808.     Result := StrMove(StrAlloc(Size), Str, Size);
  2809.   end;
  2810. end;
  2811.  
  2812. procedure StrDispose(Str: PChar);
  2813. begin
  2814.   if Str <> nil then
  2815.   begin
  2816.     Dec(Str, SizeOf(Cardinal));
  2817.     FreeMem(Str, Cardinal(Pointer(Str)^));
  2818.   end;
  2819. end;
  2820.  
  2821. { String formatting routines }
  2822.  
  2823. procedure FormatError(ErrorCode: Integer; Format: PChar; FmtLen: Cardinal);
  2824. var
  2825.   Buffer: array[0..31] of Char;
  2826. begin
  2827.   if FmtLen > 31 then FmtLen := 31;
  2828.   StrMove(Buffer, Format, FmtLen);
  2829.   Buffer[FmtLen] := #0;
  2830.   ConvertErrorFmt(SInvalidFormat + ErrorCode, [PChar(@Buffer)]);
  2831. end;
  2832.  
  2833. procedure FormatVarToStr(var S: string; const V: Variant);
  2834. begin
  2835.   S := V;
  2836. end;
  2837.  
  2838. procedure FormatClearStr(var S: string);
  2839. begin
  2840.   S := '';
  2841. end;
  2842.  
  2843. function FormatBuf(var Buffer; BufLen: Cardinal; const Format;
  2844.   FmtLen: Cardinal; const Args: array of const): Cardinal;
  2845. const
  2846.   C10000: Single = 10000;
  2847. var
  2848.   ArgIndex, Width, Prec: Integer;
  2849.   BufferOrg, FormatOrg, FormatPtr, TempStr: PChar;
  2850.   JustFlag: Byte;
  2851.   StrBuf: array[0..39] of Char;
  2852. asm
  2853.         PUSH    EBX
  2854.         PUSH    ESI
  2855.         PUSH    EDI
  2856.         MOV     EDI,EAX
  2857.         MOV     ESI,ECX
  2858.         ADD     ECX,FmtLen
  2859.         MOV     BufferOrg,EDI
  2860.         XOR     EAX,EAX
  2861.         MOV     ArgIndex,EAX
  2862.         MOV     TempStr,EAX
  2863.  
  2864. @Loop:
  2865.         OR      EDX,EDX
  2866.         JE      @Done
  2867.  
  2868. @NextChar:
  2869.         CMP     ESI,ECX
  2870.         JE      @Done
  2871.         LODSB
  2872.         CMP     AL,'%'
  2873.         JE      @Format
  2874.  
  2875. @StoreChar:
  2876.         STOSB
  2877.         DEC     EDX
  2878.         JNE     @NextChar
  2879.  
  2880. @Done:
  2881.         MOV     EAX,EDI
  2882.         SUB     EAX,BufferOrg
  2883.         JMP     @Exit
  2884.  
  2885. @Format:
  2886.         CMP     ESI,ECX
  2887.         JE      @Done
  2888.         LODSB
  2889.         CMP     AL,'%'
  2890.         JE      @StoreChar
  2891.         LEA     EBX,[ESI-2]
  2892.         MOV     FormatOrg,EBX
  2893. @A0:    MOV     JustFlag,AL
  2894.         CMP     AL,'-'
  2895.         JNE     @A1
  2896.         CMP     ESI,ECX
  2897.         JE      @Done
  2898.         LODSB
  2899. @A1:    CALL    @Specifier
  2900.         CMP     AL,':'
  2901.         JNE     @A2
  2902.         MOV     ArgIndex,EBX
  2903.         CMP     ESI,ECX
  2904.         JE      @Done
  2905.         LODSB
  2906.         JMP     @A0
  2907. @A2:    MOV     Width,EBX
  2908.         MOV     EBX,-1
  2909.         CMP     AL,'.'
  2910.         JNE     @A3
  2911.         CMP     ESI,ECX
  2912.         JE      @Done
  2913.         LODSB
  2914.         CALL    @Specifier
  2915. @A3:    MOV     Prec,EBX
  2916.         MOV     FormatPtr,ESI
  2917.         PUSH    ECX
  2918.         PUSH    EDX
  2919.         CALL    @Convert
  2920.         POP     EDX
  2921.         MOV     EBX,Width
  2922.         SUB     EBX,ECX
  2923.         JAE     @A4
  2924.         XOR     EBX,EBX
  2925. @A4:    CMP     JustFlag,'-'
  2926.         JNE     @A6
  2927.         SUB     EDX,ECX
  2928.         JAE     @A5
  2929.         ADD     ECX,EDX
  2930.         XOR     EDX,EDX
  2931. @A5:    REP     MOVSB
  2932. @A6:    XCHG    EBX,ECX
  2933.         SUB     EDX,ECX
  2934.         JAE     @A7
  2935.         ADD     ECX,EDX
  2936.         XOR     EDX,EDX
  2937. @A7:    MOV     AL,' '
  2938.         REP     STOSB
  2939.         XCHG    EBX,ECX
  2940.         SUB     EDX,ECX
  2941.         JAE     @A8
  2942.         ADD     ECX,EDX
  2943.         XOR     EDX,EDX
  2944. @A8:    REP     MOVSB
  2945.         CMP     TempStr,0
  2946.         JE      @A9
  2947.         PUSH    EDX
  2948.         LEA     EAX,TempStr
  2949.         CALL    FormatClearStr
  2950.         POP     EDX
  2951. @A9:    POP     ECX
  2952.         MOV     ESI,FormatPtr
  2953.         JMP     @Loop
  2954.  
  2955. @Specifier:
  2956.         XOR     EBX,EBX
  2957.         CMP     AL,'*'
  2958.         JE      @B3
  2959. @B1:    CMP     AL,'0'
  2960.         JB      @B5
  2961.         CMP     AL,'9'
  2962.         JA      @B5
  2963.         IMUL    EBX,EBX,10
  2964.         SUB     AL,'0'
  2965.         MOVZX   EAX,AL
  2966.         ADD     EBX,EAX
  2967.         CMP     ESI,ECX
  2968.         JE      @B2
  2969.         LODSB
  2970.         JMP     @B1
  2971. @B2:    POP     EAX
  2972.         JMP     @Done
  2973. @B3:    MOV     EAX,ArgIndex
  2974.         CMP     EAX,Args.Integer[-4]
  2975.         JA      @B4
  2976.         INC     ArgIndex
  2977.         MOV     EBX,Args
  2978.         CMP     [EBX+EAX*8].Byte[4],vtInteger
  2979.         MOV     EBX,[EBX+EAX*8]
  2980.         JE      @B4
  2981.         XOR     EBX,EBX
  2982. @B4:    CMP     ESI,ECX
  2983.         JE      @B2
  2984.         LODSB
  2985. @B5:    RET
  2986.  
  2987. @Convert:
  2988.         AND     AL,0DFH
  2989.         MOV     CL,AL
  2990.         MOV     EAX,1
  2991.         MOV     EBX,ArgIndex
  2992.         CMP     EBX,Args.Integer[-4]
  2993.         JA      @ErrorExit
  2994.         INC     ArgIndex
  2995.         MOV     ESI,Args
  2996.         LEA     ESI,[ESI+EBX*8]
  2997.         MOV     EAX,[ESI].Integer[0]
  2998.         MOVZX   EBX,[ESI].Byte[4]
  2999.         JMP     @CvtVector.Pointer[EBX*4]
  3000.  
  3001. @CvtVector:
  3002.         DD      @CvtInteger
  3003.         DD      @CvtBoolean
  3004.         DD      @CvtChar
  3005.         DD      @CvtExtended
  3006.         DD      @CvtShortStr
  3007.         DD      @CvtPointer
  3008.         DD      @CvtPChar
  3009.         DD      @CvtObject
  3010.         DD      @CvtClass
  3011.         DD      @CvtWideChar
  3012.         DD      @CvtPWideChar
  3013.         DD      @CvtAnsiStr
  3014.         DD      @CvtCurrency
  3015.         DD      @CvtVariant
  3016.  
  3017. @CvtBoolean:
  3018. @CvtObject:
  3019. @CvtClass:
  3020. @CvtWideChar:
  3021. @CvtPWideChar:
  3022. @CvtError:
  3023.         XOR     EAX,EAX
  3024.  
  3025. @ErrorExit:
  3026.         MOV     EDX,FormatOrg
  3027.         MOV     ECX,FormatPtr
  3028.         SUB     ECX,EDX
  3029.         CALL    FormatError
  3030.  
  3031. @CvtInteger:
  3032.         CMP     CL,'D'
  3033.         JE      @C1
  3034.         CMP     CL,'U'
  3035.         JE      @C2
  3036.         CMP     CL,'X'
  3037.         JNE     @CvtError
  3038.         MOV     ECX,16
  3039.         JMP     @CvtLong
  3040. @C1:    OR      EAX,EAX
  3041.         JNS     @C2
  3042.         NEG     EAX
  3043.         CALL    @C2
  3044.         MOV     AL,'-'
  3045.         INC     ECX
  3046.         DEC     ESI
  3047.         MOV     [ESI],AL
  3048.         RET
  3049. @C2:    MOV     ECX,10
  3050.  
  3051. @CvtLong:
  3052.         LEA     ESI,StrBuf[16]
  3053. @D1:    XOR     EDX,EDX
  3054.         DIV     ECX
  3055.         ADD     DL,'0'
  3056.         CMP     DL,'0'+10
  3057.         JB      @D2
  3058.         ADD     DL,'A'-'0'-10
  3059. @D2:    DEC     ESI
  3060.         MOV     [ESI],DL
  3061.         OR      EAX,EAX
  3062.         JNE     @D1
  3063.         LEA     ECX,StrBuf[16]
  3064.         SUB     ECX,ESI
  3065.         MOV     EDX,Prec
  3066.         CMP     EDX,16
  3067.         JB      @D3
  3068.         RET
  3069. @D3:    SUB     EDX,ECX
  3070.         JBE     @D5
  3071.         ADD     ECX,EDX
  3072.         MOV     AL,'0'
  3073. @D4:    DEC     ESI
  3074.         MOV     [ESI],AL
  3075.         DEC     EDX
  3076.         JNE     @D4
  3077. @D5:    RET
  3078.  
  3079. @CvtChar:
  3080.         CMP     CL,'S'
  3081.         JNE     @CvtError
  3082.         MOV     ECX,1
  3083.         RET
  3084.  
  3085. @CvtVariant:
  3086.         CMP     CL,'S'
  3087.         JNE     @CvtError
  3088.         CMP     [EAX].TVarData.VType,varNull
  3089.         JBE     @CvtEmptyStr
  3090.         MOV     EDX,EAX
  3091.         LEA     EAX,TempStr
  3092.         CALL    FormatVarToStr
  3093.         MOV     ESI,TempStr
  3094.         JMP     @CvtStrRef
  3095.  
  3096. @CvtEmptyStr:
  3097.         XOR     ECX,ECX
  3098.         RET
  3099.  
  3100. @CvtShortStr:
  3101.         CMP     CL,'S'
  3102.         JNE     @CvtError
  3103.         MOV     ESI,EAX
  3104.         LODSB
  3105.         MOVZX   ECX,AL
  3106.         JMP     @CvtStrLen
  3107.  
  3108. @CvtAnsiStr:
  3109.         CMP     CL,'S'
  3110.         JNE     @CvtError
  3111.         MOV     ESI,EAX
  3112.  
  3113. @CvtStrRef:
  3114.         OR      ESI,ESI
  3115.         JE      @CvtEmptyStr
  3116.         MOV     ECX,[ESI-4]
  3117.  
  3118. @CvtStrLen:
  3119.         CMP     ECX,Prec
  3120.         JA      @E1
  3121.         RET
  3122. @E1:    MOV     ECX,Prec
  3123.         RET
  3124.  
  3125. @CvtPChar:
  3126.         CMP     CL,'S'
  3127.         JNE     @CvtError
  3128.         MOV     ESI,EAX
  3129.         PUSH    EDI
  3130.         MOV     EDI,EAX
  3131.         XOR     AL,AL
  3132.         MOV     ECX,Prec
  3133.         JECXZ   @F1
  3134.         REPNE   SCASB
  3135.         JNE     @F1
  3136.         DEC     EDI
  3137. @F1:    MOV     ECX,EDI
  3138.         SUB     ECX,ESI
  3139.         POP     EDI
  3140.         RET
  3141.  
  3142. @CvtPointer:
  3143.         CMP     CL,'P'
  3144.         JNE     @CvtError
  3145.         MOV     Prec,8
  3146.         MOV     ECX,16
  3147.         JMP     @CvtLong
  3148.  
  3149. @CvtCurrency:
  3150.         MOV     BH,fvCurrency
  3151.         JMP     @CvtFloat
  3152.  
  3153. @CvtExtended:
  3154.         MOV     BH,fvExtended
  3155.  
  3156. @CvtFloat:
  3157.         MOV     ESI,EAX
  3158.         MOV     BL,ffGeneral
  3159.         CMP     CL,'G'
  3160.         JE      @G2
  3161.         MOV     BL,ffExponent
  3162.         CMP     CL,'E'
  3163.         JE      @G2
  3164.         MOV     BL,ffFixed
  3165.         CMP     CL,'F'
  3166.         JE      @G1
  3167.         MOV     BL,ffNumber
  3168.         CMP     CL,'N'
  3169.         JE      @G1
  3170.         CMP     CL,'M'
  3171.         JNE     @CvtError
  3172.         MOV     BL,ffCurrency
  3173. @G1:    MOV     EAX,18
  3174.         MOV     EDX,Prec
  3175.         CMP     EDX,EAX
  3176.         JBE     @G3
  3177.         MOV     EDX,2
  3178.         CMP     CL,'M'
  3179.         JNE     @G3
  3180.         MOVZX   EDX,CurrencyDecimals
  3181.         JMP     @G3
  3182. @G2:    MOV     EAX,Prec
  3183.         MOV     EDX,3
  3184.         CMP     EAX,18
  3185.         JBE     @G3
  3186.         MOV     EAX,15
  3187. @G3:    PUSH    EBX
  3188.         PUSH    EAX
  3189.         PUSH    EDX
  3190.         LEA     EAX,StrBuf
  3191.         MOV     EDX,ESI
  3192.         MOVZX   ECX,BH
  3193.         CALL    FloatToText
  3194.         MOV     ECX,EAX
  3195.         LEA     ESI,StrBuf
  3196.         RET
  3197.  
  3198. @Exit:
  3199.         POP     EDI
  3200.         POP     ESI
  3201.         POP     EBX
  3202. end;
  3203.  
  3204. function StrFmt(Buffer, Format: PChar; const Args: array of const): PChar;
  3205. begin
  3206.   Buffer[FormatBuf(Buffer^, MaxInt, Format^, StrLen(Format), Args)] := #0;
  3207.   Result := Buffer;
  3208. end;
  3209.  
  3210. function StrLFmt(Buffer: PChar; MaxLen: Cardinal; Format: PChar;
  3211.   const Args: array of const): PChar;
  3212. begin
  3213.   Buffer[FormatBuf(Buffer^, MaxLen, Format^, StrLen(Format), Args)] := #0;
  3214.   Result := Buffer;
  3215. end;
  3216.  
  3217. function Format(const Format: string; const Args: array of const): string;
  3218. begin
  3219.   FmtStr(Result, Format, Args);
  3220. end;
  3221.  
  3222. procedure FmtStr(var Result: string; const Format: string;
  3223.   const Args: array of const);
  3224. var
  3225.   Len: Integer;
  3226.   Buffer: array[0..4097] of Char;
  3227. begin
  3228.   Len := FormatBuf(Buffer, SizeOf(Buffer) - 1, Pointer(Format)^,
  3229.     Length(Format), Args);
  3230.   if Len = SizeOf(Buffer) - 1 then ConvertError(SResultTooLong);
  3231.   SetString(Result, Buffer, Len);
  3232. end;
  3233.  
  3234. { Floating point conversion routines }
  3235.  
  3236. {$L FFMT.OBJ}
  3237.  
  3238. procedure FloatToDecimal(var Result: TFloatRec; const Value;
  3239.   ValueType: TFloatValue; Precision, Decimals: Integer); external;
  3240.  
  3241. function FloatToText(Buffer: PChar; const Value; ValueType: TFloatValue;
  3242.   Format: TFloatFormat; Precision, Digits: Integer): Integer; external;
  3243.  
  3244. function FloatToTextFmt(Buffer: PChar; const Value; ValueType: TFloatValue;
  3245.   Format: PChar): Integer; external;
  3246.  
  3247. function TextToFloat(Buffer: PChar; var Value;
  3248.   ValueType: TFloatValue): Boolean; external;
  3249.  
  3250. function FloatToStr(Value: Extended): string;
  3251. var
  3252.   Buffer: array[0..63] of Char;
  3253. begin
  3254.   SetString(Result, Buffer, FloatToText(Buffer, Value, fvExtended,
  3255.     ffGeneral, 15, 0));
  3256. end;
  3257.  
  3258. function CurrToStr(Value: Currency): string;
  3259. var
  3260.   Buffer: array[0..63] of Char;
  3261. begin
  3262.   SetString(Result, Buffer, FloatToText(Buffer, Value, fvCurrency,
  3263.     ffGeneral, 0, 0));
  3264. end;
  3265.  
  3266. function FloatToStrF(Value: Extended; Format: TFloatFormat;
  3267.   Precision, Digits: Integer): string;
  3268. var
  3269.   Buffer: array[0..63] of Char;
  3270. begin
  3271.   SetString(Result, Buffer, FloatToText(Buffer, Value, fvExtended,
  3272.     Format, Precision, Digits));
  3273. end;
  3274.  
  3275. function CurrToStrF(Value: Currency; Format: TFloatFormat;
  3276.   Digits: Integer): string;
  3277. var
  3278.   Buffer: array[0..63] of Char;
  3279. begin
  3280.   SetString(Result, Buffer, FloatToText(Buffer, Value, fvCurrency,
  3281.     Format, 0, Digits));
  3282. end;
  3283.  
  3284. function FormatFloat(const Format: string; Value: Extended): string;
  3285. var
  3286.   Buffer: array[0..255] of Char;
  3287. begin
  3288.   if Length(Format) > SizeOf(Buffer) - 32 then ConvertError(SFormatTooLong);
  3289.   SetString(Result, Buffer, FloatToTextFmt(Buffer, Value, fvExtended,
  3290.     PChar(Format)));
  3291. end;
  3292.  
  3293. function FormatCurr(const Format: string; Value: Currency): string;
  3294. var
  3295.   Buffer: array[0..255] of Char;
  3296. begin
  3297.   if Length(Format) > SizeOf(Buffer) - 32 then ConvertError(SFormatTooLong);
  3298.   SetString(Result, Buffer, FloatToTextFmt(Buffer, Value, fvCurrency,
  3299.     PChar(Format)));
  3300. end;
  3301.  
  3302. function StrToFloat(const S: string): Extended;
  3303. begin
  3304.   if not TextToFloat(PChar(S), Result, fvExtended) then
  3305.     ConvertErrorFmt(SInvalidFloat, [S]);
  3306. end;
  3307.  
  3308. function StrToCurr(const S: string): Currency;
  3309. begin
  3310.   if not TextToFloat(PChar(S), Result, fvCurrency) then
  3311.     ConvertErrorFmt(SInvalidFloat, [S]);
  3312. end;
  3313.  
  3314. { Date/time support routines }
  3315.  
  3316. type
  3317.   PDayTable = ^TDayTable;
  3318.   TDayTable = array[1..12] of Word;
  3319.  
  3320. const
  3321.   FMSecsPerDay: Single = MSecsPerDay;
  3322.   IMSecsPerDay: Integer = MSecsPerDay;
  3323.  
  3324. function DateTimeToTimeStamp(DateTime: TDateTime): TTimeStamp;
  3325. asm
  3326.         MOV     ECX,EAX
  3327.         FLD     DateTime
  3328.         FMUL    FMSecsPerDay
  3329.         SUB     ESP,8
  3330.         FISTP   QWORD PTR [ESP]
  3331.         FWAIT
  3332.         POP     EAX
  3333.         POP     EDX
  3334.         OR      EDX,EDX
  3335.         JNS     @@1
  3336.         NEG     EDX
  3337.         NEG     EAX
  3338.         SBB     EDX,0
  3339.         DIV     IMSecsPerDay
  3340.         NEG     EAX
  3341.         JMP     @@2
  3342. @@1:    DIV     IMSecsPerDay
  3343. @@2:    ADD     EAX,DateDelta
  3344.         MOV     [ECX].TTimeStamp.Time,EDX
  3345.         MOV     [ECX].TTimeStamp.Date,EAX
  3346. end;
  3347.  
  3348. function TimeStampToDateTime(const TimeStamp: TTimeStamp): TDateTime;
  3349. asm
  3350.         MOV     ECX,[EAX].TTimeStamp.Time
  3351.         MOV     EAX,[EAX].TTimeStamp.Date
  3352.         SUB     EAX,DateDelta
  3353.         IMUL    IMSecsPerDay
  3354.         OR      EDX,EDX
  3355.         JNS     @@1
  3356.         SUB     EAX,ECX
  3357.         SBB     EDX,0
  3358.         JMP     @@2
  3359. @@1:    ADD     EAX,ECX
  3360.         ADC     EDX,0
  3361. @@2:    PUSH    EDX
  3362.         PUSH    EAX
  3363.         FILD    QWORD PTR [ESP]
  3364.         FDIV    FMSecsPerDay
  3365.         ADD     ESP,8
  3366. end;
  3367.  
  3368. function MSecsToTimeStamp(MSecs: Comp): TTimeStamp;
  3369. asm
  3370.         MOV     ECX,EAX
  3371.         MOV     EAX,MSecs.Integer[0]
  3372.         MOV     EDX,MSecs.Integer[4]
  3373.         DIV     IMSecsPerDay
  3374.         MOV     [ECX].TTimeStamp.Time,EDX
  3375.         MOV     [ECX].TTimeStamp.Date,EAX
  3376. end;
  3377.  
  3378. function TimeStampToMSecs(const TimeStamp: TTimeStamp): Comp;
  3379. asm
  3380.         FILD    [EAX].TTimeStamp.Date
  3381.         FMUL    FMSecsPerDay
  3382.         FIADD   [EAX].TTimeStamp.Time
  3383. end;
  3384.  
  3385. { Time encoding and decoding }
  3386.  
  3387. function DoEncodeTime(Hour, Min, Sec, MSec: Word; var Time: TDateTime): Boolean;
  3388. begin
  3389.   Result := False;
  3390.   if (Hour < 24) and (Min < 60) and (Sec < 60) and (MSec < 1000) then
  3391.   begin
  3392.     Time := (Hour * 3600000 + Min * 60000 + Sec * 1000 + MSec) / MSecsPerDay;
  3393.     Result := True;
  3394.   end;
  3395. end;
  3396.  
  3397. function EncodeTime(Hour, Min, Sec, MSec: Word): TDateTime;
  3398. begin
  3399.   if not DoEncodeTime(Hour, Min, Sec, MSec, Result) then
  3400.     ConvertError(STimeEncodeError);
  3401. end;
  3402.  
  3403. procedure DecodeTime(Time: TDateTime; var Hour, Min, Sec, MSec: Word);
  3404. var
  3405.   MinCount, MSecCount: Word;
  3406. begin
  3407.   DivMod(DateTimeToTimeStamp(Time).Time, 60000, MinCount, MSecCount);
  3408.   DivMod(MinCount, 60, Hour, Min);
  3409.   DivMod(MSecCount, 1000, Sec, MSec);
  3410. end;
  3411.  
  3412. { Date encoding and decoding }
  3413.  
  3414. function IsLeapYear(Year: Word): Boolean;
  3415. begin
  3416.   Result := (Year mod 4 = 0) and ((Year mod 100 <> 0) or (Year mod 400 = 0));
  3417. end;
  3418.  
  3419. function GetDayTable(Year: Word): PDayTable;
  3420. const
  3421.   DayTable1: TDayTable = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
  3422.   DayTable2: TDayTable = (31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
  3423.   DayTables: array[Boolean] of PDayTable = (@DayTable1, @DayTable2);
  3424. begin
  3425.   Result := DayTables[IsLeapYear(Year)];
  3426. end;
  3427.  
  3428. function DoEncodeDate(Year, Month, Day: Word; var Date: TDateTime): Boolean;
  3429. var
  3430.   I: Integer;
  3431.   DayTable: PDayTable;
  3432. begin
  3433.   Result := False;
  3434.   DayTable := GetDayTable(Year);
  3435.   if (Year >= 1) and (Year <= 9999) and (Month >= 1) and (Month <= 12) and
  3436.     (Day >= 1) and (Day <= DayTable^[Month]) then
  3437.   begin
  3438.     for I := 1 to Month - 1 do Inc(Day, DayTable^[I]);
  3439.     I := Year - 1;
  3440.     Date := I * 365 + I div 4 - I div 100 + I div 400 + Day - DateDelta;
  3441.     Result := True;
  3442.   end;
  3443. end;
  3444.  
  3445. function EncodeDate(Year, Month, Day: Word): TDateTime;
  3446. begin
  3447.   if not DoEncodeDate(Year, Month, Day, Result) then
  3448.     ConvertError(SDateEncodeError);
  3449. end;
  3450.  
  3451. procedure DecodeDate(Date: TDateTime; var Year, Month, Day: Word);
  3452. const
  3453.   D1 = 365;
  3454.   D4 = D1 * 4 + 1;
  3455.   D100 = D4 * 25 - 1;
  3456.   D400 = D100 * 4 + 1;
  3457. var
  3458.   Y, M, D, I: Word;
  3459.   T: Integer;
  3460.   DayTable: PDayTable;
  3461. begin
  3462.   T := DateTimeToTimeStamp(Date).Date;
  3463.   if T <= 0 then
  3464.   begin
  3465.     Year := 0;
  3466.     Month := 0;
  3467.     Day := 0;
  3468.   end else
  3469.   begin
  3470.     Dec(T);
  3471.     Y := 1;
  3472.     while T >= D400 do
  3473.     begin
  3474.       Dec(T, D400);
  3475.       Inc(Y, 400);
  3476.     end;
  3477.     DivMod(T, D100, I, D);
  3478.     if I = 4 then
  3479.     begin
  3480.       Dec(I);
  3481.       Inc(D, D100);
  3482.     end;
  3483.     Inc(Y, I * 100);
  3484.     DivMod(D, D4, I, D);
  3485.     Inc(Y, I * 4);
  3486.     DivMod(D, D1, I, D);
  3487.     if I = 4 then
  3488.     begin
  3489.       Dec(I);
  3490.       Inc(D, D1);
  3491.     end;
  3492.     Inc(Y, I);
  3493.     DayTable := GetDayTable(Y);
  3494.     M := 1;
  3495.     while True do
  3496.     begin
  3497.       I := DayTable^[M];
  3498.       if D < I then Break;
  3499.       Dec(D, I);
  3500.       Inc(M);
  3501.     end;
  3502.     Year := Y;
  3503.     Month := M;
  3504.     Day := D + 1;
  3505.   end;
  3506. end;
  3507.  
  3508. function DayOfWeek(Date: TDateTime): Integer;
  3509. begin
  3510.   Result := DateTimeToTimeStamp(Date).Date mod 7 + 1;
  3511. end;
  3512.  
  3513. function Date: TDateTime;
  3514. var
  3515.   SystemTime: TSystemTime;
  3516. begin
  3517.   GetLocalTime(SystemTime);
  3518.   with SystemTime do Result := EncodeDate(wYear, wMonth, wDay);
  3519. end;
  3520.  
  3521. function Time: TDateTime;
  3522. var
  3523.   SystemTime: TSystemTime;
  3524. begin
  3525.   GetLocalTime(SystemTime);
  3526.   with SystemTime do
  3527.     Result := EncodeTime(wHour, wMinute, wSecond, wMilliSeconds);
  3528. end;
  3529.  
  3530. function Now: TDateTime;
  3531. begin
  3532.   Result := Date + Time;
  3533. end;
  3534.  
  3535. function CurrentYear: Word;
  3536. var
  3537.   SystemTime: TSystemTime;
  3538. begin
  3539.   GetLocalTime(SystemTime);
  3540.   Result := SystemTime.wYear;
  3541. end;
  3542.  
  3543. { Date/time to string conversions }
  3544.  
  3545. procedure DateTimeToString(var Result: string; const Format: string;
  3546.   DateTime: TDateTime);
  3547. var
  3548.   BufPos, AppendLevel: Integer;
  3549.   Buffer: array[0..255] of Char;
  3550.  
  3551.   procedure AppendChars(P: PChar; Count: Integer);
  3552.   var
  3553.     N: Integer;
  3554.   begin
  3555.     N := SizeOf(Buffer) - BufPos;
  3556.     if N > Count then N := Count;
  3557.     if N <> 0 then Move(P[0], Buffer[BufPos], N);
  3558.     Inc(BufPos, N);
  3559.   end;
  3560.  
  3561.   procedure AppendString(const S: string);
  3562.   begin
  3563.     AppendChars(Pointer(S), Length(S));
  3564.   end;
  3565.  
  3566.   procedure AppendNumber(Number, Digits: Integer);
  3567.   const
  3568.     Format: array[0..3] of Char = '%.*d';
  3569.   var
  3570.     NumBuf: array[0..15] of Char;
  3571.   begin
  3572.     AppendChars(NumBuf, FormatBuf(NumBuf, SizeOf(NumBuf), Format,
  3573.       SizeOf(Format), [Digits, Number]));
  3574.   end;
  3575.  
  3576.   procedure AppendFormat(Format: PChar);
  3577.   var
  3578.     Starter, Token, LastToken: Char;
  3579.     DateDecoded, TimeDecoded, LastWasHour, Use12HourClock,
  3580.     BetweenQuotes: Boolean;
  3581.     P: PChar;
  3582.     Count: Integer;
  3583.     Year, Month, Day, Hour, Min, Sec, MSec, H: Word;
  3584.  
  3585.     procedure GetCount;
  3586.     var
  3587.       P: PChar;
  3588.     begin
  3589.       P := Format;
  3590.       while Format^ = Starter do Inc(Format);
  3591.       Count := Format - P + 1;
  3592.     end;
  3593.  
  3594.     procedure GetDate;
  3595.     begin
  3596.       if not DateDecoded then
  3597.       begin
  3598.         DecodeDate(DateTime, Year, Month, Day);
  3599.         DateDecoded := True;
  3600.       end;
  3601.     end;
  3602.  
  3603.     procedure GetTime;
  3604.     begin
  3605.       if not TimeDecoded then
  3606.       begin
  3607.         DecodeTime(DateTime, Hour, Min, Sec, MSec);
  3608.         TimeDecoded := True;
  3609.       end;
  3610.     end;
  3611.  
  3612.   begin
  3613.     if (Format <> nil) and (AppendLevel < 2) then
  3614.     begin
  3615.       Inc(AppendLevel);
  3616.       LastToken := ' ';
  3617.       DateDecoded := False;
  3618.       TimeDecoded := False;
  3619.       while Format^ <> #0 do
  3620.       begin
  3621.         Starter := Format^;
  3622.         Inc(Format);
  3623.         Token := Starter;
  3624.         if Token in ['a'..'z'] then Dec(Token, 32);
  3625.         if Token in ['A'..'Z'] then
  3626.         begin
  3627.           if (Token = 'M') and (LastToken = 'H') then Token := 'N';
  3628.           LastToken := Token;
  3629.         end;
  3630.         case Token of
  3631.           'Y':
  3632.             begin
  3633.               GetCount;
  3634.               GetDate;
  3635.               if Count <= 2 then
  3636.                 AppendNumber(Year mod 100, 2) else
  3637.                 AppendNumber(Year, 4);
  3638.             end;
  3639.           'M':
  3640.             begin
  3641.               GetCount;
  3642.               GetDate;
  3643.               case Count of
  3644.                 1, 2: AppendNumber(Month, Count);
  3645.                 3: AppendString(ShortMonthNames[Month]);
  3646.               else
  3647.                 AppendString(LongMonthNames[Month]);
  3648.               end;
  3649.             end;
  3650.           'D':
  3651.             begin
  3652.               GetCount;
  3653.               case Count of
  3654.                 1, 2:
  3655.                   begin
  3656.                     GetDate;
  3657.                     AppendNumber(Day, Count);
  3658.                   end;
  3659.                 3: AppendString(ShortDayNames[DayOfWeek(DateTime)]);
  3660.                 4: AppendString(LongDayNames[DayOfWeek(DateTime)]);
  3661.                 5: AppendFormat(Pointer(ShortDateFormat));
  3662.               else
  3663.                 AppendFormat(Pointer(LongDateFormat));
  3664.               end;
  3665.             end;
  3666.           'H':
  3667.             begin
  3668.               GetCount;
  3669.               GetTime;
  3670.               Use12HourClock := False;
  3671.               BetweenQuotes := False;
  3672.               P := Format;
  3673.               while P^ <> #0 do
  3674.               begin
  3675.                 case P^ of
  3676.                   'A', 'a':
  3677.                     if not BetweenQuotes then
  3678.                     begin
  3679.                       Use12HourClock := True;
  3680.                       Break;
  3681.                     end;
  3682.                   'H', 'h':
  3683.                     Break;
  3684.                   '''', '"': BetweenQuotes := not BetweenQuotes;
  3685.                 end;
  3686.                 Inc(P);
  3687.               end;
  3688.               H := Hour;
  3689.               if Use12HourClock then
  3690.                 if H = 0 then H := 12 else if H > 12 then Dec(H, 12);
  3691.               if Count > 2 then Count := 2;
  3692.               AppendNumber(H, Count);
  3693.             end;
  3694.           'N':
  3695.             begin
  3696.               GetCount;
  3697.               GetTime;
  3698.               if Count > 2 then Count := 2;
  3699.               AppendNumber(Min, Count);
  3700.             end;
  3701.           'S':
  3702.             begin
  3703.               GetCount;
  3704.               GetTime;
  3705.               if Count > 2 then Count := 2;
  3706.               AppendNumber(Sec, Count);
  3707.             end;
  3708.           'T':
  3709.             begin
  3710.               GetCount;
  3711.               if Count = 1 then
  3712.                 AppendFormat(Pointer(ShortTimeFormat)) else
  3713.                 AppendFormat(Pointer(LongTimeFormat));
  3714.             end;
  3715.           'A':
  3716.             begin
  3717.               GetTime;
  3718.               P := Format - 1;
  3719.               if StrLIComp(P, 'AM/PM', 5) = 0 then
  3720.               begin
  3721.                 if Hour >= 12 then Inc(P, 3);
  3722.                 AppendChars(P, 2);
  3723.                 Inc(Format, 4);
  3724.               end else
  3725.               if StrLIComp(P, 'A/P', 3) = 0 then
  3726.               begin
  3727.                 if Hour >= 12 then Inc(P, 2);
  3728.                 AppendChars(P, 1);
  3729.                 Inc(Format, 2);
  3730.               end else
  3731.               if StrLIComp(P, 'AMPM', 4) = 0 then
  3732.               begin
  3733.                 if Hour < 12 then
  3734.                   AppendString(TimeAMString) else
  3735.                   AppendString(TimePMString);
  3736.                 Inc(Format, 3);
  3737.               end else
  3738.               AppendChars(@Starter, 1);
  3739.             end;
  3740.           'C':
  3741.             begin
  3742.               GetCount;
  3743.               AppendFormat(Pointer(ShortDateFormat));
  3744.               GetTime;
  3745.               if (Hour <> 0) or (Min <> 0) or (Sec <> 0) then
  3746.               begin
  3747.                 AppendChars(' ', 1);
  3748.                 AppendFormat(Pointer(LongTimeFormat));
  3749.               end;
  3750.             end;
  3751.           '/':
  3752.             AppendChars(@DateSeparator, 1);
  3753.           ':':
  3754.             AppendChars(@TimeSeparator, 1);
  3755.           '''', '"':
  3756.             begin
  3757.               P := Format;
  3758.               while (Format^ <> #0) and (Format^ <> Starter) do Inc(Format);
  3759.               AppendChars(P, Format - P);
  3760.               if Format^ <> #0 then Inc(Format);
  3761.             end;
  3762.         else
  3763.           AppendChars(@Starter, 1);
  3764.         end;
  3765.       end;
  3766.       Dec(AppendLevel);
  3767.     end;
  3768.   end;
  3769.  
  3770. begin
  3771.   BufPos := 0;
  3772.   AppendLevel := 0;
  3773.   if Format <> '' then AppendFormat(Pointer(Format)) else AppendFormat('C');
  3774.   SetString(Result, Buffer, BufPos);
  3775. end;
  3776.  
  3777. function DateToStr(Date: TDateTime): string;
  3778. begin
  3779.   DateTimeToString(Result, ShortDateFormat, Date);
  3780. end;
  3781.  
  3782. function TimeToStr(Time: TDateTime): string;
  3783. begin
  3784.   DateTimeToString(Result, LongTimeFormat, Time);
  3785. end;
  3786.  
  3787. function DateTimeToStr(DateTime: TDateTime): string;
  3788. begin
  3789.   DateTimeToString(Result, '', DateTime);
  3790. end;
  3791.  
  3792. function FormatDateTime(const Format: string; DateTime: TDateTime): string;
  3793. begin
  3794.   DateTimeToString(Result, Format, DateTime);
  3795. end;
  3796.  
  3797. { String to date/time conversions }
  3798.  
  3799. type
  3800.   TDateOrder = (doMDY, doDMY, doYMD);
  3801.  
  3802. procedure ScanBlanks(const S: string; var Pos: Integer);
  3803. var
  3804.   I: Integer;
  3805. begin
  3806.   I := Pos;
  3807.   while (I <= Length(S)) and (S[I] = ' ') do Inc(I);
  3808.   Pos := I;
  3809. end;
  3810.  
  3811. function ScanNumber(const S: string; var Pos: Integer;
  3812.   var Number: Word): Boolean;
  3813. var
  3814.   I: Integer;
  3815.   N: Word;
  3816. begin
  3817.   Result := False;
  3818.   ScanBlanks(S, Pos);
  3819.   I := Pos;
  3820.   N := 0;
  3821.   while (I <= Length(S)) and (S[I] in ['0'..'9']) and (N < 1000) do
  3822.   begin
  3823.     N := N * 10 + (Ord(S[I]) - Ord('0'));
  3824.     Inc(I);
  3825.   end;
  3826.   if I > Pos then
  3827.   begin
  3828.     Pos := I;
  3829.     Number := N;
  3830.     Result := True;
  3831.   end;
  3832. end;
  3833.  
  3834. function ScanString(const S: string; var Pos: Integer;
  3835.   const Symbol: string): Boolean;
  3836. begin
  3837.   Result := False;
  3838.   if Symbol <> '' then
  3839.   begin
  3840.     ScanBlanks(S, Pos);
  3841.     if AnsiCompareText(Symbol, Copy(S, Pos, Length(Symbol))) = 0 then
  3842.     begin
  3843.       Inc(Pos, Length(Symbol));
  3844.       Result := True;
  3845.     end;
  3846.   end;
  3847. end;
  3848.  
  3849. function ScanChar(const S: string; var Pos: Integer; Ch: Char): Boolean;
  3850. begin
  3851.   Result := False;
  3852.   ScanBlanks(S, Pos);
  3853.   if (Pos <= Length(S)) and (S[Pos] = Ch) then
  3854.   begin
  3855.     Inc(Pos);
  3856.     Result := True;
  3857.   end;
  3858. end;
  3859.  
  3860. function GetDateOrder(const DateFormat: string): TDateOrder;
  3861. var
  3862.   I: Integer;
  3863. begin
  3864.   I := 1;
  3865.   while I <= Length(DateFormat) do
  3866.   begin
  3867.     case Chr(Ord(DateFormat[I]) and $DF) of
  3868.       'Y': Result := doYMD;
  3869.       'M': Result := doMDY;
  3870.       'D': Result := doDMY;
  3871.     else
  3872.       Inc(I);
  3873.       Continue;
  3874.     end;
  3875.     Exit;
  3876.   end;
  3877.   Result := doMDY;
  3878. end;
  3879.  
  3880. function ScanDate(const S: string; var Pos: Integer;
  3881.   var Date: TDateTime): Boolean;
  3882. var
  3883.   DateOrder: TDateOrder;
  3884.   I: Integer;
  3885.   N1, N2, N3, Y, M, D: Word;
  3886. begin
  3887.   Result := False;
  3888.   DateOrder := GetDateOrder(ShortDateFormat);
  3889.   if not (ScanNumber(S, Pos, N1) and ScanChar(S, Pos, DateSeparator) and
  3890.     ScanNumber(S, Pos, N2)) then Exit;
  3891.   if ScanChar(S, Pos, DateSeparator) then
  3892.   begin
  3893.     if not ScanNumber(S, Pos, N3) then Exit;
  3894.     case DateOrder of
  3895.       doMDY: begin Y := N3; M := N1; D := N2; end;
  3896.       doDMY: begin Y := N3; M := N2; D := N1; end;
  3897.       doYMD: begin Y := N1; M := N2; D := N3; end;
  3898.     end;
  3899.     if Y <= 99 then Inc(Y, CurrentYear div 100 * 100);
  3900.   end else
  3901.   begin
  3902.     Y := CurrentYear;
  3903.     if DateOrder = doDMY then
  3904.     begin
  3905.       D := N1; M := N2;
  3906.     end else
  3907.     begin
  3908.       M := N1; D := N2;
  3909.     end;
  3910.   end;
  3911.   ScanChar(S, Pos, DateSeparator);
  3912.   ScanBlanks(S, Pos);
  3913.   Result := DoEncodeDate(Y, M, D, Date);
  3914. end;
  3915.  
  3916. function ScanTime(const S: string; var Pos: Integer;
  3917.   var Time: TDateTime): Boolean;
  3918. var
  3919.   BaseHour: Integer;
  3920.   Hour, Min, Sec: Word;
  3921. begin
  3922.   Result := False;
  3923.   if not ScanNumber(S, Pos, Hour) then Exit;
  3924.   Min := 0;
  3925.   if ScanChar(S, Pos, TimeSeparator) then
  3926.     if not ScanNumber(S, Pos, Min) then Exit;
  3927.   Sec := 0;
  3928.   if ScanChar(S, Pos, TimeSeparator) then
  3929.     if not ScanNumber(S, Pos, Sec) then Exit;
  3930.   BaseHour := -1;
  3931.   if ScanString(S, Pos, TimeAMString) or ScanString(S, Pos, 'AM') then
  3932.     BaseHour := 0
  3933.   else
  3934.     if ScanString(S, Pos, TimePMString) or ScanString(S, Pos, 'PM') then
  3935.       BaseHour := 12;
  3936.   if BaseHour >= 0 then
  3937.   begin
  3938.     if (Hour = 0) or (Hour > 12) then Exit;
  3939.     if Hour = 12 then Hour := 0;
  3940.     Inc(Hour, BaseHour);
  3941.   end;
  3942.   ScanBlanks(S, Pos);
  3943.   Result := DoEncodeTime(Hour, Min, Sec, 0, Time);
  3944. end;
  3945.  
  3946. function StrToDate(const S: string): TDateTime;
  3947. var
  3948.   Pos: Integer;
  3949. begin
  3950.   Pos := 1;
  3951.   if not ScanDate(S, Pos, Result) or (Pos <= Length(S)) then
  3952.     ConvertErrorFmt(SInvalidDate, [S]);
  3953. end;
  3954.  
  3955. function StrToTime(const S: string): TDateTime;
  3956. var
  3957.   Pos: Integer;
  3958. begin
  3959.   Pos := 1;
  3960.   if not ScanTime(S, Pos, Result) or (Pos <= Length(S)) then
  3961.     ConvertErrorFmt(SInvalidTime, [S]);
  3962. end;
  3963.  
  3964. function StrToDateTime(const S: string): TDateTime;
  3965. var
  3966.   Pos: Integer;
  3967.   Date, Time: TDateTime;
  3968. begin
  3969.   Pos := 1;
  3970.   Time := 0;
  3971.   if not ScanDate(S, Pos, Date) or not ((Pos > Length(S)) or
  3972.     ScanTime(S, Pos, Time)) then
  3973.     ConvertErrorFmt(SInvalidDateTime, [S]);
  3974.   if Date >= 0 then
  3975.     Result := Date + Time else
  3976.     Result := Date - Time;
  3977. end;
  3978.  
  3979. { System error messages }
  3980.  
  3981. function SysErrorMessage(ErrorCode: Integer): string;
  3982. var
  3983.   Len: Integer;
  3984.   Buffer: array[0..255] of Char;
  3985. begin
  3986.   Len := FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM or
  3987.     FORMAT_MESSAGE_ARGUMENT_ARRAY, nil, ErrorCode, 0, Buffer,
  3988.     SizeOf(Buffer), nil);
  3989.   while (Len > 0) and (Buffer[Len - 1] in [#0..#32, '.']) do Dec(Len);
  3990.   SetString(Result, Buffer, Len);
  3991. end;
  3992.  
  3993. { Initialization file support }
  3994.  
  3995. function GetLocaleStr(Locale, LocaleType: Integer; const Default: string): string;
  3996. var
  3997.   L: Integer;
  3998.   Buffer: array[0..255] of Char;
  3999. begin
  4000.   L := GetLocaleInfo(Locale, LocaleType, Buffer, SizeOf(Buffer));
  4001.   if L > 0 then SetString(Result, Buffer, L - 1) else Result := Default;
  4002. end;
  4003.  
  4004. function GetLocaleChar(Locale, LocaleType: Integer; Default: Char): Char;
  4005. var
  4006.   Buffer: array[0..1] of Char;
  4007. begin
  4008.   if GetLocaleInfo(Locale, LocaleType, Buffer, 2) > 0 then
  4009.     Result := Buffer[0] else
  4010.     Result := Default;
  4011. end;
  4012.  
  4013. procedure GetMonthDayNames;
  4014. var
  4015.   I, Day: Integer;
  4016.   DefaultLCID: LCID;
  4017. begin
  4018.   DefaultLCID := GetSystemDefaultLCID;
  4019.   for I := 1 to 12 do
  4020.   begin
  4021.     ShortMonthNames[I] := GetLocaleStr(DefaultLCID, LOCALE_SABBREVMONTHNAME1 + I - 1,
  4022.       LoadStr(I + (SShortMonthNames - 1)));
  4023.     LongMonthNames[I] := GetLocaleStr(DefaultLCID, LOCALE_SMONTHNAME1 + I - 1,
  4024.       LoadStr(I + (SLongMonthNames - 1)));
  4025.   end;
  4026.   for I := 1 to 7 do
  4027.   begin
  4028.     Day := (I + 5) mod 7;
  4029.     ShortDayNames[I] := GetLocaleStr(DefaultLCID, LOCALE_SABBREVDAYNAME1 + Day,
  4030.       LoadStr(I + (SShortDayNames - 1)));
  4031.     LongDayNames[I] := GetLocaleStr(DefaultLCID, LOCALE_SDAYNAME1 + Day,
  4032.       LoadStr(I + (SLongDayNames - 1)));
  4033.   end;
  4034. end;
  4035.  
  4036. procedure GetFormatSettings;
  4037. var
  4038.   HourFormat, TimePostfix: string;
  4039.   DefaultLCID: LCID;
  4040. begin
  4041.   DefaultLCID := GetSystemDefaultLCID;
  4042.   CurrencyString := GetLocaleStr(DefaultLCID, LOCALE_SCURRENCY, '');
  4043.   CurrencyFormat := StrToIntDef(GetLocaleStr(DefaultLCID, LOCALE_ICURRENCY, '0'), 0);
  4044.   NegCurrFormat := StrToIntDef(GetLocaleStr(DefaultLCID, LOCALE_INEGCURR, '0'), 0);
  4045.   ThousandSeparator := GetLocaleChar(DefaultLCID, LOCALE_STHOUSAND, ',');
  4046.   DecimalSeparator := GetLocaleChar(DefaultLCID, LOCALE_SDECIMAL, '.');
  4047.   CurrencyDecimals := StrToIntDef(GetLocaleStr(DefaultLCID, LOCALE_ICURRDIGITS, '0'), 0);
  4048.   DateSeparator := GetLocaleChar(DefaultLCID, LOCALE_SDATE, '/');
  4049.   ShortDateFormat := GetLocaleStr(DefaultLCID, LOCALE_SSHORTDATE, 'm/d/yy');
  4050.   LongDateFormat := GetLocaleStr(DefaultLCID, LOCALE_SLONGDATE, 'mmmm d, yyyy');
  4051.   TimeSeparator := GetLocaleChar(DefaultLCID, LOCALE_STIME, ':');
  4052.   TimeAMString := GetLocaleStr(DefaultLCID, LOCALE_S1159, 'am');
  4053.   TimePMString := GetLocaleStr(DefaultLCID, LOCALE_S2359, 'pm');
  4054.   if StrToIntDef(GetLocaleStr(DefaultLCID, LOCALE_ITLZERO, '0'), 0) = 0 then
  4055.     HourFormat := 'h' else
  4056.     HourFormat := 'hh';
  4057.   if StrToIntDef(GetLocaleStr(DefaultLCID, LOCALE_ITIME, '0'), 0) = 0 then
  4058.     TimePostfix := ' AMPM' else
  4059.     TimePostfix := '';
  4060.   ShortTimeFormat := HourFormat + ':mm' + TimePostfix;
  4061.   LongTimeFormat := HourFormat + ':mm:ss' + TimePostfix;
  4062. end;
  4063.  
  4064. { Exception handling routines }
  4065.  
  4066. var
  4067.   OutOfMemory: EOutOfMemory;
  4068.  
  4069. type
  4070.   PRaiseFrame = ^TRaiseFrame;
  4071.   TRaiseFrame = record
  4072.     NextRaise: PRaiseFrame;
  4073.     ExceptAddr: Pointer;
  4074.     ExceptObject: TObject;
  4075.     ExceptionRecord: PExceptionRecord;
  4076.   end;
  4077.  
  4078. { Return current exception object }
  4079.  
  4080. function ExceptObject: TObject;
  4081. begin
  4082.   if RaiseList <> nil then
  4083.     Result := PRaiseFrame(RaiseList)^.ExceptObject else
  4084.     Result := nil;
  4085. end;
  4086.  
  4087. { Return current exception address }
  4088.  
  4089. function ExceptAddr: Pointer;
  4090. begin
  4091.   if RaiseList <> nil then
  4092.     Result := PRaiseFrame(RaiseList)^.ExceptAddr else
  4093.     Result := nil;
  4094. end;
  4095.  
  4096. { Convert physical address to logical address }
  4097.  
  4098. function ConvertAddr(Address: Pointer): Pointer; assembler;
  4099. asm
  4100.         TEST    EAX,EAX         { Always convert nil to nil }
  4101.         JE      @@1
  4102.         SUB     EAX,OFFSET TextStart
  4103. @@1:
  4104. end;
  4105.  
  4106. { Display exception message box }
  4107.  
  4108. procedure ShowException(ExceptObject: TObject; ExceptAddr: Pointer);
  4109. var
  4110.   MsgPtr: PChar;
  4111.   MsgEnd: PChar;
  4112.   MsgLen: Integer;
  4113.   ModuleName: array[0..63] of Char;
  4114.   Temp: array[0..63] of Char;
  4115.   Buffer: array[0..255] of Char;
  4116. begin
  4117.   GetModuleFileName(HInstance, Buffer, SizeOf(Buffer));
  4118.   StrLCopy(ModuleName, StrRScan(Buffer, '\') + 1, SizeOf(ModuleName) - 1);
  4119.   MsgPtr := '';
  4120.   MsgEnd := '';
  4121.   if ExceptObject is Exception then
  4122.   begin
  4123.     MsgPtr := PChar(Exception(ExceptObject).Message);
  4124.     MsgLen := StrLen(MsgPtr);
  4125.     if (MsgLen <> 0) and (MsgPtr[MsgLen - 1] <> '.') then MsgEnd := '.';
  4126.   end;
  4127.   LoadString(HInstance, SException, Temp, SizeOf(Temp));
  4128.   StrFmt(Buffer, Temp, [ExceptObject.ClassName, ModuleName,
  4129.     ConvertAddr(ExceptAddr), MsgPtr, MsgEnd]);
  4130.   LoadString(HInstance, SExceptTitle, Temp, SizeOf(Temp));
  4131.   if IsConsole then
  4132.     WriteLn(Buffer)
  4133.   else
  4134.     MessageBox(0, Buffer, Temp, MB_OK or MB_ICONSTOP or MB_TASKMODAL);
  4135. end;
  4136.  
  4137. { Raise abort exception }
  4138.  
  4139. procedure Abort;
  4140.  
  4141.   function ReturnAddr: Pointer;
  4142.   asm
  4143.           MOV     EAX,[ESP+4]
  4144.   end;
  4145.  
  4146. begin
  4147.   raise EAbort.CreateRes(SOperationAborted) at ReturnAddr;
  4148. end;
  4149.  
  4150. { Raise out of memory exception }
  4151.  
  4152. procedure OutOfMemoryError;
  4153. begin
  4154.   raise OutOfMemory;
  4155. end;
  4156.  
  4157. { Exception class }
  4158.  
  4159. constructor Exception.Create(const Msg: string);
  4160. begin
  4161.   FMessage := Msg;
  4162. end;
  4163.  
  4164. constructor Exception.CreateFmt(const Msg: string;
  4165.   const Args: array of const);
  4166. begin
  4167.   FMessage := Format(Msg, Args);
  4168. end;
  4169.  
  4170. constructor Exception.CreateRes(Ident: Integer);
  4171. begin
  4172.   FMessage := LoadStr(Ident);
  4173. end;
  4174.  
  4175. constructor Exception.CreateResFmt(Ident: Integer;
  4176.   const Args: array of const);
  4177. begin
  4178.   FMessage := Format(LoadStr(Ident), Args);
  4179. end;
  4180.  
  4181. constructor Exception.CreateHelp(const Msg: string; AHelpContext: Integer);
  4182. begin
  4183.   FMessage := Msg;
  4184.   FHelpContext := AHelpContext;
  4185. end;
  4186.  
  4187. constructor Exception.CreateFmtHelp(const Msg: string; const Args: array of const;
  4188.   AHelpContext: Integer);
  4189. begin
  4190.   FMessage := Format(Msg, Args);
  4191.   FHelpContext := AHelpContext;
  4192. end;
  4193.  
  4194. constructor Exception.CreateResHelp(Ident: Integer; AHelpContext: Integer);
  4195. begin
  4196.   FMessage := LoadStr(Ident);
  4197.   FHelpContext := AHelpContext;
  4198. end;
  4199.  
  4200. constructor Exception.CreateResFmtHelp(Ident: Integer;
  4201.   const Args: array of const;
  4202.   AHelpContext: Integer);
  4203. begin
  4204.   FMessage := Format(LoadStr(Ident), Args);
  4205.   FHelpContext := AHelpContext;
  4206. end;
  4207.  
  4208. { EOutOfMemory class }
  4209.  
  4210. destructor EOutOfMemory.Destroy;
  4211. begin
  4212. end;
  4213.  
  4214. procedure EOutOfMemory.FreeInstance;
  4215. begin
  4216. end;
  4217.  
  4218. { Create I/O exception }
  4219.  
  4220. function CreateInOutError: EInOutError;
  4221. type
  4222.   TErrorRec = record
  4223.     Code: Integer;
  4224.     Ident: Integer;
  4225.   end;
  4226. const
  4227.   ErrorMap: array[0..6] of TErrorRec = (
  4228.     (Code: 2; Ident: SFileNotFound),
  4229.     (Code: 3; Ident: SInvalidFilename),
  4230.     (Code: 4; Ident: STooManyOpenFiles),
  4231.     (Code: 5; Ident: SAccessDenied),
  4232.     (Code: 100; Ident: SEndOfFile),
  4233.     (Code: 101; Ident: SDiskFull),
  4234.     (Code: 106; Ident: SInvalidInput));
  4235. var
  4236.   I: Integer;
  4237. begin
  4238.   I := Low(ErrorMap);
  4239.   while (I <= High(ErrorMap)) and (ErrorMap[I].Code <> InOutRes) do Inc(I);
  4240.   if I <= High(ErrorMap) then
  4241.     Result := EInOutError.CreateRes(ErrorMap[I].Ident) else
  4242.     Result := EInOutError.CreateResFmt(SInOutError, [InOutRes]);
  4243.   Result.ErrorCode := InOutRes;
  4244.   InOutRes := 0;
  4245. end;
  4246.  
  4247. { RTL error handler }
  4248.  
  4249. type
  4250.   TExceptRec = record
  4251.     EClass: ExceptClass;
  4252.     EIdent: Integer;
  4253.   end;
  4254.  
  4255. const
  4256.   ExceptMap: array[2..21] of TExceptRec = (
  4257.     (EClass: EInvalidPointer; EIdent: SInvalidPointer),
  4258.     (EClass: EDivByZero; EIdent: SDivByZero),
  4259.     (EClass: ERangeError; EIdent: SRangeError),
  4260.     (EClass: EIntOverflow; EIdent: SIntOverflow),
  4261.     (EClass: EInvalidOp; EIdent: SInvalidOp),
  4262.     (EClass: EZeroDivide; EIdent: SZeroDivide),
  4263.     (EClass: EOverflow; EIdent: SOverflow),
  4264.     (EClass: EUnderflow; EIdent: SUnderflow),
  4265.     (EClass: EInvalidCast; EIdent: SInvalidCast),
  4266.     (EClass: EAccessViolation; EIdent: SAccessViolation),
  4267.     (EClass: EPrivilege; EIdent: SPrivilege),
  4268.     (EClass: EControlC; EIdent: SControlC),
  4269.     (EClass: EStackOverflow; EIdent: SStackOverflow),
  4270.     (EClass: EVariantError; EIdent: SInvalidVarCast),
  4271.     (EClass: EVariantError; EIdent: SInvalidVarOp),
  4272.     (EClass: EVariantError; EIdent: SDispatchError),
  4273.     (EClass: EVariantError; EIdent: SVarArrayCreate),
  4274.     (EClass: EVariantError; EIdent: SVarNotArray),
  4275.     (EClass: EVariantError; EIdent: SVarArrayBounds),
  4276.     (EClass: EExternalException; EIdent: SExternalException));
  4277.  
  4278. procedure ErrorHandler(ErrorCode: Integer; ErrorAddr: Pointer);
  4279. var
  4280.   E: Exception;
  4281. begin
  4282.   case ErrorCode of
  4283.     1: E := OutOfMemory;
  4284.     2..20: with ExceptMap[ErrorCode] do E := EClass.CreateRes(EIdent);
  4285.   else
  4286.     E := CreateInOutError;
  4287.   end;
  4288.   raise E at ErrorAddr;
  4289. end;
  4290.  
  4291. function MapException(P: PExceptionRecord):Byte;
  4292. begin
  4293.   case P.ExceptionCode of
  4294.     STATUS_INTEGER_DIVIDE_BY_ZERO:  Result := 3;
  4295.     STATUS_ARRAY_BOUNDS_EXCEEDED:   Result := 4;
  4296.     STATUS_INTEGER_OVERFLOW:        Result := 5;
  4297.     STATUS_FLOAT_INEXACT_RESULT,
  4298.     STATUS_FLOAT_INVALID_OPERATION,
  4299.     STATUS_FLOAT_STACK_CHECK:       Result := 6;
  4300.     STATUS_FLOAT_DIVIDE_BY_ZERO:    Result := 7;
  4301.     STATUS_FLOAT_OVERFLOW:          Result := 8;
  4302.     STATUS_FLOAT_UNDERFLOW,
  4303.     STATUS_FLOAT_DENORMAL_OPERAND:  Result := 9;
  4304.     STATUS_ACCESS_VIOLATION:        Result := 11;
  4305.     STATUS_PRIVILEGED_INSTRUCTION:  Result := 12;
  4306.     STATUS_CONTROL_C_EXIT:          Result := 13;
  4307.     STATUS_STACK_OVERFLOW:          Result := 14;
  4308.   else                              Result := 21;
  4309.   end;
  4310. end;
  4311.  
  4312. function GetExceptionClass(P: PExceptionRecord):ExceptClass;
  4313. var
  4314.   ErrorCode: Byte;
  4315. begin
  4316.   ErrorCode := MapException(P);
  4317.   Result := ExceptMap[ErrorCode].EClass;
  4318. end;
  4319.  
  4320. function GetExceptionObject(P: PExceptionRecord):Exception;
  4321. var
  4322.   ErrorCode: Integer;
  4323.   AccessOp: Integer; // string ID indicating the access type READ or WRITE
  4324.   AccessAddress: Pointer;
  4325. begin
  4326.   ErrorCode := MapException(P);
  4327.   case ErrorCode of
  4328.     3..10,12..20:
  4329.       with ExceptMap[ErrorCode] do Result := EClass.CreateRes(EIdent);
  4330.     11:
  4331.       begin
  4332.         with P^ do
  4333.         begin
  4334.           if ExceptionInformation[0] = 0 then
  4335.             AccessOp := sReadAccess
  4336.           else AccessOp := sWriteAccess;
  4337.           AccessAddress := Pointer(ExceptionInformation[1]);
  4338.           Result := EAccessViolation.CreateResFmt(sAccessViolation,
  4339.             [ExceptionAddress, LoadStr(AccessOp), AccessAddress]);
  4340.         end;
  4341.       end;
  4342.   else
  4343.     Result := EExternalException.CreateResFmt(SExternalException,
  4344.       [P.ExceptionCode]);
  4345.     EExternalException(Result).ExceptionRecord := P;
  4346.   end;
  4347. end;
  4348.  
  4349. { RTL exception handler }
  4350.  
  4351. procedure ExceptHandler(ExceptObject: TObject; ExceptAddr: Pointer); far;
  4352. begin
  4353.   ShowException(ExceptObject, ExceptAddr);
  4354.   Halt(1);
  4355. end;
  4356.  
  4357. procedure InitExceptions;
  4358. begin
  4359.   OutOfMemory := EOutOfMemory.CreateRes(SOutOfMemory);
  4360.   ErrorProc := @ErrorHandler;
  4361.   ExceptProc := @ExceptHandler;
  4362.   ExceptionClass := Exception;
  4363.   ExceptClsProc := @GetExceptionClass;
  4364.   ExceptObjProc := @GetExceptionObject;
  4365. end;
  4366.  
  4367. procedure InitPlatformId;
  4368. var
  4369.   OSVersionInfo: TOSVersionInfo;
  4370. begin
  4371.   OSVersionInfo.dwOSVersionInfoSize := SizeOf(OSVersionInfo);
  4372.   if GetVersionEx(OSVersionInfo) then
  4373.     Win32Platform := OSVersionInfo.dwPLatformId;
  4374. end;
  4375.  
  4376. procedure Beep;
  4377. begin
  4378.   MessageBeep(0);
  4379. end;
  4380.  
  4381. begin
  4382.   InitExceptions;
  4383.   GetMonthDayNames;
  4384.   GetFormatSettings;
  4385.   InitPLatformId;
  4386. end.
  4387.  
  4388.