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