PageRenderTime 121ms CodeModel.GetById 43ms RepoModel.GetById 0ms app.codeStats 1ms

/system/SysUtils.pas

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