home *** CD-ROM | disk | FTP | other *** search
/ PC Plus SuperCD (UK) 2000 March / pcp161b.iso / full / delphi / RUNIMAGE / DELPHI30 / SOURCE / RTL / SYS / SYSUTILS.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1997-08-03  |  186.5 KB  |  6,293 lines

  1.  
  2. {*******************************************************}
  3. {                                                       }
  4. {       Delphi Runtime Library                          }
  5. {       System Utilities Unit                           }
  6. {                                                       }
  7. {       Copyright (C) 1995,97 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. { MultiByte Character Set (MBCS) byte type }
  157.   TMbcsByteType = (mbSingleByte, mbLeadByte, mbTrailByte);
  158.  
  159. { System Locale information record }
  160.   TSysLocale = packed record
  161.     DefaultLCID: LCID;
  162.     PriLangID: LANGID;
  163.     SubLangID: LANGID;
  164.     FarEast: Boolean;
  165.   end;
  166.  
  167. { Exceptions }
  168.  
  169.   Exception = class(TObject)
  170.   private
  171.     FMessage: string;
  172.     FHelpContext: Integer;
  173.   public
  174.     constructor Create(const Msg: string);
  175.     constructor CreateFmt(const Msg: string; const Args: array of const);
  176.     constructor CreateRes(Ident: Integer);
  177.     constructor CreateResFmt(Ident: Integer; const Args: array of const);
  178.     constructor CreateHelp(const Msg: string; AHelpContext: Integer);
  179.     constructor CreateFmtHelp(const Msg: string; const Args: array of const;
  180.       AHelpContext: Integer);
  181.     constructor CreateResHelp(Ident: Integer; AHelpContext: Integer);
  182.     constructor CreateResFmtHelp(Ident: Integer; const Args: array of const;
  183.       AHelpContext: Integer);
  184.     property HelpContext: Integer read FHelpContext write FHelpContext;
  185.     property Message: string read FMessage write FMessage;
  186.   end;
  187.  
  188.   ExceptClass = class of Exception;
  189.  
  190.   EAbort = class(Exception);
  191.  
  192.   EOutOfMemory = class(Exception)
  193.   private
  194.     AllowFree: Boolean;
  195.   public
  196.     destructor Destroy; override;
  197.     procedure FreeInstance; override;
  198.   end;
  199.  
  200.   EInOutError = class(Exception)
  201.   public
  202.     ErrorCode: Integer;
  203.   end;
  204.  
  205.   EIntError = class(Exception);
  206.   EDivByZero = class(EIntError);
  207.   ERangeError = class(EIntError);
  208.   EIntOverflow = class(EIntError);
  209.  
  210.   EMathError = class(Exception);
  211.   EInvalidOp = class(EMathError);
  212.   EZeroDivide = class(EMathError);
  213.   EOverflow = class(EMathError);
  214.   EUnderflow = class(EMathError);
  215.  
  216.   EInvalidPointer = class(Exception);
  217.  
  218.   EInvalidCast = class(Exception);
  219.  
  220.   EConvertError = class(Exception);
  221.  
  222.   EAccessViolation = class(Exception);
  223.   EPrivilege = class(Exception);
  224.   EStackOverflow = class(Exception);
  225.   EControlC = class(Exception);
  226.  
  227.   EVariantError = class(Exception);
  228.  
  229.   EPropReadOnly = class(Exception);
  230.   EPropWriteOnly = class(Exception);
  231.  
  232.   EExternalException = class(Exception)
  233.   public
  234.     ExceptionRecord: PExceptionRecord;
  235.   end;
  236.  
  237.   EAssertionFailed = class(Exception);
  238.  
  239.   EAbstractError = class(Exception);
  240.  
  241.   EIntfCastError = class(Exception);
  242.  
  243.   EInvalidContainer = class(Exception);
  244.   EInvalidInsert = class(Exception);
  245.  
  246.   EPackageError = class(Exception);
  247.  
  248.   EWin32Error = class(Exception)
  249.   public
  250.     ErrorCode: DWORD;
  251.   end;
  252.  
  253. var
  254.  
  255. { Empty string and null string pointer. These constants are provided for
  256.   backwards compatibility only.  }
  257.  
  258.   EmptyStr: string = '';
  259.   NullStr: PString = @EmptyStr;
  260.  
  261. { Win32 platform identifier.  This will be one of the following values:
  262.  
  263.     VER_PLATFORM_WIN32s
  264.     VER_PLATFORM_WIN32_WINDOWS
  265.     VER_PLATFORM_WIN32_NT
  266.  
  267.   See WINDOWS.PAS for the numerical values. }
  268.  
  269.   Win32Platform: Integer = 0;
  270.  
  271. { Win32 OS version information -
  272.  
  273.   see TOSVersionInfo.dwMajorVersion/dwMinorVersion/dwBuildNumber }
  274.  
  275.   Win32MajorVersion: Integer = 0;
  276.   Win32MinorVersion: Integer = 0;
  277.   Win32BuildNumber: Integer = 0;
  278.  
  279. { Win32 OS extra version info string -
  280.  
  281.   see TOSVersionInfo.szCSDVersion }
  282.  
  283.   Win32CSDVersion: string = '';
  284.  
  285. { Currency and date/time formatting options
  286.  
  287.   The initial values of these variables are fetched from the system registry
  288.   using the GetLocaleInfo function in the Win32 API. The description of each
  289.   variable specifies the LOCALE_XXXX constant used to fetch the initial
  290.   value.
  291.  
  292.   CurrencyString - Defines the currency symbol used in floating-point to
  293.   decimal conversions. The initial value is fetched from LOCALE_SCURRENCY.
  294.  
  295.   CurrencyFormat - Defines the currency symbol placement and separation
  296.   used in floating-point to decimal conversions. Possible values are:
  297.  
  298.     0 = '$1'
  299.     1 = '1$'
  300.     2 = '$ 1'
  301.     3 = '1 $'
  302.  
  303.   The initial value is fetched from LOCALE_ICURRENCY.
  304.  
  305.   NegCurrFormat - Defines the currency format for used in floating-point to
  306.   decimal conversions of negative numbers. Possible values are:
  307.  
  308.     0 = '($1)'      4 = '(1$)'      8 = '-1 $'      12 = '$ -1'
  309.     1 = '-$1'       5 = '-1$'       9 = '-$ 1'      13 = '1- $'
  310.     2 = '$-1'       6 = '1-$'      10 = '1 $-'      14 = '($ 1)'
  311.     3 = '$1-'       7 = '1$-'      11 = '$ 1-'      15 = '(1 $)'
  312.  
  313.   The initial value is fetched from LOCALE_INEGCURR.
  314.  
  315.   ThousandSeparator - The character used to separate thousands in numbers
  316.   with more than three digits to the left of the decimal separator. The
  317.   initial value is fetched from LOCALE_STHOUSAND.
  318.  
  319.   DecimalSeparator - The character used to separate the integer part from
  320.   the fractional part of a number. The initial value is fetched from
  321.   LOCALE_SDECIMAL.
  322.  
  323.   CurrencyDecimals - The number of digits to the right of the decimal point
  324.   in a currency amount. The initial value is fetched from LOCALE_ICURRDIGITS.
  325.  
  326.   DateSeparator - The character used to separate the year, month, and day
  327.   parts of a date value. The initial value is fetched from LOCATE_SDATE.
  328.  
  329.   ShortDateFormat - The format string used to convert a date value to a
  330.   short string suitable for editing. For a complete description of date and
  331.   time format strings, refer to the documentation for the FormatDate
  332.   function. The short date format should only use the date separator
  333.   character and the  m, mm, d, dd, yy, and yyyy format specifiers. The
  334.   initial value is fetched from LOCALE_SSHORTDATE.
  335.  
  336.   LongDateFormat - The format string used to convert a date value to a long
  337.   string suitable for display but not for editing. For a complete description
  338.   of date and time format strings, refer to the documentation for the
  339.   FormatDate function. The initial value is fetched from LOCALE_SLONGDATE.
  340.  
  341.   TimeSeparator - The character used to separate the hour, minute, and
  342.   second parts of a time value. The initial value is fetched from
  343.   LOCALE_STIME.
  344.  
  345.   TimeAMString - The suffix string used for time values between 00:00 and
  346.   11:59 in 12-hour clock format. The initial value is fetched from
  347.   LOCALE_S1159.
  348.  
  349.   TimePMString - The suffix string used for time values between 12:00 and
  350.   23:59 in 12-hour clock format. The initial value is fetched from
  351.   LOCALE_S2359.
  352.  
  353.   ShortTimeFormat - The format string used to convert a time value to a
  354.   short string with only hours and minutes. The default value is computed
  355.   from LOCALE_ITIME and LOCALE_ITLZERO.
  356.  
  357.   LongTimeFormat - The format string used to convert a time value to a long
  358.   string with hours, minutes, and seconds. The default value is computed
  359.   from LOCALE_ITIME and LOCALE_ITLZERO.
  360.  
  361.   ShortMonthNames - Array of strings containing short month names. The mmm
  362.   format specifier in a format string passed to FormatDate causes a short
  363.   month name to be substituted. The default values are fecthed from the
  364.   LOCALE_SABBREVMONTHNAME system locale entries.
  365.  
  366.   LongMonthNames - Array of strings containing long month names. The mmmm
  367.   format specifier in a format string passed to FormatDate causes a long
  368.   month name to be substituted. The default values are fecthed from the
  369.   LOCALE_SMONTHNAME system locale entries.
  370.  
  371.   ShortDayNames - Array of strings containing short day names. The ddd
  372.   format specifier in a format string passed to FormatDate causes a short
  373.   day name to be substituted. The default values are fecthed from the
  374.   LOCALE_SABBREVDAYNAME system locale entries.
  375.  
  376.   LongDayNames - Array of strings containing long day names. The dddd
  377.   format specifier in a format string passed to FormatDate causes a long
  378.   day name to be substituted. The default values are fecthed from the
  379.   LOCALE_SDAYNAME system locale entries. }
  380.  
  381. var
  382.   CurrencyString: string;
  383.   CurrencyFormat: Byte;
  384.   NegCurrFormat: Byte;
  385.   ThousandSeparator: Char;
  386.   DecimalSeparator: Char;
  387.   CurrencyDecimals: Byte;
  388.   DateSeparator: Char;
  389.   ShortDateFormat: string;
  390.   LongDateFormat: string;
  391.   TimeSeparator: Char;
  392.   TimeAMString: string;
  393.   TimePMString: string;
  394.   ShortTimeFormat: string;
  395.   LongTimeFormat: string;
  396.   ShortMonthNames: array[1..12] of string;
  397.   LongMonthNames: array[1..12] of string;
  398.   ShortDayNames: array[1..7] of string;
  399.   LongDayNames: array[1..7] of string;
  400.   SysLocale: TSysLocale;
  401.   EraNames: array[1..7] of string;
  402.   EraYearOffsets: array[1..7] of Integer;
  403.  
  404. { Memory management routines }
  405.  
  406. { AllocMem allocates a block of the given size on the heap. Each byte in
  407.   the allocated buffer is set to zero. To dispose the buffer, use the
  408.   FreeMem standard procedure. }
  409.  
  410. function AllocMem(Size: Cardinal): Pointer;
  411.  
  412. { Exit procedure handling }
  413.  
  414. { AddExitProc adds the given procedure to the run-time library's exit
  415.   procedure list. When an application terminates, its exit procedures are
  416.   executed in reverse order of definition, i.e. the last procedure passed
  417.   to AddExitProc is the first one to get executed upon termination. }
  418.  
  419. procedure AddExitProc(Proc: TProcedure);
  420.  
  421. { String handling routines }
  422.  
  423. { NewStr allocates a string on the heap. NewStr is provided for backwards
  424.   compatibility only. }
  425.  
  426. function NewStr(const S: string): PString;
  427.  
  428. { DisposeStr disposes a string pointer that was previously allocated using
  429.   NewStr. DisposeStr is provided for backwards compatibility only. }
  430.  
  431. procedure DisposeStr(P: PString);
  432.  
  433. { AssignStr assigns a new dynamically allocated string to the given string
  434.   pointer. AssignStr is provided for backwards compatibility only. }
  435.  
  436. procedure AssignStr(var P: PString; const S: string);
  437.  
  438. { AppendStr appends S to the end of Dest. AppendStr is provided for
  439.   backwards compatibility only. Use "Dest := Dest + S" instead. }
  440.  
  441. procedure AppendStr(var Dest: string; const S: string);
  442.  
  443. { UpperCase converts all ASCII characters in the given string to upper case.
  444.   The conversion affects only 7-bit ASCII characters between 'a' and 'z'. To
  445.   convert 8-bit international characters, use AnsiUpperCase. }
  446.  
  447. function UpperCase(const S: string): string;
  448.  
  449. { LowerCase converts all ASCII characters in the given string to lower case.
  450.   The conversion affects only 7-bit ASCII characters between 'A' and 'Z'. To
  451.   convert 8-bit international characters, use AnsiLowerCase. }
  452.  
  453. function LowerCase(const S: string): string;
  454.  
  455. { CompareStr compares S1 to S2, with case-sensitivity. The return value is
  456.   less than 0 if S1 < S2, 0 if S1 = S2, or greater than 0 if S1 > S2. The
  457.   compare operation is based on the 8-bit ordinal value of each character
  458.   and is not affected by the current Windows locale. }
  459.  
  460. function CompareStr(const S1, S2: string): Integer;
  461.  
  462. { CompareMem performs a binary compare of Length bytes of memory referenced
  463.   by P1 to that of P2.  CompareMem returns True if the memory referenced by
  464.   P1 is identical to that of P2. }
  465.  
  466. function CompareMem(P1, P2: Pointer; Length: Integer): Boolean; assembler;
  467.  
  468. { CompareText compares S1 to S2, without case-sensitivity. The return value
  469.   is the same as for CompareStr. The compare operation is based on the 8-bit
  470.   ordinal value of each character, after converting 'a'..'z' to 'A'..'Z',
  471.   and is not affected by the current Windows locale. }
  472.  
  473. function CompareText(const S1, S2: string): Integer;
  474.  
  475. { AnsiUpperCase converts all characters in the given string to upper case.
  476.   The conversion uses the current Windows locale. }
  477.  
  478. function AnsiUpperCase(const S: string): string;
  479.  
  480. { AnsiLowerCase converts all characters in the given string to lower case.
  481.   The conversion uses the current Windows locale. }
  482.  
  483. function AnsiLowerCase(const S: string): string;
  484.  
  485. { AnsiCompareStr compares S1 to S2, with case-sensitivity. The compare
  486.   operation is controlled by the current Windows locale. The return value
  487.   is the same as for CompareStr. }
  488.  
  489. function AnsiCompareStr(const S1, S2: string): Integer;
  490.  
  491. { AnsiCompareText compares S1 to S2, without case-sensitivity. The compare
  492.   operation is controlled by the current Windows locale. The return value
  493.   is the same as for CompareStr. }
  494.  
  495. function AnsiCompareText(const S1, S2: string): Integer;
  496.  
  497. { AnsiStrComp compares S1 to S2, with case-sensitivity. The compare
  498.   operation is controlled by the current Windows locale. The return value
  499.   is the same as for CompareStr. }
  500.  
  501. function AnsiStrComp(S1, S2: PChar): Integer;
  502.  
  503. { AnsiStrIComp compares S1 to S2, without case-sensitivity. The compare
  504.   operation is controlled by the current Windows locale. The return value
  505.   is the same as for CompareStr. }
  506.  
  507. function AnsiStrIComp(S1, S2: PChar): Integer;
  508.  
  509. { AnsiStrLComp compares S1 to S2, with case-sensitivity, up to a maximum
  510.   length of MaxLen bytes. The compare operation is controlled by the
  511.   current Windows locale. The return value is the same as for CompareStr. }
  512.  
  513. function AnsiStrLComp(S1, S2: PChar; MaxLen: Cardinal): Integer;
  514.  
  515. { AnsiStrLIComp compares S1 to S2, without case-sensitivity, up to a maximum
  516.   length of MaxLen bytes. The compare operation is controlled by the
  517.   current Windows locale. The return value is the same as for CompareStr. }
  518.  
  519. function AnsiStrLIComp(S1, S2: PChar; MaxLen: Cardinal): Integer;
  520.  
  521. { AnsiStrLower converts all characters in the given string to lower case.
  522.   The conversion uses the current Windows locale. }
  523.  
  524. function AnsiStrLower(Str: PChar): PChar;
  525.  
  526. { AnsiStrUpper converts all characters in the given string to upper case.
  527.   The conversion uses the current Windows locale. }
  528.  
  529. function AnsiStrUpper(Str: PChar): PChar;
  530.  
  531. { AnsiLastChar returns a pointer to the last full character in the string.
  532.   This function supports multibyte characters  }
  533.  
  534. function AnsiLastChar(const S: string): PChar;
  535.  
  536. { AnsiStrLastChar returns a pointer to the last full character in the string.
  537.   This function supports multibyte characters.  }
  538.  
  539. function AnsiStrLastChar(P: PChar): PChar;
  540.  
  541. { Trim trims leading and trailing spaces and control characters from the
  542.   given string. }
  543.  
  544. function Trim(const S: string): string;
  545.  
  546. { TrimLeft trims leading spaces and control characters from the given
  547.   string. }
  548.  
  549. function TrimLeft(const S: string): string;
  550.  
  551. { TrimRight trims trailing spaces and control characters from the given
  552.   string. }
  553.  
  554. function TrimRight(const S: string): string;
  555.  
  556. { QuotedStr returns the given string as a quoted string. A single quote
  557.   character is inserted at the beginning and the end of the string, and
  558.   for each single quote character in the string, another one is added. }
  559.  
  560. function QuotedStr(const S: string): string;
  561.  
  562. { AnsiQuotedStr returns the given string as a quoted string, using the
  563.   provided Quote character.  A Quote character is inserted at the beginning
  564.   and end of thestring, and each Quote character in the string is doubled.
  565.   This function supports multibyte character strings (MBCS). }
  566.  
  567. function AnsiQuotedStr(const S: string; Quote: Char): string;
  568.  
  569. { AnsiExtractQuotedStr removes the Quote characters from the beginning and end
  570.   of a quoted string, and reduces pairs of Quote characters within the quoted
  571.   string to a single character. If the first character in Src is not the Quote
  572.   character, the function returns an empty string.  The function copies
  573.   characters from the Src to the result string until the second solitary
  574.   Quote character or the first null character in Src. The Src parameter is
  575.   updated to point to the first character following the quoted string.  If
  576.   the Src string does not contain a matching end Quote character, the Src
  577.   parameter is updated to point to the terminating null character in Src.
  578.   This function supports multibyte character strings (MBCS).  }
  579.  
  580. function AnsiExtractQuotedStr(var Src: PChar; Quote: Char): string;
  581.  
  582. { AdjustLineBreaks adjusts all line breaks in the given string to be true
  583.   CR/LF sequences. The function changes any CR characters not followed by
  584.   a LF and any LF characters not preceded by a CR into CR/LF pairs. }
  585.  
  586. function AdjustLineBreaks(const S: string): string;
  587.  
  588. { IsValidIdent returns true if the given string is a valid identifier. An
  589.   identifier is defined as a character from the set ['A'..'Z', 'a'..'z', '_']
  590.   followed by zero or more characters from the set ['A'..'Z', 'a'..'z',
  591.   '0..'9', '_']. }
  592.  
  593. function IsValidIdent(const Ident: string): Boolean;
  594.  
  595. { IntToStr converts the given value to its decimal string representation. }
  596.  
  597. function IntToStr(Value: Integer): string;
  598.  
  599. { IntToHex converts the given value to a hexadecimal string representation
  600.   with the minimum number of digits specified. }
  601.  
  602. function IntToHex(Value: Integer; Digits: Integer): string;
  603.  
  604. { StrToInt converts the given string to an integer value. If the string
  605.   doesn't contain a valid value, an EConvertError exception is raised. }
  606.  
  607. function StrToInt(const S: string): Integer;
  608.  
  609. { StrToIntDef converts the given string to an integer value. If the string
  610.   doesn't contain a valid value, the value given by Default is returned. }
  611.  
  612. function StrToIntDef(const S: string; Default: Integer): Integer;
  613.  
  614. { LoadStr loads the string resource given by Ident from the application's
  615.   executable file. If the string resource does not exist, an empty string
  616.   is returned. }
  617.  
  618. function LoadStr(Ident: Integer): string;
  619.  
  620. { LoadStr loads the string resource given by Ident from the application's
  621.   executable file, and uses it as the format string in a call to the
  622.   Format function with the given arguments. }
  623.  
  624. function FmtLoadStr(Ident: Integer; const Args: array of const): string;
  625.  
  626. { File management routines }
  627.  
  628. { FileOpen opens the specified file using the specified access mode. The
  629.   access mode value is constructed by OR-ing one of the fmOpenXXXX constants
  630.   with one of the fmShareXXXX constants. If the return value is positive,
  631.   the function was successful and the value is the file handle of the opened
  632.   file. A return value of -1 indicates that an error occurred. }
  633.  
  634. function FileOpen(const FileName: string; Mode: Integer): Integer;
  635.  
  636. { FileCreate creates a new file by the specified name. If the return value
  637.   is positive, the function was successful and the value is the file handle
  638.   of the new file. A return value of -1 indicates that an error occurred. }
  639.  
  640. function FileCreate(const FileName: string): Integer;
  641.  
  642. { FileRead reads Count bytes from the file given by Handle into the buffer
  643.   specified by Buffer. The return value is the number of bytes actually
  644.   read; it is less than Count if the end of the file was reached. The return
  645.   value is -1 if an error occurred. }
  646.  
  647. function FileRead(Handle: Integer; var Buffer; Count: Integer): Integer;
  648.  
  649. { FileWrite writes Count bytes to the file given by Handle from the buffer
  650.   specified by Buffer. The return value is the number of bytes actually
  651.   written, or -1 if an error occurred. }
  652.  
  653. function FileWrite(Handle: Integer; const Buffer; Count: Integer): Integer;
  654.  
  655. { FileSeek changes the current position of the file given by Handle to be
  656.   Offset bytes relative to the point given by Origin. Origin = 0 means that
  657.   Offset is relative to the beginning of the file, Origin = 1 means that
  658.   Offset is relative to the current position, and Origin = 2 means that
  659.   Offset is relative to the end of the file. The return value is the new
  660.   current position, relative to the beginning of the file, or -1 if an error
  661.   occurred. }
  662.  
  663. function FileSeek(Handle, Offset, Origin: Integer): Integer;
  664.  
  665. { FileClose closes the specified file. }
  666.  
  667. procedure FileClose(Handle: Integer);
  668.  
  669. { FileAge returns the date-and-time stamp of the specified file. The return
  670.   value can be converted to a TDateTime value using the FileDateToDateTime
  671.   function. The return value is -1 if the file does not exist. }
  672.  
  673. function FileAge(const FileName: string): Integer;
  674.  
  675. { FileExists returns a boolean value that indicates whether the specified
  676.   file exists. }
  677.  
  678. function FileExists(const FileName: string): Boolean;
  679.  
  680. { FindFirst searches the directory given by Path for the first entry that
  681.   matches the filename given by Path and the attributes given by Attr. The
  682.   result is returned in the search record given by SearchRec. The return
  683.   value is zero if the function was successful. Otherwise the return value
  684.   is a Windows error code. FindFirst is typically used in conjunction with
  685.   FindNext and FindClose as follows:
  686.  
  687.     Result := FindFirst(Path, Attr, SearchRec);
  688.     while Result = 0 do
  689.     begin
  690.       ProcessSearchRec(SearchRec);
  691.       Result := FindNext(SearchRec);
  692.     end;
  693.     FindClose(SearchRec);
  694.  
  695.   where ProcessSearchRec represents user-defined code that processes the
  696.   information in a search record. }
  697.  
  698. function FindFirst(const Path: string; Attr: Integer;
  699.   var F: TSearchRec): Integer;
  700.  
  701. { FindNext returs the next entry that matches the name and attributes
  702.   specified in a previous call to FindFirst. The search record must be one
  703.   that was passed to FindFirst. The return value is zero if the function was
  704.   successful. Otherwise the return value is a Windows error code. }
  705.  
  706. function FindNext(var F: TSearchRec): Integer;
  707.  
  708. { FindClose terminates a FindFirst/FindNext sequence. FindClose does nothing
  709.   in the 16-bit version of Windows, but is required in the 32-bit version,
  710.   so for maximum portability every FindFirst/FindNext sequence should end
  711.   with a call to FindClose. }
  712.  
  713. procedure FindClose(var F: TSearchRec);
  714.  
  715. { FileGetDate returns the DOS date-and-time stamp of the file given by
  716.   Handle. The return value is -1 if the handle is invalid. The
  717.   FileDateToDateTime function can be used to convert the returned value to
  718.   a TDateTime value. }
  719.  
  720. function FileGetDate(Handle: Integer): Integer;
  721.  
  722. { FileSetDate sets the DOS date-and-time stamp of the file given by Handle
  723.   to the value given by Age. The DateTimeToFileDate function can be used to
  724.   convert a TDateTime value to a DOS date-and-time stamp. The return value
  725.   is zero if the function was successful. Otherwise the return value is a
  726.   Windows error code. }
  727.  
  728. function FileSetDate(Handle: Integer; Age: Integer): Integer;
  729.  
  730. { FileGetAttr returns the file attributes of the file given by FileName. The
  731.   attributes can be examined by AND-ing with the faXXXX constants defined
  732.   above. A return value of -1 indicates that an error occurred. }
  733.  
  734. function FileGetAttr(const FileName: string): Integer;
  735.  
  736. { FileSetAttr sets the file attributes of the file given by FileName to the
  737.   value given by Attr. The attribute value is formed by OR-ing the
  738.   appropriate faXXXX constants. The return value is zero if the function was
  739.   successful. Otherwise the return value is a Windows error code. }
  740.  
  741. function FileSetAttr(const FileName: string; Attr: Integer): Integer;
  742.  
  743. { DeleteFile deletes the file given by FileName. The return value is True if
  744.   the file was successfully deleted, or False if an error occurred. }
  745.  
  746. function DeleteFile(const FileName: string): Boolean;
  747.  
  748. { RenameFile renames the file given by OldName to the name given by NewName.
  749.   The return value is True if the file was successfully renamed, or False if
  750.   an error occurred. }
  751.  
  752. function RenameFile(const OldName, NewName: string): Boolean;
  753.  
  754. { ChangeFileExt changes the extension of a filename. FileName specifies a
  755.   filename with or without an extension, and Extension specifies the new
  756.   extension for the filename. The new extension can be a an empty string or
  757.   a period followed by up to three characters. }
  758.  
  759. function ChangeFileExt(const FileName, Extension: string): string;
  760.  
  761. { ExtractFilePath extracts the drive and directory parts of the given
  762.   filename. The resulting string is the leftmost characters of FileName,
  763.   up to and including the colon or backslash that separates the path
  764.   information from the name and extension. The resulting string is empty
  765.   if FileName contains no drive and directory parts. }
  766.  
  767. function ExtractFilePath(const FileName: string): string;
  768.  
  769. { ExtractFileDir extracts the drive and directory parts of the given
  770.   filename. The resulting string is a directory name suitable for passing
  771.   to SetCurrentDir, CreateDir, etc. The resulting string is empty if
  772.   FileName contains no drive and directory parts. }
  773.  
  774. function ExtractFileDir(const FileName: string): string;
  775.  
  776. { ExtractFileDrive extracts the drive part of the given filename.  For
  777.   filenames with drive letters, the resulting string is '<drive>:'.
  778.   For filenames with a UNC path, the resulting string is in the form
  779.   '\\<servername>\<sharename>'.  If the given path contains neither
  780.   style of filename, the result is an empty string. }
  781.  
  782. function ExtractFileDrive(const FileName: string): string;
  783.  
  784. { ExtractFileName extracts the name and extension parts of the given
  785.   filename. The resulting string is the leftmost characters of FileName,
  786.   starting with the first character after the colon or backslash that
  787.   separates the path information from the name and extension. The resulting
  788.   string is equal to FileName if FileName contains no drive and directory
  789.   parts. }
  790.  
  791. function ExtractFileName(const FileName: string): string;
  792.  
  793. { ExtractFileExt extracts the extension part of the given filename. The
  794.   resulting string includes the period character that separates the name
  795.   and extension parts. The resulting string is empty if the given filename
  796.   has no extension. }
  797.  
  798. function ExtractFileExt(const FileName: string): string;
  799.  
  800. { ExpandFileName expands the given filename to a fully qualified filename.
  801.   The resulting string consists of a drive letter, a colon, a root relative
  802.   directory path, and a filename. Embedded '.' and '..' directory references
  803.   are removed. }
  804.  
  805. function ExpandFileName(const FileName: string): string;
  806.  
  807. { ExpandUNCFileName expands the given filename to a fully qualified filename.
  808.   This function is the same as ExpandFileName except that it will return the
  809.   drive portion of the filename in the format '\\<servername>\<sharename> if
  810.   that drive is actually a network resource instead of a local resource.
  811.   Like ExpandFileName, embedded '.' and '..' directory references are
  812.   removed. }
  813.  
  814. function ExpandUNCFileName(const FileName: string): string;
  815.  
  816. {  ExtractRelativePath will return a file path name relative to the given
  817.    BaseName.  It strips the common path dirs and adds '..\' for each level
  818.    up from the BaseName path. }
  819.  
  820. function ExtractRelativePath(const BaseName, DestName: string): string;
  821.  
  822. { FileSearch searches for the file given by Name in the list of directories
  823.   given by DirList. The directory paths in DirList must be separated by
  824.   semicolons. The search always starts with the current directory of the
  825.   current drive. The returned value is a concatenation of one of the
  826.   directory paths and the filename, or an empty string if the file could not
  827.   be located. }
  828.  
  829. function FileSearch(const Name, DirList: string): string;
  830.  
  831. { DiskFree returns the number of free bytes on the specified drive number,
  832.   where 0 = Current, 1 = A, 2 = B, etc. DiskFree returns -1 if the drive
  833.   number is invalid. }
  834.  
  835. function DiskFree(Drive: Byte): Integer;
  836.  
  837. { DiskSize returns the size in bytes of the specified drive number, where
  838.   0 = Current, 1 = A, 2 = B, etc. DiskSize returns -1 if the drive number
  839.   is invalid. }
  840.  
  841. function DiskSize(Drive: Byte): Integer;
  842.  
  843. { FileDateToDateTime converts a DOS date-and-time value to a TDateTime
  844.   value. The FileAge, FileGetDate, and FileSetDate routines operate on DOS
  845.   date-and-time values, and the Time field of a TSearchRec used by the
  846.   FindFirst and FindNext functions contains a DOS date-and-time value. }
  847.  
  848. function FileDateToDateTime(FileDate: Integer): TDateTime;
  849.  
  850. { DateTimeToFileDate converts a TDateTime value to a DOS date-and-time
  851.   value. The FileAge, FileGetDate, and FileSetDate routines operate on DOS
  852.   date-and-time values, and the Time field of a TSearchRec used by the
  853.   FindFirst and FindNext functions contains a DOS date-and-time value. }
  854.  
  855. function DateTimeToFileDate(DateTime: TDateTime): Integer;
  856.  
  857. { GetCurrentDir returns the current directory. }
  858.  
  859. function GetCurrentDir: string;
  860.  
  861. { SetCurrentDir sets the current directory. The return value is True if
  862.   the current directory was successfully changed, or False if an error
  863.   occurred. }
  864.  
  865. function SetCurrentDir(const Dir: string): Boolean;
  866.  
  867. { CreateDir creates a new directory. The return value is True if a new
  868.   directory was successfully created, or False if an error occurred. }
  869.  
  870. function CreateDir(const Dir: string): Boolean;
  871.  
  872. { RemoveDir deletes an existing empty directory. The return value is
  873.   True if the directory was successfully deleted, or False if an error
  874.   occurred. }
  875.  
  876. function RemoveDir(const Dir: string): Boolean;
  877.  
  878. { PChar routines }
  879.  
  880. { StrLen returns the number of characters in Str, not counting the null
  881.   terminator. }
  882.  
  883. function StrLen(Str: PChar): Cardinal;
  884.  
  885. { StrEnd returns a pointer to the null character that terminates Str. }
  886.  
  887. function StrEnd(Str: PChar): PChar;
  888.  
  889. { StrMove copies exactly Count characters from Source to Dest and returns
  890.   Dest. Source and Dest may overlap. }
  891.  
  892. function StrMove(Dest, Source: PChar; Count: Cardinal): PChar;
  893.  
  894. { StrCopy copies Source to Dest and returns Dest. }
  895.  
  896. function StrCopy(Dest, Source: PChar): PChar;
  897.  
  898. { StrECopy copies Source to Dest and returns StrEnd(Dest). }
  899.  
  900. function StrECopy(Dest, Source: PChar): PChar;
  901.  
  902. { StrLCopy copies at most MaxLen characters from Source to Dest and
  903.   returns Dest. }
  904.  
  905. function StrLCopy(Dest, Source: PChar; MaxLen: Cardinal): PChar;
  906.  
  907. { StrPCopy copies the Pascal style string Source into Dest and
  908.   returns Dest. }
  909.  
  910. function StrPCopy(Dest: PChar; const Source: string): PChar;
  911.  
  912. { StrPLCopy copies at most MaxLen characters from the Pascal style string
  913.   Source into Dest and returns Dest. }
  914.  
  915. function StrPLCopy(Dest: PChar; const Source: string;
  916.   MaxLen: Cardinal): PChar;
  917.  
  918. { StrCat appends a copy of Source to the end of Dest and returns Dest. }
  919.  
  920. function StrCat(Dest, Source: PChar): PChar;
  921.  
  922. { StrLCat appends at most MaxLen - StrLen(Dest) characters from Source to
  923.   the end of Dest, and returns Dest. }
  924.  
  925. function StrLCat(Dest, Source: PChar; MaxLen: Cardinal): PChar;
  926.  
  927. { StrComp compares Str1 to Str2. The return value is less than 0 if
  928.   Str1 < Str2, 0 if Str1 = Str2, or greater than 0 if Str1 > Str2. }
  929.  
  930. function StrComp(Str1, Str2: PChar): Integer;
  931.  
  932. { StrIComp compares Str1 to Str2, without case sensitivity. The return
  933.   value is the same as StrComp. }
  934.  
  935. function StrIComp(Str1, Str2: PChar): Integer;
  936.  
  937. { StrLComp compares Str1 to Str2, for a maximum length of MaxLen
  938.   characters. The return value is the same as StrComp. }
  939.  
  940. function StrLComp(Str1, Str2: PChar; MaxLen: Cardinal): Integer;
  941.  
  942. { StrLIComp compares Str1 to Str2, for a maximum length of MaxLen
  943.   characters, without case sensitivity. The return value is the same
  944.   as StrComp. }
  945.  
  946. function StrLIComp(Str1, Str2: PChar; MaxLen: Cardinal): Integer;
  947.  
  948. { StrScan returns a pointer to the first occurrence of Chr in Str. If Chr
  949.   does not occur in Str, StrScan returns NIL. The null terminator is
  950.   considered to be part of the string. }
  951.  
  952. function StrScan(Str: PChar; Chr: Char): PChar;
  953.  
  954. { StrRScan returns a pointer to the last occurrence of Chr in Str. If Chr
  955.   does not occur in Str, StrRScan returns NIL. The null terminator is
  956.   considered to be part of the string. }
  957.  
  958. function StrRScan(Str: PChar; Chr: Char): PChar;
  959.  
  960. { StrPos returns a pointer to the first occurrence of Str2 in Str1. If
  961.   Str2 does not occur in Str1, StrPos returns NIL. }
  962.  
  963. function StrPos(Str1, Str2: PChar): PChar;
  964.  
  965. { StrUpper converts Str to upper case and returns Str. }
  966.  
  967. function StrUpper(Str: PChar): PChar;
  968.  
  969. { StrLower converts Str to lower case and returns Str. }
  970.  
  971. function StrLower(Str: PChar): PChar;
  972.  
  973. { StrPas converts Str to a Pascal style string. This function is provided
  974.   for backwards compatibility only. To convert a null terminated string to
  975.   a Pascal style string, use a type cast or an assignment. }
  976.  
  977. function StrPas(Str: PChar): string;
  978.  
  979. { StrAlloc allocates a buffer of the given size on the heap. The size of
  980.   the allocated buffer is encoded in a four byte header that immediately
  981.   preceeds the buffer. To dispose the buffer, use StrDispose. }
  982.  
  983. function StrAlloc(Size: Cardinal): PChar;
  984.  
  985. { StrBufSize returns the allocated size of the given buffer, not including
  986.   the two byte header. }
  987.  
  988. function StrBufSize(Str: PChar): Cardinal;
  989.  
  990. { StrNew allocates a copy of Str on the heap. If Str is NIL, StrNew returns
  991.   NIL and doesn't allocate any heap space. Otherwise, StrNew makes a
  992.   duplicate of Str, obtaining space with a call to the StrAlloc function,
  993.   and returns a pointer to the duplicated string. To dispose the string,
  994.   use StrDispose. }
  995.  
  996. function StrNew(Str: PChar): PChar;
  997.  
  998. { StrDispose disposes a string that was previously allocated with StrAlloc
  999.   or StrNew. If Str is NIL, StrDispose does nothing. }
  1000.  
  1001. procedure StrDispose(Str: PChar);
  1002.  
  1003. { String formatting routines }
  1004.  
  1005. { The Format routine formats the argument list given by the Args parameter
  1006.   using the format string given by the Format parameter.
  1007.  
  1008.   Format strings contain two types of objects--plain characters and format
  1009.   specifiers. Plain characters are copied verbatim to the resulting string.
  1010.   Format specifiers fetch arguments from the argument list and apply
  1011.   formatting to them.
  1012.  
  1013.   Format specifiers have the following form:
  1014.  
  1015.     "%" [index ":"] ["-"] [width] ["." prec] type
  1016.  
  1017.   A format specifier begins with a % character. After the % come the
  1018.   following, in this order:
  1019.  
  1020.   -  an optional argument index specifier, [index ":"]
  1021.   -  an optional left-justification indicator, ["-"]
  1022.   -  an optional width specifier, [width]
  1023.   -  an optional precision specifier, ["." prec]
  1024.   -  the conversion type character, type
  1025.  
  1026.   The following conversion characters are supported:
  1027.  
  1028.   d  Decimal. The argument must be an integer value. The value is converted
  1029.      to a string of decimal digits. If the format string contains a precision
  1030.      specifier, it indicates that the resulting string must contain at least
  1031.      the specified number of digits; if the value has less digits, the
  1032.      resulting string is left-padded with zeros.
  1033.  
  1034.   e  Scientific. The argument must be a floating-point value. The value is
  1035.      converted to a string of the form "-d.ddd...E+ddd". The resulting
  1036.      string starts with a minus sign if the number is negative, and one digit
  1037.      always precedes the decimal point. The total number of digits in the
  1038.      resulting string (including the one before the decimal point) is given
  1039.      by the precision specifer in the format string--a default precision of
  1040.      15 is assumed if no precision specifer is present. The "E" exponent
  1041.      character in the resulting string is always followed by a plus or minus
  1042.      sign and at least three digits.
  1043.  
  1044.   f  Fixed. The argument must be a floating-point value. The value is
  1045.      converted to a string of the form "-ddd.ddd...". The resulting string
  1046.      starts with a minus sign if the number is negative. The number of digits
  1047.      after the decimal point is given by the precision specifier in the
  1048.      format string--a default of 2 decimal digits is assumed if no precision
  1049.      specifier is present.
  1050.  
  1051.   g  General. The argument must be a floating-point value. The value is
  1052.      converted to the shortest possible decimal string using fixed or
  1053.      scientific format. The number of significant digits in the resulting
  1054.      string is given by the precision specifier in the format string--a
  1055.      default precision of 15 is assumed if no precision specifier is present.
  1056.      Trailing zeros are removed from the resulting string, and a decimal
  1057.      point appears only if necessary. The resulting string uses fixed point
  1058.      format if the number of digits to the left of the decimal point in the
  1059.      value is less than or equal to the specified precision, and if the
  1060.      value is greater than or equal to 0.00001. Otherwise the resulting
  1061.      string uses scientific format.
  1062.  
  1063.   n  Number. The argument must be a floating-point value. The value is
  1064.      converted to a string of the form "-d,ddd,ddd.ddd...". The "n" format
  1065.      corresponds to the "f" format, except that the resulting string
  1066.      contains thousand separators.
  1067.  
  1068.   m  Money. The argument must be a floating-point value. The value is
  1069.      converted to a string that represents a currency amount. The conversion
  1070.      is controlled by the CurrencyString, CurrencyFormat, NegCurrFormat,
  1071.      ThousandSeparator, DecimalSeparator, and CurrencyDecimals global
  1072.      variables, all of which are initialized from the Currency Format in
  1073.      the International section of the Windows Control Panel. If the format
  1074.      string contains a precision specifier, it overrides the value given
  1075.      by the CurrencyDecimals global variable.
  1076.  
  1077.   p  Pointer. The argument must be a pointer value. The value is converted
  1078.      to a string of the form "XXXX:YYYY" where XXXX and YYYY are the
  1079.      segment and offset parts of the pointer expressed as four hexadecimal
  1080.      digits.
  1081.  
  1082.   s  String. The argument must be a character, a string, or a PChar value.
  1083.      The string or character is inserted in place of the format specifier.
  1084.      The precision specifier, if present in the format string, specifies the
  1085.      maximum length of the resulting string. If the argument is a string
  1086.      that is longer than this maximum, the string is truncated.
  1087.  
  1088.   x  Hexadecimal. The argument must be an integer value. The value is
  1089.      converted to a string of hexadecimal digits. If the format string
  1090.      contains a precision specifier, it indicates that the resulting string
  1091.      must contain at least the specified number of digits; if the value has
  1092.      less digits, the resulting string is left-padded with zeros.
  1093.  
  1094.   Conversion characters may be specified in upper case as well as in lower
  1095.   case--both produce the same results.
  1096.  
  1097.   For all floating-point formats, the actual characters used as decimal and
  1098.   thousand separators are obtained from the DecimalSeparator and
  1099.   ThousandSeparator global variables.
  1100.  
  1101.   Index, width, and precision specifiers can be specified directly using
  1102.   decimal digit string (for example "%10d"), or indirectly using an asterisk
  1103.   charcater (for example "%*.*f"). When using an asterisk, the next argument
  1104.   in the argument list (which must be an integer value) becomes the value
  1105.   that is actually used. For example "Format('%*.*f', [8, 2, 123.456])" is
  1106.   the same as "Format('%8.2f', [123.456])".
  1107.  
  1108.   A width specifier sets the minimum field width for a conversion. If the
  1109.   resulting string is shorter than the minimum field width, it is padded
  1110.   with blanks to increase the field width. The default is to right-justify
  1111.   the result by adding blanks in front of the value, but if the format
  1112.   specifier contains a left-justification indicator (a "-" character
  1113.   preceding the width specifier), the result is left-justified by adding
  1114.   blanks after the value.
  1115.  
  1116.   An index specifier sets the current argument list index to the specified
  1117.   value. The index of the first argument in the argument list is 0. Using
  1118.   index specifiers, it is possible to format the same argument multiple
  1119.   times. For example "Format('%d %d %0:d %d', [10, 20])" produces the string
  1120.   '10 20 10 20'.
  1121.  
  1122.   The Format function can be combined with other formatting functions. For
  1123.   example
  1124.  
  1125.     S := Format('Your total was %s on %s', [
  1126.       FormatFloat('$#,##0.00;;zero', Total),
  1127.       FormatDateTime('mm/dd/yy', Date)]);
  1128.  
  1129.   which uses the FormatFloat and FormatDateTime functions to customize the
  1130.   format beyond what is possible with Format. }
  1131.  
  1132. function Format(const Format: string; const Args: array of const): string;
  1133.  
  1134. { FmtStr formats the argument list given by Args using the format string
  1135.   given by Format into the string variable given by Result. For further
  1136.   details, see the description of the Format function. }
  1137.  
  1138. procedure FmtStr(var Result: string; const Format: string;
  1139.   const Args: array of const);
  1140.  
  1141. { StrFmt formats the argument list given by Args using the format string
  1142.   given by Format into the buffer given by Buffer. It is up to the caller to
  1143.   ensure that Buffer is large enough for the resulting string. The returned
  1144.   value is Buffer. For further details, see the description of the Format
  1145.   function. }
  1146.  
  1147. function StrFmt(Buffer, Format: PChar; const Args: array of const): PChar;
  1148.  
  1149. { StrFmt formats the argument list given by Args using the format string
  1150.   given by Format into the buffer given by Buffer. The resulting string will
  1151.   contain no more than MaxLen characters, not including the null terminator.
  1152.   The returned value is Buffer. For further details, see the description of
  1153.   the Format function. }
  1154.  
  1155. function StrLFmt(Buffer: PChar; MaxLen: Cardinal; Format: PChar;
  1156.   const Args: array of const): PChar;
  1157.  
  1158. { FormatBuf formats the argument list given by Args using the format string
  1159.   given by Format and FmtLen into the buffer given by Buffer and BufLen.
  1160.   The Format parameter is a reference to a buffer containing FmtLen
  1161.   characters, and the Buffer parameter is a reference to a buffer of BufLen
  1162.   characters. The returned value is the number of characters actually stored
  1163.   in Buffer. The returned value is always less than or equal to BufLen. For
  1164.   further details, see the description of the Format function. }
  1165.  
  1166. function FormatBuf(var Buffer; BufLen: Cardinal; const Format;
  1167.   FmtLen: Cardinal; const Args: array of const): Cardinal;
  1168.  
  1169. { Floating point conversion routines }
  1170.  
  1171. { FloatToStr converts the floating-point value given by Value to its string
  1172.   representation. The conversion uses general number format with 15
  1173.   significant digits. For further details, see the description of the
  1174.   FloatToStrF function. }
  1175.  
  1176. function FloatToStr(Value: Extended): string;
  1177.  
  1178. { CurrToStr converts the currency value given by Value to its string
  1179.   representation. The conversion uses general number format. For further
  1180.   details, see the description of the CurrToStrF function. }
  1181.  
  1182. function CurrToStr(Value: Currency): string;
  1183.  
  1184. { FloatToStrF converts the floating-point value given by Value to its string
  1185.   representation. The Format parameter controls the format of the resulting
  1186.   string. The Precision parameter specifies the precision of the given value.
  1187.   It should be 7 or less for values of type Single, 15 or less for values of
  1188.   type Double, and 18 or less for values of type Extended. The meaning of the
  1189.   Digits parameter depends on the particular format selected.
  1190.  
  1191.   The possible values of the Format parameter, and the meaning of each, are
  1192.   described below.
  1193.  
  1194.   ffGeneral - General number format. The value is converted to the shortest
  1195.   possible decimal string using fixed or scientific format. Trailing zeros
  1196.   are removed from the resulting string, and a decimal point appears only
  1197.   if necessary. The resulting string uses fixed point format if the number
  1198.   of digits to the left of the decimal point in the value is less than or
  1199.   equal to the specified precision, and if the value is greater than or
  1200.   equal to 0.00001. Otherwise the resulting string uses scientific format,
  1201.   and the Digits parameter specifies the minimum number of digits in the
  1202.   exponent (between 0 and 4).
  1203.  
  1204.   ffExponent - Scientific format. The value is converted to a string of the
  1205.   form "-d.ddd...E+dddd". The resulting string starts with a minus sign if
  1206.   the number is negative, and one digit always precedes the decimal point.
  1207.   The total number of digits in the resulting string (including the one
  1208.   before the decimal point) is given by the Precision parameter. The "E"
  1209.   exponent character in the resulting string is always followed by a plus
  1210.   or minus sign and up to four digits. The Digits parameter specifies the
  1211.   minimum number of digits in the exponent (between 0 and 4).
  1212.  
  1213.   ffFixed - Fixed point format. The value is converted to a string of the
  1214.   form "-ddd.ddd...". The resulting string starts with a minus sign if the
  1215.   number is negative, and at least one digit always precedes the decimal
  1216.   point. The number of digits after the decimal point is given by the Digits
  1217.   parameter--it must be between 0 and 18. If the number of digits to the
  1218.   left of the decimal point is greater than the specified precision, the
  1219.   resulting value will use scientific format.
  1220.  
  1221.   ffNumber - Number format. The value is converted to a string of the form
  1222.   "-d,ddd,ddd.ddd...". The ffNumber format corresponds to the ffFixed format,
  1223.   except that the resulting string contains thousand separators.
  1224.  
  1225.   ffCurrency - Currency format. The value is converted to a string that
  1226.   represents a currency amount. The conversion is controlled by the
  1227.   CurrencyString, CurrencyFormat, NegCurrFormat, ThousandSeparator, and
  1228.   DecimalSeparator global variables, all of which are initialized from the
  1229.   Currency Format in the International section of the Windows Control Panel.
  1230.   The number of digits after the decimal point is given by the Digits
  1231.   parameter--it must be between 0 and 18.
  1232.  
  1233.   For all formats, the actual characters used as decimal and thousand
  1234.   separators are obtained from the DecimalSeparator and ThousandSeparator
  1235.   global variables.
  1236.  
  1237.   If the given value is a NAN (not-a-number), the resulting string is 'NAN'.
  1238.   If the given value is positive infinity, the resulting string is 'INF'. If
  1239.   the given value is negative infinity, the resulting string is '-INF'. }
  1240.  
  1241. function FloatToStrF(Value: Extended; Format: TFloatFormat;
  1242.   Precision, Digits: Integer): string;
  1243.  
  1244. { CurrToStrF converts the currency value given by Value to its string
  1245.   representation. A call to CurrToStrF corresponds to a call to
  1246.   FloatToStrF with an implied precision of 19 digits. }
  1247.  
  1248. function CurrToStrF(Value: Currency; Format: TFloatFormat;
  1249.   Digits: Integer): string;
  1250.  
  1251. { FloatToText converts the given floating-point value to its decimal
  1252.   representation using the specified format, precision, and digits. The
  1253.   Value parameter must be a variable of type Extended or Currency, as
  1254.   indicated by the ValueType parameter. The resulting string of characters
  1255.   is stored in the given buffer, and the returned value is the number of
  1256.   characters stored. The resulting string is not null-terminated. For
  1257.   further details, see the description of the FloatToStrF function. }
  1258.  
  1259. function FloatToText(Buffer: PChar; const Value; ValueType: TFloatValue;
  1260.   Format: TFloatFormat; Precision, Digits: Integer): Integer;
  1261.  
  1262. { FormatFloat formats the floating-point value given by Value using the
  1263.   format string given by Format. The following format specifiers are
  1264.   supported in the format string:
  1265.  
  1266.   0     Digit placeholder. If the value being formatted has a digit in the
  1267.         position where the '0' appears in the format string, then that digit
  1268.         is copied to the output string. Otherwise, a '0' is stored in that
  1269.         position in the output string.
  1270.  
  1271.   #     Digit placeholder. If the value being formatted has a digit in the
  1272.         position where the '#' appears in the format string, then that digit
  1273.         is copied to the output string. Otherwise, nothing is stored in that
  1274.         position in the output string.
  1275.  
  1276.   .     Decimal point. The first '.' character in the format string
  1277.         determines the location of the decimal separator in the formatted
  1278.         value; any additional '.' characters are ignored. The actual
  1279.         character used as a the decimal separator in the output string is
  1280.         determined by the DecimalSeparator global variable. The default value
  1281.         of DecimalSeparator is specified in the Number Format of the
  1282.         International section in the Windows Control Panel.
  1283.  
  1284.   ,     Thousand separator. If the format string contains one or more ','
  1285.         characters, the output will have thousand separators inserted between
  1286.         each group of three digits to the left of the decimal point. The
  1287.         placement and number of ',' characters in the format string does not
  1288.         affect the output, except to indicate that thousand separators are
  1289.         wanted. The actual character used as a the thousand separator in the
  1290.         output is determined by the ThousandSeparator global variable. The
  1291.         default value of ThousandSeparator is specified in the Number Format
  1292.         of the International section in the Windows Control Panel.
  1293.  
  1294.   E+    Scientific notation. If any of the strings 'E+', 'E-', 'e+', or 'e-'
  1295.   E-    are contained in the format string, the number is formatted using
  1296.   e+    scientific notation. A group of up to four '0' characters can
  1297.   e-    immediately follow the 'E+', 'E-', 'e+', or 'e-' to determine the
  1298.         minimum number of digits in the exponent. The 'E+' and 'e+' formats
  1299.         cause a plus sign to be output for positive exponents and a minus
  1300.         sign to be output for negative exponents. The 'E-' and 'e-' formats
  1301.         output a sign character only for negative exponents.
  1302.  
  1303.   'xx'  Characters enclosed in single or double quotes are output as-is, and
  1304.   "xx"  do not affect formatting.
  1305.  
  1306.   ;     Separates sections for positive, negative, and zero numbers in the
  1307.         format string.
  1308.  
  1309.   The locations of the leftmost '0' before the decimal point in the format
  1310.   string and the rightmost '0' after the decimal point in the format string
  1311.   determine the range of digits that are always present in the output string.
  1312.  
  1313.   The number being formatted is always rounded to as many decimal places as
  1314.   there are digit placeholders ('0' or '#') to the right of the decimal
  1315.   point. If the format string contains no decimal point, the value being
  1316.   formatted is rounded to the nearest whole number.
  1317.  
  1318.   If the number being formatted has more digits to the left of the decimal
  1319.   separator than there are digit placeholders to the left of the '.'
  1320.   character in the format string, the extra digits are output before the
  1321.   first digit placeholder.
  1322.  
  1323.   To allow different formats for positive, negative, and zero values, the
  1324.   format string can contain between one and three sections separated by
  1325.   semicolons.
  1326.  
  1327.   One section - The format string applies to all values.
  1328.  
  1329.   Two sections - The first section applies to positive values and zeros, and
  1330.   the second section applies to negative values.
  1331.  
  1332.   Three sections - The first section applies to positive values, the second
  1333.   applies to negative values, and the third applies to zeros.
  1334.  
  1335.   If the section for negative values or the section for zero values is empty,
  1336.   that is if there is nothing between the semicolons that delimit the
  1337.   section, the section for positive values is used instead.
  1338.  
  1339.   If the section for positive values is empty, or if the entire format string
  1340.   is empty, the value is formatted using general floating-point formatting
  1341.   with 15 significant digits, corresponding to a call to FloatToStrF with
  1342.   the ffGeneral format. General floating-point formatting is also used if
  1343.   the value has more than 18 digits to the left of the decimal point and
  1344.   the format string does not specify scientific notation.
  1345.  
  1346.   The table below shows some sample formats and the results produced when
  1347.   the formats are applied to different values:
  1348.  
  1349.   Format string          1234        -1234       0.5         0
  1350.   -----------------------------------------------------------------------
  1351.                          1234        -1234       0.5         0
  1352.   0                      1234        -1234       1           0
  1353.   0.00                   1234.00     -1234.00    0.50        0.00
  1354.   #.##                   1234        -1234       .5
  1355.   #,##0.00               1,234.00    -1,234.00   0.50        0.00
  1356.   #,##0.00;(#,##0.00)    1,234.00    (1,234.00)  0.50        0.00
  1357.   #,##0.00;;Zero         1,234.00    -1,234.00   0.50        Zero
  1358.   0.000E+00              1.234E+03   -1.234E+03  5.000E-01   0.000E+00
  1359.   #.###E-0               1.234E3     -1.234E3    5E-1        0E0
  1360.   ----------------------------------------------------------------------- }
  1361.  
  1362. function FormatFloat(const Format: string; Value: Extended): string;
  1363.  
  1364. { FormatCurr formats the currency value given by Value using the format
  1365.   string given by Format. For further details, see the description of the
  1366.   FormatFloat function. }
  1367.  
  1368. function FormatCurr(const Format: string; Value: Currency): string;
  1369.  
  1370. { FloatToTextFmt converts the given floating-point value to its decimal
  1371.   representation using the specified format. The Value parameter must be a
  1372.   variable of type Extended or Currency, as indicated by the ValueType
  1373.   parameter. The resulting string of characters is stored in the given
  1374.   buffer, and the returned value is the number of characters stored. The
  1375.   resulting string is not null-terminated. For further details, see the
  1376.   description of the FormatFloat function. }
  1377.  
  1378. function FloatToTextFmt(Buffer: PChar; const Value; ValueType: TFloatValue;
  1379.   Format: PChar): Integer;
  1380.  
  1381. { StrToFloat converts the given string to a floating-point value. The string
  1382.   must consist of an optional sign (+ or -), a string of digits with an
  1383.   optional decimal point, and an optional 'E' or 'e' followed by a signed
  1384.   integer. Leading and trailing blanks in the string are ignored. The
  1385.   DecimalSeparator global variable defines the character that must be used
  1386.   as a decimal point. Thousand separators and currency symbols are not
  1387.   allowed in the string. If the string doesn't contain a valid value, an
  1388.   EConvertError exception is raised. }
  1389.  
  1390. function StrToFloat(const S: string): Extended;
  1391.  
  1392. { StrToCurr converts the given string to a currency value. For further
  1393.   details, see the description of the StrToFloat function. }
  1394.  
  1395. function StrToCurr(const S: string): Currency;
  1396.  
  1397. { TextToFloat converts the null-terminated string given by Buffer to a
  1398.   floating-point value which is returned in the variable given by Value.
  1399.   The Value parameter must be a variable of type Extended or Currency, as
  1400.   indicated by the ValueType parameter. The return value is True if the
  1401.   conversion was successful, or False if the string is not a valid
  1402.   floating-point value. For further details, see the description of the
  1403.   StrToFloat function. }
  1404.  
  1405. function TextToFloat(Buffer: PChar; var Value;
  1406.   ValueType: TFloatValue): Boolean;
  1407.  
  1408. { FloatToDecimal converts a floating-point value to a decimal representation
  1409.   that is suited for further formatting. The Value parameter must be a
  1410.   variable of type Extended or Currency, as indicated by the ValueType
  1411.   parameter. For values of type Extended, the Precision parameter specifies
  1412.   the requested number of significant digits in the result--the allowed range
  1413.   is 1..18. For values of type Currency, the Precision parameter is ignored,
  1414.   and the implied precision of the conversion is 19 digits. The Decimals
  1415.   parameter specifies the requested maximum number of digits to the left of
  1416.   the decimal point in the result. Precision and Decimals together control
  1417.   how the result is rounded. To produce a result that always has a given
  1418.   number of significant digits regardless of the magnitude of the number,
  1419.   specify 9999 for the Decimals parameter. The result of the conversion is
  1420.   stored in the specified TFloatRec record as follows:
  1421.  
  1422.   Exponent - Contains the magnitude of the number, i.e. the number of
  1423.   significant digits to the right of the decimal point. The Exponent field
  1424.   is negative if the absolute value of the number is less than one. If the
  1425.   number is a NAN (not-a-number), Exponent is set to -32768. If the number
  1426.   is INF or -INF (positive or negative infinity), Exponent is set to 32767.
  1427.  
  1428.   Negative - True if the number is negative, False if the number is zero
  1429.   or positive.
  1430.  
  1431.   Digits - Contains up to 18 (for type Extended) or 19 (for type Currency)
  1432.   significant digits followed by a null terminator. The implied decimal
  1433.   point (if any) is not stored in Digits. Trailing zeros are removed, and
  1434.   if the resulting number is zero, NAN, or INF, Digits contains nothing but
  1435.   the null terminator. }
  1436.  
  1437. procedure FloatToDecimal(var Result: TFloatRec; const Value;
  1438.   ValueType: TFloatValue; Precision, Decimals: Integer);
  1439.  
  1440. { Date/time support routines }
  1441.  
  1442. function DateTimeToTimeStamp(DateTime: TDateTime): TTimeStamp;
  1443.  
  1444. function TimeStampToDateTime(const TimeStamp: TTimeStamp): TDateTime;
  1445. function MSecsToTimeStamp(MSecs: Comp): TTimeStamp;
  1446. function TimeStampToMSecs(const TimeStamp: TTimeStamp): Comp;
  1447.  
  1448. { EncodeDate encodes the given year, month, and day into a TDateTime value.
  1449.   The year must be between 1 and 9999, the month must be between 1 and 12,
  1450.   and the day must be between 1 and N, where N is the number of days in the
  1451.   specified month. If the specified values are not within range, an
  1452.   EConvertError exception is raised. The resulting value is the number of
  1453.   days between 12/30/1899 and the given date. }
  1454.  
  1455. function EncodeDate(Year, Month, Day: Word): TDateTime;
  1456.  
  1457. { EncodeTime encodes the given hour, minute, second, and millisecond into a
  1458.   TDateTime value. The hour must be between 0 and 23, the minute must be
  1459.   between 0 and 59, the second must be between 0 and 59, and the millisecond
  1460.   must be between 0 and 999. If the specified values are not within range, an
  1461.   EConvertError exception is raised. The resulting value is a number between
  1462.   0 (inclusive) and 1 (not inclusive) that indicates the fractional part of
  1463.   a day given by the specified time. The value 0 corresponds to midnight,
  1464.   0.5 corresponds to noon, 0.75 corresponds to 6:00 pm, etc. }
  1465.  
  1466. function EncodeTime(Hour, Min, Sec, MSec: Word): TDateTime;
  1467.  
  1468. { DecodeDate decodes the integral (date) part of the given TDateTime value
  1469.   into its corresponding year, month, and day. If the given TDateTime value
  1470.   is less than or equal to zero, the year, month, and day return parameters
  1471.   are all set to zero. }
  1472.  
  1473. procedure DecodeDate(Date: TDateTime; var Year, Month, Day: Word);
  1474.  
  1475. { DecodeTime decodes the fractional (time) part of the given TDateTime value
  1476.   into its corresponding hour, minute, second, and millisecond. }
  1477.  
  1478. procedure DecodeTime(Time: TDateTime; var Hour, Min, Sec, MSec: Word);
  1479.  
  1480. { DateTimeToSystemTime converts a date and time from Delphi's TDateTime
  1481.   format into the Win32 API's TSystemTime format. }
  1482.  
  1483. procedure DateTimeToSystemTime(DateTime: TDateTime; var SystemTime: TSystemTime);
  1484.  
  1485. { SystemTimeToDateTime converts a date and time from the Win32 API's
  1486.   TSystemTime format into Delphi's TDateTime format. }
  1487.  
  1488. function SystemTimeToDateTime(const SystemTime: TSystemTime): TDateTime;
  1489.  
  1490. { DayOfWeek returns the day of the week of the given date. The result is an
  1491.   integer between 1 and 7, corresponding to Sunday through Saturday. }
  1492.  
  1493. function DayOfWeek(Date: TDateTime): Integer;
  1494.  
  1495. { Date returns the current date. }
  1496.  
  1497. function Date: TDateTime;
  1498.  
  1499. { Time returns the current time. }
  1500.  
  1501. function Time: TDateTime;
  1502.  
  1503. { Now returns the current date and time, corresponding to Date + Time. }
  1504.  
  1505. function Now: TDateTime;
  1506.  
  1507. { IncMonth returns Date shifted by the specified number of months.
  1508.   NumberOfMonths parameter can be negative, to return a date N months ago.
  1509.   If the input day of month is greater than the last day of the resulting
  1510.   month, the day is set to the last day of the resulting month.
  1511.   Input time of day is copied to the DateTime result.  }
  1512.  
  1513. function IncMonth(const Date: TDateTime; NumberOfMonths: Integer): TDateTime;
  1514.  
  1515. { IsLeapYear determines whether the given year is a leap year. }
  1516.  
  1517. function IsLeapYear(Year: Word): Boolean;
  1518.  
  1519. type
  1520.   PDayTable = ^TDayTable;
  1521.   TDayTable = array[1..12] of Word;
  1522.  
  1523. { The MonthDays array can be used to quickly find the number of
  1524.   days in a month:  MonthDays[IsLeapYear(Y), M]      }
  1525.  
  1526. const
  1527.   MonthDays: array [Boolean] of TDayTable =
  1528.     ((31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31),
  1529.      (31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31));
  1530.  
  1531. { DateToStr converts the date part of the given TDateTime value to a string.
  1532.   The conversion uses the format specified by the ShortDateFormat global
  1533.   variable. }
  1534.  
  1535. function DateToStr(Date: TDateTime): string;
  1536.  
  1537. { TimeToStr converts the time part of the given TDateTime value to a string.
  1538.   The conversion uses the format specified by the LongTimeFormat global
  1539.   variable. }
  1540.  
  1541. function TimeToStr(Time: TDateTime): string;
  1542.  
  1543. { DateTimeToStr converts the given date and time to a string. The resulting
  1544.   string consists of a date and time formatted using the ShortDateFormat and
  1545.   LongTimeFormat global variables. Time information is included in the
  1546.   resulting string only if the fractional part of the given date and time
  1547.   value is non-zero. }
  1548.  
  1549. function DateTimeToStr(DateTime: TDateTime): string;
  1550.  
  1551. { StrToDate converts the given string to a date value. The string must
  1552.   consist of two or three numbers, separated by the character defined by
  1553.   the DateSeparator global variable. The order for month, day, and year is
  1554.   determined by the ShortDateFormat global variable--possible combinations
  1555.   are m/d/y, d/m/y, and y/m/d. If the string contains only two numbers, it
  1556.   is interpreted as a date (m/d or d/m) in the current year. Year values
  1557.   between 0 and 99 are assumed to be in the current century. If the given
  1558.   string does not contain a valid date, an EConvertError exception is
  1559.   raised. }
  1560.  
  1561. function StrToDate(const S: string): TDateTime;
  1562.  
  1563. { StrToTime converts the given string to a time value. The string must
  1564.   consist of two or three numbers, separated by the character defined by
  1565.   the TimeSeparator global variable, optionally followed by an AM or PM
  1566.   indicator. The numbers represent hour, minute, and (optionally) second,
  1567.   in that order. If the time is followed by AM or PM, it is assumed to be
  1568.   in 12-hour clock format. If no AM or PM indicator is included, the time
  1569.   is assumed to be in 24-hour clock format. If the given string does not
  1570.   contain a valid time, an EConvertError exception is raised. }
  1571.  
  1572. function StrToTime(const S: string): TDateTime;
  1573.  
  1574. { StrToDateTime converts the given string to a date and time value. The
  1575.   string must contain a date optionally followed by a time. The date and
  1576.   time parts of the string must follow the formats described for the
  1577.   StrToDate and StrToTime functions. }
  1578.  
  1579. function StrToDateTime(const S: string): TDateTime;
  1580.  
  1581. { FormatDateTime formats the date-and-time value given by DateTime using the
  1582.   format given by Format. The following format specifiers are supported:
  1583.  
  1584.   c       Displays the date using the format given by the ShortDateFormat
  1585.           global variable, followed by the time using the format given by
  1586.           the LongTimeFormat global variable. The time is not displayed if
  1587.           the fractional part of the DateTime value is zero.
  1588.  
  1589.   d       Displays the day as a number without a leading zero (1-31).
  1590.  
  1591.   dd      Displays the day as a number with a leading zero (01-31).
  1592.  
  1593.   ddd     Displays the day as an abbreviation (Sun-Sat) using the strings
  1594.           given by the ShortDayNames global variable.
  1595.  
  1596.   dddd    Displays the day as a full name (Sunday-Saturday) using the strings
  1597.           given by the LongDayNames global variable.
  1598.  
  1599.   ddddd   Displays the date using the format given by the ShortDateFormat
  1600.           global variable.
  1601.  
  1602.   dddddd  Displays the date using the format given by the LongDateFormat
  1603.           global variable.
  1604.  
  1605.   g       Displays the period/era as an abbreviation (Japanese and
  1606.           Taiwanese locales only).
  1607.  
  1608.   gg      Displays the period/era as a full name.
  1609.  
  1610.   e       Displays the year in the current period/era as a number without
  1611.           a leading zero (Japanese, Korean and Taiwanese locales only).
  1612.  
  1613.   ee      Displays the year in the current period/era as a number with
  1614.           a leading zero (Japanese, Korean and Taiwanese locales only).
  1615.  
  1616.   m       Displays the month as a number without a leading zero (1-12). If
  1617.           the m specifier immediately follows an h or hh specifier, the
  1618.           minute rather than the month is displayed.
  1619.  
  1620.   mm      Displays the month as a number with a leading zero (01-12). If
  1621.           the mm specifier immediately follows an h or hh specifier, the
  1622.           minute rather than the month is displayed.
  1623.  
  1624.   mmm     Displays the month as an abbreviation (Jan-Dec) using the strings
  1625.           given by the ShortMonthNames global variable.
  1626.  
  1627.   mmmm    Displays the month as a full name (January-December) using the
  1628.           strings given by the LongMonthNames global variable.
  1629.  
  1630.   yy      Displays the year as a two-digit number (00-99).
  1631.  
  1632.   yyyy    Displays the year as a four-digit number (0000-9999).
  1633.  
  1634.   h       Displays the hour without a leading zero (0-23).
  1635.  
  1636.   hh      Displays the hour with a leading zero (00-23).
  1637.  
  1638.   n       Displays the minute without a leading zero (0-59).
  1639.  
  1640.   nn      Displays the minute with a leading zero (00-59).
  1641.  
  1642.   s       Displays the second without a leading zero (0-59).
  1643.  
  1644.   ss      Displays the second with a leading zero (00-59).
  1645.  
  1646.   t       Displays the time using the format given by the ShortTimeFormat
  1647.           global variable.
  1648.  
  1649.   tt      Displays the time using the format given by the LongTimeFormat
  1650.           global variable.
  1651.  
  1652.   am/pm   Uses the 12-hour clock for the preceding h or hh specifier, and
  1653.           displays 'am' for any hour before noon, and 'pm' for any hour
  1654.           after noon. The am/pm specifier can use lower, upper, or mixed
  1655.           case, and the result is displayed accordingly.
  1656.  
  1657.   a/p     Uses the 12-hour clock for the preceding h or hh specifier, and
  1658.           displays 'a' for any hour before noon, and 'p' for any hour after
  1659.           noon. The a/p specifier can use lower, upper, or mixed case, and
  1660.           the result is displayed accordingly.
  1661.  
  1662.   ampm    Uses the 12-hour clock for the preceding h or hh specifier, and
  1663.           displays the contents of the TimeAMString global variable for any
  1664.           hour before noon, and the contents of the TimePMString global
  1665.           variable for any hour after noon.
  1666.  
  1667.   /       Displays the date separator character given by the DateSeparator
  1668.           global variable.
  1669.  
  1670.   :       Displays the time separator character given by the TimeSeparator
  1671.           global variable.
  1672.  
  1673.   'xx'    Characters enclosed in single or double quotes are displayed as-is,
  1674.   "xx"    and do not affect formatting.
  1675.  
  1676.   Format specifiers may be written in upper case as well as in lower case
  1677.   letters--both produce the same result.
  1678.  
  1679.   If the string given by the Format parameter is empty, the date and time
  1680.   value is formatted as if a 'c' format specifier had been given.
  1681.  
  1682.   The following example:
  1683.  
  1684.     S := FormatDateTime('"The meeting is on" dddd, mmmm d, yyyy, ' +
  1685.       '"at" hh:mm AM/PM', StrToDateTime('2/15/95 10:30am'));
  1686.  
  1687.   assigns 'The meeting is on Wednesday, February 15, 1995 at 10:30 AM' to
  1688.   the string variable S. }
  1689.  
  1690. function FormatDateTime(const Format: string; DateTime: TDateTime): string;
  1691.  
  1692. { DateTimeToString converts the date and time value given by DateTime using
  1693.   the format string given by Format into the string variable given by Result.
  1694.   For further details, see the description of the FormatDateTime function. }
  1695.  
  1696. procedure DateTimeToString(var Result: string; const Format: string;
  1697.   DateTime: TDateTime);
  1698.  
  1699. { System error messages }
  1700.  
  1701. function SysErrorMessage(ErrorCode: Integer): string;
  1702.  
  1703. { Initialization file support }
  1704.  
  1705. function GetLocaleStr(Locale, LocaleType: Integer; const Default: string): string;
  1706. function GetLocaleChar(Locale, LocaleType: Integer; Default: Char): Char;
  1707.  
  1708. { GetFormatSettings resets all date and number format variables to their
  1709.   default values. }
  1710.  
  1711. procedure GetFormatSettings;
  1712.  
  1713. { Exception handling routines }
  1714.  
  1715. function ExceptObject: TObject;
  1716. function ExceptAddr: Pointer;
  1717.  
  1718. function ExceptionErrorMessage(ExceptObject: TObject; ExceptAddr: Pointer;
  1719.   Buffer: PChar; Size: Integer): Integer;
  1720.  
  1721. procedure ShowException(ExceptObject: TObject; ExceptAddr: Pointer);
  1722.  
  1723. procedure Abort;
  1724.  
  1725. procedure OutOfMemoryError;
  1726.  
  1727. procedure Beep;
  1728.  
  1729. { MBCS functions }
  1730.  
  1731. { LeadBytes is a char set that indicates which char values are lead bytes
  1732.   in multibyte character sets (Japanese, Chinese, etc).
  1733.   This set is always empty for western locales. }
  1734. var
  1735.   LeadBytes: set of Char = [];
  1736.  
  1737. { ByteType indicates what kind of byte exists at the Index'th byte in S.
  1738.   Western locales always return mbSingleByte.  Far East multibyte locales
  1739.   may also return mbLeadByte, indicating the byte is the first in a multibyte
  1740.   character sequence, and mbTrailByte, indicating that the byte is the second
  1741.   in a multibyte character sequence.  Parameters are assumed to be valid. }
  1742.  
  1743. function ByteType(const S: string; Index: Integer): TMbcsByteType;
  1744.  
  1745. { StrByteType works the same as ByteType, but on null-terminated PChar strings }
  1746.  
  1747. function StrByteType(Str: PChar; Index: Cardinal): TMbcsByteType;
  1748.  
  1749. { ByteToCharLen returns the character length of a MBCS string, scanning the
  1750.   string for up to MaxLen bytes.  In multibyte character sets, the number of
  1751.   characters in a string may be less than the number of bytes.  }
  1752.  
  1753. function ByteToCharLen(const S: string; MaxLen: Integer): Integer;
  1754.  
  1755. { CharToByteLen returns the byte length of a MBCS string, scanning the string
  1756.   for up to MaxLen characters. }
  1757.  
  1758. function CharToByteLen(const S: string; MaxLen: Integer): Integer;
  1759.  
  1760. { ByteToCharIndex returns the 1-based character index of the Index'th byte in
  1761.   a MBCS string.  Returns zero if Index is out of range:
  1762.   (Index <= 0) or (Index > Length(S)) }
  1763.  
  1764. function ByteToCharIndex(const S: string; Index: Integer): Integer;
  1765.  
  1766. { CharToByteIndex returns the 1-based byte index of the Index'th character
  1767.   in a MBCS string.  Returns zero if Index or Result are out of range:
  1768.   (Index <= 0) or (Index > Length(S)) or (Result would be > Length(S)) }
  1769.  
  1770. function CharToByteIndex(const S: string; Index: Integer): Integer;
  1771.  
  1772. { IsPathDelimiter returns True if the character at byte S[Index]
  1773.   is '\', and it is not a MBCS lead or trail byte. }
  1774.  
  1775. function IsPathDelimiter(const S: string; Index: Integer): Boolean;
  1776.  
  1777. { IsDelimiter returns True if the character at byte S[Index] matches any
  1778.   character in the Delimiters string, and the character is not a MBCS lead or
  1779.   trail byte.  S may contain multibyte characters; Delimiters must contain
  1780.   only single byte characters. }
  1781.  
  1782. function IsDelimiter(const Delimiters, S: string; Index: Integer): Boolean;
  1783.  
  1784. { LastDelimiter returns the byte index in S of the rightmost whole
  1785.   character that matches any character in Delimiters (except null (#0)).
  1786.   S may contain multibyte characters; Delimiters must contain only single
  1787.   byte non-null characters.
  1788.   Example: LastDelimiter('\.:', 'c:\filename.ext') returns 12. }
  1789.  
  1790. function LastDelimiter(const Delimiters, S: string): Integer;
  1791.  
  1792. { AnsiCompareFileName supports DOS file name comparison idiosyncracies
  1793.   in Far East locales (Zenkaku).  In non-MBCS locales, AnsiCompareFileName
  1794.   is identical to AnsiCompareText.  For general purpose file name comparisions,
  1795.   you should use this function instead of AnsiCompareText. }
  1796.  
  1797. function AnsiCompareFileName(const S1, S2: string): Integer;
  1798.  
  1799. { AnsiLowerCaseFileName supports lowercase conversion idiosyncracies of
  1800.   DOS file names in Far East locales (Zenkaku).  In non-MBCS locales,
  1801.   AnsiLowerCaseFileName is identical to AnsiLowerCase. }
  1802.  
  1803. function AnsiLowerCaseFileName(const S: string): string;
  1804.  
  1805. { AnsiUpperCaseFileName supports uppercase conversion idiosyncracies of
  1806.   DOS file names in Far East locales (Zenkaku).  In non-MBCS locales,
  1807.   AnsiUpperCaseFileName is identical to AnsiUpperCase. }
  1808.  
  1809. function AnsiUpperCaseFileName(const S: string): string;
  1810.  
  1811. { AnsiPos:  Same as Pos but supports MBCS strings }
  1812.  
  1813. function AnsiPos(const Substr, S: string): Integer;
  1814.  
  1815. { AnsiStrPos: Same as StrPos but supports MBCS strings }
  1816.  
  1817. function AnsiStrPos(Str, SubStr: PChar): PChar;
  1818.  
  1819. { AnsiStrRScan: Same as StrRScan but supports MBCS strings }
  1820.  
  1821. function AnsiStrRScan(Str: PChar; Chr: Char): PChar;
  1822.  
  1823. { AnsiStrScan: Same as StrScan but supports MBCS strings }
  1824.  
  1825. function AnsiStrScan(Str: PChar; Chr: Char): PChar;
  1826.  
  1827. { Package support routines }
  1828.  
  1829. { Package Info flags }
  1830.  
  1831. const
  1832.   pfNeverBuild = $00000001;
  1833.   pfDesignOnly = $00000002;
  1834.   pfRunOnly = $00000004;
  1835.   pfModuleTypeMask = $C0000000;
  1836.   pfExeModule = $00000000;
  1837.   pfPackageModule = $40000000;
  1838.   pfLibraryModule = $80000000;
  1839.  
  1840. { Unit info flags }
  1841.  
  1842. const
  1843.   ufMainUnit = $01;
  1844.   ufPackageUnit = $02;
  1845.   ufWeakUnit = $04;
  1846.   ufOrgWeakUnit = $08;
  1847.   ufImplicitUnit = $10;
  1848.  
  1849.   ufWeakPackageUnit = ufPackageUnit or ufWeakUnit;
  1850.  
  1851. { Procedure type of the callback given to GetPackageInfo.  Name is the actual
  1852.   name of the package element.  If IsUnit is True then Name is the name of
  1853.   a contained unit; a required package if False.  Param is the value passed
  1854.   to GetPackageInfo }
  1855.  
  1856. type
  1857.   TNameType = (ntContainsUnit, ntRequiresPackage);
  1858.  
  1859.   TPackageInfoProc = procedure (const Name: string; NameType: TNameType; Flags: Byte; Param: Pointer);
  1860.  
  1861. { LoadPackage loads a given package DLL, checks for duplicate units and
  1862.   calls the initialization blocks of all the contained units }
  1863.  
  1864. function LoadPackage(const Name: string): HMODULE;
  1865.  
  1866. { UnloadPackage does the opposite of LoadPackage by calling the finalization
  1867.   blocks of all contained units, then unloading the package DLL }
  1868.  
  1869. procedure UnloadPackage(Module: HMODULE);
  1870.  
  1871. { GetPackageInfo accesses the given package's info table and enumerates
  1872.   all the contained units and required packages }
  1873.  
  1874. procedure GetPackageInfo(Module: HMODULE; Param: Pointer; var Flags: Integer;
  1875.   InfoProc: TPackageInfoProc);
  1876.  
  1877. { GetPackageDescription loads the description resource from the package
  1878.   library. If the description resource does not exist,
  1879.   an empty string is returned. }
  1880. function GetPackageDescription(ModuleName: PChar): string;
  1881.  
  1882. { InitializePackage Validates and initializes the given package DLL }
  1883.  
  1884. procedure InitializePackage(Module: HMODULE);
  1885.  
  1886. { FinalizePackage finalizes the given package DLL }
  1887.  
  1888. procedure FinalizePackage(Module: HMODULE);
  1889.  
  1890. { RaiseLastWin32Error calls the GetLastError API to retrieve the code for }
  1891. { the last occuring Win32 error.  If GetLastError returns an error code,  }
  1892. { RaiseLastWin32Error then raises an exception with the error code and    }
  1893. { message associated with with error. }
  1894.  
  1895. procedure RaiseLastWin32Error;
  1896.  
  1897. { Win32Check is used to check the return value of a Win32 API function     }
  1898. { which returns a BOOL to indicate success.  If the Win32 API function     }
  1899. { returns False (indicating failure), Win32Check calls RaiseLastWin32Error }
  1900. { to raise an exception.  If the Win32 API function returns True,          }
  1901. { Win32Check returns True. }
  1902.  
  1903. function Win32Check(RetVal: BOOL): BOOL;
  1904.  
  1905. { Termination procedure support }
  1906.  
  1907. type
  1908.   TTerminateProc = function: Boolean;
  1909.  
  1910. { Call AddTerminateProc to add a terminate procedure to the system list of }
  1911. { termination procedures.  Delphi will call all of the function in the     }
  1912. { termination procedure list before an application terminates.  The user-  }
  1913. { defined TermProc function should return True if the application can      }
  1914. { safely terminate or False if the application cannot safely terminate.    }
  1915. { If one of the functions in the termination procedure list returns False, }
  1916. { the application will not terminate. }
  1917.  
  1918. procedure AddTerminateProc(TermProc: TTerminateProc);
  1919.  
  1920. { CallTerminateProcs is called by VCL when an application is about to }
  1921. { terminate.  It returns True only if all of the functions in the     }
  1922. { system's terminate procedure list return True.  This function is    }
  1923. { intended only to be called by Delphi, and it should not be called   }
  1924. { directly. }
  1925.  
  1926. function CallTerminateProcs: Boolean;
  1927.  
  1928. function GDAL: Longint;
  1929. procedure RCS;
  1930. procedure RPR;
  1931.  
  1932. {$I SYSUTILS.INC}
  1933.  
  1934. implementation
  1935.  
  1936. { R SYSUTILS.RES}
  1937.  
  1938. { Utility routines }
  1939.  
  1940. procedure DivMod(Dividend: Integer; Divisor: Word;
  1941.   var Result, Remainder: Word);
  1942. asm
  1943.         PUSH    EBX
  1944.         MOV     EBX,EDX
  1945.         MOV     EDX,EAX
  1946.         SHR     EDX,16
  1947.         DIV     BX
  1948.         MOV     EBX,Remainder
  1949.         MOV     [ECX],AX
  1950.         MOV     [EBX],DX
  1951.         POP     EBX
  1952. end;
  1953.  
  1954. procedure ConvertError(const Ident: string);
  1955. begin
  1956.   raise EConvertError.Create(Ident);
  1957. end;
  1958.  
  1959. procedure ConvertErrorFmt(const Ident: string; const Args: array of const);
  1960. begin
  1961.   raise EConvertError.CreateFmt(Ident, Args);
  1962. end;
  1963.  
  1964. { Memory management routines }
  1965.  
  1966. function AllocMem(Size: Cardinal): Pointer;
  1967. begin
  1968.   GetMem(Result, Size);
  1969.   FillChar(Result^, Size, 0);
  1970. end;
  1971.  
  1972. { Exit procedure handling }
  1973.  
  1974. type
  1975.   PExitProcInfo = ^TExitProcInfo;
  1976.   TExitProcInfo = record
  1977.     Next: PExitProcInfo;
  1978.     SaveExit: Pointer;
  1979.     Proc: TProcedure;
  1980.   end;
  1981.  
  1982. const
  1983.   ExitProcList: PExitProcInfo = nil;
  1984.  
  1985. procedure DoExitProc;
  1986. var
  1987.   P: PExitProcInfo;
  1988.   Proc: TProcedure;
  1989. begin
  1990.   P := ExitProcList;
  1991.   ExitProcList := P^.Next;
  1992.   ExitProc := P^.SaveExit;
  1993.   Proc := P^.Proc;
  1994.   Dispose(P);
  1995.   Proc;
  1996. end;
  1997.  
  1998. procedure AddExitProc(Proc: TProcedure);
  1999. var
  2000.   P: PExitProcInfo;
  2001. begin
  2002.   New(P);
  2003.   P^.Next := ExitProcList;
  2004.   P^.SaveExit := ExitProc;
  2005.   P^.Proc := Proc;
  2006.   ExitProcList := P;
  2007.   ExitProc := @DoExitProc;
  2008. end;
  2009.  
  2010. { String handling routines }
  2011.  
  2012. function NewStr(const S: string): PString;
  2013. begin
  2014.   if S = '' then Result := NullStr else
  2015.   begin
  2016.     New(Result);
  2017.     Result^ := S;
  2018.   end;
  2019. end;
  2020.  
  2021. procedure DisposeStr(P: PString);
  2022. begin
  2023.   if (P <> nil) and (P^ <> '') then Dispose(P);
  2024. end;
  2025.  
  2026. procedure AssignStr(var P: PString; const S: string);
  2027. var
  2028.   Temp: PString;
  2029. begin
  2030.   Temp := P;
  2031.   P := NewStr(S);
  2032.   DisposeStr(Temp);
  2033. end;
  2034.  
  2035. procedure AppendStr(var Dest: string; const S: string);
  2036. begin
  2037.   Dest := Dest + S;
  2038. end;
  2039.  
  2040. function UpperCase(const S: string): string;
  2041. var
  2042.   Ch: Char;
  2043.   L: Integer;
  2044.   Source, Dest: PChar;
  2045. begin
  2046.   L := Length(S);
  2047.   SetLength(Result, L);
  2048.   Source := Pointer(S);
  2049.   Dest := Pointer(Result);
  2050.   while L <> 0 do
  2051.   begin
  2052.     Ch := Source^;
  2053.     if (Ch >= 'a') and (Ch <= 'z') then Dec(Ch, 32);
  2054.     Dest^ := Ch;
  2055.     Inc(Source);
  2056.     Inc(Dest);
  2057.     Dec(L);
  2058.   end;
  2059. end;
  2060.  
  2061. function LowerCase(const S: string): string;
  2062. var
  2063.   Ch: Char;
  2064.   L: Integer;
  2065.   Source, Dest: PChar;
  2066. begin
  2067.   L := Length(S);
  2068.   SetLength(Result, L);
  2069.   Source := Pointer(S);
  2070.   Dest := Pointer(Result);
  2071.   while L <> 0 do
  2072.   begin
  2073.     Ch := Source^;
  2074.     if (Ch >= 'A') and (Ch <= 'Z') then Inc(Ch, 32);
  2075.     Dest^ := Ch;
  2076.     Inc(Source);
  2077.     Inc(Dest);
  2078.     Dec(L);
  2079.   end;
  2080. end;
  2081.  
  2082. function CompareStr(const S1, S2: string): Integer; assembler;
  2083. asm
  2084.         PUSH    ESI
  2085.         PUSH    EDI
  2086.         MOV     ESI,EAX
  2087.         MOV     EDI,EDX
  2088.         OR      EAX,EAX
  2089.         JE      @@1
  2090.         MOV     EAX,[EAX-4]
  2091. @@1:    OR      EDX,EDX
  2092.         JE      @@2
  2093.         MOV     EDX,[EDX-4]
  2094. @@2:    MOV     ECX,EAX
  2095.         CMP     ECX,EDX
  2096.         JBE     @@3
  2097.         MOV     ECX,EDX
  2098. @@3:    CMP     ECX,ECX
  2099.         REPE    CMPSB
  2100.         JE      @@4
  2101.         MOVZX   EAX,BYTE PTR [ESI-1]
  2102.         MOVZX   EDX,BYTE PTR [EDI-1]
  2103. @@4:    SUB     EAX,EDX
  2104.         POP     EDI
  2105.         POP     ESI
  2106. end;
  2107.  
  2108. function CompareMem(P1, P2: Pointer; Length: Integer): Boolean; assembler;
  2109. asm
  2110.         PUSH    ESI
  2111.         PUSH    EDI
  2112.         MOV     ESI,P1
  2113.         MOV     EDI,P2
  2114.         MOV     EDX,ECX
  2115.         XOR     EAX,EAX
  2116.         AND     EDX,3
  2117.         SHR     ECX,1
  2118.         SHR     ECX,1
  2119.         REPE    CMPSD
  2120.         JNE     @@2
  2121.         MOV     ECX,EDX
  2122.         REPE    CMPSB
  2123.         JNE     @@2
  2124. @@1:    INC     EAX
  2125. @@2:    POP     EDI
  2126.         POP     ESI
  2127. end;
  2128.  
  2129. function CompareText(const S1, S2: string): Integer; assembler;
  2130. asm
  2131.         PUSH    ESI
  2132.         PUSH    EDI
  2133.         PUSH    EBX
  2134.         MOV     ESI,EAX
  2135.         MOV     EDI,EDX
  2136.         OR      EAX,EAX
  2137.         JE      @@0
  2138.         MOV     EAX,[EAX-4]
  2139. @@0:    OR      EDX,EDX
  2140.         JE      @@1
  2141.         MOV     EDX,[EDX-4]
  2142. @@1:    MOV     ECX,EAX
  2143.         CMP     ECX,EDX
  2144.         JBE     @@2
  2145.         MOV     ECX,EDX
  2146. @@2:    CMP     ECX,ECX
  2147. @@3:    REPE    CMPSB
  2148.         JE      @@6
  2149.         MOV     BL,BYTE PTR [ESI-1]
  2150.         CMP     BL,'a'
  2151.         JB      @@4
  2152.         CMP     BL,'z'
  2153.         JA      @@4
  2154.         SUB     BL,20H
  2155. @@4:    MOV     BH,BYTE PTR [EDI-1]
  2156.         CMP     BH,'a'
  2157.         JB      @@5
  2158.         CMP     BH,'z'
  2159.         JA      @@5
  2160.         SUB     BH,20H
  2161. @@5:    CMP     BL,BH
  2162.         JE      @@3
  2163.         MOVZX   EAX,BL
  2164.         MOVZX   EDX,BH
  2165. @@6:    SUB     EAX,EDX
  2166.         POP     EBX
  2167.         POP     EDI
  2168.         POP     ESI
  2169. end;
  2170.  
  2171. function AnsiUpperCase(const S: string): string;
  2172. var
  2173.   Len: Integer;
  2174. begin
  2175.   Len := Length(S);
  2176.   SetString(Result, PChar(S), Len);
  2177.   CharUpperBuff(Pointer(Result), Len);
  2178. end;
  2179.  
  2180. function AnsiLowerCase(const S: string): string;
  2181. var
  2182.   Len: Integer;
  2183. begin
  2184.   Len := Length(S);
  2185.   SetString(Result, PChar(S), Len);
  2186.   CharLowerBuff(Pointer(Result), Len);
  2187. end;
  2188.  
  2189. function AnsiCompareStr(const S1, S2: string): Integer;
  2190. begin
  2191.   Result := CompareString(LOCALE_USER_DEFAULT, 0, PChar(S1), Length(S1),
  2192.     PChar(S2), Length(S2)) - 2;
  2193. end;
  2194.  
  2195. function AnsiCompareText(const S1, S2: string): Integer;
  2196. begin
  2197.   Result := CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, PChar(S1),
  2198.     Length(S1), PChar(S2), Length(S2)) - 2;
  2199. end;
  2200.  
  2201. function AnsiStrComp(S1, S2: PChar): Integer;
  2202. begin
  2203.   Result := CompareString(LOCALE_USER_DEFAULT, 0, S1, -1, S2, -1) - 2;
  2204. end;
  2205.  
  2206. function AnsiStrIComp(S1, S2: PChar): Integer;
  2207. begin
  2208.   Result := CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, S1, -1,
  2209.     S2, -1) - 2;
  2210. end;
  2211.  
  2212. function AnsiStrLComp(S1, S2: PChar; MaxLen: Cardinal): Integer;
  2213. begin
  2214.   Result := CompareString(LOCALE_USER_DEFAULT, 0,
  2215.     S1, MaxLen, S2, MaxLen) - 2;
  2216. end;
  2217.  
  2218. function AnsiStrLIComp(S1, S2: PChar; MaxLen: Cardinal): Integer;
  2219. begin
  2220.   Result := CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE,
  2221.     S1, MaxLen, S2, MaxLen) - 2;
  2222. end;
  2223.  
  2224. function AnsiStrLower(Str: PChar): PChar;
  2225. begin
  2226.   CharLower(Str);
  2227.   Result := Str;
  2228. end;
  2229.  
  2230. function AnsiStrUpper(Str: PChar): PChar;
  2231. begin
  2232.   CharUpper(Str);
  2233.   Result := Str;
  2234. end;
  2235.  
  2236. function Trim(const S: string): string;
  2237. var
  2238.   I, L: Integer;
  2239. begin
  2240.   L := Length(S);
  2241.   I := 1;
  2242.   while (I <= L) and (S[I] <= ' ') do Inc(I);
  2243.   if I > L then Result := '' else
  2244.   begin
  2245.     while S[L] <= ' ' do Dec(L);
  2246.     Result := Copy(S, I, L - I + 1);
  2247.   end;
  2248. end;
  2249.  
  2250. function TrimLeft(const S: string): string;
  2251. var
  2252.   I, L: Integer;
  2253. begin
  2254.   L := Length(S);
  2255.   I := 1;
  2256.   while (I <= L) and (S[I] <= ' ') do Inc(I);
  2257.   Result := Copy(S, I, Maxint);
  2258. end;
  2259.  
  2260. function TrimRight(const S: string): string;
  2261. var
  2262.   I: Integer;
  2263. begin
  2264.   I := Length(S);
  2265.   while (I > 0) and (S[I] <= ' ') do Dec(I);
  2266.   Result := Copy(S, 1, I);
  2267. end;
  2268.  
  2269. function QuotedStr(const S: string): string;
  2270. var
  2271.   I: Integer;
  2272. begin
  2273.   Result := S;
  2274.   for I := Length(Result) downto 1 do
  2275.     if Result[I] = '''' then Insert('''', Result, I);
  2276.   Result := '''' + Result + '''';
  2277. end;
  2278.  
  2279. function AnsiQuotedStr(const S: string; Quote: Char): string;
  2280. var
  2281.   P, Src, Dest: PChar;
  2282.   AddCount: Integer;
  2283. begin
  2284.   AddCount := 0;
  2285.   P := AnsiStrScan(PChar(S), Quote);
  2286.   while P <> nil do
  2287.   begin
  2288.     Inc(P);
  2289.     Inc(AddCount);
  2290.     P := AnsiStrScan(P, Quote);
  2291.   end;
  2292.   if AddCount = 0 then
  2293.   begin
  2294.     Result := Quote + S + Quote;
  2295.     Exit;
  2296.   end;
  2297.   SetLength(Result, Length(S) + AddCount + 2);
  2298.   Dest := Pointer(Result);
  2299.   Dest^ := Quote;
  2300.   Inc(Dest);
  2301.   Src := Pointer(S);
  2302.   P := AnsiStrScan(Src, Quote);
  2303.   repeat
  2304.     Inc(P);
  2305.     Move(Src^, Dest^, P - Src);
  2306.     Inc(Dest, P - Src);
  2307.     Dest^ := Quote;
  2308.     Inc(Dest);
  2309.     Src := P;
  2310.     P := AnsiStrScan(Src, Quote);
  2311.   until P = nil;
  2312.   P := StrEnd(Src);
  2313.   Move(Src^, Dest^, P - Src);
  2314.   Inc(Dest, P - Src);
  2315.   Dest^ := Quote;
  2316. end;
  2317.  
  2318. function AnsiExtractQuotedStr(var Src: PChar; Quote: Char): string;
  2319. var
  2320.   P, Dest: PChar;
  2321.   DropCount: Integer;
  2322. begin
  2323.   Result := '';
  2324.   if (Src = nil) or (Src^ <> Quote) then Exit;
  2325.   Inc(Src);
  2326.   DropCount := 1;
  2327.   P := Src;
  2328.   Src := AnsiStrScan(Src, Quote);
  2329.   while Src <> nil do   // count adjacent pairs of quote chars
  2330.   begin
  2331.     Inc(Src);
  2332.     if Src^ <> Quote then Break;
  2333.     Inc(Src);
  2334.     Inc(DropCount);
  2335.     Src := AnsiStrScan(Src, Quote);
  2336.   end;
  2337.   if Src = nil then Src := StrEnd(P);
  2338.   if ((Src - P) <= 1) then Exit;
  2339.   if DropCount = 1 then
  2340.     SetString(Result, P, Src - P - 1)
  2341.   else
  2342.   begin
  2343.     SetLength(Result, Src - P - DropCount);
  2344.     Dest := PChar(Result);
  2345.     Src := AnsiStrScan(P, Quote);
  2346.     while Src <> nil do
  2347.     begin
  2348.       Inc(Src);
  2349.       if Src^ <> Quote then Break;
  2350.       Move(P^, Dest^, Src - P);
  2351.       Inc(Dest, Src - P);
  2352.       Inc(Src);
  2353.       P := Src;
  2354.       Src := AnsiStrScan(Src, Quote);
  2355.     end;
  2356.     if Src = nil then Src := StrEnd(P);
  2357.     Move(P^, Dest^, Src - P - 1);
  2358.   end;
  2359. end;
  2360.  
  2361. function AdjustLineBreaks(const S: string): string;
  2362. var
  2363.   Source, SourceEnd, Dest: PChar;
  2364.   Extra: Integer;
  2365. begin
  2366.   Source := Pointer(S);
  2367.   SourceEnd := Source + Length(S);
  2368.   Extra := 0;
  2369.   while Source < SourceEnd do
  2370.   begin
  2371.     case Source^ of
  2372.       #10:
  2373.         Inc(Extra);
  2374.       #13:
  2375.         if Source[1] = #10 then Inc(Source) else Inc(Extra);
  2376.     else
  2377.       if Source^ in LeadBytes then
  2378.         Inc(Source)
  2379.     end;
  2380.     Inc(Source);
  2381.   end;
  2382.   if Extra = 0 then Result := S else
  2383.   begin
  2384.     Source := Pointer(S);
  2385.     SetString(Result, nil, SourceEnd - Source + Extra);
  2386.     Dest := Pointer(Result);
  2387.     while Source < SourceEnd do
  2388.       case Source^ of
  2389.         #10:
  2390.           begin
  2391.             Dest^ := #13;
  2392.             Inc(Dest);
  2393.             Dest^ := #10;
  2394.             Inc(Dest);
  2395.             Inc(Source);
  2396.           end;
  2397.         #13:
  2398.           begin
  2399.             Dest^ := #13;
  2400.             Inc(Dest);
  2401.             Dest^ := #10;
  2402.             Inc(Dest);
  2403.             Inc(Source);
  2404.             if Source^ = #10 then Inc(Source);
  2405.           end;
  2406.       else
  2407.         if Source^ in LeadBytes then
  2408.         begin
  2409.           Dest^ := Source^;
  2410.           Inc(Dest);
  2411.           Inc(Source);
  2412.         end;
  2413.         Dest^ := Source^;
  2414.         Inc(Dest);
  2415.         Inc(Source);
  2416.       end;
  2417.   end;
  2418. end;
  2419.  
  2420. function IsValidIdent(const Ident: string): Boolean;
  2421. const
  2422.   Alpha = ['A'..'Z', 'a'..'z', '_'];
  2423.   AlphaNumeric = Alpha + ['0'..'9'];
  2424. var
  2425.   I: Integer;
  2426. begin
  2427.   Result := False;
  2428.   if (Length(Ident) = 0) or not (Ident[1] in Alpha) then Exit;
  2429.   for I := 2 to Length(Ident) do if not (Ident[I] in AlphaNumeric) then Exit;
  2430.   Result := True;
  2431. end;
  2432.  
  2433. function IntToStr(Value: Integer): string;
  2434. begin
  2435.   FmtStr(Result, '%d', [Value]);
  2436. end;
  2437.  
  2438. function IntToHex(Value: Integer; Digits: Integer): string;
  2439. begin
  2440.   FmtStr(Result, '%.*x', [Digits, Value]);
  2441. end;
  2442.  
  2443. function StrToInt(const S: string): Integer;
  2444. var
  2445.   E: Integer;
  2446. begin
  2447.   Val(S, Result, E);
  2448.   if E <> 0 then ConvertErrorFmt(SInvalidInteger, [S]);
  2449. end;
  2450.  
  2451. function StrToIntDef(const S: string; Default: Integer): Integer;
  2452. var
  2453.   E: Integer;
  2454. begin
  2455.   Val(S, Result, E);
  2456.   if E <> 0 then Result := Default;
  2457. end;
  2458.  
  2459. type
  2460.   PStrData = ^TStrData;
  2461.   TStrData = record
  2462.     Ident: Integer;
  2463.     Buffer: PChar;
  2464.     BufSize: Integer;
  2465.     nChars: Integer;
  2466.   end;
  2467.  
  2468. function EnumStringModules(Instance: Longint; Data: Pointer): Boolean;
  2469. begin
  2470.   with PStrData(Data)^ do
  2471.   begin
  2472.     nChars := LoadString(Instance, Ident, Buffer, BufSize);
  2473.     Result := nChars = 0;
  2474.   end;
  2475. end;
  2476.  
  2477. function FindStringResource(Ident: Integer; Buffer: PChar; BufSize: Integer): Integer;
  2478. var
  2479.   StrData: TStrData;
  2480. begin
  2481.   StrData.Ident := Ident;
  2482.   StrData.Buffer := Buffer;
  2483.   StrData.BufSize := BufSize;
  2484.   StrData.nChars := 0;
  2485.   EnumResourceModules(EnumStringModules, @StrData);
  2486.   Result := StrData.nChars;
  2487. end;
  2488.  
  2489. function LoadStr(Ident: Integer): string;
  2490. var
  2491.   Buffer: array[0..1023] of Char;
  2492. begin
  2493.   SetString(Result, Buffer, FindStringResource(Ident, Buffer, SizeOf(Buffer)));
  2494. end;
  2495.  
  2496. function FmtLoadStr(Ident: Integer; const Args: array of const): string;
  2497. begin
  2498.   FmtStr(Result, LoadStr(Ident), Args);
  2499. end;
  2500.  
  2501. { File management routines }
  2502.  
  2503. function FileOpen(const FileName: string; Mode: Integer): Integer;
  2504. const
  2505.   AccessMode: array[0..2] of Integer = (
  2506.     GENERIC_READ,
  2507.     GENERIC_WRITE,
  2508.     GENERIC_READ or GENERIC_WRITE);
  2509.   ShareMode: array[0..4] of Integer = (
  2510.     0,
  2511.     0,
  2512.     FILE_SHARE_READ,
  2513.     FILE_SHARE_WRITE,
  2514.     FILE_SHARE_READ or FILE_SHARE_WRITE);
  2515. begin
  2516.   Result := CreateFile(PChar(FileName), AccessMode[Mode and 3],
  2517.     ShareMode[(Mode and $F0) shr 4], nil, OPEN_EXISTING,
  2518.     FILE_ATTRIBUTE_NORMAL, 0);
  2519. end;
  2520.  
  2521. function FileCreate(const FileName: string): Integer;
  2522. begin
  2523.   Result := CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE,
  2524.     0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  2525. end;
  2526.  
  2527. function FileRead(Handle: Integer; var Buffer; Count: Integer): Integer;
  2528. begin
  2529.   if not ReadFile(Handle, Buffer, Count, Result, nil) then Result := -1;
  2530. end;
  2531.  
  2532. function FileWrite(Handle: Integer; const Buffer; Count: Integer): Integer;
  2533. begin
  2534.   if not WriteFile(Handle, Buffer, Count, Result, nil) then Result := -1;
  2535. end;
  2536.  
  2537. function FileSeek(Handle, Offset, Origin: Integer): Integer;
  2538. begin
  2539.   Result := SetFilePointer(Handle, Offset, nil, Origin);
  2540. end;
  2541.  
  2542. procedure FileClose(Handle: Integer);
  2543. begin
  2544.   CloseHandle(Handle);
  2545. end;
  2546.  
  2547. function FileAge(const FileName: string): Integer;
  2548. var
  2549.   Handle: THandle;
  2550.   FindData: TWin32FindData;
  2551.   LocalFileTime: TFileTime;
  2552. begin
  2553.   Handle := FindFirstFile(PChar(FileName), FindData);
  2554.   if Handle <> INVALID_HANDLE_VALUE then
  2555.   begin
  2556.     Windows.FindClose(Handle);
  2557.     if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
  2558.     begin
  2559.       FileTimeToLocalFileTime(FindData.ftLastWriteTime, LocalFileTime);
  2560.       if FileTimeToDosDateTime(LocalFileTime, LongRec(Result).Hi,
  2561.         LongRec(Result).Lo) then Exit;
  2562.     end;
  2563.   end;
  2564.   Result := -1;
  2565. end;
  2566.  
  2567. function FileExists(const FileName: string): Boolean;
  2568. begin
  2569.   Result := FileAge(FileName) <> -1;
  2570. end;
  2571.  
  2572. function FileGetDate(Handle: Integer): Integer;
  2573. var
  2574.   FileTime, LocalFileTime: TFileTime;
  2575. begin
  2576.   if GetFileTime(Handle, nil, nil, @FileTime) and
  2577.     FileTimeToLocalFileTime(FileTime, LocalFileTime) and
  2578.     FileTimeToDosDateTime(LocalFileTime, LongRec(Result).Hi,
  2579.       LongRec(Result).Lo) then Exit;
  2580.   Result := -1;
  2581. end;
  2582.  
  2583. function FileSetDate(Handle: Integer; Age: Integer): Integer;
  2584. var
  2585.   LocalFileTime, FileTime: TFileTime;
  2586. begin
  2587.   Result := 0;
  2588.   if DosDateTimeToFileTime(LongRec(Age).Hi, LongRec(Age).Lo, LocalFileTime) and
  2589.     LocalFileTimeToFileTime(LocalFileTime, FileTime) and
  2590.     SetFileTime(Handle, nil, nil, @FileTime) then Exit;
  2591.   Result := GetLastError;
  2592. end;
  2593.  
  2594. function FileGetAttr(const FileName: string): Integer;
  2595. begin
  2596.   Result := GetFileAttributes(PChar(FileName));
  2597. end;
  2598.  
  2599. function FileSetAttr(const FileName: string; Attr: Integer): Integer;
  2600. begin
  2601.   Result := 0;
  2602.   if not SetFileAttributes(PChar(FileName), Attr) then
  2603.     Result := GetLastError;
  2604. end;
  2605.  
  2606. function FindMatchingFile(var F: TSearchRec): Integer;
  2607. var
  2608.   LocalFileTime: TFileTime;
  2609. begin
  2610.   with F do
  2611.   begin
  2612.     while FindData.dwFileAttributes and ExcludeAttr <> 0 do
  2613.       if not FindNextFile(FindHandle, FindData) then
  2614.       begin
  2615.         Result := GetLastError;
  2616.         Exit;
  2617.       end;
  2618.     FileTimeToLocalFileTime(FindData.ftLastWriteTime, LocalFileTime);
  2619.     FileTimeToDosDateTime(LocalFileTime, LongRec(Time).Hi,
  2620.       LongRec(Time).Lo);
  2621.     Size := FindData.nFileSizeLow;
  2622.     Attr := FindData.dwFileAttributes;
  2623.     Name := FindData.cFileName;
  2624.   end;
  2625.   Result := 0;
  2626. end;
  2627.  
  2628. function FindFirst(const Path: string; Attr: Integer;
  2629.   var F: TSearchRec): Integer;
  2630. const
  2631.   faSpecial = faHidden or faSysFile or faVolumeID or faDirectory;
  2632. begin
  2633.   F.ExcludeAttr := not Attr and faSpecial;
  2634.   F.FindHandle := FindFirstFile(PChar(Path), F.FindData);
  2635.   if F.FindHandle <> INVALID_HANDLE_VALUE then
  2636.   begin
  2637.     Result := FindMatchingFile(F);
  2638.     if Result <> 0 then FindClose(F);
  2639.   end else
  2640.     Result := GetLastError;
  2641. end;
  2642.  
  2643. function FindNext(var F: TSearchRec): Integer;
  2644. begin
  2645.   if FindNextFile(F.FindHandle, F.FindData) then
  2646.     Result := FindMatchingFile(F) else
  2647.     Result := GetLastError;
  2648. end;
  2649.  
  2650. procedure FindClose(var F: TSearchRec);
  2651. begin
  2652.   if F.FindHandle <> INVALID_HANDLE_VALUE then
  2653.     Windows.FindClose(F.FindHandle);
  2654. end;
  2655.  
  2656. function DeleteFile(const FileName: string): Boolean;
  2657. begin
  2658.   Result := Windows.DeleteFile(PChar(FileName));
  2659. end;
  2660.  
  2661. function RenameFile(const OldName, NewName: string): Boolean;
  2662. begin
  2663.   Result := MoveFile(PChar(OldName), PChar(NewName));
  2664. end;
  2665.  
  2666. function AnsiStrLastChar(P: PChar): PChar;
  2667. var
  2668.   LastByte: Integer;
  2669. begin
  2670.   LastByte := StrLen(P) - 1;
  2671.   Result := @P[LastByte];
  2672.   if StrByteType(P, LastByte) = mbTrailByte then Dec(Result);
  2673. end;
  2674.  
  2675. function AnsiLastChar(const S: string): PChar;
  2676. var
  2677.   LastByte: Integer;
  2678. begin
  2679.   LastByte := Length(S);
  2680.   if LastByte <> 0 then
  2681.   begin
  2682.     Result := @S[LastByte];
  2683.     if ByteType(S, LastByte) = mbTrailByte then Dec(Result);
  2684.   end
  2685.   else
  2686.     Result := nil;
  2687. end;
  2688.  
  2689. function LastDelimiter(const Delimiters, S: string): Integer;
  2690. var
  2691.   P: PChar;
  2692. begin
  2693.   Result := Length(S);
  2694.   P := PChar(Delimiters);
  2695.   while Result > 0 do
  2696.   begin
  2697.     if (S[Result] <> #0) and (StrScan(P, S[Result]) <> nil) then
  2698.       if (ByteType(S, Result) = mbTrailByte) then
  2699.         Dec(Result)
  2700.       else
  2701.         Exit;
  2702.     Dec(Result);
  2703.   end;
  2704. end;
  2705.  
  2706. function ChangeFileExt(const FileName, Extension: string): string;
  2707. var
  2708.   I: Integer;
  2709. begin
  2710.   I := LastDelimiter('.\:',Filename);
  2711.   if (I = 0) or (FileName[I] <> '.') then I := MaxInt;
  2712.   Result := Copy(FileName, 1, I - 1) + Extension;
  2713. end;
  2714.  
  2715. function ExtractFilePath(const FileName: string): string;
  2716. var
  2717.   I: Integer;
  2718. begin
  2719.   I := LastDelimiter('\:', FileName);
  2720.   Result := Copy(FileName, 1, I);
  2721. end;
  2722.  
  2723. function ExtractFileDir(const FileName: string): string;
  2724. var
  2725.   I: Integer;
  2726. begin
  2727.   I := LastDelimiter('\:',Filename);
  2728.   if (I > 1) and (FileName[I] = '\') and
  2729.     (not (FileName[I - 1] in ['\', ':']) or
  2730.     (ByteType(FileName, I-1) = mbTrailByte)) then Dec(I);
  2731.   Result := Copy(FileName, 1, I);
  2732. end;
  2733.  
  2734. function ExtractFileDrive(const FileName: string): string;
  2735. var
  2736.   I, J: Integer;
  2737. begin
  2738.   if (Length(FileName) >= 2) and (FileName[2] = ':') then
  2739.     Result := Copy(FileName, 1, 2)
  2740.   else if (Length(FileName) >= 2) and (FileName[1] = '\') and
  2741.     (FileName[2] = '\') then
  2742.   begin
  2743.     J := 0;
  2744.     I := 3;
  2745.     While (I < Length(FileName)) and (J < 2) do
  2746.     begin
  2747.       if FileName[I] = '\' then Inc(J);
  2748.       if J < 2 then Inc(I);
  2749.     end;
  2750.     if FileName[I] = '\' then Dec(I);
  2751.     Result := Copy(FileName, 1, I);
  2752.   end else Result := '';
  2753. end;
  2754.  
  2755. function ExtractFileName(const FileName: string): string;
  2756. var
  2757.   I: Integer;
  2758. begin
  2759.   I := LastDelimiter('\:', FileName);
  2760.   Result := Copy(FileName, I + 1, MaxInt);
  2761. end;
  2762.  
  2763. function ExtractFileExt(const FileName: string): string;
  2764. var
  2765.   I: Integer;
  2766. begin
  2767.   I := LastDelimiter('.\:', FileName);
  2768.   if (I > 0) and (FileName[I] = '.') then
  2769.     Result := Copy(FileName, I, MaxInt) else
  2770.     Result := '';
  2771. end;
  2772.  
  2773. function ExpandFileName(const FileName: string): string;
  2774. var
  2775.   FName: PChar;
  2776.   Buffer: array[0..MAX_PATH - 1] of Char;
  2777. begin
  2778.   SetString(Result, Buffer, GetFullPathName(PChar(FileName), SizeOf(Buffer),
  2779.     Buffer, FName));
  2780. end;
  2781.  
  2782. function GetUniversalName(const FileName: string): string;
  2783. type
  2784.   PNetResourceArray = ^TNetResourceArray;
  2785.   TNetResourceArray = array[0..MaxInt div SizeOf(TNetResource) - 1] of TNetResource;
  2786. var
  2787.   I, Count, BufSize, Size, NetResult: Integer;
  2788.   Drive: Char;
  2789.   NetHandle: THandle;
  2790.   NetResources: PNetResourceArray;
  2791.   RemoteNameInfo: array[0..1023] of Byte;
  2792. begin
  2793.   Result := FileName;
  2794.   if Win32Platform <> VER_PLATFORM_WIN32_WINDOWS then
  2795.   begin
  2796.     Size := SizeOf(RemoteNameInfo);
  2797.     if WNetGetUniversalName(PChar(FileName), UNIVERSAL_NAME_INFO_LEVEL,
  2798.       @RemoteNameInfo, Size) <> NO_ERROR then Exit;
  2799.     Result := PRemoteNameInfo(@RemoteNameInfo).lpUniversalName;
  2800.   end else
  2801.   begin
  2802.   { The following works around a bug in WNetGetUniversalName under Windows 95 }
  2803.     Drive := UpCase(FileName[1]);
  2804.     if (Drive < 'A') or (Drive > 'Z') or (Length(FileName) < 3) or
  2805.       (FileName[2] <> ':') or (FileName[3] <> '\') then
  2806.       Exit;
  2807.     if WNetOpenEnum(RESOURCE_CONNECTED, RESOURCETYPE_DISK, 0, nil,
  2808.       NetHandle) <> NO_ERROR then Exit;
  2809.     try
  2810.       BufSize := 50 * SizeOf(TNetResource);
  2811.       GetMem(NetResources, BufSize);
  2812.       try
  2813.         while True do
  2814.         begin
  2815.           Count := -1;
  2816.           Size := BufSize;
  2817.           NetResult := WNetEnumResource(NetHandle, Count, NetResources, Size);
  2818.           if NetResult = ERROR_MORE_DATA then
  2819.           begin
  2820.             BufSize := Size;
  2821.             ReallocMem(NetResources, BufSize);
  2822.             Continue;
  2823.           end;
  2824.           if NetResult <> NO_ERROR then Exit;
  2825.           for I := 0 to Count - 1 do
  2826.             with NetResources^[I] do
  2827.               if (lpLocalName <> nil) and (Drive = UpCase(lpLocalName[0])) then
  2828.               begin
  2829.                 Result := lpRemoteName + Copy(FileName, 3, Length(FileName) - 2);
  2830.                 Exit;
  2831.               end;
  2832.         end;
  2833.       finally
  2834.         FreeMem(NetResources, BufSize);
  2835.       end;
  2836.     finally
  2837.       WNetCloseEnum(NetHandle);
  2838.     end;
  2839.   end;
  2840. end;
  2841.  
  2842. function ExpandUNCFileName(const FileName: string): string;
  2843. begin
  2844.   { First get the local resource version of the file name }
  2845.   Result := ExpandFileName(FileName);
  2846.   if (Length(Result) >= 3) and (Result[2] = ':') and (Upcase(Result[1]) >= 'A')
  2847.     and (Upcase(Result[1]) <= 'Z') then
  2848.     Result := GetUniversalName(Result);
  2849. end;
  2850.  
  2851. function ExtractRelativePath(const BaseName, DestName: string): string;
  2852. var
  2853.   BasePath, DestPath: string;
  2854.   BaseDirs, DestDirs: array[0..129] of PChar;
  2855.   BaseDirCount, DestDirCount: Integer;
  2856.   I, J: Integer;
  2857.  
  2858.   function ExtractFilePathNoDrive(const FileName: string): string;
  2859.   begin
  2860.     Result := ExtractFilePath(FileName);
  2861.     Result := Copy(Result, Length(ExtractFileDrive(FileName)) + 1, 32767);
  2862.   end;
  2863.  
  2864.   procedure SplitDirs(var Path: string; var Dirs: array of PChar;
  2865.     var DirCount: Integer);
  2866.   var
  2867.     I, J: Integer;
  2868.   begin
  2869.     I := 1;
  2870.     J := 0;
  2871.     while I <= Length(Path) do
  2872.     begin
  2873.       if Path[I] in LeadBytes then Inc(I)
  2874.       else if Path[I] = '\' then             { Do not localize }
  2875.       begin
  2876.         Path[I] := #0;
  2877.         Dirs[J] := @Path[I + 1];
  2878.         Inc(J);
  2879.       end;
  2880.       Inc(I);
  2881.     end;
  2882.     DirCount := J - 1;
  2883.   end;
  2884.  
  2885. begin
  2886.   if AnsiCompareText(ExtractFileDrive(BaseName), ExtractFileDrive(DestName)) = 0 then
  2887.   begin
  2888.     BasePath := ExtractFilePathNoDrive(BaseName);
  2889.     DestPath := ExtractFilePathNoDrive(DestName);
  2890.     SplitDirs(BasePath, BaseDirs, BaseDirCount);
  2891.     SplitDirs(DestPath, DestDirs, DestDirCount);
  2892.     I := 0;
  2893.     while (I < BaseDirCount) and (I < DestDirCount) do
  2894.     begin
  2895.       if AnsiStrIComp(BaseDirs[I], DestDirs[I]) = 0 then
  2896.         Inc(I)
  2897.       else Break;
  2898.     end;
  2899.     Result := '';
  2900.     for J := I to BaseDirCount - 1 do
  2901.       Result := Result + '..\';              { Do not localize }
  2902.     for J := I to DestDirCount - 1 do
  2903.       Result := Result + DestDirs[J] + '\';  { Do not localize }
  2904.     Result := Result + ExtractFileName(DestName);
  2905.   end else Result := DestName;
  2906. end;
  2907.  
  2908. function FileSearch(const Name, DirList: string): string;
  2909. var
  2910.   I, P, L: Integer;
  2911. begin
  2912.   Result := Name;
  2913.   P := 1;
  2914.   L := Length(DirList);
  2915.   while True do
  2916.   begin
  2917.     if FileExists(Result) then Exit;
  2918.     while (P <= L) and (DirList[P] = ';') do Inc(P);
  2919.     if P > L then Break;
  2920.     I := P;
  2921.     while (P <= L) and (DirList[P] <> ';') do
  2922.     begin
  2923.       if DirList[P] in LeadBytes then Inc(P);
  2924.       Inc(P);
  2925.     end;
  2926.     Result := Copy(DirList, I, P - I);
  2927.     if not (AnsiLastChar(Result)^ in [':', '\']) then Result := Result + '\';
  2928.     Result := Result + Name;
  2929.   end;
  2930.   Result := '';
  2931. end;
  2932.  
  2933. function DiskFree(Drive: Byte): Integer;
  2934. var
  2935.   RootPath: array[0..4] of Char;
  2936.   RootPtr: PChar;
  2937.   SectorsPerCluster,
  2938.   BytesPerSector,
  2939.   FreeClusters,
  2940.   TotalClusters: Integer;
  2941. begin
  2942.   RootPtr := nil;
  2943.   if Drive > 0 then
  2944.   begin
  2945.     StrCopy(RootPath, 'A:\');
  2946.     RootPath[0] := Char(Drive + $40);
  2947.     RootPtr := RootPath;
  2948.   end;
  2949.   if GetDiskFreeSpace(RootPtr, SectorsPerCluster, BytesPerSector,
  2950.     FreeClusters, TotalClusters) then
  2951.     Result := SectorsPerCluster * BytesPerSector * FreeClusters
  2952.   else Result := -1;
  2953. end;
  2954.  
  2955. function DiskSize(Drive: Byte): Integer;
  2956. var
  2957.   RootPath: array[0..4] of Char;
  2958.   RootPtr: PChar;
  2959.   SectorsPerCluster,
  2960.   BytesPerSector,
  2961.   FreeClusters,
  2962.   TotalClusters: Integer;
  2963. begin
  2964.   RootPtr := nil;
  2965.   if Drive > 0 then
  2966.   begin
  2967.     StrCopy(RootPath, 'A:\');
  2968.     RootPath[0] := Char(Drive + $40);
  2969.     RootPtr := RootPath;
  2970.   end;
  2971.   if GetDiskFreeSpace(RootPtr, SectorsPerCluster, BytesPerSector,
  2972.     FreeClusters, TotalClusters) then
  2973.     Result := SectorsPerCluster * BytesPerSector * TotalClusters
  2974.   else Result := -1;
  2975. end;
  2976.  
  2977. function FileDateToDateTime(FileDate: Integer): TDateTime;
  2978. begin
  2979.   Result :=
  2980.     EncodeDate(
  2981.       LongRec(FileDate).Hi shr 9 + 1980,
  2982.       LongRec(FileDate).Hi shr 5 and 15,
  2983.       LongRec(FileDate).Hi and 31) +
  2984.     EncodeTime(
  2985.       LongRec(FileDate).Lo shr 11,
  2986.       LongRec(FileDate).Lo shr 5 and 63,
  2987.       LongRec(FileDate).Lo and 31 shl 1, 0);
  2988. end;
  2989.  
  2990. function DateTimeToFileDate(DateTime: TDateTime): Integer;
  2991. var
  2992.   Year, Month, Day, Hour, Min, Sec, MSec: Word;
  2993. begin
  2994.   DecodeDate(DateTime, Year, Month, Day);
  2995.   if (Year < 1980) or (Year > 2099) then Result := 0 else
  2996.   begin
  2997.     DecodeTime(DateTime, Hour, Min, Sec, MSec);
  2998.     LongRec(Result).Lo := (Sec shr 1) or (Min shl 5) or (Hour shl 11);
  2999.     LongRec(Result).Hi := Day or (Month shl 5) or ((Year - 1980) shl 9);
  3000.   end;
  3001. end;
  3002.  
  3003. function GetCurrentDir: string;
  3004. var
  3005.   Buffer: array[0..MAX_PATH - 1] of Char;
  3006. begin
  3007.   SetString(Result, Buffer, GetCurrentDirectory(SizeOf(Buffer), Buffer));
  3008. end;
  3009.  
  3010. function SetCurrentDir(const Dir: string): Boolean;
  3011. begin
  3012.   Result := SetCurrentDirectory(PChar(Dir));
  3013. end;
  3014.  
  3015. function CreateDir(const Dir: string): Boolean;
  3016. begin
  3017.   Result := CreateDirectory(PChar(Dir), nil);
  3018. end;
  3019.  
  3020. function RemoveDir(const Dir: string): Boolean;
  3021. begin
  3022.   Result := RemoveDirectory(PChar(Dir));
  3023. end;
  3024.  
  3025. { PChar routines }
  3026.  
  3027. function StrLen(Str: PChar): Cardinal; assembler;
  3028. asm
  3029.         MOV     EDX,EDI
  3030.         MOV     EDI,EAX
  3031.         MOV     ECX,0FFFFFFFFH
  3032.         XOR     AL,AL
  3033.         REPNE   SCASB
  3034.         MOV     EAX,0FFFFFFFEH
  3035.         SUB     EAX,ECX
  3036.         MOV     EDI,EDX
  3037. end;
  3038.  
  3039. function StrEnd(Str: PChar): PChar; assembler;
  3040. asm
  3041.         MOV     EDX,EDI
  3042.         MOV     EDI,EAX
  3043.         MOV     ECX,0FFFFFFFFH
  3044.         XOR     AL,AL
  3045.         REPNE   SCASB
  3046.         LEA     EAX,[EDI-1]
  3047.         MOV     EDI,EDX
  3048. end;
  3049.  
  3050. function StrMove(Dest, Source: PChar; Count: Cardinal): PChar; assembler;
  3051. asm
  3052.         PUSH    ESI
  3053.         PUSH    EDI
  3054.         MOV     ESI,EDX
  3055.         MOV     EDI,EAX
  3056.         MOV     EDX,ECX
  3057.         CMP     EDI,ESI
  3058.         JG      @@1
  3059.         JE      @@2
  3060.         SHR     ECX,2
  3061.         REP     MOVSD
  3062.         MOV     ECX,EDX
  3063.         AND     ECX,3
  3064.         REP     MOVSB
  3065.         JMP     @@2
  3066. @@1:    LEA     ESI,[ESI+ECX-1]
  3067.         LEA     EDI,[EDI+ECX-1]
  3068.         AND     ECX,3
  3069.         STD
  3070.         REP     MOVSB
  3071.         SUB     ESI,3
  3072.         SUB     EDI,3
  3073.         MOV     ECX,EDX
  3074.         SHR     ECX,2
  3075.         REP     MOVSD
  3076.         CLD
  3077. @@2:    POP     EDI
  3078.         POP     ESI
  3079. end;
  3080.  
  3081. function StrCopy(Dest, Source: PChar): PChar; assembler;
  3082. asm
  3083.         PUSH    EDI
  3084.         PUSH    ESI
  3085.         MOV     ESI,EAX
  3086.         MOV     EDI,EDX
  3087.         MOV     ECX,0FFFFFFFFH
  3088.         XOR     AL,AL
  3089.         REPNE   SCASB
  3090.         NOT     ECX
  3091.         MOV     EDI,ESI
  3092.         MOV     ESI,EDX
  3093.         MOV     EDX,ECX
  3094.         MOV     EAX,EDI
  3095.         SHR     ECX,2
  3096.         REP     MOVSD
  3097.         MOV     ECX,EDX
  3098.         AND     ECX,3
  3099.         REP     MOVSB
  3100.         POP     ESI
  3101.         POP     EDI
  3102. end;
  3103.  
  3104. function StrECopy(Dest, Source: PChar): PChar; assembler;
  3105. asm
  3106.         PUSH    EDI
  3107.         PUSH    ESI
  3108.         MOV     ESI,EAX
  3109.         MOV     EDI,EDX
  3110.         MOV     ECX,0FFFFFFFFH
  3111.         XOR     AL,AL
  3112.         REPNE   SCASB
  3113.         NOT     ECX
  3114.         MOV     EDI,ESI
  3115.         MOV     ESI,EDX
  3116.         MOV     EDX,ECX
  3117.         SHR     ECX,2
  3118.         REP     MOVSD
  3119.         MOV     ECX,EDX
  3120.         AND     ECX,3
  3121.         REP     MOVSB
  3122.         LEA     EAX,[EDI-1]
  3123.         POP     ESI
  3124.         POP     EDI
  3125. end;
  3126.  
  3127. function StrLCopy(Dest, Source: PChar; MaxLen: Cardinal): PChar; assembler;
  3128. asm
  3129.         PUSH    EDI
  3130.         PUSH    ESI
  3131.         PUSH    EBX
  3132.         MOV     ESI,EAX
  3133.         MOV     EDI,EDX
  3134.         MOV     EBX,ECX
  3135.         XOR     AL,AL
  3136.         TEST    ECX,ECX
  3137.         JZ      @@1
  3138.         REPNE   SCASB
  3139.         JNE     @@1
  3140.         INC     ECX
  3141. @@1:    SUB     EBX,ECX
  3142.         MOV     EDI,ESI
  3143.         MOV     ESI,EDX
  3144.         MOV     EDX,EDI
  3145.         MOV     ECX,EBX
  3146.         SHR     ECX,2
  3147.         REP     MOVSD
  3148.         MOV     ECX,EBX
  3149.         AND     ECX,3
  3150.         REP     MOVSB
  3151.         STOSB
  3152.         MOV     EAX,EDX
  3153.         POP     EBX
  3154.         POP     ESI
  3155.         POP     EDI
  3156. end;
  3157.  
  3158. function StrPCopy(Dest: PChar; const Source: string): PChar;
  3159. begin
  3160.   Result := StrLCopy(Dest, PChar(Source), 255);
  3161. end;
  3162.  
  3163. function StrPLCopy(Dest: PChar; const Source: string;
  3164.   MaxLen: Cardinal): PChar;
  3165. begin
  3166.   Result := StrLCopy(Dest, PChar(Source), MaxLen);
  3167. end;
  3168.  
  3169. function StrCat(Dest, Source: PChar): PChar;
  3170. begin
  3171.   StrCopy(StrEnd(Dest), Source);
  3172.   Result := Dest;
  3173. end;
  3174.  
  3175. function StrLCat(Dest, Source: PChar; MaxLen: Cardinal): PChar; assembler;
  3176. asm
  3177.         PUSH    EDI
  3178.         PUSH    ESI
  3179.         PUSH    EBX
  3180.         MOV     EDI,Dest
  3181.         MOV     ESI,Source
  3182.         MOV     EBX,MaxLen
  3183.         CALL    StrEnd
  3184.         MOV     ECX,EDI
  3185.         ADD     ECX,EBX
  3186.         SUB     ECX,EAX
  3187.         JBE     @@1
  3188.         MOV     EDX,ESI
  3189.         CALL    StrLCopy
  3190. @@1:    MOV     EAX,EDI
  3191.         POP     EBX
  3192.         POP     ESI
  3193.         POP     EDI
  3194. end;
  3195.  
  3196. function StrComp(Str1, Str2: PChar): Integer; assembler;
  3197. asm
  3198.         PUSH    EDI
  3199.         PUSH    ESI
  3200.         MOV     EDI,EDX
  3201.         MOV     ESI,EAX
  3202.         MOV     ECX,0FFFFFFFFH
  3203.         XOR     EAX,EAX
  3204.         REPNE   SCASB
  3205.         NOT     ECX
  3206.         MOV     EDI,EDX
  3207.         XOR     EDX,EDX
  3208.         REPE    CMPSB
  3209.         MOV     AL,[ESI-1]
  3210.         MOV     DL,[EDI-1]
  3211.         SUB     EAX,EDX
  3212.         POP     ESI
  3213.         POP     EDI
  3214. end;
  3215.  
  3216. function StrIComp(Str1, Str2: PChar): Integer; assembler;
  3217. asm
  3218.         PUSH    EDI
  3219.         PUSH    ESI
  3220.         MOV     EDI,EDX
  3221.         MOV     ESI,EAX
  3222.         MOV     ECX,0FFFFFFFFH
  3223.         XOR     EAX,EAX
  3224.         REPNE   SCASB
  3225.         NOT     ECX
  3226.         MOV     EDI,EDX
  3227.         XOR     EDX,EDX
  3228. @@1:    REPE    CMPSB
  3229.         JE      @@4
  3230.         MOV     AL,[ESI-1]
  3231.         CMP     AL,'a'
  3232.         JB      @@2
  3233.         CMP     AL,'z'
  3234.         JA      @@2
  3235.         SUB     AL,20H
  3236. @@2:    MOV     DL,[EDI-1]
  3237.         CMP     DL,'a'
  3238.         JB      @@3
  3239.         CMP     DL,'z'
  3240.         JA      @@3
  3241.         SUB     DL,20H
  3242. @@3:    SUB     EAX,EDX
  3243.         JE      @@1
  3244. @@4:    POP     ESI
  3245.         POP     EDI
  3246. end;
  3247.  
  3248. function StrLComp(Str1, Str2: PChar; MaxLen: Cardinal): Integer; assembler;
  3249. asm
  3250.         PUSH    EDI
  3251.         PUSH    ESI
  3252.         PUSH    EBX
  3253.         MOV     EDI,EDX
  3254.         MOV     ESI,EAX
  3255.         MOV     EBX,ECX
  3256.         XOR     EAX,EAX
  3257.         OR      ECX,ECX
  3258.         JE      @@1
  3259.         REPNE   SCASB
  3260.         SUB     EBX,ECX
  3261.         MOV     ECX,EBX
  3262.         MOV     EDI,EDX
  3263.         XOR     EDX,EDX
  3264.         REPE    CMPSB
  3265.         MOV     AL,[ESI-1]
  3266.         MOV     DL,[EDI-1]
  3267.         SUB     EAX,EDX
  3268. @@1:    POP     EBX
  3269.         POP     ESI
  3270.         POP     EDI
  3271. end;
  3272.  
  3273. function StrLIComp(Str1, Str2: PChar; MaxLen: Cardinal): Integer; assembler;
  3274. asm
  3275.         PUSH    EDI
  3276.         PUSH    ESI
  3277.         PUSH    EBX
  3278.         MOV     EDI,EDX
  3279.         MOV     ESI,EAX
  3280.         MOV     EBX,ECX
  3281.         XOR     EAX,EAX
  3282.         OR      ECX,ECX
  3283.         JE      @@4
  3284.         REPNE   SCASB
  3285.         SUB     EBX,ECX
  3286.         MOV     ECX,EBX
  3287.         MOV     EDI,EDX
  3288.         XOR     EDX,EDX
  3289. @@1:    REPE    CMPSB
  3290.         JE      @@4
  3291.         MOV     AL,[ESI-1]
  3292.         CMP     AL,'a'
  3293.         JB      @@2
  3294.         CMP     AL,'z'
  3295.         JA      @@2
  3296.         SUB     AL,20H
  3297. @@2:    MOV     DL,[EDI-1]
  3298.         CMP     DL,'a'
  3299.         JB      @@3
  3300.         CMP     DL,'z'
  3301.         JA      @@3
  3302.         SUB     DL,20H
  3303. @@3:    SUB     EAX,EDX
  3304.         JE      @@1
  3305. @@4:    POP     EBX
  3306.         POP     ESI
  3307.         POP     EDI
  3308. end;
  3309.  
  3310. function StrScan(Str: PChar; Chr: Char): PChar; assembler;
  3311. asm
  3312.         PUSH    EDI
  3313.         PUSH    EAX
  3314.         MOV     EDI,Str
  3315.         MOV     ECX,0FFFFFFFFH
  3316.         XOR     AL,AL
  3317.         REPNE   SCASB
  3318.         NOT     ECX
  3319.         POP     EDI
  3320.         MOV     AL,Chr
  3321.         REPNE   SCASB
  3322.         MOV     EAX,0
  3323.         JNE     @@1
  3324.         MOV     EAX,EDI
  3325.         DEC     EAX
  3326. @@1:    POP     EDI
  3327. end;
  3328.  
  3329. function StrRScan(Str: PChar; Chr: Char): PChar; assembler;
  3330. asm
  3331.         PUSH    EDI
  3332.         MOV     EDI,Str
  3333.         MOV     ECX,0FFFFFFFFH
  3334.         XOR     AL,AL
  3335.         REPNE   SCASB
  3336.         NOT     ECX
  3337.         STD
  3338.         DEC     EDI
  3339.         MOV     AL,Chr
  3340.         REPNE   SCASB
  3341.         MOV     EAX,0
  3342.         JNE     @@1
  3343.         MOV     EAX,EDI
  3344.         INC     EAX
  3345. @@1:    CLD
  3346.         POP     EDI
  3347. end;
  3348.  
  3349. function StrPos(Str1, Str2: PChar): PChar; assembler;
  3350. asm
  3351.         PUSH    EDI
  3352.         PUSH    ESI
  3353.         PUSH    EBX
  3354.         OR      EAX,EAX
  3355.         JE      @@2
  3356.         OR      EDX,EDX
  3357.         JE      @@2
  3358.         MOV     EBX,EAX
  3359.         MOV     EDI,EDX
  3360.         XOR     AL,AL
  3361.         MOV     ECX,0FFFFFFFFH
  3362.         REPNE   SCASB
  3363.         NOT     ECX
  3364.         DEC     ECX
  3365.         JE      @@2
  3366.         MOV     ESI,ECX
  3367.         MOV     EDI,EBX
  3368.         MOV     ECX,0FFFFFFFFH
  3369.         REPNE   SCASB
  3370.         NOT     ECX
  3371.         SUB     ECX,ESI
  3372.         JBE     @@2
  3373.         MOV     EDI,EBX
  3374.         LEA     EBX,[ESI-1]
  3375. @@1:    MOV     ESI,EDX
  3376.         LODSB
  3377.         REPNE   SCASB
  3378.         JNE     @@2
  3379.         MOV     EAX,ECX
  3380.         PUSH    EDI
  3381.         MOV     ECX,EBX
  3382.         REPE    CMPSB
  3383.         POP     EDI
  3384.         MOV     ECX,EAX
  3385.         JNE     @@1
  3386.         LEA     EAX,[EDI-1]
  3387.         JMP     @@3
  3388. @@2:    XOR     EAX,EAX
  3389. @@3:    POP     EBX
  3390.         POP     ESI
  3391.         POP     EDI
  3392. end;
  3393.  
  3394. function StrUpper(Str: PChar): PChar; assembler;
  3395. asm
  3396.         PUSH    ESI
  3397.         MOV     ESI,Str
  3398.         MOV     EDX,Str
  3399. @@1:    LODSB
  3400.         OR      AL,AL
  3401.         JE      @@2
  3402.         CMP     AL,'a'
  3403.         JB      @@1
  3404.         CMP     AL,'z'
  3405.         JA      @@1
  3406.         SUB     AL,20H
  3407.         MOV     [ESI-1],AL
  3408.         JMP     @@1
  3409. @@2:    XCHG    EAX,EDX
  3410.         POP     ESI
  3411. end;
  3412.  
  3413. function StrLower(Str: PChar): PChar; assembler;
  3414. asm
  3415.         PUSH    ESI
  3416.         MOV     ESI,Str
  3417.         MOV     EDX,Str
  3418. @@1:    LODSB
  3419.         OR      AL,AL
  3420.         JE      @@2
  3421.         CMP     AL,'A'
  3422.         JB      @@1
  3423.         CMP     AL,'Z'
  3424.         JA      @@1
  3425.         ADD     AL,20H
  3426.         MOV     [ESI-1],AL
  3427.         JMP     @@1
  3428. @@2:    XCHG    EAX,EDX
  3429.         POP     ESI
  3430. end;
  3431.  
  3432. function StrPas(Str: PChar): string;
  3433. begin
  3434.   Result := Str;
  3435. end;
  3436.  
  3437. function StrAlloc(Size: Cardinal): PChar;
  3438. begin
  3439.   Inc(Size, SizeOf(Cardinal));
  3440.   GetMem(Result, Size);
  3441.   Cardinal(Pointer(Result)^) := Size;
  3442.   Inc(Result, SizeOf(Cardinal));
  3443. end;
  3444.  
  3445. function StrBufSize(Str: PChar): Cardinal;
  3446. begin
  3447.   Dec(Str, SizeOf(Cardinal));
  3448.   Result := Cardinal(Pointer(Str)^) - SizeOf(Cardinal);
  3449. end;
  3450.  
  3451. function StrNew(Str: PChar): PChar;
  3452. var
  3453.   Size: Cardinal;
  3454. begin
  3455.   if Str = nil then Result := nil else
  3456.   begin
  3457.     Size := StrLen(Str) + 1;
  3458.     Result := StrMove(StrAlloc(Size), Str, Size);
  3459.   end;
  3460. end;
  3461.  
  3462. procedure StrDispose(Str: PChar);
  3463. begin
  3464.   if Str <> nil then
  3465.   begin
  3466.     Dec(Str, SizeOf(Cardinal));
  3467.     FreeMem(Str, Cardinal(Pointer(Str)^));
  3468.   end;
  3469. end;
  3470.  
  3471. { String formatting routines }
  3472.  
  3473. var
  3474.   FormatErrorStrs: array[0..1] of string = (
  3475.     SInvalidFormat, SArgumentMissing);
  3476.  
  3477. procedure FormatError(ErrorCode: Integer; Format: PChar; FmtLen: Cardinal);
  3478. var
  3479.   Buffer: array[0..31] of Char;
  3480. begin
  3481.   if FmtLen > 31 then FmtLen := 31;
  3482.   if StrByteType(Format, FmtLen-1) = mbLeadByte then Dec(FmtLen);
  3483.   StrMove(Buffer, Format, FmtLen);
  3484.   Buffer[FmtLen] := #0;
  3485.   ConvertErrorFmt(FormatErrorStrs[ErrorCode], [PChar(@Buffer)]);
  3486. end;
  3487.  
  3488. procedure FormatVarToStr(var S: string; const V: Variant);
  3489. begin
  3490.   S := V;
  3491. end;
  3492.  
  3493. procedure FormatClearStr(var S: string);
  3494. begin
  3495.   S := '';
  3496. end;
  3497.  
  3498. function FormatBuf(var Buffer; BufLen: Cardinal; const Format;
  3499.   FmtLen: Cardinal; const Args: array of const): Cardinal;
  3500. const
  3501.   C10000: Single = 10000;
  3502. var
  3503.   ArgIndex, Width, Prec: Integer;
  3504.   BufferOrg, FormatOrg, FormatPtr, TempStr: PChar;
  3505.   JustFlag: Byte;
  3506.   StrBuf: array[0..39] of Char;
  3507.   TempAnsiStr: string;
  3508. asm
  3509.         PUSH    EBX
  3510.         PUSH    ESI
  3511.         PUSH    EDI
  3512.         MOV     EDI,EAX
  3513.         MOV     ESI,ECX
  3514.         ADD     ECX,FmtLen
  3515.         MOV     BufferOrg,EDI
  3516.         XOR     EAX,EAX
  3517.         MOV     ArgIndex,EAX
  3518.         MOV     TempStr,EAX
  3519.         MOV     TempAnsiStr,EAX
  3520.  
  3521. @Loop:
  3522.         OR      EDX,EDX
  3523.         JE      @Done
  3524.  
  3525. @NextChar:
  3526.         CMP     ESI,ECX
  3527.         JE      @Done
  3528.         LODSB
  3529.         CMP     AL,'%'
  3530.         JE      @Format
  3531.  
  3532. @StoreChar:
  3533.         STOSB
  3534.         DEC     EDX
  3535.         JNE     @NextChar
  3536.  
  3537. @Done:
  3538.         MOV     EAX,EDI
  3539.         SUB     EAX,BufferOrg
  3540.         JMP     @Exit
  3541.  
  3542. @Format:
  3543.         CMP     ESI,ECX
  3544.         JE      @Done
  3545.         LODSB
  3546.         CMP     AL,'%'
  3547.         JE      @StoreChar
  3548.         LEA     EBX,[ESI-2]
  3549.         MOV     FormatOrg,EBX
  3550. @A0:    MOV     JustFlag,AL
  3551.         CMP     AL,'-'
  3552.         JNE     @A1
  3553.         CMP     ESI,ECX
  3554.         JE      @Done
  3555.         LODSB
  3556. @A1:    CALL    @Specifier
  3557.         CMP     AL,':'
  3558.         JNE     @A2
  3559.         MOV     ArgIndex,EBX
  3560.         CMP     ESI,ECX
  3561.         JE      @Done
  3562.         LODSB
  3563.         JMP     @A0
  3564. @A2:    MOV     Width,EBX
  3565.         MOV     EBX,-1
  3566.         CMP     AL,'.'
  3567.         JNE     @A3
  3568.         CMP     ESI,ECX
  3569.         JE      @Done
  3570.         LODSB
  3571.         CALL    @Specifier
  3572. @A3:    MOV     Prec,EBX
  3573.         MOV     FormatPtr,ESI
  3574.         PUSH    ECX
  3575.         PUSH    EDX
  3576.         CALL    @Convert
  3577.         POP     EDX
  3578.         MOV     EBX,Width
  3579.         SUB     EBX,ECX
  3580.         JAE     @A4
  3581.         XOR     EBX,EBX
  3582. @A4:    CMP     JustFlag,'-'
  3583.         JNE     @A6
  3584.         SUB     EDX,ECX
  3585.         JAE     @A5
  3586.         ADD     ECX,EDX
  3587.         XOR     EDX,EDX
  3588. @A5:    REP     MOVSB
  3589. @A6:    XCHG    EBX,ECX
  3590.         SUB     EDX,ECX
  3591.         JAE     @A7
  3592.         ADD     ECX,EDX
  3593.         XOR     EDX,EDX
  3594. @A7:    MOV     AL,' '
  3595.         REP     STOSB
  3596.         XCHG    EBX,ECX
  3597.         SUB     EDX,ECX
  3598.         JAE     @A8
  3599.         ADD     ECX,EDX
  3600.         XOR     EDX,EDX
  3601. @A8:    REP     MOVSB
  3602.         CMP     TempStr,0
  3603.         JE      @A9
  3604.         PUSH    EDX
  3605.         LEA     EAX,TempStr
  3606.         CALL    FormatClearStr
  3607.         POP     EDX
  3608. @A9:    POP     ECX
  3609.         MOV     ESI,FormatPtr
  3610.         JMP     @Loop
  3611.  
  3612. @Specifier:
  3613.         XOR     EBX,EBX
  3614.         CMP     AL,'*'
  3615.         JE      @B3
  3616. @B1:    CMP     AL,'0'
  3617.         JB      @B5
  3618.         CMP     AL,'9'
  3619.         JA      @B5
  3620.         IMUL    EBX,EBX,10
  3621.         SUB     AL,'0'
  3622.         MOVZX   EAX,AL
  3623.         ADD     EBX,EAX
  3624.         CMP     ESI,ECX
  3625.         JE      @B2
  3626.         LODSB
  3627.         JMP     @B1
  3628. @B2:    POP     EAX
  3629.         JMP     @Done
  3630. @B3:    MOV     EAX,ArgIndex
  3631.         CMP     EAX,Args.Integer[-4]
  3632.         JA      @B4
  3633.         INC     ArgIndex
  3634.         MOV     EBX,Args
  3635.         CMP     [EBX+EAX*8].Byte[4],vtInteger
  3636.         MOV     EBX,[EBX+EAX*8]
  3637.         JE      @B4
  3638.         XOR     EBX,EBX
  3639. @B4:    CMP     ESI,ECX
  3640.         JE      @B2
  3641.         LODSB
  3642. @B5:    RET
  3643.  
  3644. @Convert:
  3645.         AND     AL,0DFH
  3646.         MOV     CL,AL
  3647.         MOV     EAX,1
  3648.         MOV     EBX,ArgIndex
  3649.         CMP     EBX,Args.Integer[-4]
  3650.         JA      @ErrorExit
  3651.         INC     ArgIndex
  3652.         MOV     ESI,Args
  3653.         LEA     ESI,[ESI+EBX*8]
  3654.         MOV     EAX,[ESI].Integer[0]
  3655.         MOVZX   EBX,[ESI].Byte[4]
  3656.         JMP     @CvtVector.Pointer[EBX*4]
  3657.  
  3658. @CvtVector:
  3659.         DD      @CvtInteger
  3660.         DD      @CvtBoolean
  3661.         DD      @CvtChar
  3662.         DD      @CvtExtended
  3663.         DD      @CvtShortStr
  3664.         DD      @CvtPointer
  3665.         DD      @CvtPChar
  3666.         DD      @CvtObject
  3667.         DD      @CvtClass
  3668.         DD      @CvtWideChar
  3669.         DD      @CvtPWideChar
  3670.         DD      @CvtAnsiStr
  3671.         DD      @CvtCurrency
  3672.         DD      @CvtVariant
  3673.         DD      @CvtInterface
  3674.         DD      @CvtWideString
  3675.  
  3676. @CvtBoolean:
  3677. @CvtObject:
  3678. @CvtClass:
  3679. @CvtWideChar:
  3680. @CvtInterface:
  3681. @CvtError:
  3682.         XOR     EAX,EAX
  3683.  
  3684. @ErrorExit:
  3685.         CALL    @ClearTmpAnsiStr
  3686.         MOV     EDX,FormatOrg
  3687.         MOV     ECX,FormatPtr
  3688.         SUB     ECX,EDX
  3689.         CALL    FormatError
  3690.         // The above call raises an exception and does not return
  3691.  
  3692. @CvtInteger:
  3693.         CMP     CL,'D'
  3694.         JE      @C1
  3695.         CMP     CL,'U'
  3696.         JE      @C2
  3697.         CMP     CL,'X'
  3698.         JNE     @CvtError
  3699.         MOV     ECX,16
  3700.         JMP     @CvtLong
  3701. @C1:    OR      EAX,EAX
  3702.         JNS     @C2
  3703.         NEG     EAX
  3704.         CALL    @C2
  3705.         MOV     AL,'-'
  3706.         INC     ECX
  3707.         DEC     ESI
  3708.         MOV     [ESI],AL
  3709.         RET
  3710. @C2:    MOV     ECX,10
  3711.  
  3712. @CvtLong:
  3713.         LEA     ESI,StrBuf[16]
  3714. @D1:    XOR     EDX,EDX
  3715.         DIV     ECX
  3716.         ADD     DL,'0'
  3717.         CMP     DL,'0'+10
  3718.         JB      @D2
  3719.         ADD     DL,'A'-'0'-10
  3720. @D2:    DEC     ESI
  3721.         MOV     [ESI],DL
  3722.         OR      EAX,EAX
  3723.         JNE     @D1
  3724.         LEA     ECX,StrBuf[16]
  3725.         SUB     ECX,ESI
  3726.         MOV     EDX,Prec
  3727.         CMP     EDX,16
  3728.         JB      @D3
  3729.         RET
  3730. @D3:    SUB     EDX,ECX
  3731.         JBE     @D5
  3732.         ADD     ECX,EDX
  3733.         MOV     AL,'0'
  3734. @D4:    DEC     ESI
  3735.         MOV     [ESI],AL
  3736.         DEC     EDX
  3737.         JNE     @D4
  3738. @D5:    RET
  3739.  
  3740. @CvtChar:
  3741.         CMP     CL,'S'
  3742.         JNE     @CvtError
  3743.         MOV     ECX,1
  3744.         RET
  3745.  
  3746. @CvtVariant:
  3747.         CMP     CL,'S'
  3748.         JNE     @CvtError
  3749.         CMP     [EAX].TVarData.VType,varNull
  3750.         JBE     @CvtEmptyStr
  3751.         MOV     EDX,EAX
  3752.         LEA     EAX,TempStr
  3753.         CALL    FormatVarToStr
  3754.         MOV     ESI,TempStr
  3755.         JMP     @CvtStrRef
  3756.  
  3757. @CvtEmptyStr:
  3758.         XOR     ECX,ECX
  3759.         RET
  3760.  
  3761. @CvtShortStr:
  3762.         CMP     CL,'S'
  3763.         JNE     @CvtError
  3764.         MOV     ESI,EAX
  3765.         LODSB
  3766.         MOVZX   ECX,AL
  3767.         JMP     @CvtStrLen
  3768.  
  3769. @CvtPWideChar:
  3770.         MOV    ESI,OFFSET System.@LStrFromPWChar
  3771.         JMP    @CvtWideThing
  3772.  
  3773. @CvtWideString:
  3774.         MOV    ESI,OFFSET System.@LStrFromWStr
  3775.  
  3776. @CvtWideThing:
  3777.         CMP    CL,'S'
  3778.         JNE    @CvtError
  3779.         MOV    EDX,EAX
  3780.         LEA    EAX,TempAnsiStr
  3781.         CALL   ESI
  3782.         MOV    ESI,TempAnsiStr
  3783.         MOV    EAX,ESI
  3784.         JMP    @CvtStrRef
  3785.  
  3786. @CvtAnsiStr:
  3787.         CMP     CL,'S'
  3788.         JNE     @CvtError
  3789.         MOV     ESI,EAX
  3790.  
  3791. @CvtStrRef:
  3792.         OR      ESI,ESI
  3793.         JE      @CvtEmptyStr
  3794.         MOV     ECX,[ESI-4]
  3795.  
  3796. @CvtStrLen:
  3797.         CMP     ECX,Prec
  3798.         JA      @E1
  3799.         RET
  3800. @E1:    MOV     ECX,Prec
  3801.         RET
  3802.  
  3803. @CvtPChar:
  3804.         CMP     CL,'S'
  3805.         JNE     @CvtError
  3806.         MOV     ESI,EAX
  3807.         PUSH    EDI
  3808.         MOV     EDI,EAX
  3809.         XOR     AL,AL
  3810.         MOV     ECX,Prec
  3811.         JECXZ   @F1
  3812.         REPNE   SCASB
  3813.         JNE     @F1
  3814.         DEC     EDI
  3815. @F1:    MOV     ECX,EDI
  3816.         SUB     ECX,ESI
  3817.         POP     EDI
  3818.         RET
  3819.  
  3820. @CvtPointer:
  3821.         CMP     CL,'P'
  3822.         JNE     @CvtError
  3823.         MOV     Prec,8
  3824.         MOV     ECX,16
  3825.         JMP     @CvtLong
  3826.  
  3827. @CvtCurrency:
  3828.         MOV     BH,fvCurrency
  3829.         JMP     @CvtFloat
  3830.  
  3831. @CvtExtended:
  3832.         MOV     BH,fvExtended
  3833.  
  3834. @CvtFloat:
  3835.         MOV     ESI,EAX
  3836.         MOV     BL,ffGeneral
  3837.         CMP     CL,'G'
  3838.         JE      @G2
  3839.         MOV     BL,ffExponent
  3840.         CMP     CL,'E'
  3841.         JE      @G2
  3842.         MOV     BL,ffFixed
  3843.         CMP     CL,'F'
  3844.         JE      @G1
  3845.         MOV     BL,ffNumber
  3846.         CMP     CL,'N'
  3847.         JE      @G1
  3848.         CMP     CL,'M'
  3849.         JNE     @CvtError
  3850.         MOV     BL,ffCurrency
  3851. @G1:    MOV     EAX,18
  3852.         MOV     EDX,Prec
  3853.         CMP     EDX,EAX
  3854.         JBE     @G3
  3855.         MOV     EDX,2
  3856.         CMP     CL,'M'
  3857.         JNE     @G3
  3858.         MOVZX   EDX,CurrencyDecimals
  3859.         JMP     @G3
  3860. @G2:    MOV     EAX,Prec
  3861.         MOV     EDX,3
  3862.         CMP     EAX,18
  3863.         JBE     @G3
  3864.         MOV     EAX,15
  3865. @G3:    PUSH    EBX
  3866.         PUSH    EAX
  3867.         PUSH    EDX
  3868.         LEA     EAX,StrBuf
  3869.         MOV     EDX,ESI
  3870.         MOVZX   ECX,BH
  3871.         CALL    FloatToText
  3872.         MOV     ECX,EAX
  3873.         LEA     ESI,StrBuf
  3874.         RET
  3875.  
  3876. @ClearTmpAnsiStr:
  3877.         PUSH    EAX
  3878.         LEA     EAX,TempAnsiStr
  3879.         CALL    System.@LStrClr
  3880.         POP     EAX
  3881.         RET
  3882.  
  3883. @Exit:
  3884.         CALL    @ClearTmpAnsiStr
  3885.         POP     EDI
  3886.         POP     ESI
  3887.         POP     EBX
  3888. end;
  3889.  
  3890. function StrFmt(Buffer, Format: PChar; const Args: array of const): PChar;
  3891. begin
  3892.   Buffer[FormatBuf(Buffer^, MaxInt, Format^, StrLen(Format), Args)] := #0;
  3893.   Result := Buffer;
  3894. end;
  3895.  
  3896. function StrLFmt(Buffer: PChar; MaxLen: Cardinal; Format: PChar;
  3897.   const Args: array of const): PChar;
  3898. begin
  3899.   Buffer[FormatBuf(Buffer^, MaxLen, Format^, StrLen(Format), Args)] := #0;
  3900.   Result := Buffer;
  3901. end;
  3902.  
  3903. function Format(const Format: string; const Args: array of const): string;
  3904. begin
  3905.   FmtStr(Result, Format, Args);
  3906. end;
  3907.  
  3908. procedure FmtStr(var Result: string; const Format: string;
  3909.   const Args: array of const);
  3910. var
  3911.   Len, BufLen: Integer;
  3912.   Buffer: array[0..4097] of Char;
  3913. begin
  3914.   BufLen := SizeOf(Buffer);
  3915.   if Length(Format) < (BufLen - (BufLen div 4)) then
  3916.     Len := FormatBuf(Buffer, BufLen - 1, Pointer(Format)^,
  3917.       Length(Format), Args)
  3918.   else
  3919.   begin
  3920.     BufLen := Length(Format);
  3921.     Len := BufLen;
  3922.   end;
  3923.   if Len >= BufLen - 1 then
  3924.   begin
  3925.     while Len >= BufLen - 1 do
  3926.     begin
  3927.       Inc(BufLen, BufLen);
  3928.       Result := '';          // prevent copying of existing data, for speed
  3929.       SetLength(Result, BufLen);
  3930.       Len := FormatBuf(Pointer(Result)^, BufLen - 1, Pointer(Format)^,
  3931.         Length(Format), Args);
  3932.     end;
  3933.     SetLength(Result, Len);
  3934.   end
  3935.   else
  3936.     SetString(Result, Buffer, Len);
  3937. end;
  3938.  
  3939. { Floating point conversion routines }
  3940.  
  3941. {$L FFMT.OBJ}
  3942.  
  3943. procedure FloatToDecimal(var Result: TFloatRec; const Value;
  3944.   ValueType: TFloatValue; Precision, Decimals: Integer); external;
  3945.  
  3946. function FloatToText(Buffer: PChar; const Value; ValueType: TFloatValue;
  3947.   Format: TFloatFormat; Precision, Digits: Integer): Integer; external;
  3948.  
  3949. function FloatToTextFmt(Buffer: PChar; const Value; ValueType: TFloatValue;
  3950.   Format: PChar): Integer; external;
  3951.  
  3952. function TextToFloat(Buffer: PChar; var Value;
  3953.   ValueType: TFloatValue): Boolean; external;
  3954.  
  3955. function FloatToStr(Value: Extended): string;
  3956. var
  3957.   Buffer: array[0..63] of Char;
  3958. begin
  3959.   SetString(Result, Buffer, FloatToText(Buffer, Value, fvExtended,
  3960.     ffGeneral, 15, 0));
  3961. end;
  3962.  
  3963. function CurrToStr(Value: Currency): string;
  3964. var
  3965.   Buffer: array[0..63] of Char;
  3966. begin
  3967.   SetString(Result, Buffer, FloatToText(Buffer, Value, fvCurrency,
  3968.     ffGeneral, 0, 0));
  3969. end;
  3970.  
  3971. function FloatToStrF(Value: Extended; Format: TFloatFormat;
  3972.   Precision, Digits: Integer): string;
  3973. var
  3974.   Buffer: array[0..63] of Char;
  3975. begin
  3976.   SetString(Result, Buffer, FloatToText(Buffer, Value, fvExtended,
  3977.     Format, Precision, Digits));
  3978. end;
  3979.  
  3980. function CurrToStrF(Value: Currency; Format: TFloatFormat;
  3981.   Digits: Integer): string;
  3982. var
  3983.   Buffer: array[0..63] of Char;
  3984. begin
  3985.   SetString(Result, Buffer, FloatToText(Buffer, Value, fvCurrency,
  3986.     Format, 0, Digits));
  3987. end;
  3988.  
  3989. function FormatFloat(const Format: string; Value: Extended): string;
  3990. var
  3991.   Buffer: array[0..255] of Char;
  3992. begin
  3993.   if Length(Format) > SizeOf(Buffer) - 32 then ConvertError(SFormatTooLong);
  3994.   SetString(Result, Buffer, FloatToTextFmt(Buffer, Value, fvExtended,
  3995.     PChar(Format)));
  3996. end;
  3997.  
  3998. function FormatCurr(const Format: string; Value: Currency): string;
  3999. var
  4000.   Buffer: array[0..255] of Char;
  4001. begin
  4002.   if Length(Format) > SizeOf(Buffer) - 32 then ConvertError(SFormatTooLong);
  4003.   SetString(Result, Buffer, FloatToTextFmt(Buffer, Value, fvCurrency,
  4004.     PChar(Format)));
  4005. end;
  4006.  
  4007. function StrToFloat(const S: string): Extended;
  4008. begin
  4009.   if not TextToFloat(PChar(S), Result, fvExtended) then
  4010.     ConvertErrorFmt(SInvalidFloat, [S]);
  4011. end;
  4012.  
  4013. function StrToCurr(const S: string): Currency;
  4014. begin
  4015.   if not TextToFloat(PChar(S), Result, fvCurrency) then
  4016.     ConvertErrorFmt(SInvalidFloat, [S]);
  4017. end;
  4018.  
  4019. { Date/time support routines }
  4020.  
  4021. const
  4022.   FMSecsPerDay: Single = MSecsPerDay;
  4023.   IMSecsPerDay: Integer = MSecsPerDay;
  4024.  
  4025. function DateTimeToTimeStamp(DateTime: TDateTime): TTimeStamp;
  4026. asm
  4027.         MOV     ECX,EAX
  4028.         FLD     DateTime
  4029.         FMUL    FMSecsPerDay
  4030.         SUB     ESP,8
  4031.         FISTP   QWORD PTR [ESP]
  4032.         FWAIT
  4033.         POP     EAX
  4034.         POP     EDX
  4035.         OR      EDX,EDX
  4036.         JNS     @@1
  4037.         NEG     EDX
  4038.         NEG     EAX
  4039.         SBB     EDX,0
  4040.         DIV     IMSecsPerDay
  4041.         NEG     EAX
  4042.         JMP     @@2
  4043. @@1:    DIV     IMSecsPerDay
  4044. @@2:    ADD     EAX,DateDelta
  4045.         MOV     [ECX].TTimeStamp.Time,EDX
  4046.         MOV     [ECX].TTimeStamp.Date,EAX
  4047. end;
  4048.  
  4049. function TimeStampToDateTime(const TimeStamp: TTimeStamp): TDateTime;
  4050. asm
  4051.         MOV     ECX,[EAX].TTimeStamp.Time
  4052.         MOV     EAX,[EAX].TTimeStamp.Date
  4053.         SUB     EAX,DateDelta
  4054.         IMUL    IMSecsPerDay
  4055.         OR      EDX,EDX
  4056.         JNS     @@1
  4057.         SUB     EAX,ECX
  4058.         SBB     EDX,0
  4059.         JMP     @@2
  4060. @@1:    ADD     EAX,ECX
  4061.         ADC     EDX,0
  4062. @@2:    PUSH    EDX
  4063.         PUSH    EAX
  4064.         FILD    QWORD PTR [ESP]
  4065.         FDIV    FMSecsPerDay
  4066.         ADD     ESP,8
  4067. end;
  4068.  
  4069. function MSecsToTimeStamp(MSecs: Comp): TTimeStamp;
  4070. asm
  4071.         MOV     ECX,EAX
  4072.         MOV     EAX,MSecs.Integer[0]
  4073.         MOV     EDX,MSecs.Integer[4]
  4074.         DIV     IMSecsPerDay
  4075.         MOV     [ECX].TTimeStamp.Time,EDX
  4076.         MOV     [ECX].TTimeStamp.Date,EAX
  4077. end;
  4078.  
  4079. function TimeStampToMSecs(const TimeStamp: TTimeStamp): Comp;
  4080. asm
  4081.         FILD    [EAX].TTimeStamp.Date
  4082.         FMUL    FMSecsPerDay
  4083.         FIADD   [EAX].TTimeStamp.Time
  4084. end;
  4085.  
  4086. { Time encoding and decoding }
  4087.  
  4088. function DoEncodeTime(Hour, Min, Sec, MSec: Word; var Time: TDateTime): Boolean;
  4089. begin
  4090.   Result := False;
  4091.   if (Hour < 24) and (Min < 60) and (Sec < 60) and (MSec < 1000) then
  4092.   begin
  4093.     Time := (Hour * 3600000 + Min * 60000 + Sec * 1000 + MSec) / MSecsPerDay;
  4094.     Result := True;
  4095.   end;
  4096. end;
  4097.  
  4098. function EncodeTime(Hour, Min, Sec, MSec: Word): TDateTime;
  4099. begin
  4100.   if not DoEncodeTime(Hour, Min, Sec, MSec, Result) then
  4101.     ConvertError(STimeEncodeError);
  4102. end;
  4103.  
  4104. procedure DecodeTime(Time: TDateTime; var Hour, Min, Sec, MSec: Word);
  4105. var
  4106.   MinCount, MSecCount: Word;
  4107. begin
  4108.   DivMod(DateTimeToTimeStamp(Time).Time, 60000, MinCount, MSecCount);
  4109.   DivMod(MinCount, 60, Hour, Min);
  4110.   DivMod(MSecCount, 1000, Sec, MSec);
  4111. end;
  4112.  
  4113. { Date encoding and decoding }
  4114.  
  4115. function IsLeapYear(Year: Word): Boolean;
  4116. begin
  4117.   Result := (Year mod 4 = 0) and ((Year mod 100 <> 0) or (Year mod 400 = 0));
  4118. end;
  4119.  
  4120. function DoEncodeDate(Year, Month, Day: Word; var Date: TDateTime): Boolean;
  4121. var
  4122.   I: Integer;
  4123.   DayTable: PDayTable;
  4124. begin
  4125.   Result := False;
  4126.   DayTable := @MonthDays[IsLeapYear(Year)];
  4127.   if (Year >= 1) and (Year <= 9999) and (Month >= 1) and (Month <= 12) and
  4128.     (Day >= 1) and (Day <= DayTable^[Month]) then
  4129.   begin
  4130.     for I := 1 to Month - 1 do Inc(Day, DayTable^[I]);
  4131.     I := Year - 1;
  4132.     Date := I * 365 + I div 4 - I div 100 + I div 400 + Day - DateDelta;
  4133.     Result := True;
  4134.   end;
  4135. end;
  4136.  
  4137. function EncodeDate(Year, Month, Day: Word): TDateTime;
  4138. begin
  4139.   if not DoEncodeDate(Year, Month, Day, Result) then
  4140.     ConvertError(SDateEncodeError);
  4141. end;
  4142.  
  4143. procedure DecodeDate(Date: TDateTime; var Year, Month, Day: Word);
  4144. const
  4145.   D1 = 365;
  4146.   D4 = D1 * 4 + 1;
  4147.   D100 = D4 * 25 - 1;
  4148.   D400 = D100 * 4 + 1;
  4149. var
  4150.   Y, M, D, I: Word;
  4151.   T: Integer;
  4152.   DayTable: PDayTable;
  4153. begin
  4154.   T := DateTimeToTimeStamp(Date).Date;
  4155.   if T <= 0 then
  4156.   begin
  4157.     Year := 0;
  4158.     Month := 0;
  4159.     Day := 0;
  4160.   end else
  4161.   begin
  4162.     Dec(T);
  4163.     Y := 1;
  4164.     while T >= D400 do
  4165.     begin
  4166.       Dec(T, D400);
  4167.       Inc(Y, 400);
  4168.     end;
  4169.     DivMod(T, D100, I, D);
  4170.     if I = 4 then
  4171.     begin
  4172.       Dec(I);
  4173.       Inc(D, D100);
  4174.     end;
  4175.     Inc(Y, I * 100);
  4176.     DivMod(D, D4, I, D);
  4177.     Inc(Y, I * 4);
  4178.     DivMod(D, D1, I, D);
  4179.     if I = 4 then
  4180.     begin
  4181.       Dec(I);
  4182.       Inc(D, D1);
  4183.     end;
  4184.     Inc(Y, I);
  4185.     DayTable := @MonthDays[IsLeapYear(Y)];
  4186.     M := 1;
  4187.     while True do
  4188.     begin
  4189.       I := DayTable^[M];
  4190.       if D < I then Break;
  4191.       Dec(D, I);
  4192.       Inc(M);
  4193.     end;
  4194.     Year := Y;
  4195.     Month := M;
  4196.     Day := D + 1;
  4197.   end;
  4198. end;
  4199.  
  4200. procedure DateTimeToSystemTime(DateTime: TDateTime; var SystemTime: TSystemTime);
  4201. begin
  4202.   with SystemTime do
  4203.   begin
  4204.     DecodeDate(DateTime, wYear, wMonth, wDay);
  4205.     DecodeTime(DateTime, wHour, wMinute, wSecond, wMilliseconds);
  4206.   end;
  4207. end;
  4208.  
  4209. function SystemTimeToDateTime(const SystemTime: TSystemTime): TDateTime;
  4210. begin
  4211.   with SystemTime do
  4212.     Result := EncodeDate(wYear, wMonth, wDay) +
  4213.       EncodeTime(wHour, wMinute, wSecond, wMilliSeconds);
  4214. end;
  4215.  
  4216. function DayOfWeek(Date: TDateTime): Integer;
  4217. begin
  4218.   Result := DateTimeToTimeStamp(Date).Date mod 7 + 1;
  4219. end;
  4220.  
  4221. function Date: TDateTime;
  4222. var
  4223.   SystemTime: TSystemTime;
  4224. begin
  4225.   GetLocalTime(SystemTime);
  4226.   with SystemTime do Result := EncodeDate(wYear, wMonth, wDay);
  4227. end;
  4228.  
  4229. function Time: TDateTime;
  4230. var
  4231.   SystemTime: TSystemTime;
  4232. begin
  4233.   GetLocalTime(SystemTime);
  4234.   with SystemTime do
  4235.     Result := EncodeTime(wHour, wMinute, wSecond, wMilliSeconds);
  4236. end;
  4237.  
  4238. function Now: TDateTime;
  4239. begin
  4240.   Result := Date + Time;
  4241. end;
  4242.  
  4243. function IncMonth(const Date: TDateTime; NumberOfMonths: Integer): TDateTime;
  4244. var
  4245.   DayTable: PDayTable;
  4246.   Year, Month, Day: Word;
  4247.   Sign: Integer;
  4248. begin
  4249.   if NumberOfMonths >= 0 then Sign := 1 else Sign := -1;
  4250.   DecodeDate(Date, Year, Month, Day);
  4251.   Year := Year + (NumberOfMonths div 12);
  4252.   NumberOfMonths := NumberOfMonths mod 12;
  4253.   Inc(Month, NumberOfMonths);
  4254.   if Word(Month-1) > 11 then    // if Month <= 0, word(Month-1) > 11)
  4255.   begin
  4256.     Inc(Year, Sign);
  4257.     Inc(Month, -12 * Sign);
  4258.   end;
  4259.   DayTable := @MonthDays[IsLeapYear(Year)];
  4260.   if Day > DayTable^[Month] then Day := DayTable^[Month];
  4261.   Result := EncodeDate(Year, Month, Day) + Frac(Date);
  4262. end;
  4263.  
  4264. function CurrentYear: Word;
  4265. var
  4266.   SystemTime: TSystemTime;
  4267. begin
  4268.   GetLocalTime(SystemTime);
  4269.   Result := SystemTime.wYear;
  4270. end;
  4271.  
  4272. { Date/time to string conversions }
  4273.  
  4274. procedure DateTimeToString(var Result: string; const Format: string;
  4275.   DateTime: TDateTime);
  4276. var
  4277.   BufPos, AppendLevel: Integer;
  4278.   Buffer: array[0..255] of Char;
  4279.  
  4280.   procedure AppendChars(P: PChar; Count: Integer);
  4281.   var
  4282.     N: Integer;
  4283.   begin
  4284.     N := SizeOf(Buffer) - BufPos;
  4285.     if N > Count then N := Count;
  4286.     if N <> 0 then Move(P[0], Buffer[BufPos], N);
  4287.     Inc(BufPos, N);
  4288.   end;
  4289.  
  4290.   procedure AppendString(const S: string);
  4291.   begin
  4292.     AppendChars(Pointer(S), Length(S));
  4293.   end;
  4294.  
  4295.   procedure AppendNumber(Number, Digits: Integer);
  4296.   const
  4297.     Format: array[0..3] of Char = '%.*d';
  4298.   var
  4299.     NumBuf: array[0..15] of Char;
  4300.   begin
  4301.     AppendChars(NumBuf, FormatBuf(NumBuf, SizeOf(NumBuf), Format,
  4302.       SizeOf(Format), [Digits, Number]));
  4303.   end;
  4304.  
  4305.   procedure AppendFormat(Format: PChar);
  4306.   var
  4307.     Starter, Token, LastToken: Char;
  4308.     DateDecoded, TimeDecoded, Use12HourClock,
  4309.     BetweenQuotes: Boolean;
  4310.     P: PChar;
  4311.     Count: Integer;
  4312.     Year, Month, Day, Hour, Min, Sec, MSec, H: Word;
  4313.  
  4314.     procedure GetCount;
  4315.     var
  4316.       P: PChar;
  4317.     begin
  4318.       P := Format;
  4319.       while Format^ = Starter do Inc(Format);
  4320.       Count := Format - P + 1;
  4321.     end;
  4322.  
  4323.     procedure GetDate;
  4324.     begin
  4325.       if not DateDecoded then
  4326.       begin
  4327.         DecodeDate(DateTime, Year, Month, Day);
  4328.         DateDecoded := True;
  4329.       end;
  4330.     end;
  4331.  
  4332.     procedure GetTime;
  4333.     begin
  4334.       if not TimeDecoded then
  4335.       begin
  4336.         DecodeTime(DateTime, Hour, Min, Sec, MSec);
  4337.         TimeDecoded := True;
  4338.       end;
  4339.     end;
  4340.  
  4341.     function ConvertEraString(const Count: Integer) : string;
  4342.     var
  4343.       FormatStr: string;
  4344.       SystemTime: TSystemTime;
  4345.       Buffer: array[Byte] of Char;
  4346.       P: PChar;
  4347.     begin
  4348.       Result := '';
  4349.       with SystemTime do
  4350.       begin
  4351.         wYear  := Year;
  4352.         wMonth := Month;
  4353.         wDay   := Day;
  4354.       end;
  4355.  
  4356.       FormatStr := 'gg';
  4357.       if GetDateFormat(GetThreadLocale, DATE_USE_ALT_CALENDAR, @SystemTime,
  4358.         PChar(FormatStr), Buffer, SizeOf(Buffer)) <> 0 then
  4359.       begin
  4360.         Result := Buffer;
  4361.         if Count = 1 then
  4362.         begin
  4363.           case SysLocale.PriLangID of
  4364.             LANG_JAPANESE:
  4365.               Result := Copy(Result, 1, CharToBytelen(Result, 1));
  4366.             LANG_CHINESE:
  4367.               if (SysLocale.SubLangID = SUBLANG_CHINESE_TRADITIONAL)
  4368.                 and (ByteToCharLen(Result, Length(Result)) = 4) then
  4369.               begin
  4370.                 P := Buffer + CharToByteIndex(Result, 3) - 1;
  4371.                 SetString(Result, P, CharToByteLen(P, 2));
  4372.               end;
  4373.           end;
  4374.         end;
  4375.       end;
  4376.     end;
  4377.  
  4378.     function ConvertYearString(const Count: Integer): string;
  4379.     var
  4380.       FormatStr: string;
  4381.       SystemTime: TSystemTime;
  4382.       Buffer: array[Byte] of Char;
  4383.     begin
  4384.       Result := '';
  4385.       with SystemTime do
  4386.       begin
  4387.         wYear  := Year;
  4388.         wMonth := Month;
  4389.         wDay   := Day;
  4390.       end;
  4391.  
  4392.       if Count <= 2 then
  4393.         FormatStr := 'yy' // avoid Win95 bug.
  4394.       else
  4395.         FormatStr := 'yyyy';
  4396.  
  4397.       if GetDateFormat(GetThreadLocale, DATE_USE_ALT_CALENDAR, @SystemTime,
  4398.         PChar(FormatStr), Buffer, SizeOf(Buffer)) <> 0 then
  4399.       begin
  4400.         Result := Buffer;
  4401.         if (Count = 1) and (Result[1] = '0') then
  4402.           Result := Copy(Result, 2, Length(Result)-1);
  4403.       end;
  4404.     end;
  4405.  
  4406.   begin
  4407.     if (Format <> nil) and (AppendLevel < 2) then
  4408.     begin
  4409.       Inc(AppendLevel);
  4410.       LastToken := ' ';
  4411.       DateDecoded := False;
  4412.       TimeDecoded := False;
  4413.       Use12HourClock := False;
  4414.       while Format^ <> #0 do
  4415.       begin
  4416.         Starter := Format^;
  4417.         Inc(Format);
  4418.         if Starter in LeadBytes then
  4419.         begin
  4420.           if Format^ = #0 then Break;
  4421.           Inc(Format);
  4422.           LastToken := ' ';
  4423.           Continue;
  4424.         end;
  4425.         Token := Starter;
  4426.         if Token in ['a'..'z'] then Dec(Token, 32);
  4427.         if Token in ['A'..'Z'] then
  4428.         begin
  4429.           if (Token = 'M') and (LastToken = 'H') then Token := 'N';
  4430.           LastToken := Token;
  4431.         end;
  4432.         case Token of
  4433.           'Y':
  4434.             begin
  4435.               GetCount;
  4436.               GetDate;
  4437.               if Count <= 2 then
  4438.                 AppendNumber(Year mod 100, 2) else
  4439.                 AppendNumber(Year, 4);
  4440.             end;
  4441.           'G':
  4442.             begin
  4443.               GetCount;
  4444.               GetDate;
  4445.               AppendString(ConvertEraString(Count));
  4446.             end;
  4447.           'E':
  4448.             begin
  4449.               GetCount;
  4450.               GetDate;
  4451.               AppendString(ConvertYearString(Count));
  4452.             end;
  4453.           'M':
  4454.             begin
  4455.               GetCount;
  4456.               GetDate;
  4457.               case Count of
  4458.                 1, 2: AppendNumber(Month, Count);
  4459.                 3: AppendString(ShortMonthNames[Month]);
  4460.               else
  4461.                 AppendString(LongMonthNames[Month]);
  4462.               end;
  4463.             end;
  4464.           'D':
  4465.             begin
  4466.               GetCount;
  4467.               case Count of
  4468.                 1, 2:
  4469.                   begin
  4470.                     GetDate;
  4471.                     AppendNumber(Day, Count);
  4472.                   end;
  4473.                 3: AppendString(ShortDayNames[DayOfWeek(DateTime)]);
  4474.                 4: AppendString(LongDayNames[DayOfWeek(DateTime)]);
  4475.                 5: AppendFormat(Pointer(ShortDateFormat));
  4476.               else
  4477.                 AppendFormat(Pointer(LongDateFormat));
  4478.               end;
  4479.             end;
  4480.           'H':
  4481.             begin
  4482.               GetCount;
  4483.               GetTime;
  4484.               BetweenQuotes := False;
  4485.               P := Format;
  4486.               while P^ <> #0 do
  4487.               begin
  4488.                 if P^ in LeadBytes then
  4489.                 begin
  4490.                   Inc(P);
  4491.                   if P^ = #0 then Break;
  4492.                   Inc(P);
  4493.                   Continue;
  4494.                 end;
  4495.                 case P^ of
  4496.                   'A', 'a':
  4497.                     if not BetweenQuotes then
  4498.                     begin
  4499.                       if ( (StrLIComp(P, 'AM/PM', 5) = 0)
  4500.                         or (StrLIComp(P, 'A/P',   3) = 0)
  4501.                         or (StrLIComp(P, 'AMPM',  4) = 0) ) then
  4502.                         Use12HourClock := True;
  4503.                       Break;
  4504.                     end;
  4505.                   'H', 'h':
  4506.                     Break;
  4507.                   '''', '"': BetweenQuotes := not BetweenQuotes;
  4508.                 end;
  4509.                 Inc(P);
  4510.               end;
  4511.               H := Hour;
  4512.               if Use12HourClock then
  4513.                 if H = 0 then H := 12 else if H > 12 then Dec(H, 12);
  4514.               if Count > 2 then Count := 2;
  4515.               AppendNumber(H, Count);
  4516.             end;
  4517.           'N':
  4518.             begin
  4519.               GetCount;
  4520.               GetTime;
  4521.               if Count > 2 then Count := 2;
  4522.               AppendNumber(Min, Count);
  4523.             end;
  4524.           'S':
  4525.             begin
  4526.               GetCount;
  4527.               GetTime;
  4528.               if Count > 2 then Count := 2;
  4529.               AppendNumber(Sec, Count);
  4530.             end;
  4531.           'T':
  4532.             begin
  4533.               GetCount;
  4534.               if Count = 1 then
  4535.                 AppendFormat(Pointer(ShortTimeFormat)) else
  4536.                 AppendFormat(Pointer(LongTimeFormat));
  4537.             end;
  4538.           'A':
  4539.             begin
  4540.               GetTime;
  4541.               P := Format - 1;
  4542.               if StrLIComp(P, 'AM/PM', 5) = 0 then
  4543.               begin
  4544.                 if Hour >= 12 then Inc(P, 3);
  4545.                 AppendChars(P, 2);
  4546.                 Inc(Format, 4);
  4547.                 Use12HourClock := TRUE;
  4548.               end else
  4549.               if StrLIComp(P, 'A/P', 3) = 0 then
  4550.               begin
  4551.                 if Hour >= 12 then Inc(P, 2);
  4552.                 AppendChars(P, 1);
  4553.                 Inc(Format, 2);
  4554.                 Use12HourClock := TRUE;
  4555.               end else
  4556.               if StrLIComp(P, 'AMPM', 4) = 0 then
  4557.               begin
  4558.                 if Hour < 12 then
  4559.                   AppendString(TimeAMString) else
  4560.                   AppendString(TimePMString);
  4561.                 Inc(Format, 3);
  4562.                 Use12HourClock := TRUE;
  4563.               end else
  4564.               if StrLIComp(P, 'AAAA', 4) = 0 then
  4565.               begin
  4566.                 GetDate;
  4567.                 AppendString(LongDayNames[DayOfWeek(DateTime)]);
  4568.                 Inc(Format, 3);
  4569.               end else
  4570.               if StrLIComp(P, 'AAA', 3) = 0 then
  4571.               begin
  4572.                 GetDate;
  4573.                 AppendString(ShortDayNames[DayOfWeek(DateTime)]);
  4574.                 Inc(Format, 2);
  4575.               end else
  4576.               AppendChars(@Starter, 1);
  4577.             end;
  4578.           'C':
  4579.             begin
  4580.               GetCount;
  4581.               AppendFormat(Pointer(ShortDateFormat));
  4582.               GetTime;
  4583.               if (Hour <> 0) or (Min <> 0) or (Sec <> 0) then
  4584.               begin
  4585.                 AppendChars(' ', 1);
  4586.                 AppendFormat(Pointer(LongTimeFormat));
  4587.               end;
  4588.             end;
  4589.           '/':
  4590.             AppendChars(@DateSeparator, 1);
  4591.           ':':
  4592.             AppendChars(@TimeSeparator, 1);
  4593.           '''', '"':
  4594.             begin
  4595.               P := Format;
  4596.               while (Format^ <> #0) and (Format^ <> Starter) do
  4597.               begin
  4598.                 if Format^ in LeadBytes then
  4599.                 begin
  4600.                   Inc(Format);
  4601.                   if Format^ = #0 then Break;
  4602.                 end;
  4603.                 Inc(Format);
  4604.               end;
  4605.               AppendChars(P, Format - P);
  4606.               if Format^ <> #0 then Inc(Format);
  4607.             end;
  4608.         else
  4609.           AppendChars(@Starter, 1);
  4610.         end;
  4611.       end;
  4612.       Dec(AppendLevel);
  4613.     end;
  4614.   end;
  4615.  
  4616. begin
  4617.   BufPos := 0;
  4618.   AppendLevel := 0;
  4619.   if Format <> '' then AppendFormat(Pointer(Format)) else AppendFormat('C');
  4620.   SetString(Result, Buffer, BufPos);
  4621. end;
  4622.  
  4623. function DateToStr(Date: TDateTime): string;
  4624. begin
  4625.   DateTimeToString(Result, ShortDateFormat, Date);
  4626. end;
  4627.  
  4628. function TimeToStr(Time: TDateTime): string;
  4629. begin
  4630.   DateTimeToString(Result, LongTimeFormat, Time);
  4631. end;
  4632.  
  4633. function DateTimeToStr(DateTime: TDateTime): string;
  4634. begin
  4635.   DateTimeToString(Result, '', DateTime);
  4636. end;
  4637.  
  4638. function FormatDateTime(const Format: string; DateTime: TDateTime): string;
  4639. begin
  4640.   DateTimeToString(Result, Format, DateTime);
  4641. end;
  4642.  
  4643. { String to date/time conversions }
  4644.  
  4645. type
  4646.   TDateOrder = (doMDY, doDMY, doYMD);
  4647.  
  4648. procedure ScanBlanks(const S: string; var Pos: Integer);
  4649. var
  4650.   I: Integer;
  4651. begin
  4652.   I := Pos;
  4653.   while (I <= Length(S)) and (S[I] = ' ') do Inc(I);
  4654.   Pos := I;
  4655. end;
  4656.  
  4657. function ScanNumber(const S: string; var Pos: Integer;
  4658.   var Number: Word): Boolean;
  4659. var
  4660.   I: Integer;
  4661.   N: Word;
  4662. begin
  4663.   Result := False;
  4664.   ScanBlanks(S, Pos);
  4665.   I := Pos;
  4666.   N := 0;
  4667.   while (I <= Length(S)) and (S[I] in ['0'..'9']) and (N < 1000) do
  4668.   begin
  4669.     N := N * 10 + (Ord(S[I]) - Ord('0'));
  4670.     Inc(I);
  4671.   end;
  4672.   if I > Pos then
  4673.   begin
  4674.     Pos := I;
  4675.     Number := N;
  4676.     Result := True;
  4677.   end;
  4678. end;
  4679.  
  4680. function ScanString(const S: string; var Pos: Integer;
  4681.   const Symbol: string): Boolean;
  4682. begin
  4683.   Result := False;
  4684.   if Symbol <> '' then
  4685.   begin
  4686.     ScanBlanks(S, Pos);
  4687.     if AnsiCompareText(Symbol, Copy(S, Pos, Length(Symbol))) = 0 then
  4688.     begin
  4689.       Inc(Pos, Length(Symbol));
  4690.       Result := True;
  4691.     end;
  4692.   end;
  4693. end;
  4694.  
  4695. function ScanChar(const S: string; var Pos: Integer; Ch: Char): Boolean;
  4696. begin
  4697.   Result := False;
  4698.   ScanBlanks(S, Pos);
  4699.   if (Pos <= Length(S)) and (S[Pos] = Ch) then
  4700.   begin
  4701.     Inc(Pos);
  4702.     Result := True;
  4703.   end;
  4704. end;
  4705.  
  4706. function GetDateOrder(const DateFormat: string): TDateOrder;
  4707. var
  4708.   I: Integer;
  4709. begin
  4710.   Result := doMDY;
  4711.   I := 1;
  4712.   while I <= Length(DateFormat) do
  4713.   begin
  4714.     case Chr(Ord(DateFormat[I]) and $DF) of
  4715.       'E': Result := doYMD;
  4716.       'Y': Result := doYMD;
  4717.       'M': Result := doMDY;
  4718.       'D': Result := doDMY;
  4719.     else
  4720.       Inc(I);
  4721.       Continue;
  4722.     end;
  4723.     Exit;
  4724.   end;
  4725.   Result := doMDY;
  4726. end;
  4727.  
  4728. procedure ScanToNumber(const S: string; var Pos: Integer);
  4729. begin
  4730.   while (Pos <= Length(S)) and not (S[Pos] in ['0'..'9']) do
  4731.   begin
  4732.     if S[Pos] in LeadBytes then Inc(Pos);
  4733.     Inc(Pos);
  4734.   end;
  4735. end;
  4736.  
  4737. function GetEraYearOffset(const Name: string): Integer;
  4738. var
  4739.   I: Integer;
  4740. begin
  4741.   Result := 0;
  4742.   for I := Low(EraNames) to High(EraNames) do
  4743.   begin
  4744.     if EraNames[I] = '' then Break;
  4745.     if AnsiStrPos(PChar(EraNames[I]), PChar(Name)) <> nil then
  4746.     begin
  4747.       Result := EraYearOffsets[I];
  4748.       Exit;
  4749.     end;
  4750.   end;
  4751. end;
  4752.  
  4753. function ScanDate(const S: string; var Pos: Integer;
  4754.   var Date: TDateTime): Boolean;
  4755. var
  4756.   DateOrder: TDateOrder;
  4757.   N1, N2, N3, Y, M, D: Word;
  4758.   EraName : string;
  4759.   EraYearOffset: Integer;
  4760.  
  4761.   function EraToYear(Year: Integer): Integer;
  4762.   begin
  4763.     if SysLocale.PriLangID = LANG_KOREAN then
  4764.     begin
  4765.       if Year <= 99 then
  4766.         Inc(Year, (CurrentYear + Abs(EraYearOffset)) div 100 * 100);
  4767.       if EraYearOffset > 0 then
  4768.         EraYearOffset := -EraYearOffset;
  4769.     end
  4770.     else
  4771.       Dec(EraYearOffset);
  4772.     Result := Year + EraYearOffset;
  4773.   end;
  4774.  
  4775. begin
  4776.   Y := 0;
  4777.   M := 0;
  4778.   D := 0;
  4779.   Result := False;
  4780.   DateOrder := GetDateOrder(ShortDateFormat);
  4781.   EraYearOffset := 0;
  4782.   if ShortDateFormat[1] = 'g' then  // skip over prefix text
  4783.   begin
  4784.     ScanToNumber(S, Pos);
  4785.     EraName := Trim(Copy(S, 1, Pos-1));
  4786.     EraYearOffset := GetEraYearOffset(EraName);
  4787.   end
  4788.   else
  4789.     if AnsiPos('e', ShortDateFormat) > 0 then
  4790.       EraYearOffset := EraYearOffsets[1];
  4791.   if not (ScanNumber(S, Pos, N1) and ScanChar(S, Pos, DateSeparator) and
  4792.     ScanNumber(S, Pos, N2)) then Exit;
  4793.   if ScanChar(S, Pos, DateSeparator) then
  4794.   begin
  4795.     if not ScanNumber(S, Pos, N3) then Exit;
  4796.     case DateOrder of
  4797.       doMDY: begin Y := N3; M := N1; D := N2; end;
  4798.       doDMY: begin Y := N3; M := N2; D := N1; end;
  4799.       doYMD: begin Y := N1; M := N2; D := N3; end;
  4800.     end;
  4801.     if EraYearOffset > 0 then Y := EraToYear(Y);
  4802.     if Y <= 99 then Inc(Y, CurrentYear div 100 * 100);
  4803.   end else
  4804.   begin
  4805.     Y := CurrentYear;
  4806.     if DateOrder = doDMY then
  4807.     begin
  4808.       D := N1; M := N2;
  4809.     end else
  4810.     begin
  4811.       M := N1; D := N2;
  4812.     end;
  4813.   end;
  4814.   ScanChar(S, Pos, DateSeparator);
  4815.   ScanBlanks(S, Pos);
  4816.   if SysLocale.FarEast and (System.Pos('ddd', ShortDateFormat) <> 0) then
  4817.   begin     // ignore trailing text
  4818.     if ShortTimeFormat[1] in ['0'..'9'] then  // stop at time digit
  4819.       ScanToNumber(S, Pos)
  4820.     else  // stop at time prefix
  4821.       repeat
  4822.         while (Pos <= Length(S)) and (S[Pos] <> ' ') do Inc(Pos);
  4823.         ScanBlanks(S, Pos);
  4824.       until (Pos > Length(S)) or
  4825.         (AnsiCompareText(TimeAMString, Copy(S, Pos, Length(TimeAMString))) = 0) or
  4826.         (AnsiCompareText(TimePMString, Copy(S, Pos, Length(TimePMString))) = 0);
  4827.   end;
  4828.   Result := DoEncodeDate(Y, M, D, Date);
  4829. end;
  4830.  
  4831. function ScanTime(const S: string; var Pos: Integer;
  4832.   var Time: TDateTime): Boolean;
  4833. var
  4834.   BaseHour: Integer;
  4835.   Hour, Min, Sec: Word;
  4836. begin
  4837.   Result := False;
  4838.   BaseHour := -1;
  4839.   if ScanString(S, Pos, TimeAMString) or ScanString(S, Pos, 'AM') then
  4840.     BaseHour := 0
  4841.   else if ScanString(S, Pos, TimePMString) or ScanString(S, Pos, 'PM') then
  4842.     BaseHour := 12;
  4843.   if BaseHour >= 0 then ScanBlanks(S, Pos);
  4844.   if not ScanNumber(S, Pos, Hour) then Exit;
  4845.   Min := 0;
  4846.   if ScanChar(S, Pos, TimeSeparator) then
  4847.     if not ScanNumber(S, Pos, Min) then Exit;
  4848.   Sec := 0;
  4849.   if ScanChar(S, Pos, TimeSeparator) then
  4850.     if not ScanNumber(S, Pos, Sec) then Exit;
  4851.   if BaseHour < 0 then
  4852.     if ScanString(S, Pos, TimeAMString) or ScanString(S, Pos, 'AM') then
  4853.       BaseHour := 0
  4854.     else
  4855.       if ScanString(S, Pos, TimePMString) or ScanString(S, Pos, 'PM') then
  4856.         BaseHour := 12;
  4857.   if BaseHour >= 0 then
  4858.   begin
  4859.     if (Hour = 0) or (Hour > 12) then Exit;
  4860.     if Hour = 12 then Hour := 0;
  4861.     Inc(Hour, BaseHour);
  4862.   end;
  4863.   ScanBlanks(S, Pos);
  4864.   Result := DoEncodeTime(Hour, Min, Sec, 0, Time);
  4865. end;
  4866.  
  4867. function StrToDate(const S: string): TDateTime;
  4868. var
  4869.   Pos: Integer;
  4870. begin
  4871.   Pos := 1;
  4872.   if not ScanDate(S, Pos, Result) or (Pos <= Length(S)) then
  4873.     ConvertErrorFmt(SInvalidDate, [S]);
  4874. end;
  4875.  
  4876. function StrToTime(const S: string): TDateTime;
  4877. var
  4878.   Pos: Integer;
  4879. begin
  4880.   Pos := 1;
  4881.   if not ScanTime(S, Pos, Result) or (Pos <= Length(S)) then
  4882.     ConvertErrorFmt(SInvalidTime, [S]);
  4883. end;
  4884.  
  4885. function StrToDateTime(const S: string): TDateTime;
  4886. var
  4887.   Pos: Integer;
  4888.   Date, Time: TDateTime;
  4889. begin
  4890.   Pos := 1;
  4891.   Time := 0;
  4892.   if not ScanDate(S, Pos, Date) or not ((Pos > Length(S)) or
  4893.     ScanTime(S, Pos, Time)) then
  4894.     ConvertErrorFmt(SInvalidDateTime, [S]);
  4895.   if Date >= 0 then
  4896.     Result := Date + Time else
  4897.     Result := Date - Time;
  4898. end;
  4899.  
  4900. { System error messages }
  4901.  
  4902. function SysErrorMessage(ErrorCode: Integer): string;
  4903. var
  4904.   Len: Integer;
  4905.   Buffer: array[0..255] of Char;
  4906. begin
  4907.   Len := FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM or
  4908.     FORMAT_MESSAGE_ARGUMENT_ARRAY, nil, ErrorCode, 0, Buffer,
  4909.     SizeOf(Buffer), nil);
  4910.   while (Len > 0) and (Buffer[Len - 1] in [#0..#32, '.']) do Dec(Len);
  4911.   SetString(Result, Buffer, Len);
  4912. end;
  4913.  
  4914. { Initialization file support }
  4915.  
  4916. function GetLocaleStr(Locale, LocaleType: Integer; const Default: string): string;
  4917. var
  4918.   L: Integer;
  4919.   Buffer: array[0..255] of Char;
  4920. begin
  4921.   L := GetLocaleInfo(Locale, LocaleType, Buffer, SizeOf(Buffer));
  4922.   if L > 0 then SetString(Result, Buffer, L - 1) else Result := Default;
  4923. end;
  4924.  
  4925. function GetLocaleChar(Locale, LocaleType: Integer; Default: Char): Char;
  4926. var
  4927.   Buffer: array[0..1] of Char;
  4928. begin
  4929.   if GetLocaleInfo(Locale, LocaleType, Buffer, 2) > 0 then
  4930.     Result := Buffer[0] else
  4931.     Result := Default;
  4932. end;
  4933.  
  4934. var
  4935.   DefShortMonthNames: array[1..12] of Pointer = (@SShortMonthNameJan,
  4936.     @SShortMonthNameFeb, @SShortMonthNameMar, @SShortMonthNameApr,
  4937.     @SShortMonthNameMay, @SShortMonthNameJun, @SShortMonthNameJul,
  4938.     @SShortMonthNameAug, @SShortMonthNameSep, @SShortMonthNameOct,
  4939.     @SShortMonthNameNov, @SShortMonthNameDec);
  4940.  
  4941.   DefLongMonthNames: array[1..12] of Pointer = (@SLongMonthNameJan,
  4942.     @SLongMonthNameFeb, @SLongMonthNameMar, @SLongMonthNameApr,
  4943.     @SLongMonthNameMay, @SLongMonthNameJun, @SLongMonthNameJul,
  4944.     @SLongMonthNameAug, @SLongMonthNameSep, @SLongMonthNameOct,
  4945.     @SLongMonthNameNov, @SLongMonthNameDec);
  4946.  
  4947.   DefShortDayNames: array[1..7] of Pointer = (@SShortDayNameSun,
  4948.     @SShortDayNameMon, @SShortDayNameTue, @SShortDayNameWed,
  4949.     @SShortDayNameThu, @SShortDayNameFri, @SShortDayNameSat);
  4950.  
  4951.   DefLongDayNames: array[1..7] of Pointer = (@SLongDayNameSun,
  4952.     @SLongDayNameMon, @SLongDayNameTue, @SLongDayNameWed,
  4953.     @SLongDayNameThu, @SLongDayNameFri, @SLongDayNameSat);
  4954.  
  4955. procedure GetMonthDayNames;
  4956. var
  4957.   I, Day: Integer;
  4958.   DefaultLCID: LCID;
  4959.  
  4960.   function LocalGetLocaleStr(LocaleType, Index: Integer;
  4961.     const DefValues: array of Pointer): string;
  4962.   begin
  4963.     Result := GetLocaleStr(DefaultLCID, LocaleType, '');
  4964.     if Result = '' then Result := LoadResString(DefValues[Index]);
  4965.   end;
  4966.  
  4967. begin
  4968.   DefaultLCID := GetThreadLocale;
  4969.   for I := 1 to 12 do
  4970.   begin
  4971.     ShortMonthNames[I] := LocalGetLocaleStr(LOCALE_SABBREVMONTHNAME1 + I - 1,
  4972.       I, DefShortMonthNames);
  4973.     LongMonthNames[I] := LocalGetLocaleStr(LOCALE_SMONTHNAME1 + I - 1, I,
  4974.       DefLongMonthNames);
  4975.   end;
  4976.   for I := 1 to 7 do
  4977.   begin
  4978.     Day := (I + 5) mod 7;
  4979.     ShortDayNames[I] := LocalGetLocaleStr(LOCALE_SABBREVDAYNAME1 + Day, I, DefShortDayNames);
  4980.     LongDayNames[I] := LocalGetLocaleStr(LOCALE_SDAYNAME1 + Day, I, DefLongDayNames);
  4981.   end;
  4982. end;
  4983.  
  4984. procedure GetEraNamesAndYearOffsets;
  4985. var
  4986.   CalendarType: CALTYPE;
  4987.  
  4988.   function EnumEraNames(Names: PChar): BOOL; stdcall;
  4989.   var
  4990.     I: Integer;
  4991.   begin
  4992.     I := 1;
  4993.     repeat
  4994.       EraNames[I] := Names;
  4995.       Inc(Names, Length(EraNames[I])+1);
  4996.       Inc(I);
  4997.     until Names^ = #0;
  4998.     Result := FALSE;
  4999.   end;
  5000.  
  5001.   function EnumEraYearOffsets(YearOffsets: PChar): BOOL; stdcall;
  5002.   var
  5003.     I: Integer;
  5004.     ErrorCode: Integer;
  5005.   begin
  5006.     I := 1;
  5007.     repeat
  5008.       Val(YearOffsets, EraYearOffsets[I], ErrorCode);
  5009.       if ErrorCode <> 0 then EraYearOffsets[I] := 0;
  5010.       Inc(YearOffsets, StrLen(YearOffsets)+1);
  5011.       Inc(I);
  5012.     until YearOffsets^ = #0;
  5013.     Result := FALSE;
  5014.   end;
  5015.  
  5016. begin
  5017.   CalendarType := StrToIntDef(GetLocaleStr(GetThreadLocale,
  5018.     LOCALE_IOPTIONALCALENDAR, '1'), 1);
  5019.   if CalendarType in [CAL_JAPAN, CAL_TAIWAN, CAL_KOREA] then
  5020.   begin
  5021.     EnumCalendarInfoA(@EnumEraNames, GetThreadLocale, CalendarType,
  5022.       CAL_SERASTRING);
  5023.     EnumCalendarInfoA(@EnumEraYearOffsets, GetThreadLocale, CalendarType,
  5024.       CAL_IYEAROFFSETRANGE);
  5025.   end;
  5026. end;
  5027.  
  5028. function TranslateDateFormat(const FormatStr: string): string;
  5029. var
  5030.   I: Integer;
  5031.   CalendarType: CALTYPE;
  5032.   RemoveEra: Boolean;
  5033. begin
  5034.   I := 1;
  5035.   Result := '';
  5036.   CalendarType := StrToIntDef(GetLocaleStr(GetThreadLocale,
  5037.     LOCALE_ICALENDARTYPE, '1'), 1);
  5038.   if not (CalendarType in [CAL_JAPAN, CAL_TAIWAN, CAL_KOREA]) then
  5039.   begin
  5040.     RemoveEra := SysLocale.PriLangID in [LANG_JAPANESE, LANG_CHINESE, LANG_KOREAN];
  5041.     if RemoveEra then
  5042.     begin
  5043.       While I <= Length(FormatStr) do
  5044.       begin
  5045.         if not (FormatStr[I] in ['g', 'G']) then
  5046.           Result := Result + FormatStr[I];
  5047.         Inc(I);
  5048.       end;
  5049.     end
  5050.     else
  5051.       Result := FormatStr;
  5052.     Exit;
  5053.   end;
  5054.  
  5055.   while I <= Length(FormatStr) do
  5056.   begin
  5057.     if FormatStr[I] in LeadBytes then
  5058.     begin
  5059.       Result := Result + Copy(FormatStr, I, 2);
  5060.       Inc(I, 2);
  5061.     end else
  5062.     begin
  5063.       if StrLIComp(@FormatStr[I], 'gg', 2) = 0 then
  5064.       begin
  5065.         Result := Result + 'ggg';
  5066.         Inc(I, 1);
  5067.       end
  5068.       else if StrLIComp(@FormatStr[I], 'yyyy', 4) = 0 then
  5069.       begin
  5070.         Result := Result + 'eeee';
  5071.         Inc(I, 4-1);
  5072.       end
  5073.       else if StrLIComp(@FormatStr[I], 'yy', 2) = 0 then
  5074.       begin
  5075.         Result := Result + 'ee';
  5076.         Inc(I, 2-1);
  5077.       end
  5078.       else if FormatStr[I] in ['y', 'Y'] then
  5079.         Result := Result + 'e'
  5080.       else
  5081.         Result := Result + FormatStr[I];
  5082.       Inc(I);
  5083.     end;
  5084.   end;
  5085. end;
  5086.  
  5087. procedure GetFormatSettings;
  5088. var
  5089.   HourFormat, TimePrefix, TimePostfix: string;
  5090.   DefaultLCID: LCID;
  5091. begin
  5092.   DefaultLCID := GetThreadLocale;
  5093.   CurrencyString := GetLocaleStr(DefaultLCID, LOCALE_SCURRENCY, '');
  5094.   CurrencyFormat := StrToIntDef(GetLocaleStr(DefaultLCID, LOCALE_ICURRENCY, '0'), 0);
  5095.   NegCurrFormat := StrToIntDef(GetLocaleStr(DefaultLCID, LOCALE_INEGCURR, '0'), 0);
  5096.   ThousandSeparator := GetLocaleChar(DefaultLCID, LOCALE_STHOUSAND, ',');
  5097.   DecimalSeparator := GetLocaleChar(DefaultLCID, LOCALE_SDECIMAL, '.');
  5098.   CurrencyDecimals := StrToIntDef(GetLocaleStr(DefaultLCID, LOCALE_ICURRDIGITS, '0'), 0);
  5099.   DateSeparator := GetLocaleChar(DefaultLCID, LOCALE_SDATE, '/');
  5100.   ShortDateFormat := TranslateDateFormat(GetLocaleStr(DefaultLCID, LOCALE_SSHORTDATE, 'm/d/yy'));
  5101.   LongDateFormat := TranslateDateFormat(GetLocaleStr(DefaultLCID, LOCALE_SLONGDATE, 'mmmm d, yyyy'));
  5102.   TimeSeparator := GetLocaleChar(DefaultLCID, LOCALE_STIME, ':');
  5103.   TimeAMString := GetLocaleStr(DefaultLCID, LOCALE_S1159, 'am');
  5104.   TimePMString := GetLocaleStr(DefaultLCID, LOCALE_S2359, 'pm');
  5105.   TimePrefix := '';
  5106.   TimePostfix := '';
  5107.   if StrToIntDef(GetLocaleStr(DefaultLCID, LOCALE_ITLZERO, '0'), 0) = 0 then
  5108.     HourFormat := 'h' else
  5109.     HourFormat := 'hh';
  5110.   if StrToIntDef(GetLocaleStr(DefaultLCID, LOCALE_ITIME, '0'), 0) = 0 then
  5111.     if StrToIntDef(GetLocaleStr(DefaultLCID, LOCALE_ITIMEMARKPOSN, '0'), 0) = 0 then
  5112.       TimePostfix := ' AMPM'
  5113.     else
  5114.       TimePrefix := 'AMPM ';
  5115.   ShortTimeFormat := TimePrefix + HourFormat + ':mm' + TimePostfix;
  5116.   LongTimeFormat := TimePrefix + HourFormat + ':mm:ss' + TimePostfix;
  5117. end;
  5118.  
  5119. { Exception handling routines }
  5120.  
  5121. var
  5122.   OutOfMemory: EOutOfMemory;
  5123.   InvalidPointer: EInvalidPointer;
  5124.  
  5125. type
  5126.   PRaiseFrame = ^TRaiseFrame;
  5127.   TRaiseFrame = record
  5128.     NextRaise: PRaiseFrame;
  5129.     ExceptAddr: Pointer;
  5130.     ExceptObject: TObject;
  5131.     ExceptionRecord: PExceptionRecord;
  5132.   end;
  5133.  
  5134. { Return current exception object }
  5135.  
  5136. function ExceptObject: TObject;
  5137. begin
  5138.   if RaiseList <> nil then
  5139.     Result := PRaiseFrame(RaiseList)^.ExceptObject else
  5140.     Result := nil;
  5141. end;
  5142.  
  5143. { Return current exception address }
  5144.  
  5145. function ExceptAddr: Pointer;
  5146. begin
  5147.   if RaiseList <> nil then
  5148.     Result := PRaiseFrame(RaiseList)^.ExceptAddr else
  5149.     Result := nil;
  5150. end;
  5151.  
  5152. { Convert physical address to logical address }
  5153.  
  5154. function ConvertAddr(Address: Pointer): Pointer; assembler;
  5155. asm
  5156.         TEST    EAX,EAX         { Always convert nil to nil }
  5157.         JE      @@1
  5158.         SUB     EAX, $1000      { offset from code start; code start set by linker to $1000 }
  5159. @@1:
  5160. end;
  5161.  
  5162. { Format and return an exception error message }
  5163.  
  5164. function ExceptionErrorMessage(ExceptObject: TObject; ExceptAddr: Pointer;
  5165.   Buffer: PChar; Size: Integer): Integer;
  5166. var
  5167.   MsgPtr: PChar;
  5168.   MsgEnd: PChar;
  5169.   MsgLen: Integer;
  5170.   ModuleName: array[0..MAX_PATH] of Char;
  5171.   Temp: array[0..MAX_PATH] of Char;
  5172.   Format: array[0..255] of Char;
  5173.   Info: TMemoryBasicInformation;
  5174.   ConvertedAddress: Pointer;
  5175. begin
  5176.   VirtualQuery(ExceptAddr, Info, sizeof(Info));
  5177.   if (Info.State <> MEM_COMMIT) or
  5178.     (GetModuleFilename(THandle(Info.AllocationBase), Temp, SizeOf(Temp)) = 0) then
  5179.   begin
  5180.     GetModuleFileName(HInstance, Temp, SizeOf(Temp));
  5181.     ConvertedAddress := ConvertAddr(ExceptAddr);
  5182.   end
  5183.   else
  5184.     Integer(ConvertedAddress) := Integer(ExceptAddr) - Integer(Info.AllocationBase);
  5185.   StrLCopy(ModuleName, AnsiStrRScan(Temp, '\') + 1, SizeOf(ModuleName) - 1);
  5186.   MsgPtr := '';
  5187.   MsgEnd := '';
  5188.   if ExceptObject is Exception then
  5189.   begin
  5190.     MsgPtr := PChar(Exception(ExceptObject).Message);
  5191.     MsgLen := StrLen(MsgPtr);
  5192.     if (MsgLen <> 0) and (MsgPtr[MsgLen - 1] <> '.') then MsgEnd := '.';
  5193.   end;
  5194.   LoadString(FindResourceHInstance(HInstance),
  5195.     PResStringRec(@SException).Identifier, Format, SizeOf(Format));
  5196.   StrLFmt(Buffer, Size, Format, [ExceptObject.ClassName, ModuleName,
  5197.     ConvertedAddress, MsgPtr, MsgEnd]);
  5198.   Result := StrLen(Buffer);
  5199. end;
  5200.  
  5201. { Display exception message box }
  5202.  
  5203. procedure ShowException(ExceptObject: TObject; ExceptAddr: Pointer);
  5204. var
  5205.   Title: array[0..63] of Char;
  5206.   Buffer: array[0..1023] of Char;
  5207. begin
  5208.   ExceptionErrorMessage(ExceptObject, ExceptAddr, Buffer, SizeOf(Buffer));
  5209.   if IsConsole then
  5210.     WriteLn(Buffer)
  5211.   else
  5212.   begin
  5213.     LoadString(FindResourceHInstance(HInstance), PResStringRec(SExceptTitle).Identifier,
  5214.       Title, SizeOf(Title));
  5215.     MessageBox(0, Buffer, Title, MB_OK or MB_ICONSTOP or MB_TASKMODAL);
  5216.   end;
  5217. end;
  5218.  
  5219. { Raise abort exception }
  5220.  
  5221. procedure Abort;
  5222.  
  5223.   function ReturnAddr: Pointer;
  5224.   asm
  5225. //          MOV     EAX,[ESP + 4] !!! codegen dependant
  5226.           MOV     EAX,[EBP - 4]
  5227.   end;
  5228.  
  5229. begin
  5230.   raise EAbort.Create(SOperationAborted) at ReturnAddr;
  5231. end;
  5232.  
  5233. { Raise out of memory exception }
  5234.  
  5235. procedure OutOfMemoryError;
  5236. begin
  5237.   raise OutOfMemory;
  5238. end;
  5239.  
  5240. { Exception class }
  5241.  
  5242. constructor Exception.Create(const Msg: string);
  5243. begin
  5244.   FMessage := Msg;
  5245. end;
  5246.  
  5247. constructor Exception.CreateFmt(const Msg: string;
  5248.   const Args: array of const);
  5249. begin
  5250.   FMessage := Format(Msg, Args);
  5251. end;
  5252.  
  5253. constructor Exception.CreateRes(Ident: Integer);
  5254. begin
  5255.   FMessage := LoadStr(Ident);
  5256. end;
  5257.  
  5258. constructor Exception.CreateResFmt(Ident: Integer;
  5259.   const Args: array of const);
  5260. begin
  5261.   FMessage := Format(LoadStr(Ident), Args);
  5262. end;
  5263.  
  5264. constructor Exception.CreateHelp(const Msg: string; AHelpContext: Integer);
  5265. begin
  5266.   FMessage := Msg;
  5267.   FHelpContext := AHelpContext;
  5268. end;
  5269.  
  5270. constructor Exception.CreateFmtHelp(const Msg: string; const Args: array of const;
  5271.   AHelpContext: Integer);
  5272. begin
  5273.   FMessage := Format(Msg, Args);
  5274.   FHelpContext := AHelpContext;
  5275. end;
  5276.  
  5277. constructor Exception.CreateResHelp(Ident: Integer; AHelpContext: Integer);
  5278. begin
  5279.   FMessage := LoadStr(Ident);
  5280.   FHelpContext := AHelpContext;
  5281. end;
  5282.  
  5283. constructor Exception.CreateResFmtHelp(Ident: Integer;
  5284.   const Args: array of const;
  5285.   AHelpContext: Integer);
  5286. begin
  5287.   FMessage := Format(LoadStr(Ident), Args);
  5288.   FHelpContext := AHelpContext;
  5289. end;
  5290.  
  5291. { EOutOfMemory class }
  5292.  
  5293. destructor EOutOfMemory.Destroy;
  5294. begin
  5295. end;
  5296.  
  5297. procedure EOutOfMemory.FreeInstance;
  5298. begin
  5299.   if AllowFree then
  5300.     inherited FreeInstance;
  5301. end;
  5302.  
  5303. { Create I/O exception }
  5304.  
  5305. function CreateInOutError: EInOutError;
  5306. type
  5307.   TErrorRec = record
  5308.     Code: Integer;
  5309.     Ident: string;
  5310.   end;
  5311. const
  5312.   ErrorMap: array[0..6] of TErrorRec = (
  5313.     (Code: 2; Ident: SFileNotFound),
  5314.     (Code: 3; Ident: SInvalidFilename),
  5315.     (Code: 4; Ident: STooManyOpenFiles),
  5316.     (Code: 5; Ident: SAccessDenied),
  5317.     (Code: 100; Ident: SEndOfFile),
  5318.     (Code: 101; Ident: SDiskFull),
  5319.     (Code: 106; Ident: SInvalidInput));
  5320. var
  5321.   I: Integer;
  5322. begin
  5323.   I := Low(ErrorMap);
  5324.   while (I <= High(ErrorMap)) and (ErrorMap[I].Code <> InOutRes) do Inc(I);
  5325.   if I <= High(ErrorMap) then
  5326.     Result := EInOutError.Create(ErrorMap[I].Ident) else
  5327.     Result := EInOutError.CreateFmt(SInOutError, [InOutRes]);
  5328.   Result.ErrorCode := InOutRes;
  5329.   InOutRes := 0;
  5330. end;
  5331.  
  5332. { RTL error handler }
  5333.  
  5334. type
  5335.   TExceptRec = record
  5336.     EClass: ExceptClass;
  5337.     EIdent: string;
  5338.   end;
  5339.  
  5340. const
  5341.   ExceptMap: array[3..23] of TExceptRec = (
  5342.     (EClass: EDivByZero; EIdent: SDivByZero),
  5343.     (EClass: ERangeError; EIdent: SRangeError),
  5344.     (EClass: EIntOverflow; EIdent: SIntOverflow),
  5345.     (EClass: EInvalidOp; EIdent: SInvalidOp),
  5346.     (EClass: EZeroDivide; EIdent: SZeroDivide),
  5347.     (EClass: EOverflow; EIdent: SOverflow),
  5348.     (EClass: EUnderflow; EIdent: SUnderflow),
  5349.     (EClass: EInvalidCast; EIdent: SInvalidCast),
  5350.     (EClass: EAccessViolation; EIdent: SAccessViolation),
  5351.     (EClass: EPrivilege; EIdent: SPrivilege),
  5352.     (EClass: EControlC; EIdent: SControlC),
  5353.     (EClass: EStackOverflow; EIdent: SStackOverflow),
  5354.     (EClass: EVariantError; EIdent: SInvalidVarCast),
  5355.     (EClass: EVariantError; EIdent: SInvalidVarOp),
  5356.     (EClass: EVariantError; EIdent: SDispatchError),
  5357.     (EClass: EVariantError; EIdent: SVarArrayCreate),
  5358.     (EClass: EVariantError; EIdent: SVarNotArray),
  5359.     (EClass: EVariantError; EIdent: SVarArrayBounds),
  5360.     (EClass: EAssertionFailed; EIdent: SAssertionFailed),
  5361.     (EClass: EExternalException; EIdent: SExternalException),
  5362.     (EClass: EIntfCastError; EIdent: SIntfCastError));
  5363.  
  5364. procedure ErrorHandler(ErrorCode: Integer; ErrorAddr: Pointer);
  5365. var
  5366.   E: Exception;
  5367. begin
  5368.   case ErrorCode of
  5369.     1: E := OutOfMemory;
  5370.     2: E := InvalidPointer;
  5371.     3..23: with ExceptMap[ErrorCode] do E := EClass.Create(EIdent);
  5372.   else
  5373.     E := CreateInOutError;
  5374.   end;
  5375.   raise E at ErrorAddr;
  5376. end;
  5377.  
  5378. { Assertion error handler }
  5379.  
  5380. procedure AssertErrorHandler(const Message, Filename: string;
  5381.   LineNumber: Integer; ErrorAddr: Pointer);
  5382. var
  5383.   S: string;
  5384. begin
  5385.   if Message <> '' then S := Message else S := SAssertionFailed;
  5386.   raise EAssertionFailed.CreateFmt(SAssertError,
  5387.     [S, Filename, LineNumber]) at ErrorAddr;
  5388. end;
  5389.  
  5390. { Abstract method invoke error handler }
  5391.  
  5392. procedure AbstractErrorHandler;
  5393. begin
  5394.   raise EAbstractError.CreateFmt(SAbstractError, ['']);
  5395. end;
  5396.  
  5397. function MapException(P: PExceptionRecord): Byte;
  5398. begin
  5399.   case P.ExceptionCode of
  5400.     STATUS_INTEGER_DIVIDE_BY_ZERO:
  5401.       Result := 3;
  5402.     STATUS_ARRAY_BOUNDS_EXCEEDED:
  5403.       Result := 4;
  5404.     STATUS_INTEGER_OVERFLOW:
  5405.       Result := 5;
  5406.     STATUS_FLOAT_INEXACT_RESULT,
  5407.     STATUS_FLOAT_INVALID_OPERATION,
  5408.     STATUS_FLOAT_STACK_CHECK:
  5409.       Result := 6;
  5410.     STATUS_FLOAT_DIVIDE_BY_ZERO:
  5411.       Result := 7;
  5412.     STATUS_FLOAT_OVERFLOW:
  5413.       Result := 8;
  5414.     STATUS_FLOAT_UNDERFLOW,
  5415.     STATUS_FLOAT_DENORMAL_OPERAND:
  5416.       Result := 9;
  5417.     STATUS_ACCESS_VIOLATION:
  5418.       Result := 11;
  5419.     STATUS_PRIVILEGED_INSTRUCTION:
  5420.       Result := 12;
  5421.     STATUS_CONTROL_C_EXIT:
  5422.       Result := 13;
  5423.     STATUS_STACK_OVERFLOW:
  5424.       Result := 14;
  5425.   else
  5426.     Result := 22; { must match System.reExternalException }
  5427.   end;
  5428. end;
  5429.  
  5430. function GetExceptionClass(P: PExceptionRecord): ExceptClass;
  5431. var
  5432.   ErrorCode: Byte;
  5433. begin
  5434.   ErrorCode := MapException(P);
  5435.   Result := ExceptMap[ErrorCode].EClass;
  5436. end;
  5437.  
  5438. function GetExceptionObject(P: PExceptionRecord): Exception;
  5439. var
  5440.   ErrorCode: Integer;
  5441.  
  5442.   function CreateAVObject: Exception;
  5443.   var
  5444.     AccessOp: string; // string ID indicating the access type READ or WRITE
  5445.     AccessAddress: Pointer;
  5446.     MemInfo: TMemoryBasicInformation;
  5447.     ModName: array[0..MAX_PATH] of Char;
  5448.   begin
  5449.     with P^ do
  5450.     begin
  5451.       if ExceptionInformation[0] = 0 then
  5452.         AccessOp := SReadAccess else
  5453.         AccessOp := SWriteAccess;
  5454.       AccessAddress := Pointer(ExceptionInformation[1]);
  5455.       VirtualQuery(ExceptionAddress, MemInfo, SizeOf(MemInfo));
  5456.       if (MemInfo.State = MEM_COMMIT) and (GetModuleFileName(THandle(MemInfo.AllocationBase),
  5457.         ModName, SizeOf(ModName)) <> 0) then
  5458.         Result := EAccessViolation.CreateFmt(sModuleAccessViolation,
  5459.           [ExceptionAddress, ExtractFileName(ModName), AccessOp,
  5460.           AccessAddress])
  5461.       else Result := EAccessViolation.CreateFmt(sAccessViolation,
  5462.           [ExceptionAddress, AccessOp, AccessAddress]);
  5463.     end;
  5464.   end;
  5465.  
  5466. begin
  5467.   ErrorCode := MapException(P);
  5468.   case ErrorCode of
  5469.     3..10, 12..21:
  5470.       with ExceptMap[ErrorCode] do Result := EClass.Create(EIdent);
  5471.     11: Result := CreateAVObject;
  5472.   else
  5473.     Result := EExternalException.CreateFmt(SExternalException,
  5474.       [P.ExceptionCode]);
  5475.     EExternalException(Result).ExceptionRecord := P;
  5476.   end;
  5477. end;
  5478.  
  5479. { RTL exception handler }
  5480.  
  5481. procedure ExceptHandler(ExceptObject: TObject; ExceptAddr: Pointer); far;
  5482. begin
  5483.   ShowException(ExceptObject, ExceptAddr);
  5484.   Halt(1);
  5485. end;
  5486.  
  5487. procedure InitExceptions;
  5488. begin
  5489.   OutOfMemory := EOutOfMemory.Create(SOutOfMemory);
  5490.   InvalidPointer := EInvalidPointer.Create(SInvalidPointer);
  5491.   ErrorProc := @ErrorHandler;
  5492.   ExceptProc := @ExceptHandler;
  5493.   ExceptionClass := Exception;
  5494.   ExceptClsProc := @GetExceptionClass;
  5495.   ExceptObjProc := @GetExceptionObject;
  5496.   AssertErrorProc := @AssertErrorHandler;
  5497.   AbstractErrorProc := @AbstractErrorHandler;
  5498. end;
  5499.  
  5500. procedure DoneExceptions;
  5501. begin
  5502.   OutOfMemory.AllowFree := True;
  5503.   OutOfMemory.FreeInstance;
  5504.   OutOfMemory := nil;
  5505.   InvalidPointer.Free;
  5506.   InvalidPointer := nil;
  5507.   ErrorProc := nil;
  5508.   ExceptProc := nil;
  5509.   ExceptionClass := nil;
  5510.   ExceptClsProc := nil;
  5511.   ExceptObjProc := nil;
  5512.   AssertErrorProc := nil;
  5513. end;
  5514.  
  5515. procedure InitPlatformId;
  5516. var
  5517.   OSVersionInfo: TOSVersionInfo;
  5518. begin
  5519.   OSVersionInfo.dwOSVersionInfoSize := SizeOf(OSVersionInfo);
  5520.   if GetVersionEx(OSVersionInfo) then
  5521.     with OSVersionInfo do
  5522.     begin
  5523.       Win32Platform := dwPlatformId;
  5524.       Win32MajorVersion := dwMajorVersion;
  5525.       Win32MinorVersion := dwMinorVersion;
  5526.       Win32BuildNumber := dwBuildNumber;
  5527.       Win32CSDVersion := szCSDVersion;
  5528.     end;
  5529. end;
  5530.  
  5531. procedure Beep;
  5532. begin
  5533.   MessageBeep(0);
  5534. end;
  5535.  
  5536. { MBCS functions }
  5537.  
  5538. function ByteTypeTest(P: PChar; Index: Integer): TMbcsByteType;
  5539. begin
  5540.   Result := mbSingleByte;
  5541.   if (Index = 0) then
  5542.   begin
  5543.     if P[Index] in LeadBytes then Result := mbLeadByte;
  5544.   end
  5545.   else
  5546.   begin
  5547.     if (P[Index-1] in LeadBytes) and (ByteTypeTest(P, Index-1) = mbLeadByte) then
  5548.       Result := mbTrailByte
  5549.     else if P[Index] in LeadBytes then
  5550.       Result := mbLeadByte;
  5551.   end;
  5552. end;
  5553.  
  5554. function ByteType(const S: string; Index: Integer): TMbcsByteType;
  5555. begin
  5556.   Result := mbSingleByte;
  5557.   if SysLocale.FarEast then
  5558.     Result := ByteTypeTest(PChar(S), Index-1);
  5559. end;
  5560.  
  5561. function StrByteType(Str: PChar; Index: Cardinal): TMbcsByteType;
  5562. begin
  5563.   Result := mbSingleByte;
  5564.   if SysLocale.FarEast then
  5565.     Result := ByteTypeTest(Str, Index);
  5566. end;
  5567.  
  5568. function ByteToCharLen(const S: string; MaxLen: Integer): Integer;
  5569. begin
  5570.   if Length(S) < MaxLen then MaxLen := Length(S);
  5571.   Result := ByteToCharIndex(S, MaxLen);
  5572. end;
  5573.  
  5574. function ByteToCharIndex(const S: string; Index: Integer): Integer;
  5575. var
  5576.   I: Integer;
  5577. begin
  5578.   Result := 0;
  5579.   if (Index <= 0) or (Index > Length(S)) then Exit;
  5580.   Result := Index;
  5581.   if not SysLocale.FarEast then Exit;
  5582.   I := 1;
  5583.   Result := 0;
  5584.   while I <= Index do
  5585.   begin
  5586.     if S[I] in LeadBytes then Inc(I);
  5587.     Inc(I);
  5588.     Inc(Result);
  5589.   end;
  5590. end;
  5591.  
  5592. procedure CountChars(const S: string; MaxChars: Integer; var CharCount, ByteCount: Integer);
  5593. var
  5594.   C, L, B: Integer;
  5595. begin
  5596.   L := Length(S);
  5597.   C := 1;
  5598.   B := 1;
  5599.   while (B < L) and (C < MaxChars) do
  5600.   begin
  5601.     Inc(C);
  5602.     if S[B] in LeadBytes then Inc(B);
  5603.     Inc(B);
  5604.   end;
  5605.   if (C = MaxChars) and (B < L) and (S[B] in LeadBytes) then Inc(B);
  5606.   CharCount := C;
  5607.   ByteCount := B;
  5608. end;
  5609.  
  5610. function CharToByteIndex(const S: string; Index: Integer): Integer;
  5611. var
  5612.   Chars: Integer;
  5613. begin
  5614.   Result := 0;
  5615.   if (Index <= 0) or (Index > Length(S)) then Exit;
  5616.   if (Index > 1) and SysLocale.FarEast then
  5617.   begin
  5618.     CountChars(S, Index-1, Chars, Result);
  5619.     if (Chars < (Index-1)) or (Result >= Length(S)) then
  5620.       Result := 0  // Char index out of range
  5621.     else
  5622.       Inc(Result);
  5623.   end
  5624.   else
  5625.     Result := Index;
  5626. end;
  5627.  
  5628. function CharToByteLen(const S: string; MaxLen: Integer): Integer;
  5629. var
  5630.   Chars: Integer;
  5631. begin
  5632.   Result := 0;
  5633.   if MaxLen <= 0 then Exit;
  5634.   if MaxLen > Length(S) then MaxLen := Length(S);
  5635.   if SysLocale.FarEast then
  5636.   begin
  5637.     CountChars(S, MaxLen, Chars, Result);
  5638.     if Result > Length(S) then
  5639.       Result := Length(S);
  5640.   end
  5641.   else
  5642.     Result := MaxLen;
  5643. end;
  5644.  
  5645. function IsPathDelimiter(const S: string; Index: Integer): Boolean;
  5646. begin
  5647.   Result := (Index > 0) and (Index <= Length(S)) and (S[Index] = '\')
  5648.     and (ByteType(S, Index) = mbSingleByte);
  5649. end;
  5650.  
  5651. function IsDelimiter(const Delimiters, S: string; Index: Integer): Boolean;
  5652. begin
  5653.   Result := False;
  5654.   if (Index <= 0) or (Index > Length(S)) or (ByteType(S, Index) <> mbSingleByte) then exit;
  5655.   Result := StrScan(PChar(Delimiters), S[Index]) <> nil;
  5656. end;
  5657.  
  5658. function AnsiPos(const Substr, S: string): Integer;
  5659. var
  5660.   P: PChar;
  5661. begin
  5662.   Result := 0;
  5663.   P := AnsiStrPos(PChar(S), PChar(SubStr));
  5664.   if P <> nil then
  5665.     Result := Integer(P) - Integer(PChar(S)) + 1;
  5666. end;
  5667.  
  5668. function AnsiCompareFileName(const S1, S2: string): Integer;
  5669. begin
  5670.   Result := AnsiCompareStr(AnsiLowerCaseFileName(S1), AnsiLowerCaseFileName(S2));
  5671. end;
  5672.  
  5673. function AnsiLowerCaseFileName(const S: string): string;
  5674. var
  5675.   I,L: Integer;
  5676. begin
  5677.   if SysLocale.FarEast then
  5678.   begin
  5679.     L := Length(S);
  5680.     SetLength(Result, L);
  5681.     I := 1;
  5682.     while I <= L do
  5683.     begin
  5684.       Result[I] := S[I];
  5685.       if S[I] in LeadBytes then
  5686.       begin
  5687.         Inc(I);
  5688.         Result[I] := S[I];
  5689.       end
  5690.       else
  5691.         if Result[I] in ['A'..'Z'] then Inc(Byte(Result[I]), 32);
  5692.       Inc(I);
  5693.     end;
  5694.   end
  5695.   else
  5696.     Result := AnsiLowerCase(S);
  5697. end;
  5698.  
  5699. function AnsiUpperCaseFileName(const S: string): string;
  5700. var
  5701.   I,L: Integer;
  5702. begin
  5703.   if SysLocale.FarEast then
  5704.   begin
  5705.     L := Length(S);
  5706.     SetLength(Result, L);
  5707.     I := 1;
  5708.     while I <= L do
  5709.     begin
  5710.       Result[I] := S[I];
  5711.       if S[I] in LeadBytes then
  5712.       begin
  5713.         Inc(I);
  5714.         Result[I] := S[I];
  5715.       end
  5716.       else
  5717.         if Result[I] in ['a'..'z'] then Dec(Byte(Result[I]), 32);
  5718.       Inc(I);
  5719.     end;
  5720.   end
  5721.   else
  5722.     Result := AnsiUpperCase(S);
  5723. end;
  5724.  
  5725. function AnsiStrPos(Str, SubStr: PChar): PChar;
  5726. var
  5727.   L1, L2: Cardinal;
  5728.   ByteType : TMbcsByteType;
  5729. begin
  5730.   Result := nil;
  5731.   if (Str = nil) or (Str^ = #0) or (SubStr = nil) or (SubStr^ = #0) then Exit;
  5732.   L1 := StrLen(Str);
  5733.   L2 := StrLen(SubStr);
  5734.   Result := StrPos(Str, SubStr);
  5735.   while (Result <> nil) and ((L1 - (Result - Str)) >= L2) do
  5736.   begin
  5737.     ByteType := StrByteType(Str, Integer(Result-Str));
  5738.     if (ByteType <> mbTrailByte) and
  5739.       (CompareString(LOCALE_USER_DEFAULT, 0, Result, L2, SubStr, L2) = 2) then Exit;
  5740.     if (ByteType = mbLeadByte) then Inc(Result);
  5741.     Inc(Result);
  5742.     Result := StrPos(Result, SubStr);
  5743.   end;
  5744.   Result := nil;
  5745. end;
  5746.  
  5747. function AnsiStrRScan(Str: PChar; Chr: Char): PChar;
  5748. begin
  5749.   Str := AnsiStrScan(Str, Chr);
  5750.   Result := Str;
  5751.   if Chr <> #$0 then
  5752.   begin
  5753.     while Str <> nil do
  5754.     begin
  5755.       Result := Str;
  5756.       Inc(Str);
  5757.       Str := AnsiStrScan(Str, Chr);
  5758.     end;
  5759.   end
  5760. end;
  5761.  
  5762. function AnsiStrScan(Str: PChar; Chr: Char): PChar;
  5763. begin
  5764.   Result := StrScan(Str, Chr);
  5765.   while Result <> nil do
  5766.   begin
  5767.     case StrByteType(Str, Integer(Result-Str)) of
  5768.       mbSingleByte: Exit;
  5769.       mbLeadByte: Inc(Result);
  5770.     end;
  5771.     Inc(Result);
  5772.     Result := StrScan(Result, Chr);
  5773.   end;
  5774. end;
  5775.  
  5776. procedure InitSysLocale;
  5777. var
  5778.   DefaultLCID: LCID;
  5779.   DefaultLangID: LANGID;
  5780.   AnsiCPInfo: TCPInfo;
  5781.   I: Integer;
  5782.   J: Byte;
  5783. begin
  5784.   { Set default to English (US). }
  5785.   SysLocale.DefaultLCID := $0409;
  5786.   SysLocale.PriLangID := LANG_ENGLISH;
  5787.   SysLocale.SubLangID := SUBLANG_ENGLISH_US;
  5788.   SysLocale.FarEast := False;
  5789.  
  5790.   DefaultLCID := GetThreadLocale;
  5791.   if DefaultLCID <> 0 then SysLocale.DefaultLCID := DefaultLCID;
  5792.  
  5793.   DefaultLangID := Word(DefaultLCID);
  5794.   if DefaultLangID <> 0 then
  5795.   begin
  5796.     SysLocale.PriLangID := DefaultLangID and $3ff;
  5797.     SysLocale.SubLangID := DefaultLangID shr 10;
  5798.   end;
  5799.  
  5800.   SysLocale.FarEast := GetSystemMetrics(SM_DBCSENABLED) <> 0;
  5801.   if not SysLocale.FarEast then Exit;
  5802.  
  5803.   GetCPInfo(CP_ACP, AnsiCPInfo);
  5804.   with AnsiCPInfo do
  5805.   begin
  5806.     I := 0;
  5807.     while (I < MAX_LEADBYTES) and ((LeadByte[I] or LeadByte[I+1]) <> 0) do
  5808.     begin
  5809.       for J := LeadByte[I] to LeadByte[I+1] do
  5810.         Include(LeadBytes, Char(J));
  5811.       Inc(I,2);
  5812.     end;
  5813.   end;
  5814. end;
  5815.  
  5816. { Package info structures }
  5817.  
  5818. type
  5819.   PPkgName = ^TPkgName;
  5820.   TPkgName = packed record
  5821.     HashCode: Byte;
  5822.     Name: array[0..255] of Char;
  5823.   end;
  5824.  
  5825.   { PackageUnitFlags:
  5826.     bit      meaning
  5827.     -----------------------------------------------------------------------------------------
  5828.     0      | main unit
  5829.     1      | package unit (dpk source)
  5830.     2      | $WEAKPACKAGEUNIT unit
  5831.     3      | original containment of $WEAKPACKAGEUNIT (package into which it was compiled)
  5832.     4      | implicitly imported
  5833.     5..7   | reserved
  5834.   }
  5835.   PUnitName = ^TUnitName;
  5836.   TUnitName = packed record
  5837.     Flags : Byte;
  5838.     HashCode: Byte;
  5839.     Name: array[0..255] of Char;
  5840.   end;
  5841.  
  5842.   { Package flags:
  5843.     bit     meaning
  5844.     -----------------------------------------------------------------------------------------
  5845.     0     | 1: never-build              0: always build
  5846.     1     | 1: design-time only         0: not design-time only      on => bit 2 = off
  5847.     2     | 1: run-time only            0: not run-time only         on => bit 1 = off
  5848.     3..29 | reserved
  5849.     30..31| 0: EXE, 1: Package DLL, 2: Library DLL, 3: undefined
  5850.   }
  5851.   PPackageInfoHeader = ^TPackageInfoHeader;
  5852.   TPackageInfoHeader = packed record
  5853.     Flags: Integer;
  5854.     RequiresCount: Integer;
  5855.     {Requires: array[0..9999] of TPkgName;
  5856.     ContainsCount: Integer;
  5857.     Contains: array[0..9999] of TUnitName;}
  5858.   end;
  5859.  
  5860. function PackageInfoTable(Module: HMODULE): PPackageInfoHeader;
  5861. var
  5862.   ResInfo: HRSRC;
  5863.   Data: THandle;
  5864. begin
  5865.   Result := nil;
  5866.   ResInfo := FindResource(Module, 'PACKAGEINFO', RT_RCDATA);
  5867.   if ResInfo <> 0 then
  5868.   begin
  5869.     Data := LoadResource(Module, ResInfo);
  5870.     if Data <> 0 then
  5871.     try
  5872.       Result := LockResource(Data);
  5873.       UnlockResource(Data);
  5874.     finally
  5875.       FreeResource(Data);
  5876.     end;
  5877.   end;
  5878. end;
  5879.  
  5880. function GetModuleName(Module: HMODULE): string;
  5881. var
  5882.   ModName: array[0..MAX_PATH] of Char;
  5883. begin
  5884.   SetString(Result, ModName, Windows.GetModuleFileName(Module, ModName, SizeOf(ModName)));
  5885. end;
  5886.  
  5887. var
  5888.   Reserved: Integer;
  5889.  
  5890. procedure CheckForDuplicateUnits(Module: HMODULE);
  5891.  
  5892.   function IsUnitPresent(HC: Byte; UnitName: PChar; Module: HMODULE;
  5893.     const ModuleName: string; var UnitPackage: string): Boolean;
  5894.   var
  5895.     I: Integer;
  5896.     InfoTable: PPackageInfoHeader;
  5897.     LibModule: PLibModule;
  5898.     PkgName: PPkgName;
  5899.     UName : PUnitName;
  5900.     Count: Integer;
  5901.   begin
  5902.     Result := True;
  5903.     if (StrIComp(UnitName, 'SysInit') <> 0) and
  5904.           (StrIComp(UnitName, PChar(ModuleName)) <> 0) then
  5905.     begin
  5906.       LibModule := LibModuleList;
  5907.       while LibModule <> nil do
  5908.       begin
  5909.         if LibModule.Instance <> Module then
  5910.         begin
  5911.           InfoTable := PackageInfoTable(HMODULE(LibModule.Instance));
  5912.           if (InfoTable <> nil) and (InfoTable.Flags and pfModuleTypeMask = pfPackageModule) then
  5913.           begin
  5914.             PkgName := PPkgName(Integer(InfoTable) + SizeOf(InfoTable^));
  5915.             Count := InfoTable.RequiresCount;
  5916.             { Skip the Requires list }
  5917.             for I := 0 to Count - 1 do Inc(Integer(PkgName), StrLen(PkgName.Name) + 2);
  5918.             Count := Integer(Pointer(PkgName)^);
  5919.             UName := PUnitName(Integer(PkgName) + 4);
  5920.             for I := 0 to Count - 1 do
  5921.             begin
  5922.               with UName^ do
  5923.                 // Test Flags to ignore weak package units
  5924.                 if (HashCode = HC) and ((Flags and $06) = 0) and (StrIComp(UnitName, Name) = 0) then
  5925.                 begin
  5926.                   UnitPackage := ChangeFileExt(ExtractFileName(
  5927.                     GetModuleName(HMODULE(LibModule.Instance))), '');
  5928.                   Exit;
  5929.                 end;
  5930.               Inc(Integer(UName), StrLen(UName.Name) + 3);
  5931.             end;
  5932.           end;
  5933.         end;
  5934.         LibModule := LibModule.Next;
  5935.       end;
  5936.     end;
  5937.     Result := False;
  5938.   end;
  5939.  
  5940.   function FindLibModule(Module: HModule): PLibModule;
  5941.   begin
  5942.     Result := LibModuleList;
  5943.     while Result <> nil do
  5944.     begin
  5945.       if Result.Instance = Module then Exit;
  5946.       Result := Result.Next;
  5947.     end;
  5948.   end;
  5949.  
  5950.   procedure InternalUnitCheck(Module: HModule);
  5951.   var
  5952.     I: Integer;
  5953.     InfoTable: PPackageInfoHeader;
  5954.     UnitPackage: string;
  5955.     ModuleName: string;
  5956.     PkgName: PPkgName;
  5957.     UName: PUnitName;
  5958.     Count: Integer;
  5959.     LibModule: PLibModule;
  5960.   begin
  5961.     InfoTable := PackageInfoTable(Module);
  5962.     if (InfoTable <> nil) and (InfoTable.Flags and pfModuleTypeMask = pfPackageModule) then
  5963.     begin
  5964.       ModuleName := ChangeFileExt(ExtractFileName(GetModuleName(Module)), '');
  5965.       PkgName := PPkgName(Integer(InfoTable) + SizeOf(InfoTable^));
  5966.       Count := InfoTable.RequiresCount;
  5967.       for I := 0 to Count - 1 do
  5968.       begin
  5969.         with PkgName^ do
  5970.           InternalUnitCheck(GetModuleHandle(PChar(ChangeFileExt(Name, '.dll'))));
  5971.         Inc(Integer(PkgName), StrLen(PkgName.Name) + 2);
  5972.       end;
  5973.       LibModule := FindLibModule(Module);
  5974.       if (LibModule = nil) or ((LibModule <> nil) and (LibModule.Reserved <> Reserved)) then
  5975.       begin
  5976.         if LibModule <> nil then LibModule.Reserved := Reserved;
  5977.         Count := Integer(Pointer(PkgName)^);
  5978.         UName := PUnitName(Integer(PkgName) + 4);
  5979.         for I := 0 to Count - 1 do
  5980.         begin
  5981.           with UName^ do
  5982.             // Test Flags to ignore weak package units
  5983.             if ((Flags and ufWeakPackageUnit) = 0 ) and
  5984.               IsUnitPresent(HashCode, Name, Module, ModuleName, UnitPackage) then
  5985.               raise EPackageError.CreateFmt(SDuplicatePackageUnit,
  5986.                 [ModuleName, Name, UnitPackage]);
  5987.           Inc(Integer(UName), StrLen(UName.Name) + 3);
  5988.         end;
  5989.       end;
  5990.     end;
  5991.   end;
  5992.  
  5993. begin
  5994.   Inc(Reserved);
  5995.   InternalUnitCheck(Module);
  5996. end;
  5997.  
  5998. { InitializePackage }
  5999.  
  6000. procedure InitializePackage(Module: HMODULE);
  6001. type
  6002.   TPackageLoad = procedure;
  6003. var
  6004.   PackageLoad: TPackageLoad;
  6005. begin
  6006.   CheckForDuplicateUnits(Module);
  6007.   @PackageLoad := GetProcAddress(Module, 'Initialize'); //Do not localize
  6008.   if Assigned(PackageLoad) then
  6009.     PackageLoad else
  6010.     raise Exception.CreateFmt(sInvalidPackageFile, [GetModuleName(Module)]);
  6011. end;
  6012.  
  6013. { FinalizePackage }
  6014.  
  6015. procedure FinalizePackage(Module: HMODULE);
  6016. type
  6017.   TPackageUnload = procedure;
  6018. var
  6019.   PackageUnload: TPackageUnload;
  6020. begin
  6021.   @PackageUnload := GetProcAddress(Module, 'Finalize'); //Do not localize
  6022.   if Assigned(PackageUnload) then
  6023.     PackageUnload else
  6024.     raise EPackageError.Create(sInvalidPackageHandle);
  6025. end;
  6026.  
  6027. { LoadPackage }
  6028.  
  6029. function LoadPackage(const Name: string): HMODULE;
  6030. begin
  6031.   Result := LoadLibrary(PChar(Name));
  6032.   if (Result > -1) and (Result <= 32) then
  6033.     raise EPackageError.CreateFmt(sErrorLoadingPackage,
  6034.       [Name, SysErrorMessage(GetLastError)]);
  6035.   try
  6036.     InitializePackage(Result);
  6037.   except
  6038.     FreeLibrary(Result);
  6039.     raise;
  6040.   end;
  6041. end;
  6042.  
  6043. { UnloadPackage }
  6044.  
  6045. procedure UnloadPackage(Module: HMODULE);
  6046. begin
  6047.   FinalizePackage(Module);
  6048.   FreeLibrary(Module);
  6049. end;
  6050.  
  6051. { GetPackageInfo }
  6052.  
  6053. procedure GetPackageInfo(Module: HMODULE; Param: Pointer; var Flags: Integer;
  6054.   InfoProc: TPackageInfoProc);
  6055. var
  6056.   InfoTable: PPackageInfoHeader;
  6057.   I: Integer;
  6058.   PkgName: PPkgName;
  6059.   UName: PUnitName;
  6060.   Count: Integer;
  6061. begin
  6062.   InfoTable := PackageInfoTable(Module);
  6063.   if not Assigned(InfoTable) then
  6064.     raise Exception.CreateFmt(SCannotReadPackageInfo,
  6065.       [ExtractFileName(GetModuleName(Module))]);
  6066.   Flags := InfoTable.Flags;
  6067.   with InfoTable^ do
  6068.   begin
  6069.     PkgName := PPkgName(Integer(InfoTable) + SizeOf(InfoTable^));
  6070.     Count := RequiresCount;
  6071.     for I := 0 to Count - 1 do
  6072.     begin
  6073.       InfoProc(PkgName.Name, ntRequiresPackage, 0, Param);
  6074.       Inc(Integer(PkgName), StrLen(PkgName.Name) + 2);
  6075.     end;
  6076.     Count := Integer(Pointer(PkgName)^);
  6077.     UName := PUnitName(Integer(PkgName) + 4);
  6078.     for I := 0 to Count - 1 do
  6079.     begin
  6080.       InfoProc(UName.Name, ntContainsUnit, UName.Flags, Param);
  6081.       Inc(Integer(UName), StrLen(UName.Name) + 3);
  6082.     end;
  6083.   end;
  6084. end;
  6085.  
  6086. function GetPackageDescription(ModuleName: PChar): string;
  6087. var
  6088.   ResModule: HModule;
  6089.   ResInfo: HRSRC;
  6090.   ResData: HGLOBAL;
  6091. begin
  6092.   Result := '';
  6093.   ResModule := LoadResourceModule(ModuleName);
  6094.   if ResModule = 0 then
  6095.   begin
  6096.     ResModule := LoadLibraryEx(ModuleName, 0, LOAD_LIBRARY_AS_DATAFILE);
  6097.     if (ResModule >= 0) and (ResModule <= 32) then
  6098.       raise EPackageError.CreateFmt(sErrorLoadingPackage,
  6099.         [ModuleName, SysErrorMessage(GetLastError)]);
  6100.   end;
  6101.   try
  6102.     ResInfo := FindResource(ResModule, 'DESCRIPTION', RT_RCDATA);
  6103.     if ResInfo <> 0 then
  6104.     begin
  6105.       ResData := LoadResource(ResModule, ResInfo);
  6106.       if ResData <> 0 then
  6107.       try
  6108.         Result := PWideChar(LockResource(ResData));
  6109.         UnlockResource(ResData);
  6110.       finally
  6111.         FreeResource(ResData);
  6112.       end;
  6113.     end;
  6114.   finally
  6115.     FreeLibrary(ResModule);
  6116.   end;
  6117. end;
  6118.  
  6119. { RaiseLastWin32Error }
  6120.  
  6121. procedure RaiseLastWin32Error;
  6122. var
  6123.   LastError: DWORD;
  6124.   Error: EWin32Error;
  6125. begin
  6126.   LastError := GetLastError;
  6127.   if LastError <> ERROR_SUCCESS then
  6128.     Error := EWin32Error.CreateFmt(SWin32Error, [LastError,
  6129.       SysErrorMessage(LastError)])
  6130.   else
  6131.     Error := EWin32Error.Create(SUnkWin32Error);
  6132.   Error.ErrorCode := LastError;
  6133.   raise Error;
  6134. end;
  6135.  
  6136. { Win32Check }
  6137.  
  6138. function Win32Check(RetVal: BOOL): BOOL;
  6139. begin
  6140.   if not RetVal then RaiseLastWin32Error;
  6141.   Result := RetVal;
  6142. end;
  6143.  
  6144. type
  6145.   PTerminateProcInfo = ^TTerminateProcInfo;
  6146.   TTerminateProcInfo = record
  6147.     Next: PTerminateProcInfo;
  6148.     Proc: TTerminateProc;
  6149.   end;
  6150.  
  6151. var
  6152.   TerminateProcList: PTerminateProcInfo = nil;
  6153.  
  6154. procedure AddTerminateProc(TermProc: TTerminateProc);
  6155. var
  6156.   P: PTerminateProcInfo;
  6157. begin
  6158.   New(P);
  6159.   P^.Next := TerminateProcList;
  6160.   P^.Proc := TermProc;
  6161.   TerminateProcList := P;
  6162. end;
  6163.  
  6164. function CallTerminateProcs: Boolean;
  6165. var
  6166.   PI: PTerminateProcInfo;
  6167. begin
  6168.   Result := True;
  6169.   PI := TerminateProcList;
  6170.   while Result and (PI <> nil) do
  6171.   begin
  6172.     Result := PI^.Proc;
  6173.     PI := PI^.Next;
  6174.   end;
  6175. end;
  6176.  
  6177. procedure FreeTerminateProcs;
  6178. var
  6179.   PI: PTerminateProcInfo;
  6180. begin
  6181.   while TerminateProcList <> nil do
  6182.   begin
  6183.     PI := TerminateProcList;
  6184.     TerminateProcList := PI^.Next;
  6185.     Dispose(PI);
  6186.   end;
  6187. end;
  6188.  
  6189. { --- }
  6190.  
  6191. function AL1(const P): Longint;
  6192. asm
  6193.         MOV     EDX,DWORD PTR [P]
  6194.         XOR     EDX,DWORD PTR [P+4]
  6195.         XOR     EDX,DWORD PTR [P+8]
  6196.         XOR     EDX,DWORD PTR [P+12]
  6197.         MOV     EAX,EDX
  6198. end;
  6199.  
  6200. function AL2(const P): Longint;
  6201. asm
  6202.         MOV     EDX,DWORD PTR [P]
  6203.         ROR     EDX,5
  6204.         XOR     EDX,DWORD PTR [P+4]
  6205.         ROR     EDX,5
  6206.         XOR     EDX,DWORD PTR [P+8]
  6207.         ROR     EDX,5
  6208.         XOR     EDX,DWORD PTR [P+12]
  6209.         MOV     EAX,EDX
  6210. end;
  6211.  
  6212. const
  6213.   AL1s: array[0..2] of Longint = ($FFFFFFF0, $FFFFEBF0, 0);
  6214.   AL2s: array[0..2] of Longint = ($42C3ECEF, $20F7AEB6, $D1C2F74E);
  6215.  
  6216. procedure ALV;
  6217. begin
  6218.   raise Exception.Create(SNL);
  6219. end;
  6220.  
  6221. function ALR: Pointer;
  6222. var
  6223.   LibModule: PLibModule;
  6224. begin
  6225.   if MainInstance <> 0 then
  6226.     Result := Pointer(LoadResource(MainInstance, FindResource(MainInstance, 'DVCLAL',
  6227.       RT_RCDATA)))
  6228.   else
  6229.   begin
  6230.     Result := nil;
  6231.     LibModule := LibModuleList;
  6232.     while LibModule <> nil do
  6233.       with LibModule^ do
  6234.       begin
  6235.         Result := Pointer(LoadResource(Instance, FindResource(Instance, 'DVCLAL',
  6236.           RT_RCDATA)));
  6237.         if Result <> nil then Break;
  6238.       end;
  6239.   end;
  6240.   if Result = nil then ALV;
  6241. end;
  6242.  
  6243. function GDAL: Longint;
  6244. type
  6245.   TDVCLAL = array[0..3] of Longint;
  6246.   PDVCLAL = ^TDVCLAL;
  6247. var
  6248.   P: Pointer;
  6249.   A1, A2: Integer;
  6250.   PAL1s, PAL2s: PDVCLAL;
  6251. begin
  6252.   P := ALR;
  6253.   A1 := AL1(P^);
  6254.   A2 := AL2(P^);
  6255.   Result := A1;
  6256.   PAL1s := @AL1s;
  6257.   PAL2s := @AL2s;
  6258.   if (A1 = PAL1s[0]) and (A2 = PAL2s[0]) then Exit;
  6259.   if (A1 = PAL1s[1]) and (A2 = PAL2s[1]) then Exit;
  6260.   if (A1 = PAL1s[2]) and (A2 = PAL2s[2]) then Exit;
  6261.   ALV;
  6262. end;
  6263.  
  6264. procedure RCS;
  6265. var
  6266.   P: Pointer;
  6267. begin
  6268.   P := ALR;
  6269.   if (AL1(P^) <> AL1s[2]) or (AL2(P^) <> AL2s[2]) then ALV;
  6270. end;
  6271.  
  6272. procedure RPR;
  6273. var
  6274.   AL: Integer;
  6275. begin
  6276.   AL := GDAL;
  6277.   if (AL <> AL1s[1]) and (AL <> AL1s[2]) then ALV;
  6278. end;
  6279.  
  6280. initialization
  6281.   InitExceptions;
  6282.   InitSysLocale;
  6283.   GetMonthDayNames;
  6284.   if SysLocale.FarEast then GetEraNamesAndYearOffsets;
  6285.   GetFormatSettings;
  6286.   InitPlatformId;
  6287.  
  6288. finalization
  6289.   FreeTerminateProcs;
  6290.   DoneExceptions;
  6291.  
  6292. end.
  6293.