home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / Runimage / Delphi50 / Source / Rtl / Sys / SYSUTILS.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1999-08-11  |  220.9 KB  |  7,362 lines

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