/kol/System/D2005/SysUtils.pas

https://bitbucket.org/krak/guidgenerator · Pascal · 16719 lines · 12400 code · 1484 blank · 2835 comment · 792 complexity · 7698f403b0e1da1267c4795ba961e473 MD5 · raw file

  1. { *********************************************************************** }
  2. { }
  3. { Delphi / Kylix Cross-Platform Runtime Library }
  4. { System Utilities Unit }
  5. { }
  6. { Copyright (c) 1995-2004 Borland Software Corporation }
  7. { }
  8. { Copyright and license exceptions noted in source }
  9. { }
  10. { *********************************************************************** }
  11. unit SysUtils;
  12. {$H+}
  13. {$WARN SYMBOL_PLATFORM OFF}
  14. {$WARN UNSAFE_TYPE OFF}
  15. interface
  16. uses
  17. {$IFDEF MSWINDOWS}
  18. Windows, kol,
  19. {$ENDIF}
  20. {$IFDEF LINUX}
  21. Types,
  22. Libc,
  23. {$ENDIF}
  24. SysConst;
  25. const
  26. { File open modes }
  27. {$IFDEF LINUX}
  28. fmOpenRead = O_RDONLY;
  29. fmOpenWrite = O_WRONLY;
  30. fmOpenReadWrite = O_RDWR;
  31. // fmShareCompat not supported
  32. fmShareExclusive = $0010;
  33. fmShareDenyWrite = $0020;
  34. // fmShareDenyRead not supported
  35. fmShareDenyNone = $0030;
  36. {$ENDIF}
  37. {$IFDEF MSWINDOWS}
  38. fmOpenRead = $0000;
  39. fmOpenWrite = $0001;
  40. fmOpenReadWrite = $0002;
  41. fmShareCompat = $0000 platform; // DOS compatibility mode is not portable
  42. fmShareExclusive = $0010;
  43. fmShareDenyWrite = $0020;
  44. fmShareDenyRead = $0030 platform; // write-only not supported on all platforms
  45. fmShareDenyNone = $0040;
  46. {$ENDIF}
  47. { File attribute constants }
  48. faReadOnly = $00000001 platform;
  49. faHidden = $00000002 platform;
  50. faSysFile = $00000004 platform;
  51. faVolumeID = $00000008 platform deprecated; // not used in Win32
  52. faDirectory = $00000010;
  53. faArchive = $00000020 platform;
  54. faSymLink = $00000040 platform;
  55. faAnyFile = $0000003F;
  56. { Units of time }
  57. HoursPerDay = 24;
  58. MinsPerHour = 60;
  59. SecsPerMin = 60;
  60. MSecsPerSec = 1000;
  61. MinsPerDay = HoursPerDay * MinsPerHour;
  62. SecsPerDay = MinsPerDay * SecsPerMin;
  63. MSecsPerDay = SecsPerDay * MSecsPerSec;
  64. { Days between 1/1/0001 and 12/31/1899 }
  65. DateDelta = 693594;
  66. { Days between TDateTime basis (12/31/1899) and Unix time_t basis (1/1/1970) }
  67. UnixDateDelta = 25569;
  68. type
  69. { Standard Character set type }
  70. TSysCharSet = set of Char;
  71. { Set access to an integer }
  72. TIntegerSet = set of 0..SizeOf(Integer) * 8 - 1;
  73. { Type conversion records }
  74. WordRec = packed record
  75. case Integer of
  76. 0: (Lo, Hi: Byte);
  77. 1: (Bytes: array [0..1] of Byte);
  78. end;
  79. LongRec = packed record
  80. case Integer of
  81. 0: (Lo, Hi: Word);
  82. 1: (Words: array [0..1] of Word);
  83. 2: (Bytes: array [0..3] of Byte);
  84. end;
  85. Int64Rec = packed record
  86. case Integer of
  87. 0: (Lo, Hi: Cardinal);
  88. 1: (Cardinals: array [0..1] of Cardinal);
  89. 2: (Words: array [0..3] of Word);
  90. 3: (Bytes: array [0..7] of Byte);
  91. end;
  92. { General arrays }
  93. PByteArray = ^TByteArray;
  94. TByteArray = array[0..32767] of Byte;
  95. PWordArray = ^TWordArray;
  96. TWordArray = array[0..16383] of Word;
  97. { Generic procedure pointer }
  98. TProcedure = procedure;
  99. { Generic filename type }
  100. TFileName = type string;
  101. { Search record used by FindFirst, FindNext, and FindClose }
  102. TSearchRec = record
  103. Time: Integer;
  104. Size: Integer;
  105. Attr: Integer;
  106. Name: TFileName;
  107. ExcludeAttr: Integer;
  108. {$IFDEF MSWINDOWS}
  109. FindHandle: THandle platform;
  110. FindData: TWin32FindData platform;
  111. {$ENDIF}
  112. {$IFDEF LINUX}
  113. Mode: mode_t platform;
  114. FindHandle: Pointer platform;
  115. PathOnly: String platform;
  116. Pattern: String platform;
  117. {$ENDIF}
  118. end;
  119. { FloatToText, FloatToTextFmt, TextToFloat, and FloatToDecimal type codes }
  120. TFloatValue = (fvExtended, fvCurrency);
  121. { FloatToText format codes }
  122. TFloatFormat = (ffGeneral, ffExponent, ffFixed, ffNumber, ffCurrency);
  123. { FloatToDecimal result record }
  124. TFloatRec = packed record
  125. Exponent: Smallint;
  126. Negative: Boolean;
  127. Digits: array[0..20] of Char;
  128. end;
  129. { Date and time record }
  130. TTimeStamp = record
  131. Time: Integer; { Number of milliseconds since midnight }
  132. Date: Integer; { One plus number of days since 1/1/0001 }
  133. end;
  134. { MultiByte Character Set (MBCS) byte type }
  135. TMbcsByteType = (mbSingleByte, mbLeadByte, mbTrailByte);
  136. { System Locale information record }
  137. TSysLocale = packed record
  138. DefaultLCID: Integer;
  139. PriLangID: Integer;
  140. SubLangID: Integer;
  141. FarEast: Boolean;
  142. MiddleEast: Boolean;
  143. end;
  144. {$IFDEF MSWINDOWS}
  145. { This is used by TLanguages }
  146. TLangRec = packed record
  147. FName: string;
  148. FLCID: LCID;
  149. FExt: string;
  150. end;
  151. { This stores the languages that the system supports }
  152. TLanguages = class
  153. private
  154. FSysLangs: array of TLangRec;
  155. function LocalesCallback(LocaleID: PChar): Integer; stdcall;
  156. function GetExt(Index: Integer): string;
  157. function GetID(Index: Integer): string;
  158. function GetLCID(Index: Integer): LCID;
  159. function GetName(Index: Integer): string;
  160. function GetNameFromLocaleID(ID: LCID): string;
  161. function GetNameFromLCID(const ID: string): string;
  162. function GetCount: integer;
  163. public
  164. constructor Create;
  165. function IndexOf(ID: LCID): Integer;
  166. property Count: Integer read GetCount;
  167. property Name[Index: Integer]: string read GetName;
  168. property NameFromLocaleID[ID: LCID]: string read GetNameFromLocaleID;
  169. property NameFromLCID[const ID: string]: string read GetNameFromLCID;
  170. property ID[Index: Integer]: string read GetID;
  171. property LocaleID[Index: Integer]: LCID read GetLCID;
  172. property Ext[Index: Integer]: string read GetExt;
  173. end platform;
  174. {$ENDIF}
  175. {$IFDEF LINUX}
  176. TEraRange = record
  177. StartDate : Integer; // whole days since 12/31/1899 (TDateTime basis)
  178. EndDate : Integer; // whole days since 12/31/1899 (TDateTime basis)
  179. // Direction : Char;
  180. end;
  181. {$ENDIF}
  182. { Exceptions }
  183. Exception = class(TObject)
  184. private
  185. FMessage: string;
  186. FHelpContext: Integer;
  187. public
  188. constructor Create(const Msg: string);
  189. constructor CreateFmt(const Msg: string; const Args: array of const);
  190. constructor CreateRes(Ident: Integer); overload;
  191. constructor CreateRes(const ResStringRec: string); overload;
  192. constructor CreateResFmt(Ident: Integer; const Args: array of const); overload;
  193. constructor CreateResFmt(const ResStringRec: string; const Args: array of const); overload;
  194. constructor CreateHelp(const Msg: string; AHelpContext: Integer);
  195. constructor CreateFmtHelp(const Msg: string; const Args: array of const;
  196. AHelpContext: Integer);
  197. constructor CreateResHelp(Ident: Integer; AHelpContext: Integer); overload;
  198. constructor CreateResHelp(ResStringRec: PResStringRec; AHelpContext: Integer); overload;
  199. constructor CreateResFmtHelp(ResStringRec: PResStringRec; const Args: array of const;
  200. AHelpContext: Integer); overload;
  201. constructor CreateResFmtHelp(Ident: Integer; const Args: array of const;
  202. AHelpContext: Integer); overload;
  203. property HelpContext: Integer read FHelpContext write FHelpContext;
  204. property Message: string read FMessage write FMessage;
  205. end;
  206. ExceptClass = class of Exception;
  207. EAbort = class(Exception);
  208. EHeapException = class(Exception)
  209. private
  210. AllowFree: Boolean;
  211. public
  212. procedure FreeInstance; override;
  213. end;
  214. EOutOfMemory = class(EHeapException);
  215. EInOutError = class(Exception)
  216. public
  217. ErrorCode: Integer;
  218. end;
  219. {$IFDEF MSWINDOWS}
  220. PExceptionRecord = ^TExceptionRecord;
  221. TExceptionRecord = record
  222. ExceptionCode: Cardinal;
  223. ExceptionFlags: Cardinal;
  224. ExceptionRecord: PExceptionRecord;
  225. ExceptionAddress: Pointer;
  226. NumberParameters: Cardinal;
  227. ExceptionInformation: array[0..14] of Cardinal;
  228. end;
  229. {$ENDIF}
  230. EExternal = class(Exception)
  231. public
  232. {$IFDEF MSWINDOWS}
  233. ExceptionRecord: PExceptionRecord platform;
  234. {$ENDIF}
  235. {$IFDEF LINUX}
  236. ExceptionAddress: LongWord platform;
  237. AccessAddress: LongWord platform;
  238. SignalNumber: Integer platform;
  239. {$ENDIF}
  240. end;
  241. EExternalException = class(EExternal);
  242. EIntError = class(EExternal);
  243. EDivByZero = class(EIntError);
  244. ERangeError = class(EIntError);
  245. EIntOverflow = class(EIntError);
  246. EMathError = class(EExternal);
  247. EInvalidOp = class(EMathError);
  248. EZeroDivide = class(EMathError);
  249. EOverflow = class(EMathError);
  250. EUnderflow = class(EMathError);
  251. EInvalidPointer = class(EHeapException);
  252. EInvalidCast = class(Exception);
  253. EConvertError = class(Exception);
  254. EAccessViolation = class(EExternal);
  255. EPrivilege = class(EExternal);
  256. EStackOverflow = class(EExternal)
  257. end deprecated;
  258. EControlC = class(EExternal);
  259. {$IFDEF LINUX}
  260. EQuit = class(EExternal) end platform;
  261. {$ENDIF}
  262. {$IFDEF LINUX}
  263. ECodesetConversion = class(Exception) end platform;
  264. {$ENDIF}
  265. EVariantError = class(Exception);
  266. EPropReadOnly = class(Exception);
  267. EPropWriteOnly = class(Exception);
  268. EAssertionFailed = class(Exception);
  269. {$IFNDEF PC_MAPPED_EXCEPTIONS}
  270. EAbstractError = class(Exception) end platform;
  271. {$ENDIF}
  272. EIntfCastError = class(Exception);
  273. EInvalidContainer = class(Exception);
  274. EInvalidInsert = class(Exception);
  275. EPackageError = class(Exception);
  276. EOSError = class(Exception)
  277. public
  278. ErrorCode: DWORD;
  279. end;
  280. {$IFDEF MSWINDOWS}
  281. EWin32Error = class(EOSError)
  282. end deprecated;
  283. {$ENDIF}
  284. ESafecallException = class(Exception);
  285. {$IFDEF LINUX}
  286. {
  287. Signals
  288. External exceptions, or signals, are, by default, converted to language
  289. exceptions by the Delphi RTL. Under Linux, a Delphi application installs
  290. signal handlers to trap the raw signals, and convert them. Delphi libraries
  291. do not install handlers by default. So if you are implementing a standalone
  292. library, such as an Apache DSO, and you want to have signals converted to
  293. language exceptions that you can catch, you must install signal hooks
  294. manually, using the interfaces that the Delphi RTL provides.
  295. For most libraries, installing signal handlers is pretty
  296. straightforward. Call HookSignal(RTL_SIGDEFAULT) at initialization time,
  297. and UnhookSignal(RTL_SIGNALDEFAULT) at shutdown. This will install handlers
  298. for a set of signals that the RTL normally hooks for Delphi applications.
  299. There are some cases where the above initialization will not work properly:
  300. The proper behaviour for setting up signal handlers is to set
  301. a signal handler, and then later restore the signal handler to its previous
  302. state when you clean up. If you have two libraries lib1 and lib2, and lib1
  303. installs a signal handler, and then lib2 installs a signal handler, those
  304. libraries have to uninstall in the proper order if they restore signal
  305. handlers, or the signal handlers can be left in an inconsistent and
  306. potentially fatal state. Not all libraries behave well with respect to
  307. installing signal handlers. To hedge against this possibility, and allow
  308. you to manage signal handlers better in the face of whatever behaviour
  309. you may find in external libraries, we provide a set of four interfaces to
  310. allow you to tailor the Delphi signal handler hooking/unhooking in the
  311. event of an emergency. These are:
  312. InquireSignal
  313. AbandonSignalHandler
  314. HookSignal
  315. UnhookSignal
  316. InquireSignal allows you to look at the state of a signal handler, so
  317. that you can find out if someone grabbed it out from under you.
  318. AbandonSignalHandler tells the RTL never to unhook a particular
  319. signal handler. This can be used if you find a case where it would
  320. be unsafe to return to the previous state of signal handling. For
  321. example, if the previous signal handler was installed by a library
  322. which has since been unloaded.
  323. HookSignal/UnhookSignal setup signal handlers that map certain signals
  324. into language exceptions.
  325. See additional notes at InquireSignal, et al, below.
  326. }
  327. const
  328. RTL_SIGINT = 0; // User interrupt (SIGINT)
  329. RTL_SIGFPE = 1; // Floating point exception (SIGFPE)
  330. RTL_SIGSEGV = 2; // Segmentation violation (SIGSEGV)
  331. RTL_SIGILL = 3; // Illegal instruction (SIGILL)
  332. RTL_SIGBUS = 4; // Bus error (SIGBUS)
  333. RTL_SIGQUIT = 5; // User interrupt (SIGQUIT)
  334. RTL_SIGLAST = RTL_SIGQUIT; // Used internally. Don't use this.
  335. RTL_SIGDEFAULT = -1; // Means all of a set of signals that the we capture
  336. // normally. This is currently all of the preceding
  337. // signals. You cannot pass this to InquireSignal.
  338. type
  339. { TSignalState is the state of a given signal handler, as returned by
  340. InquireSignal. See InquireSignal, below.
  341. }
  342. TSignalState = (ssNotHooked, ssHooked, ssOverridden);
  343. var
  344. {
  345. If DeferUserInterrupts is set, we do not raise either SIGINT or SIGQUIT as
  346. an exception, instead, we set SIGINTIssued or SIGQUITIssued when the
  347. signal arrives, and swallow the signal where the OS issued it. This gives
  348. GUI applications the chance to defer the actual handling of the signal
  349. until a time when it is safe to do so.
  350. }
  351. DeferUserInterrupts: Boolean;
  352. SIGINTIssued: Boolean;
  353. SIGQUITIssued: Boolean;
  354. {$ENDIF}
  355. {$IFDEF LINUX}
  356. const
  357. MAX_PATH = 4095; // From /usr/include/linux/limits.h PATH_MAX
  358. {$ENDIF}
  359. var
  360. { Empty string and null string pointer. These constants are provided for
  361. backwards compatibility only. }
  362. EmptyStr: string = '';
  363. NullStr: PString = @EmptyStr;
  364. EmptyWideStr: WideString = '';
  365. NullWideStr: PWideString = @EmptyWideStr;
  366. {$IFDEF MSWINDOWS}
  367. { Win32 platform identifier. This will be one of the following values:
  368. VER_PLATFORM_WIN32s
  369. VER_PLATFORM_WIN32_WINDOWS
  370. VER_PLATFORM_WIN32_NT
  371. See WINDOWS.PAS for the numerical values. }
  372. Win32Platform: Integer = 0;
  373. { Win32 OS version information -
  374. see TOSVersionInfo.dwMajorVersion/dwMinorVersion/dwBuildNumber }
  375. Win32MajorVersion: Integer = 0;
  376. Win32MinorVersion: Integer = 0;
  377. Win32BuildNumber: Integer = 0;
  378. { Win32 OS extra version info string -
  379. see TOSVersionInfo.szCSDVersion }
  380. Win32CSDVersion: string = '';
  381. { Win32 OS version tester }
  382. function CheckWin32Version(AMajor: Integer; AMinor: Integer = 0): Boolean;
  383. { GetFileVersion returns the most significant 32 bits of a file's binary
  384. version number. Typically, this includes the major and minor version placed
  385. together in one 32-bit integer. It generally does not include the release
  386. or build numbers. It returns Cardinal(-1) if it failed. }
  387. function GetFileVersion(const AFileName: string): Cardinal;
  388. {$ENDIF}
  389. { Currency and date/time formatting options
  390. The initial values of these variables are fetched from the system registry
  391. using the GetLocaleInfo function in the Win32 API. The description of each
  392. variable specifies the LOCALE_XXXX constant used to fetch the initial
  393. value.
  394. CurrencyString - Defines the currency symbol used in floating-point to
  395. decimal conversions. The initial value is fetched from LOCALE_SCURRENCY.
  396. CurrencyFormat - Defines the currency symbol placement and separation
  397. used in floating-point to decimal conversions. Possible values are:
  398. 0 = '$1'
  399. 1 = '1$'
  400. 2 = '$ 1'
  401. 3 = '1 $'
  402. The initial value is fetched from LOCALE_ICURRENCY.
  403. NegCurrFormat - Defines the currency format for used in floating-point to
  404. decimal conversions of negative numbers. Possible values are:
  405. 0 = '($1)' 4 = '(1$)' 8 = '-1 $' 12 = '$ -1'
  406. 1 = '-$1' 5 = '-1$' 9 = '-$ 1' 13 = '1- $'
  407. 2 = '$-1' 6 = '1-$' 10 = '1 $-' 14 = '($ 1)'
  408. 3 = '$1-' 7 = '1$-' 11 = '$ 1-' 15 = '(1 $)'
  409. The initial value is fetched from LOCALE_INEGCURR.
  410. ThousandSeparator - The character used to separate thousands in numbers
  411. with more than three digits to the left of the decimal separator. The
  412. initial value is fetched from LOCALE_STHOUSAND. A value of #0 indicates
  413. no thousand separator character should be output even if the format string
  414. specifies thousand separators.
  415. DecimalSeparator - The character used to separate the integer part from
  416. the fractional part of a number. The initial value is fetched from
  417. LOCALE_SDECIMAL. DecimalSeparator must be a non-zero value.
  418. CurrencyDecimals - The number of digits to the right of the decimal point
  419. in a currency amount. The initial value is fetched from LOCALE_ICURRDIGITS.
  420. DateSeparator - The character used to separate the year, month, and day
  421. parts of a date value. The initial value is fetched from LOCATE_SDATE.
  422. ShortDateFormat - The format string used to convert a date value to a
  423. short string suitable for editing. For a complete description of date and
  424. time format strings, refer to the documentation for the FormatDate
  425. function. The short date format should only use the date separator
  426. character and the m, mm, d, dd, yy, and yyyy format specifiers. The
  427. initial value is fetched from LOCALE_SSHORTDATE.
  428. LongDateFormat - The format string used to convert a date value to a long
  429. string suitable for display but not for editing. For a complete description
  430. of date and time format strings, refer to the documentation for the
  431. FormatDate function. The initial value is fetched from LOCALE_SLONGDATE.
  432. TimeSeparator - The character used to separate the hour, minute, and
  433. second parts of a time value. The initial value is fetched from
  434. LOCALE_STIME.
  435. TimeAMString - The suffix string used for time values between 00:00 and
  436. 11:59 in 12-hour clock format. The initial value is fetched from
  437. LOCALE_S1159.
  438. TimePMString - The suffix string used for time values between 12:00 and
  439. 23:59 in 12-hour clock format. The initial value is fetched from
  440. LOCALE_S2359.
  441. ShortTimeFormat - The format string used to convert a time value to a
  442. short string with only hours and minutes. The default value is computed
  443. from LOCALE_ITIME and LOCALE_ITLZERO.
  444. LongTimeFormat - The format string used to convert a time value to a long
  445. string with hours, minutes, and seconds. The default value is computed
  446. from LOCALE_ITIME and LOCALE_ITLZERO.
  447. ShortMonthNames - Array of strings containing short month names. The mmm
  448. format specifier in a format string passed to FormatDate causes a short
  449. month name to be substituted. The default values are fecthed from the
  450. LOCALE_SABBREVMONTHNAME system locale entries.
  451. LongMonthNames - Array of strings containing long month names. The mmmm
  452. format specifier in a format string passed to FormatDate causes a long
  453. month name to be substituted. The default values are fecthed from the
  454. LOCALE_SMONTHNAME system locale entries.
  455. ShortDayNames - Array of strings containing short day names. The ddd
  456. format specifier in a format string passed to FormatDate causes a short
  457. day name to be substituted. The default values are fecthed from the
  458. LOCALE_SABBREVDAYNAME system locale entries.
  459. LongDayNames - Array of strings containing long day names. The dddd
  460. format specifier in a format string passed to FormatDate causes a long
  461. day name to be substituted. The default values are fecthed from the
  462. LOCALE_SDAYNAME system locale entries.
  463. ListSeparator - The character used to separate items in a list. The
  464. initial value is fetched from LOCALE_SLIST.
  465. TwoDigitYearCenturyWindow - Determines what century is added to two
  466. digit years when converting string dates to numeric dates. This value
  467. is subtracted from the current year before extracting the century.
  468. This can be used to extend the lifetime of existing applications that
  469. are inextricably tied to 2 digit year data entry. The best solution
  470. to Year 2000 (Y2k) issues is not to accept 2 digit years at all - require
  471. 4 digit years in data entry to eliminate century ambiguities.
  472. Examples:
  473. Current TwoDigitCenturyWindow Century StrToDate() of:
  474. Year Value Pivot '01/01/03' '01/01/68' '01/01/50'
  475. -------------------------------------------------------------------------
  476. 1998 0 1900 1903 1968 1950
  477. 2002 0 2000 2003 2068 2050
  478. 1998 50 (default) 1948 2003 1968 1950
  479. 2002 50 (default) 1952 2003 1968 2050
  480. 2020 50 (default) 1970 2003 2068 2050
  481. }
  482. var
  483. CurrencyString: string;
  484. CurrencyFormat: Byte;
  485. NegCurrFormat: Byte;
  486. ThousandSeparator: Char;
  487. DecimalSeparator: Char;
  488. CurrencyDecimals: Byte;
  489. DateSeparator: Char;
  490. ShortDateFormat: string;
  491. LongDateFormat: string;
  492. TimeSeparator: Char;
  493. TimeAMString: string;
  494. TimePMString: string;
  495. ShortTimeFormat: string;
  496. LongTimeFormat: string;
  497. ShortMonthNames: array[1..12] of string;
  498. LongMonthNames: array[1..12] of string;
  499. ShortDayNames: array[1..7] of string;
  500. LongDayNames: array[1..7] of string;
  501. SysLocale: TSysLocale;
  502. TwoDigitYearCenturyWindow: Word = 50;
  503. ListSeparator: Char;
  504. { Thread safe currency and date/time formatting
  505. The TFormatSettings record is designed to allow thread safe formatting,
  506. equivalent to the gloabal variables described above. Each of the
  507. formatting routines that use the gloabal variables have overloaded
  508. equivalents, requiring an additional parameter of type TFormatSettings.
  509. A TFormatSettings record must be populated before use. This can be done
  510. using the GetLocaleFormatSettings function, which will populate the
  511. record with values based on the given locale (using the Win32 API
  512. function GetLocaleInfo). Note that some format specifiers still require
  513. specific thread locale settings (such as period/era names).
  514. }
  515. type
  516. TFormatSettings = record
  517. CurrencyFormat: Byte;
  518. NegCurrFormat: Byte;
  519. ThousandSeparator: Char;
  520. DecimalSeparator: Char;
  521. CurrencyDecimals: Byte;
  522. DateSeparator: Char;
  523. TimeSeparator: Char;
  524. ListSeparator: Char;
  525. CurrencyString: string;
  526. ShortDateFormat: string;
  527. LongDateFormat: string;
  528. TimeAMString: string;
  529. TimePMString: string;
  530. ShortTimeFormat: string;
  531. LongTimeFormat: string;
  532. ShortMonthNames: array[1..12] of string;
  533. LongMonthNames: array[1..12] of string;
  534. ShortDayNames: array[1..7] of string;
  535. LongDayNames: array[1..7] of string;
  536. TwoDigitYearCenturyWindow: Word;
  537. end;
  538. TLocaleOptions = (loInvariantLocale, loUserLocale);
  539. const
  540. MaxEraCount = 7;
  541. var
  542. EraNames: array [1..MaxEraCount] of string;
  543. EraYearOffsets: array [1..MaxEraCount] of Integer;
  544. {$IFDEF LINUX}
  545. EraRanges : array [1..MaxEraCount] of TEraRange platform;
  546. EraYearFormats: array [1..MaxEraCount] of string platform;
  547. EraCount: Byte platform;
  548. {$ENDIF}
  549. const
  550. PathDelim = {$IFDEF MSWINDOWS} '\'; {$ELSE} '/'; {$ENDIF}
  551. DriveDelim = {$IFDEF MSWINDOWS} ':'; {$ELSE} ''; {$ENDIF}
  552. PathSep = {$IFDEF MSWINDOWS} ';'; {$ELSE} ':'; {$ENDIF}
  553. {$IFDEF MSWINDOWS}
  554. function Languages: TLanguages;
  555. {$ENDIF}
  556. { Memory management routines }
  557. { AllocMem allocates a block of the given size on the heap. Each byte in
  558. the allocated buffer is set to zero. To dispose the buffer, use the
  559. FreeMem standard procedure. }
  560. function AllocMem(Size: Cardinal): Pointer;
  561. { Exit procedure handling }
  562. { AddExitProc adds the given procedure to the run-time library's exit
  563. procedure list. When an application terminates, its exit procedures are
  564. executed in reverse order of definition, i.e. the last procedure passed
  565. to AddExitProc is the first one to get executed upon termination. }
  566. procedure AddExitProc(Proc: TProcedure);
  567. { String handling routines }
  568. { NewStr allocates a string on the heap. NewStr is provided for backwards
  569. compatibility only. }
  570. function NewStr(const S: string): PString; deprecated;
  571. { DisposeStr disposes a string pointer that was previously allocated using
  572. NewStr. DisposeStr is provided for backwards compatibility only. }
  573. procedure DisposeStr(P: PString); deprecated;
  574. { AssignStr assigns a new dynamically allocated string to the given string
  575. pointer. AssignStr is provided for backwards compatibility only. }
  576. procedure AssignStr(var P: PString; const S: string); deprecated;
  577. { AppendStr appends S to the end of Dest. AppendStr is provided for
  578. backwards compatibility only. Use "Dest := Dest + S" instead. }
  579. procedure AppendStr(var Dest: string; const S: string); deprecated;
  580. { UpperCase converts all ASCII characters in the given string to upper case.
  581. The conversion affects only 7-bit ASCII characters between 'a' and 'z'. To
  582. convert 8-bit international characters, use AnsiUpperCase. }
  583. function UpperCase(const S: string): string; overload;
  584. function UpperCase(const S: string; LocaleOptions: TLocaleOptions): string; overload; inline;
  585. { LowerCase converts all ASCII characters in the given string to lower case.
  586. The conversion affects only 7-bit ASCII characters between 'A' and 'Z'. To
  587. convert 8-bit international characters, use AnsiLowerCase. }
  588. function LowerCase(const S: string): string; overload;
  589. function LowerCase(const S: string; LocaleOptions: TLocaleOptions): string; overload; inline;
  590. { CompareStr compares S1 to S2, with case-sensitivity. The return value is
  591. less than 0 if S1 < S2, 0 if S1 = S2, or greater than 0 if S1 > S2. The
  592. compare operation is based on the 8-bit ordinal value of each character
  593. and is not affected by the current user locale. }
  594. function CompareStr(const S1, S2: string): Integer; overload;
  595. function CompareStr(const S1, S2: string; LocaleOptions: TLocaleOptions): Integer; overload;
  596. { SameStr compares S1 to S2, with case-sensitivity. Returns true if
  597. S1 and S2 are the equal, that is, if CompareStr would return 0. }
  598. function SameStr(const S1, S2: string): Boolean; overload;
  599. function SameStr(const S1, S2: string; LocaleOptions: TLocaleOptions): Boolean; overload;
  600. { CompareMem performs a binary compare of Length bytes of memory referenced
  601. by P1 to that of P2. CompareMem returns True if the memory referenced by
  602. P1 is identical to that of P2. }
  603. function CompareMem(P1, P2: Pointer; Length: Integer): Boolean; assembler;
  604. { CompareText compares S1 to S2, without case-sensitivity. The return value
  605. is the same as for CompareStr. The compare operation is based on the 8-bit
  606. ordinal value of each character, after converting 'a'..'z' to 'A'..'Z',
  607. and is not affected by the current user locale. }
  608. function CompareText(const S1, S2: string): Integer; overload;
  609. function CompareText(const S1, S2: string; LocaleOptions: TLocaleOptions): Integer; overload;
  610. { SameText compares S1 to S2, without case-sensitivity. Returns true if
  611. S1 and S2 are the equal, that is, if CompareText would return 0. SameText
  612. has the same 8-bit limitations as CompareText }
  613. function SameText(const S1, S2: string): Boolean; overload;
  614. function SameText(const S1, S2: string; LocaleOptions: TLocaleOptions): Boolean; overload;
  615. { AnsiUpperCase converts all characters in the given string to upper case.
  616. The conversion uses the current user locale. }
  617. function AnsiUpperCase(const S: string): string;
  618. { AnsiLowerCase converts all characters in the given string to lower case.
  619. The conversion uses the current user locale. }
  620. function AnsiLowerCase(const S: string): string;
  621. { AnsiCompareStr compares S1 to S2, with case-sensitivity. The compare
  622. operation is controlled by the current user locale. The return value
  623. is the same as for CompareStr. }
  624. function AnsiCompareStr(const S1, S2: string): Integer; inline;
  625. { AnsiSameStr compares S1 to S2, with case-sensitivity. The compare
  626. operation is controlled by the current user locale. The return value
  627. is True if AnsiCompareStr would have returned 0. }
  628. function AnsiSameStr(const S1, S2: string): Boolean; inline;
  629. { AnsiCompareText compares S1 to S2, without case-sensitivity. The compare
  630. operation is controlled by the current user locale. The return value
  631. is the same as for CompareStr. }
  632. function AnsiCompareText(const S1, S2: string): Integer; inline;
  633. { AnsiSameText compares S1 to S2, without case-sensitivity. The compare
  634. operation is controlled by the current user locale. The return value
  635. is True if AnsiCompareText would have returned 0. }
  636. function AnsiSameText(const S1, S2: string): Boolean; inline;
  637. { AnsiStrComp compares S1 to S2, with case-sensitivity. The compare
  638. operation is controlled by the current user locale. The return value
  639. is the same as for CompareStr. }
  640. function AnsiStrComp(S1, S2: PChar): Integer; inline;
  641. { AnsiStrIComp compares S1 to S2, without case-sensitivity. The compare
  642. operation is controlled by the current user locale. The return value
  643. is the same as for CompareStr. }
  644. function AnsiStrIComp(S1, S2: PChar): Integer; inline;
  645. { AnsiStrLComp compares S1 to S2, with case-sensitivity, up to a maximum
  646. length of MaxLen bytes. The compare operation is controlled by the
  647. current user locale. The return value is the same as for CompareStr. }
  648. function AnsiStrLComp(S1, S2: PChar; MaxLen: Cardinal): Integer;
  649. { AnsiStrLIComp compares S1 to S2, without case-sensitivity, up to a maximum
  650. length of MaxLen bytes. The compare operation is controlled by the
  651. current user locale. The return value is the same as for CompareStr. }
  652. function AnsiStrLIComp(S1, S2: PChar; MaxLen: Cardinal): Integer;
  653. { AnsiStrLower converts all characters in the given string to lower case.
  654. The conversion uses the current user locale. }
  655. function AnsiStrLower(Str: PChar): PChar;
  656. { AnsiStrUpper converts all characters in the given string to upper case.
  657. The conversion uses the current user locale. }
  658. function AnsiStrUpper(Str: PChar): PChar;
  659. { AnsiLastChar returns a pointer to the last full character in the string.
  660. This function supports multibyte characters }
  661. function AnsiLastChar(const S: string): PChar;
  662. { AnsiStrLastChar returns a pointer to the last full character in the string.
  663. This function supports multibyte characters. }
  664. function AnsiStrLastChar(P: PChar): PChar;
  665. { WideUpperCase converts all characters in the given string to upper case. }
  666. function WideUpperCase(const S: WideString): WideString;
  667. { WideLowerCase converts all characters in the given string to lower case. }
  668. function WideLowerCase(const S: WideString): WideString;
  669. { WideCompareStr compares S1 to S2, with case-sensitivity. The return value
  670. is the same as for CompareStr. }
  671. function WideCompareStr(const S1, S2: WideString): Integer;
  672. { WideSameStr compares S1 to S2, with case-sensitivity. The return value
  673. is True if WideCompareStr would have returned 0. }
  674. function WideSameStr(const S1, S2: WideString): Boolean; inline;
  675. { WideCompareText compares S1 to S2, without case-sensitivity. The return value
  676. is the same as for CompareStr. }
  677. function WideCompareText(const S1, S2: WideString): Integer;
  678. { WideSameText compares S1 to S2, without case-sensitivity. The return value
  679. is True if WideCompareText would have returned 0. }
  680. function WideSameText(const S1, S2: WideString): Boolean; inline;
  681. { Trim trims leading and trailing spaces and control characters from the
  682. given string. }
  683. function Trim(const S: string): string; overload;
  684. function Trim(const S: WideString): WideString; overload;
  685. { TrimLeft trims leading spaces and control characters from the given
  686. string. }
  687. function TrimLeft(const S: string): string; overload;
  688. function TrimLeft(const S: WideString): WideString; overload;
  689. { TrimRight trims trailing spaces and control characters from the given
  690. string. }
  691. function TrimRight(const S: string): string; overload;
  692. function TrimRight(const S: WideString): WideString; overload;
  693. { QuotedStr returns the given string as a quoted string. A single quote
  694. character is inserted at the beginning and the end of the string, and
  695. for each single quote character in the string, another one is added. }
  696. function QuotedStr(const S: string): string;
  697. { AnsiQuotedStr returns the given string as a quoted string, using the
  698. provided Quote character. A Quote character is inserted at the beginning
  699. and end of the string, and each Quote character in the string is doubled.
  700. This function supports multibyte character strings (MBCS). }
  701. function AnsiQuotedStr(const S: string; Quote: Char): string;
  702. { AnsiExtractQuotedStr removes the Quote characters from the beginning and end
  703. of a quoted string, and reduces pairs of Quote characters within the quoted
  704. string to a single character. If the first character in Src is not the Quote
  705. character, the function returns an empty string. The function copies
  706. characters from the Src to the result string until the second solitary
  707. Quote character or the first null character in Src. The Src parameter is
  708. updated to point to the first character following the quoted string. If
  709. the Src string does not contain a matching end Quote character, the Src
  710. parameter is updated to point to the terminating null character in Src.
  711. This function supports multibyte character strings (MBCS). }
  712. function AnsiExtractQuotedStr(var Src: PChar; Quote: Char): string;
  713. { AnsiDequotedStr is a simplified version of AnsiExtractQuotedStr }
  714. function AnsiDequotedStr(const S: string; AQuote: Char): string;
  715. { AdjustLineBreaks adjusts all line breaks in the given string to the
  716. indicated style.
  717. When Style is tlbsCRLF, the function changes all
  718. CR characters not followed by LF and all LF characters not preceded
  719. by a CR into CR/LF pairs.
  720. When Style is tlbsLF, the function changes all CR/LF pairs and CR characters
  721. not followed by LF to LF characters. }
  722. function AdjustLineBreaks(const S: string; Style: TTextLineBreakStyle =
  723. {$IFDEF LINUX} tlbsLF {$ENDIF}
  724. {$IFDEF MSWINDOWS} tlbsCRLF {$ENDIF}): string;
  725. { IsValidIdent returns true if the given string is a valid identifier. An
  726. identifier is defined as a character from the set ['A'..'Z', 'a'..'z', '_']
  727. followed by zero or more characters from the set ['A'..'Z', 'a'..'z',
  728. '0..'9', '_']. With DotNet code we need to allow dots in the names.}
  729. function IsValidIdent(const Ident: string; AllowDots: Boolean = False): Boolean;
  730. { IntToStr converts the given value to its decimal string representation. }
  731. function IntToStr(Value: Integer): string; overload;
  732. function IntToStr(Value: Int64): string; overload;
  733. { IntToHex converts the given value to a hexadecimal string representation
  734. with the minimum number of digits specified. }
  735. function IntToHex(Value: Integer; Digits: Integer): string; overload;
  736. function IntToHex(Value: Int64; Digits: Integer): string; overload;
  737. { StrToInt converts the given string to an integer value. If the string
  738. doesn't contain a valid value, an EConvertError exception is raised. }
  739. function StrToInt(const S: string): Integer;
  740. function StrToIntDef(const S: string; Default: Integer): Integer;
  741. function TryStrToInt(const S: string; out Value: Integer): Boolean;
  742. { Similar to the above functions but for Int64 instead }
  743. function StrToInt64(const S: string): Int64;
  744. function StrToInt64Def(const S: string; const Default: Int64): Int64;
  745. function TryStrToInt64(const S: string; out Value: Int64): Boolean;
  746. { StrToBool converts the given string to a boolean value. If the string
  747. doesn't contain a valid value, an EConvertError exception is raised.
  748. BoolToStr converts boolean to a string value that in turn can be converted
  749. back into a boolean. BoolToStr will always pick the first element of
  750. the TrueStrs/FalseStrs arrays. }
  751. var
  752. TrueBoolStrs: array of String;
  753. FalseBoolStrs: array of String;
  754. const
  755. DefaultTrueBoolStr = 'True'; // DO NOT LOCALIZE
  756. DefaultFalseBoolStr = 'False'; // DO NOT LOCALIZE
  757. function StrToBool(const S: string): Boolean;
  758. function StrToBoolDef(const S: string; const Default: Boolean): Boolean;
  759. function TryStrToBool(const S: string; out Value: Boolean): Boolean;
  760. function BoolToStr(B: Boolean; UseBoolStrs: Boolean = False): string;
  761. { LoadStr loads the string resource given by Ident from the application's
  762. executable file or associated resource module. If the string resource
  763. does not exist, LoadStr returns an empty string. }
  764. function LoadStr(Ident: Integer): string;
  765. { FmtLoadStr loads the string resource given by Ident from the application's
  766. executable file or associated resource module, and uses it as the format
  767. string in a call to the Format function with the given arguments. }
  768. function FmtLoadStr(Ident: Integer; const Args: array of const): string;
  769. { File management routines }
  770. { FileOpen opens the specified file using the specified access mode. The
  771. access mode value is constructed by OR-ing one of the fmOpenXXXX constants
  772. with one of the fmShareXXXX constants. If the return value is positive,
  773. the function was successful and the value is the file handle of the opened
  774. file. A return value of -1 indicates that an error occurred. }
  775. function FileOpen(const FileName: string; Mode: LongWord): Integer;
  776. { FileCreate creates a new file by the specified name. If the return value
  777. is positive, the function was successful and the value is the file handle
  778. of the new file. A return value of -1 indicates that an error occurred.
  779. On Linux, this calls FileCreate(FileName, DEFFILEMODE) to create
  780. the file with read and write access for the current user only. }
  781. function FileCreate(const FileName: string): Integer; overload; inline;
  782. { This second version of FileCreate lets you specify the access rights to put on the newly
  783. created file. The access rights parameter is ignored on Win32 }
  784. function FileCreate(const FileName: string; Rights: Integer): Integer; overload; inline;
  785. { FileRead reads Count bytes from the file given by Handle into the buffer
  786. specified by Buffer. The return value is the number of bytes actually
  787. read; it is less than Count if the end of the file was reached. The return
  788. value is -1 if an error occurred. }
  789. function FileRead(Handle: Integer; var Buffer; Count: LongWord): Integer;
  790. { FileWrite writes Count bytes to the file given by Handle from the buffer
  791. specified by Buffer. The return value is the number of bytes actually
  792. written, or -1 if an error occurred. }
  793. function FileWrite(Handle: Integer; const Buffer; Count: LongWord): Integer;
  794. { FileSeek changes the current position of the file given by Handle to be
  795. Offset bytes relative to the point given by Origin. Origin = 0 means that
  796. Offset is relative to the beginning of the file, Origin = 1 means that
  797. Offset is relative to the current position, and Origin = 2 means that
  798. Offset is relative to the end of the file. The return value is the new
  799. current position, relative to the beginning of the file, or -1 if an error
  800. occurred. }
  801. function FileSeek(Handle, Offset, Origin: Integer): Integer; overload;
  802. function FileSeek(Handle: Integer; const Offset: Int64; Origin: Integer): Int64; overload;
  803. { FileClose closes the specified file. }
  804. procedure FileClose(Handle: Integer); inline;
  805. { FileAge returns the date-and-time stamp of the specified file. The return
  806. value can be converted to a TDateTime value using the FileDateToDateTime
  807. function. The return value is -1 if the file does not exist. }
  808. function FileAge(const FileName: string): Integer;
  809. { FileExists returns a boolean value that indicates whether the specified
  810. file exists. }
  811. function FileExists(const FileName: string): Boolean; inline;
  812. { DirectoryExists returns a boolean value that indicates whether the
  813. specified directory exists (and is actually a directory) }
  814. function DirectoryExists(const Directory: string): Boolean;
  815. { ForceDirectories ensures that all the directories in a specific path exist.
  816. Any portion that does not already exist will be created. Function result
  817. indicates success of the operation. The function can fail if the current
  818. user does not have sufficient file access rights to create directories in
  819. the given path. }
  820. function ForceDirectories(Dir: string): Boolean;
  821. { FindFirst searches the directory given by Path for the first entry that
  822. matches the filename given by Path and the attributes given by Attr. The
  823. result is returned in the search record given by SearchRec. The return
  824. value is zero if the function was successful. Otherwise the return value
  825. is a system error code. After calling FindFirst, always call FindClose.
  826. FindFirst is typically used with FindNext and FindClose as follows:
  827. Result := FindFirst(Path, Attr, SearchRec);
  828. while Result = 0 do
  829. begin
  830. ProcessSearchRec(SearchRec);
  831. Result := FindNext(SearchRec);
  832. end;
  833. FindClose(SearchRec);
  834. where ProcessSearchRec represents user-defined code that processes the
  835. information in a search record. }
  836. function FindFirst(const Path: string; Attr: Integer;
  837. var F: TSearchRec): Integer;
  838. { FindNext returs the next entry that matches the name and attributes
  839. specified in a previous call to FindFirst. The search record must be one
  840. that was passed to FindFirst. The return value is zero if the function was
  841. successful. Otherwise the return value is a system error code. }
  842. function FindNext(var F: TSearchRec): Integer;
  843. { FindClose terminates a FindFirst/FindNext sequence and frees memory and system
  844. resources allocated by FindFirst.
  845. Every FindFirst/FindNext must end with a call to FindClose. }
  846. procedure FindClose(var F: TSearchRec);
  847. { FileGetDate returns the OS date-and-time stamp of the file given by
  848. Handle. The return value is -1 if the handle is invalid. The
  849. FileDateToDateTime function can be used to convert the returned value to
  850. a TDateTime value. }
  851. function FileGetDate(Handle: Integer): Integer;
  852. { FileSetDate sets the OS date-and-time stamp of the file given by FileName
  853. to the value given by Age. The DateTimeToFileDate function can be used to
  854. convert a TDateTime value to an OS date-and-time stamp. The return value
  855. is zero if the function was successful. Otherwise the return value is a
  856. system error code. }
  857. function FileSetDate(const FileName: string; Age: Integer): Integer; overload;
  858. {$IFDEF MSWINDOWS}
  859. { FileSetDate by handle is not available on Unix platforms because there
  860. is no standard way to set a file's modification time using only a file
  861. handle, and no standard way to obtain the file name of an open
  862. file handle. }
  863. function FileSetDate(Handle: Integer; Age: Integer): Integer; overload; platform;
  864. { FileGetAttr returns the file attributes of the file given by FileName. The
  865. attributes can be examined by AND-ing with the faXXXX constants defined
  866. above. A return value of -1 indicates that an error occurred. }
  867. function FileGetAttr(const FileName: string): Integer; platform;
  868. { FileSetAttr sets the file attributes of the file given by FileName to the
  869. value given by Attr. The attribute value is formed by OR-ing the
  870. appropriate faXXXX constants. The return value is zero if the function was
  871. successful. Otherwise the return value is a system error code. }
  872. function FileSetAttr(const FileName: string; Attr: Integer): Integer; platform;
  873. {$ENDIF}
  874. { FileIsReadOnly tests whether a given file is read-only for the current
  875. process and effective user id. If the file does not exist, the
  876. function returns False. (Check FileExists before calling FileIsReadOnly)
  877. This function is platform portable. }
  878. function FileIsReadOnly(const FileName: string): Boolean; inline;
  879. { FileSetReadOnly sets the read only state of a file. The file must
  880. exist and the current effective user id must be the owner of the file.
  881. On Unix systems, FileSetReadOnly attempts to set or remove
  882. all three (user, group, and other) write permissions on the file.
  883. If you want to grant partial permissions (writeable for owner but not
  884. for others), use platform specific functions such as chmod.
  885. The function returns True if the file was successfully modified,
  886. False if there was an error. This function is platform portable. }
  887. function FileSetReadOnly(const FileName: string; ReadOnly: Boolean): Boolean;
  888. { DeleteFile deletes the file given by FileName. The return value is True if
  889. the file was successfully deleted, or False if an error occurred. }
  890. function DeleteFile(const FileName: string): Boolean; inline;
  891. { RenameFile renames the file given by OldName to the name given by NewName.
  892. The return value is True if the file was successfully renamed, or False if
  893. an error occurred. }
  894. function RenameFile(const OldName, NewName: string): Boolean; inline;
  895. { ChangeFileExt changes the extension of a filename. FileName specifies a
  896. filename with or without an extension, and Extension specifies the new
  897. extension for the filename. The new extension can be a an empty string or
  898. a period followed by up to three characters. }
  899. function ChangeFileExt(const FileName, Extension: string): string;
  900. { ExtractFilePath extracts the drive and directory parts of the given
  901. filename. The resulting string is the leftmost characters of FileName,
  902. up to and including the colon or backslash that separates the path
  903. information from the name and extension. The resulting string is empty
  904. if FileName contains no drive and directory parts. }
  905. function ExtractFilePath(const FileName: string): string;
  906. { ExtractFileDir extracts the drive and directory parts of the given
  907. filename. The resulting string is a directory name suitable for passing
  908. to SetCurrentDir, CreateDir, etc. The resulting string is empty if
  909. FileName contains no drive and directory parts. }
  910. function ExtractFileDir(const FileName: string): string;
  911. { ExtractFileDrive extracts the drive part of the given filename. For
  912. filenames with drive letters, the resulting string is '<drive>:'.
  913. For filenames with a UNC path, the resulting string is in the form
  914. '\\<servername>\<sharename>'. If the given path contains neither
  915. style of filename, the result is an empty string. }
  916. function ExtractFileDrive(const FileName: string): string;
  917. { ExtractFileName extracts the name and extension parts of the given
  918. filename. The resulting string is the leftmost characters of FileName,
  919. starting with the first character after the colon or backslash that
  920. separates the path information from the name and extension. The resulting
  921. string is equal to FileName if FileName contains no drive and directory
  922. parts. }
  923. function ExtractFileName(const FileName: string): string;
  924. { ExtractFileExt extracts the extension part of the given filename. The
  925. resulting string includes the period character that separates the name
  926. and extension parts. The resulting string is empty if the given filename
  927. has no extension. }
  928. function ExtractFileExt(const FileName: string): string;
  929. { ExpandFileName expands the given filename to a fully qualified filename.
  930. The resulting string consists of a drive letter, a colon, a root relative
  931. directory path, and a filename. Embedded '.' and '..' directory references
  932. are removed. }
  933. function ExpandFileName(const FileName: string): string;
  934. { ExpandFilenameCase returns a fully qualified filename like ExpandFilename,
  935. but performs a case-insensitive filename search looking for a close match
  936. in the actual file system, differing only in uppercase versus lowercase of
  937. the letters. This is useful to convert lazy user input into useable file
  938. names, or to convert filename data created on a case-insensitive file
  939. system (Win32) to something useable on a case-sensitive file system (Linux).
  940. The MatchFound out parameter indicates what kind of match was found in the
  941. file system, and what the function result is based upon:
  942. ( in order of increasing difficulty or complexity )
  943. mkExactMatch: Case-sensitive match. Result := ExpandFileName(FileName).
  944. mkSingleMatch: Exactly one file in the given directory path matches the
  945. given filename on a case-insensitive basis.
  946. Result := ExpandFileName(FileName as found in file system).
  947. mkAmbiguous: More than one file in the given directory path matches the
  948. given filename case-insensitively.
  949. In many cases, this should be considered an error.
  950. Result := ExpandFileName(First matching filename found).
  951. mkNone: File not found at all. Result := ExpandFileName(FileName).
  952. Note that because this function has to search the file system it may be
  953. much slower than ExpandFileName, particularly when the given filename is
  954. ambiguous or does not exist. Use ExpandFilenameCase only when you have
  955. a filename of dubious orgin - such as from user input - and you want
  956. to make a best guess before failing. }
  957. type
  958. TFilenameCaseMatch = (mkNone, mkExactMatch, mkSingleMatch, mkAmbiguous);
  959. function ExpandFileNameCase(const FileName: string;
  960. out MatchFound: TFilenameCaseMatch): string;
  961. { ExpandUNCFileName expands the given filename to a fully qualified filename.
  962. This function is the same as ExpandFileName except that it will return the
  963. drive portion of the filename in the format '\\<servername>\<sharename> if
  964. that drive is actually a network resource instead of a local resource.
  965. Like ExpandFileName, embedded '.' and '..' directory references are
  966. removed. }
  967. function ExpandUNCFileName(const FileName: string): string;
  968. { ExtractRelativePath will return a file path name relative to the given
  969. BaseName. It strips the common path dirs and adds '..\' on Windows,
  970. and '../' on Linux for each level up from the BaseName path. }
  971. function ExtractRelativePath(const BaseName, DestName: string): string;
  972. {$IFDEF MSWINDOWS}
  973. { ExtractShortPathName will convert the given filename to the short form
  974. by calling the GetShortPathName API. Will return an empty string if
  975. the file or directory specified does not exist }
  976. function ExtractShortPathName(const FileName: string): string;
  977. {$ENDIF}
  978. { FileSearch searches for the file given by Name in the list of directories
  979. given by DirList. The directory paths in DirList must be separated by
  980. PathSep chars. The search always starts with the current directory of the
  981. current drive. The returned value is a concatenation of one of the
  982. directory paths and the filename, or an empty string if the file could not
  983. be located. }
  984. function FileSearch(const Name, DirList: string): string;
  985. {$IFDEF MSWINDOWS}
  986. { DiskFree returns the number of free bytes on the specified drive number,
  987. where 0 = Current, 1 = A, 2 = B, etc. DiskFree returns -1 if the drive
  988. number is invalid. }
  989. function DiskFree(Drive: Byte): Int64;
  990. { DiskSize returns the size in bytes of the specified drive number, where
  991. 0 = Current, 1 = A, 2 = B, etc. DiskSize returns -1 if the drive number
  992. is invalid. }
  993. function DiskSize(Drive: Byte): Int64;
  994. {$ENDIF}
  995. { FileDateToDateTime converts an OS date-and-time value to a TDateTime
  996. value. The FileAge, FileGetDate, and FileSetDate routines operate on OS
  997. date-and-time values, and the Time field of a TSearchRec used by the
  998. FindFirst and FindNext functions contains an OS date-and-time value. }
  999. function FileDateToDateTime(FileDate: Integer): TDateTime;
  1000. { DateTimeToFileDate converts a TDateTime value to an OS date-and-time
  1001. value. The FileAge, FileGetDate, and FileSetDate routines operate on OS
  1002. date-and-time values, and the Time field of a TSearchRec used by the
  1003. FindFirst and FindNext functions contains an OS date-and-time value. }
  1004. function DateTimeToFileDate(DateTime: TDateTime): Integer;
  1005. { GetCurrentDir returns the current directory. }
  1006. function GetCurrentDir: string;
  1007. { SetCurrentDir sets the current directory. The return value is True if
  1008. the current directory was successfully changed, or False if an error
  1009. occurred. }
  1010. function SetCurrentDir(const Dir: string): Boolean;
  1011. { CreateDir creates a new directory. The return value is True if a new
  1012. directory was successfully created, or False if an error occurred. }
  1013. function CreateDir(const Dir: string): Boolean;
  1014. { RemoveDir deletes an existing empty directory. The return value is
  1015. True if the directory was successfully deleted, or False if an error
  1016. occurred. }
  1017. function RemoveDir(const Dir: string): Boolean;
  1018. { PChar routines }
  1019. { const params help simplify C++ code. No effect on pascal code }
  1020. { StrLen returns the number of characters in Str, not counting the null
  1021. terminator. }
  1022. function StrLen(const Str: PChar): Cardinal;
  1023. { StrEnd returns a pointer to the null character that terminates Str. }
  1024. function StrEnd(const Str: PChar): PChar;
  1025. { StrMove copies exactly Count characters from Source to Dest and returns
  1026. Dest. Source and Dest may overlap. }
  1027. function StrMove(Dest: PChar; const Source: PChar; Count: Cardinal): PChar;
  1028. { StrCopy copies Source to Dest and returns Dest. }
  1029. function StrCopy(Dest: PChar; const Source: PChar): PChar;
  1030. { StrECopy copies Source to Dest and returns StrEnd(Dest). }
  1031. function StrECopy(Dest:PChar; const Source: PChar): PChar;
  1032. { StrLCopy copies at most MaxLen characters from Source to Dest and
  1033. returns Dest. }
  1034. function StrLCopy(Dest: PChar; const Source: PChar; MaxLen: Cardinal): PChar;
  1035. { StrPCopy copies the Pascal style string Source into Dest and
  1036. returns Dest. }
  1037. function StrPCopy(Dest: PChar; const Source: string): PChar;
  1038. { StrPLCopy copies at most MaxLen characters from the Pascal style string
  1039. Source into Dest and returns Dest. }
  1040. function StrPLCopy(Dest: PChar; const Source: string;
  1041. MaxLen: Cardinal): PChar;
  1042. { StrCat appends a copy of Source to the end of Dest and returns Dest. }
  1043. function StrCat(Dest: PChar; const Source: PChar): PChar;
  1044. { StrLCat appends at most MaxLen - StrLen(Dest) characters from Source to
  1045. the end of Dest, and returns Dest. }
  1046. function StrLCat(Dest: PChar; const Source: PChar; MaxLen: Cardinal): PChar;
  1047. { StrComp compares Str1 to Str2. The return value is less than 0 if
  1048. Str1 < Str2, 0 if Str1 = Str2, or greater than 0 if Str1 > Str2. }
  1049. function StrComp(const Str1, Str2: PChar): Integer;
  1050. { StrIComp compares Str1 to Str2, without case sensitivity. The return
  1051. value is the same as StrComp. }
  1052. function StrIComp(const Str1, Str2: PChar): Integer;
  1053. { StrLComp compares Str1 to Str2, for a maximum length of MaxLen
  1054. characters. The return value is the same as StrComp. }
  1055. function StrLComp(const Str1, Str2: PChar; MaxLen: Cardinal): Integer;
  1056. { StrLIComp compares Str1 to Str2, for a maximum length of MaxLen
  1057. characters, without case sensitivity. The return value is the same
  1058. as StrComp. }
  1059. function StrLIComp(const Str1, Str2: PChar; MaxLen: Cardinal): Integer;
  1060. { StrScan returns a pointer to the first occurrence of Chr in Str. If Chr
  1061. does not occur in Str, StrScan returns NIL. The null terminator is
  1062. considered to be part of the string. }
  1063. function StrScan(const Str: PChar; Chr: Char): PChar;
  1064. { StrRScan returns a pointer to the last occurrence of Chr in Str. If Chr
  1065. does not occur in Str, StrRScan returns NIL. The null terminator is
  1066. considered to be part of the string. }
  1067. function StrRScan(const Str: PChar; Chr: Char): PChar;
  1068. { StrPos returns a pointer to the first occurrence of Str2 in Str1. If
  1069. Str2 does not occur in Str1, StrPos returns NIL. }
  1070. function StrPos(const Str1, Str2: PChar): PChar;
  1071. { StrUpper converts Str to upper case and returns Str. }
  1072. function StrUpper(Str: PChar): PChar;
  1073. { StrLower converts Str to lower case and returns Str. }
  1074. function StrLower(Str: PChar): PChar;
  1075. { StrPas converts Str to a Pascal style string. This function is provided
  1076. for backwards compatibility only. To convert a null terminated string to
  1077. a Pascal style string, use a string type cast or an assignment. }
  1078. function StrPas(const Str: PChar): string;
  1079. { StrAlloc allocates a buffer of the given size on the heap. The size of
  1080. the allocated buffer is encoded in a four byte header that immediately
  1081. preceeds the buffer. To dispose the buffer, use StrDispose. }
  1082. function StrAlloc(Size: Cardinal): PChar;
  1083. { StrBufSize returns the allocated size of the given buffer, not including
  1084. the two byte header. }
  1085. function StrBufSize(const Str: PChar): Cardinal;
  1086. { StrNew allocates a copy of Str on the heap. If Str is NIL, StrNew returns
  1087. NIL and doesn't allocate any heap space. Otherwise, StrNew makes a
  1088. duplicate of Str, obtaining space with a call to the StrAlloc function,
  1089. and returns a pointer to the duplicated string. To dispose the string,
  1090. use StrDispose. }
  1091. function StrNew(const Str: PChar): PChar;
  1092. { StrDispose disposes a string that was previously allocated with StrAlloc
  1093. or StrNew. If Str is NIL, StrDispose does nothing. }
  1094. procedure StrDispose(Str: PChar);
  1095. { String formatting routines }
  1096. { The Format routine formats the argument list given by the Args parameter
  1097. using the format string given by the Format parameter.
  1098. Format strings contain two types of objects--plain characters and format
  1099. specifiers. Plain characters are copied verbatim to the resulting string.
  1100. Format specifiers fetch arguments from the argument list and apply
  1101. formatting to them.
  1102. Format specifiers have the following form:
  1103. "%" [index ":"] ["-"] [width] ["." prec] type
  1104. A format specifier begins with a % character. After the % come the
  1105. following, in this order:
  1106. - an optional argument index specifier, [index ":"]
  1107. - an optional left-justification indicator, ["-"]
  1108. - an optional width specifier, [width]
  1109. - an optional precision specifier, ["." prec]
  1110. - the conversion type character, type
  1111. The following conversion characters are supported:
  1112. d Decimal. The argument must be an integer value. The value is converted
  1113. to a string of decimal digits. If the format string contains a precision
  1114. specifier, it indicates that the resulting string must contain at least
  1115. the specified number of digits; if the value has less digits, the
  1116. resulting string is left-padded with zeros.
  1117. u Unsigned decimal. Similar to 'd' but no sign is output.
  1118. e Scientific. The argument must be a floating-point value. The value is
  1119. converted to a string of the form "-d.ddd...E+ddd". The resulting
  1120. string starts with a minus sign if the number is negative, and one digit
  1121. always precedes the decimal point. The total number of digits in the
  1122. resulting string (including the one before the decimal point) is given
  1123. by the precision specifer in the format string--a default precision of
  1124. 15 is assumed if no precision specifer is present. The "E" exponent
  1125. character in the resulting string is always followed by a plus or minus
  1126. sign and at least three digits.
  1127. f Fixed. The argument must be a floating-point value. The value is
  1128. converted to a string of the form "-ddd.ddd...". The resulting string
  1129. starts with a minus sign if the number is negative. The number of digits
  1130. after the decimal point is given by the precision specifier in the
  1131. format string--a default of 2 decimal digits is assumed if no precision
  1132. specifier is present.
  1133. g General. The argument must be a floating-point value. The value is
  1134. converted to the shortest possible decimal string using fixed or
  1135. scientific format. The number of significant digits in the resulting
  1136. string is given by the precision specifier in the format string--a
  1137. default precision of 15 is assumed if no precision specifier is present.
  1138. Trailing zeros are removed from the resulting string, and a decimal
  1139. point appears only if necessary. The resulting string uses fixed point
  1140. format if the number of digits to the left of the decimal point in the
  1141. value is less than or equal to the specified precision, and if the
  1142. value is greater than or equal to 0.00001. Otherwise the resulting
  1143. string uses scientific format.
  1144. n Number. The argument must be a floating-point value. The value is
  1145. converted to a string of the form "-d,ddd,ddd.ddd...". The "n" format
  1146. corresponds to the "f" format, except that the resulting string
  1147. contains thousand separators.
  1148. m Money. The argument must be a floating-point value. The value is
  1149. converted to a string that represents a currency amount. The conversion
  1150. is controlled by the CurrencyString, CurrencyFormat, NegCurrFormat,
  1151. ThousandSeparator, DecimalSeparator, and CurrencyDecimals global
  1152. variables, all of which are initialized from locale settings provided
  1153. by the operating system. For example, Currency Format preferences can be
  1154. set in the International section of the Windows Control Panel. If the format
  1155. string contains a precision specifier, it overrides the value given
  1156. by the CurrencyDecimals global variable.
  1157. p Pointer. The argument must be a pointer value. The value is converted
  1158. to a string of the form "XXXX:YYYY" where XXXX and YYYY are the
  1159. segment and offset parts of the pointer expressed as four hexadecimal
  1160. digits.
  1161. s String. The argument must be a character, a string, or a PChar value.
  1162. The string or character is inserted in place of the format specifier.
  1163. The precision specifier, if present in the format string, specifies the
  1164. maximum length of the resulting string. If the argument is a string
  1165. that is longer than this maximum, the string is truncated.
  1166. x Hexadecimal. The argument must be an integer value. The value is
  1167. converted to a string of hexadecimal digits. If the format string
  1168. contains a precision specifier, it indicates that the resulting string
  1169. must contain at least the specified number of digits; if the value has
  1170. less digits, the resulting string is left-padded with zeros.
  1171. Conversion characters may be specified in upper case as well as in lower
  1172. case--both produce the same results.
  1173. For all floating-point formats, the actual characters used as decimal and
  1174. thousand separators are obtained from the DecimalSeparator and
  1175. ThousandSeparator global variables.
  1176. Index, width, and precision specifiers can be specified directly using
  1177. decimal digit string (for example "%10d"), or indirectly using an asterisk
  1178. charcater (for example "%*.*f"). When using an asterisk, the next argument
  1179. in the argument list (which must be an integer value) becomes the value
  1180. that is actually used. For example "Format('%*.*f', [8, 2, 123.456])" is
  1181. the same as "Format('%8.2f', [123.456])".
  1182. A width specifier sets the minimum field width for a conversion. If the
  1183. resulting string is shorter than the minimum field width, it is padded
  1184. with blanks to increase the field width. The default is to right-justify
  1185. the result by adding blanks in front of the value, but if the format
  1186. specifier contains a left-justification indicator (a "-" character
  1187. preceding the width specifier), the result is left-justified by adding
  1188. blanks after the value.
  1189. An index specifier sets the current argument list index to the specified
  1190. value. The index of the first argument in the argument list is 0. Using
  1191. index specifiers, it is possible to format the same argument multiple
  1192. times. For example "Format('%d %d %0:d %d', [10, 20])" produces the string
  1193. '10 20 10 20'.
  1194. The Format function can be combined with other formatting functions. For
  1195. example
  1196. S := Format('Your total was %s on %s', [
  1197. FormatFloat('$#,##0.00;;zero', Total),
  1198. FormatDateTime('mm/dd/yy', Date)]);
  1199. which uses the FormatFloat and FormatDateTime functions to customize the
  1200. format beyond what is possible with Format.
  1201. Each of the string formatting routines that uses global variables for
  1202. formatting (separators, decimals, date/time formats etc.), has an
  1203. overloaded equivalent requiring a parameter of type TFormatSettings. This
  1204. additional parameter provides the formatting information rather than the
  1205. global variables. For more information see the notes at TFormatSettings. }
  1206. function Format(const Format: string;
  1207. const Args: array of const): string; overload;
  1208. function Format(const Format: string; const Args: array of const;
  1209. const FormatSettings: TFormatSettings): string; overload;
  1210. { FmtStr formats the argument list given by Args using the format string
  1211. given by Format into the string variable given by Result. For further
  1212. details, see the description of the Format function. }
  1213. procedure FmtStr(var Result: string; const Format: string;
  1214. const Args: array of const); overload;
  1215. procedure FmtStr(var Result: string; const Format: string;
  1216. const Args: array of const; const FormatSettings: TFormatSettings); overload;
  1217. { StrFmt formats the argument list given by Args using the format string
  1218. given by Format into the buffer given by Buffer. It is up to the caller to
  1219. ensure that Buffer is large enough for the resulting string. The returned
  1220. value is Buffer. For further details, see the description of the Format
  1221. function. }
  1222. function StrFmt(Buffer, Format: PChar;
  1223. const Args: array of const): PChar; overload;
  1224. function StrFmt(Buffer, Format: PChar; const Args: array of const;
  1225. const FormatSettings: TFormatSettings): PChar; overload;
  1226. { StrLFmt formats the argument list given by Args using the format string
  1227. given by Format into the buffer given by Buffer. The resulting string will
  1228. contain no more than MaxBufLen characters, not including the null terminator.
  1229. The returned value is Buffer. For further details, see the description of
  1230. the Format function. }
  1231. function StrLFmt(Buffer: PChar; MaxBufLen: Cardinal; Format: PChar;
  1232. const Args: array of const): PChar; overload;
  1233. function StrLFmt(Buffer: PChar; MaxBufLen: Cardinal; Format: PChar;
  1234. const Args: array of const;
  1235. const FormatSettings: TFormatSettings): PChar; overload;
  1236. { FormatBuf formats the argument list given by Args using the format string
  1237. given by Format and FmtLen into the buffer given by Buffer and BufLen.
  1238. The Format parameter is a reference to a buffer containing FmtLen
  1239. characters, and the Buffer parameter is a reference to a buffer of BufLen
  1240. characters. The returned value is the number of characters actually stored
  1241. in Buffer. The returned value is always less than or equal to BufLen. For
  1242. further details, see the description of the Format function. }
  1243. function FormatBuf(var Buffer; BufLen: Cardinal; const Format;
  1244. FmtLen: Cardinal; const Args: array of const): Cardinal; overload;
  1245. function FormatBuf(var Buffer; BufLen: Cardinal; const Format;
  1246. FmtLen: Cardinal; const Args: array of const;
  1247. const FormatSettings: TFormatSettings): Cardinal; overload;
  1248. { The WideFormat routine formats the argument list given by the Args parameter
  1249. using the format WideString given by the Format parameter. This routine is
  1250. the WideString equivalent of Format. For further details, see the description
  1251. of the Format function. }
  1252. function WideFormat(const Format: WideString;
  1253. const Args: array of const): WideString; overload;
  1254. function WideFormat(const Format: WideString;
  1255. const Args: array of const;
  1256. const FormatSettings: TFormatSettings): WideString; overload;
  1257. { WideFmtStr formats the argument list given by Args using the format WideString
  1258. given by Format into the WideString variable given by Result. For further
  1259. details, see the description of the Format function. }
  1260. procedure WideFmtStr(var Result: WideString; const Format: WideString;
  1261. const Args: array of const); overload;
  1262. procedure WideFmtStr(var Result: WideString; const Format: WideString;
  1263. const Args: array of const; const FormatSettings: TFormatSettings); overload;
  1264. { WideFormatBuf formats the argument list given by Args using the format string
  1265. given by Format and FmtLen into the buffer given by Buffer and BufLen.
  1266. The Format parameter is a reference to a buffer containing FmtLen
  1267. UNICODE characters (WideChar), and the Buffer parameter is a reference to a
  1268. buffer of BufLen UNICODE characters (WideChar). The return value is the number
  1269. of UNICODE characters actually stored in Buffer. The return value is always
  1270. less than or equal to BufLen. For further details, see the description of the
  1271. Format function.
  1272. Important: BufLen, FmtLen and the return result are always the number of
  1273. UNICODE characters, *not* the number of bytes. To calculate the number of bytes
  1274. multiply them by SizeOf(WideChar). }
  1275. function WideFormatBuf(var Buffer; BufLen: Cardinal; const Format;
  1276. FmtLen: Cardinal; const Args: array of const): Cardinal; overload;
  1277. function WideFormatBuf(var Buffer; BufLen: Cardinal; const Format;
  1278. FmtLen: Cardinal; const Args: array of const;
  1279. const FormatSettings: TFormatSettings): Cardinal; overload;
  1280. { Floating point conversion routines }
  1281. { Each of the floating point conversion routines that uses global variables
  1282. for formatting (separators, decimals, etc.), has an overloaded equivalent
  1283. requiring a parameter of type TFormatSettings. This additional parameter
  1284. provides the formatting information rather than the global variables. For
  1285. more information see the notes at TFormatSettings. }
  1286. { FloatToStr converts the floating-point value given by Value to its string
  1287. representation. The conversion uses general number format with 15
  1288. significant digits. For further details, see the description of the
  1289. FloatToStrF function. }
  1290. function FloatToStr(Value: Extended): string; overload;
  1291. function FloatToStr(Value: Extended;
  1292. const FormatSettings: TFormatSettings): string; overload;
  1293. { CurrToStr converts the currency value given by Value to its string
  1294. representation. The conversion uses general number format. For further
  1295. details, see the description of the CurrToStrF function. }
  1296. function CurrToStr(Value: Currency): string; overload;
  1297. function CurrToStr(Value: Currency;
  1298. const FormatSettings: TFormatSettings): string; overload;
  1299. { FloatToCurr will range validate a value to make sure it falls
  1300. within the acceptable currency range }
  1301. const
  1302. MinCurrency: Currency = -922337203685477.5807 {$IFDEF LINUX} + 1 {$ENDIF}; //!! overflow?
  1303. MaxCurrency: Currency = 922337203685477.5807 {$IFDEF LINUX} - 1 {$ENDIF}; //!! overflow?
  1304. function FloatToCurr(const Value: Extended): Currency;
  1305. function TryFloatToCurr(const Value: Extended; out AResult: Currency): Boolean;
  1306. { FloatToStrF converts the floating-point value given by Value to its string
  1307. representation. The Format parameter controls the format of the resulting
  1308. string. The Precision parameter specifies the precision of the given value.
  1309. It should be 7 or less for values of type Single, 15 or less for values of
  1310. type Double, and 18 or less for values of type Extended. The meaning of the
  1311. Digits parameter depends on the particular format selected.
  1312. The possible values of the Format parameter, and the meaning of each, are
  1313. described below.
  1314. ffGeneral - General number format. The value is converted to the shortest
  1315. possible decimal string using fixed or scientific format. Trailing zeros
  1316. are removed from the resulting string, and a decimal point appears only
  1317. if necessary. The resulting string uses fixed point format if the number
  1318. of digits to the left of the decimal point in the value is less than or
  1319. equal to the specified precision, and if the value is greater than or
  1320. equal to 0.00001. Otherwise the resulting string uses scientific format,
  1321. and the Digits parameter specifies the minimum number of digits in the
  1322. exponent (between 0 and 4).
  1323. ffExponent - Scientific format. The value is converted to a string of the
  1324. form "-d.ddd...E+dddd". The resulting string starts with a minus sign if
  1325. the number is negative, and one digit always precedes the decimal point.
  1326. The total number of digits in the resulting string (including the one
  1327. before the decimal point) is given by the Precision parameter. The "E"
  1328. exponent character in the resulting string is always followed by a plus
  1329. or minus sign and up to four digits. The Digits parameter specifies the
  1330. minimum number of digits in the exponent (between 0 and 4).
  1331. ffFixed - Fixed point format. The value is converted to a string of the
  1332. form "-ddd.ddd...". The resulting string starts with a minus sign if the
  1333. number is negative, and at least one digit always precedes the decimal
  1334. point. The number of digits after the decimal point is given by the Digits
  1335. parameter--it must be between 0 and 18. If the number of digits to the
  1336. left of the decimal point is greater than the specified precision, the
  1337. resulting value will use scientific format.
  1338. ffNumber - Number format. The value is converted to a string of the form
  1339. "-d,ddd,ddd.ddd...". The ffNumber format corresponds to the ffFixed format,
  1340. except that the resulting string contains thousand separators.
  1341. ffCurrency - Currency format. The value is converted to a string that
  1342. represents a currency amount. The conversion is controlled by the
  1343. CurrencyString, CurrencyFormat, NegCurrFormat, ThousandSeparator, and
  1344. DecimalSeparator global variables, all of which are initialized from
  1345. locale settings provided by the operating system. For example,
  1346. Currency Format preferences can be set in the International section
  1347. of the Windows Control Panel.
  1348. The number of digits after the decimal point is given by the Digits
  1349. parameter--it must be between 0 and 18.
  1350. For all formats, the actual characters used as decimal and thousand
  1351. separators are obtained from the DecimalSeparator and ThousandSeparator
  1352. global variables.
  1353. If the given value is a NAN (not-a-number), the resulting string is 'NAN'.
  1354. If the given value is positive infinity, the resulting string is 'INF'. If
  1355. the given value is negative infinity, the resulting string is '-INF'. }
  1356. function FloatToStrF(Value: Extended; Format: TFloatFormat;
  1357. Precision, Digits: Integer): string; overload;
  1358. function FloatToStrF(Value: Extended; Format: TFloatFormat;
  1359. Precision, Digits: Integer;
  1360. const FormatSettings: TFormatSettings): string; overload;
  1361. { CurrToStrF converts the currency value given by Value to its string
  1362. representation. A call to CurrToStrF corresponds to a call to
  1363. FloatToStrF with an implied precision of 19 digits. }
  1364. function CurrToStrF(Value: Currency; Format: TFloatFormat;
  1365. Digits: Integer): string; overload;
  1366. function CurrToStrF(Value: Currency; Format: TFloatFormat;
  1367. Digits: Integer; const FormatSettings: TFormatSettings): string; overload;
  1368. { FloatToText converts the given floating-point value to its decimal
  1369. representation using the specified format, precision, and digits. The
  1370. Value parameter must be a variable of type Extended or Currency, as
  1371. indicated by the ValueType parameter. The resulting string of characters
  1372. is stored in the given buffer, and the returned value is the number of
  1373. characters stored. The resulting string is not null-terminated. For
  1374. further details, see the description of the FloatToStrF function. }
  1375. function FloatToText(BufferArg: PChar; const Value; ValueType: TFloatValue;
  1376. Format: TFloatFormat; Precision, Digits: Integer): Integer; overload;
  1377. function FloatToText(BufferArg: PChar; const Value; ValueType: TFloatValue;
  1378. Format: TFloatFormat; Precision, Digits: Integer;
  1379. const FormatSettings: TFormatSettings): Integer; overload;
  1380. { FormatFloat formats the floating-point value given by Value using the
  1381. format string given by Format. The following format specifiers are
  1382. supported in the format string:
  1383. 0 Digit placeholder. If the value being formatted has a digit in the
  1384. position where the '0' appears in the format string, then that digit
  1385. is copied to the output string. Otherwise, a '0' is stored in that
  1386. position in the output string.
  1387. # Digit placeholder. If the value being formatted has a digit in the
  1388. position where the '#' appears in the format string, then that digit
  1389. is copied to the output string. Otherwise, nothing is stored in that
  1390. position in the output string.
  1391. . Decimal point. The first '.' character in the format string
  1392. determines the location of the decimal separator in the formatted
  1393. value; any additional '.' characters are ignored. The actual
  1394. character used as a the decimal separator in the output string is
  1395. determined by the DecimalSeparator global variable, which is initialized
  1396. from locale settings obtained from the operating system.
  1397. , Thousand separator. If the format string contains one or more ','
  1398. characters, the output will have thousand separators inserted between
  1399. each group of three digits to the left of the decimal point. The
  1400. placement and number of ',' characters in the format string does not
  1401. affect the output, except to indicate that thousand separators are
  1402. wanted. The actual character used as a the thousand separator in the
  1403. output is determined by the ThousandSeparator global variable, which
  1404. is initialized from locale settings obtained from the operating system.
  1405. E+ Scientific notation. If any of the strings 'E+', 'E-', 'e+', or 'e-'
  1406. E- are contained in the format string, the number is formatted using
  1407. e+ scientific notation. A group of up to four '0' characters can
  1408. e- immediately follow the 'E+', 'E-', 'e+', or 'e-' to determine the
  1409. minimum number of digits in the exponent. The 'E+' and 'e+' formats
  1410. cause a plus sign to be output for positive exponents and a minus
  1411. sign to be output for negative exponents. The 'E-' and 'e-' formats
  1412. output a sign character only for negative exponents.
  1413. 'xx' Characters enclosed in single or double quotes are output as-is, and
  1414. "xx" do not affect formatting.
  1415. ; Separates sections for positive, negative, and zero numbers in the
  1416. format string.
  1417. The locations of the leftmost '0' before the decimal point in the format
  1418. string and the rightmost '0' after the decimal point in the format string
  1419. determine the range of digits that are always present in the output string.
  1420. The number being formatted is always rounded to as many decimal places as
  1421. there are digit placeholders ('0' or '#') to the right of the decimal
  1422. point. If the format string contains no decimal point, the value being
  1423. formatted is rounded to the nearest whole number.
  1424. If the number being formatted has more digits to the left of the decimal
  1425. separator than there are digit placeholders to the left of the '.'
  1426. character in the format string, the extra digits are output before the
  1427. first digit placeholder.
  1428. To allow different formats for positive, negative, and zero values, the
  1429. format string can contain between one and three sections separated by
  1430. semicolons.
  1431. One section - The format string applies to all values.
  1432. Two sections - The first section applies to positive values and zeros, and
  1433. the second section applies to negative values.
  1434. Three sections - The first section applies to positive values, the second
  1435. applies to negative values, and the third applies to zeros.
  1436. If the section for negative values or the section for zero values is empty,
  1437. that is if there is nothing between the semicolons that delimit the
  1438. section, the section for positive values is used instead.
  1439. If the section for positive values is empty, or if the entire format string
  1440. is empty, the value is formatted using general floating-point formatting
  1441. with 15 significant digits, corresponding to a call to FloatToStrF with
  1442. the ffGeneral format. General floating-point formatting is also used if
  1443. the value has more than 18 digits to the left of the decimal point and
  1444. the format string does not specify scientific notation.
  1445. The table below shows some sample formats and the results produced when
  1446. the formats are applied to different values:
  1447. Format string 1234 -1234 0.5 0
  1448. -----------------------------------------------------------------------
  1449. 1234 -1234 0.5 0
  1450. 0 1234 -1234 1 0
  1451. 0.00 1234.00 -1234.00 0.50 0.00
  1452. #.## 1234 -1234 .5
  1453. #,##0.00 1,234.00 -1,234.00 0.50 0.00
  1454. #,##0.00;(#,##0.00) 1,234.00 (1,234.00) 0.50 0.00
  1455. #,##0.00;;Zero 1,234.00 -1,234.00 0.50 Zero
  1456. 0.000E+00 1.234E+03 -1.234E+03 5.000E-01 0.000E+00
  1457. #.###E-0 1.234E3 -1.234E3 5E-1 0E0
  1458. ----------------------------------------------------------------------- }
  1459. function FormatFloat(const Format: string; Value: Extended): string; overload;
  1460. function FormatFloat(const Format: string; Value: Extended;
  1461. const FormatSettings: TFormatSettings): string; overload;
  1462. { FormatCurr formats the currency value given by Value using the format
  1463. string given by Format. For further details, see the description of the
  1464. FormatFloat function. }
  1465. function FormatCurr(const Format: string; Value: Currency): string; overload;
  1466. function FormatCurr(const Format: string; Value: Currency;
  1467. const FormatSettings: TFormatSettings): string; overload;
  1468. { FloatToTextFmt converts the given floating-point value to its decimal
  1469. representation using the specified format. The Value parameter must be a
  1470. variable of type Extended or Currency, as indicated by the ValueType
  1471. parameter. The resulting string of characters is stored in the given
  1472. buffer, and the returned value is the number of characters stored. The
  1473. resulting string is not null-terminated. For further details, see the
  1474. description of the FormatFloat function. }
  1475. function FloatToTextFmt(Buf: PChar; const Value; ValueType: TFloatValue;
  1476. Format: PChar): Integer; overload;
  1477. function FloatToTextFmt(Buf: PChar; const Value; ValueType: TFloatValue;
  1478. Format: PChar; const FormatSettings: TFormatSettings): Integer; overload;
  1479. { StrToFloat converts the given string to a floating-point value. The string
  1480. must consist of an optional sign (+ or -), a string of digits with an
  1481. optional decimal point, and an optional 'E' or 'e' followed by a signed
  1482. integer. Leading and trailing blanks in the string are ignored. The
  1483. DecimalSeparator global variable defines the character that must be used
  1484. as a decimal point. Thousand separators and currency symbols are not
  1485. allowed in the string. If the string doesn't contain a valid value, an
  1486. EConvertError exception is raised. }
  1487. function StrToFloat(const S: string): Extended; overload;
  1488. function StrToFloat(const S: string;
  1489. const FormatSettings: TFormatSettings): Extended; overload;
  1490. function StrToFloatDef(const S: string;
  1491. const Default: Extended): Extended; overload;
  1492. function StrToFloatDef(const S: string; const Default: Extended;
  1493. const FormatSettings: TFormatSettings): Extended; overload;
  1494. function TryStrToFloat(const S: string; out Value: Extended): Boolean; overload;
  1495. function TryStrToFloat(const S: string; out Value: Extended;
  1496. const FormatSettings: TFormatSettings): Boolean; overload;
  1497. function TryStrToFloat(const S: string; out Value: Double): Boolean; overload;
  1498. function TryStrToFloat(const S: string; out Value: Double;
  1499. const FormatSettings: TFormatSettings): Boolean; overload;
  1500. function TryStrToFloat(const S: string; out Value: Single): Boolean; overload;
  1501. function TryStrToFloat(const S: string; out Value: Single;
  1502. const FormatSettings: TFormatSettings): Boolean; overload;
  1503. { StrToCurr converts the given string to a currency value. For further
  1504. details, see the description of the StrToFloat function. }
  1505. function StrToCurr(const S: string): Currency; overload;
  1506. function StrToCurr(const S: string;
  1507. const FormatSettings: TFormatSettings): Currency; overload;
  1508. function StrToCurrDef(const S: string;
  1509. const Default: Currency): Currency; overload;
  1510. function StrToCurrDef(const S: string; const Default: Currency;
  1511. const FormatSettings: TFormatSettings): Currency; overload;
  1512. function TryStrToCurr(const S: string; out Value: Currency): Boolean; overload;
  1513. function TryStrToCurr(const S: string; out Value: Currency;
  1514. const FormatSettings: TFormatSettings): Boolean; overload;
  1515. { TextToFloat converts the null-terminated string given by Buffer to a
  1516. floating-point value which is returned in the variable given by Value.
  1517. The Value parameter must be a variable of type Extended or Currency, as
  1518. indicated by the ValueType parameter. The return value is True if the
  1519. conversion was successful, or False if the string is not a valid
  1520. floating-point value. For further details, see the description of the
  1521. StrToFloat function. }
  1522. function TextToFloat(Buffer: PChar; var Value;
  1523. ValueType: TFloatValue): Boolean; overload;
  1524. function TextToFloat(Buffer: PChar; var Value; ValueType: TFloatValue;
  1525. const FormatSettings: TFormatSettings): Boolean; overload;
  1526. { FloatToDecimal converts a floating-point value to a decimal representation
  1527. that is suited for further formatting. The Value parameter must be a
  1528. variable of type Extended or Currency, as indicated by the ValueType
  1529. parameter. For values of type Extended, the Precision parameter specifies
  1530. the requested number of significant digits in the result--the allowed range
  1531. is 1..18. For values of type Currency, the Precision parameter is ignored,
  1532. and the implied precision of the conversion is 19 digits. The Decimals
  1533. parameter specifies the requested maximum number of digits to the left of
  1534. the decimal point in the result. Precision and Decimals together control
  1535. how the result is rounded. To produce a result that always has a given
  1536. number of significant digits regardless of the magnitude of the number,
  1537. specify 9999 for the Decimals parameter. The result of the conversion is
  1538. stored in the specified TFloatRec record as follows:
  1539. Exponent - Contains the magnitude of the number, i.e. the number of
  1540. significant digits to the right of the decimal point. The Exponent field
  1541. is negative if the absolute value of the number is less than one. If the
  1542. number is a NAN (not-a-number), Exponent is set to -32768. If the number
  1543. is INF or -INF (positive or negative infinity), Exponent is set to 32767.
  1544. Negative - True if the number is negative, False if the number is zero
  1545. or positive.
  1546. Digits - Contains up to 18 (for type Extended) or 19 (for type Currency)
  1547. significant digits followed by a null terminator. The implied decimal
  1548. point (if any) is not stored in Digits. Trailing zeros are removed, and
  1549. if the resulting number is zero, NAN, or INF, Digits contains nothing but
  1550. the null terminator. }
  1551. procedure FloatToDecimal(var Result: TFloatRec; const Value;
  1552. ValueType: TFloatValue; Precision, Decimals: Integer);
  1553. { Date/time support routines }
  1554. function DateTimeToTimeStamp(DateTime: TDateTime): TTimeStamp;
  1555. function TimeStampToDateTime(const TimeStamp: TTimeStamp): TDateTime;
  1556. function MSecsToTimeStamp(MSecs: Comp): TTimeStamp;
  1557. function TimeStampToMSecs(const TimeStamp: TTimeStamp): Comp;
  1558. { EncodeDate encodes the given year, month, and day into a TDateTime value.
  1559. The year must be between 1 and 9999, the month must be between 1 and 12,
  1560. and the day must be between 1 and N, where N is the number of days in the
  1561. specified month. If the specified values are not within range, an
  1562. EConvertError exception is raised. The resulting value is the number of
  1563. days between 12/30/1899 and the given date. }
  1564. function EncodeDate(Year, Month, Day: Word): TDateTime;
  1565. { EncodeTime encodes the given hour, minute, second, and millisecond into a
  1566. TDateTime value. The hour must be between 0 and 23, the minute must be
  1567. between 0 and 59, the second must be between 0 and 59, and the millisecond
  1568. must be between 0 and 999. If the specified values are not within range, an
  1569. EConvertError exception is raised. The resulting value is a number between
  1570. 0 (inclusive) and 1 (not inclusive) that indicates the fractional part of
  1571. a day given by the specified time. The value 0 corresponds to midnight,
  1572. 0.5 corresponds to noon, 0.75 corresponds to 6:00 pm, etc. }
  1573. function EncodeTime(Hour, Min, Sec, MSec: Word): TDateTime;
  1574. { Instead of generating errors the following variations of EncodeDate and
  1575. EncodeTime simply return False if the parameters given are not valid.
  1576. Other than that, these functions are functionally the same as the above
  1577. functions. }
  1578. function TryEncodeDate(Year, Month, Day: Word; out Date: TDateTime): Boolean;
  1579. function TryEncodeTime(Hour, Min, Sec, MSec: Word; out Time: TDateTime): Boolean;
  1580. { DecodeDate decodes the integral (date) part of the given TDateTime value
  1581. into its corresponding year, month, and day. If the given TDateTime value
  1582. is less than or equal to zero, the year, month, and day return parameters
  1583. are all set to zero. }
  1584. procedure DecodeDate(const DateTime: TDateTime; var Year, Month, Day: Word);
  1585. { This variation of DecodeDate works similarly to the above function but
  1586. returns more information. The result value of this function indicates
  1587. whether the year decoded is a leap year or not. }
  1588. function DecodeDateFully(const DateTime: TDateTime; var Year, Month, Day,
  1589. DOW: Word): Boolean;
  1590. {$IFDEF LINUX}
  1591. function InternalDecodeDate(const DateTime: TDateTime; var Year, Month, Day, DOW: Word): Boolean;
  1592. {$ENDIF}
  1593. { DecodeTime decodes the fractional (time) part of the given TDateTime value
  1594. into its corresponding hour, minute, second, and millisecond. }
  1595. procedure DecodeTime(const DateTime: TDateTime; var Hour, Min, Sec, MSec: Word);
  1596. {$IFDEF MSWINDOWS}
  1597. { DateTimeToSystemTime converts a date and time from Delphi's TDateTime
  1598. format into the Win32 API's TSystemTime format. }
  1599. procedure DateTimeToSystemTime(const DateTime: TDateTime; var SystemTime: TSystemTime);
  1600. { SystemTimeToDateTime converts a date and time from the Win32 API's
  1601. TSystemTime format into Delphi's TDateTime format. }
  1602. function SystemTimeToDateTime(const SystemTime: TSystemTime): TDateTime;
  1603. {$ENDIF}
  1604. { DayOfWeek returns the day of the week of the given date. The result is an
  1605. integer between 1 and 7, corresponding to Sunday through Saturday.
  1606. This function is not ISO 8601 compliant, for that see the DateUtils unit. }
  1607. function DayOfWeek(const DateTime: TDateTime): Word;
  1608. { Date returns the current date. }
  1609. function Date: TDateTime;
  1610. { Time returns the current time. }
  1611. function Time: TDateTime;
  1612. {$IFDEF LINUX}
  1613. { clashes with Time in <X11/Xlib.h>, use GetTime instead }
  1614. {$EXTERNALSYM Time}
  1615. {$ENDIF}
  1616. function GetTime: TDateTime;
  1617. { Now returns the current date and time, corresponding to Date + Time. }
  1618. function Now: TDateTime;
  1619. { Current year returns the year portion of the date returned by Now }
  1620. function CurrentYear: Word;
  1621. { IncMonth returns Date shifted by the specified number of months.
  1622. NumberOfMonths parameter can be negative, to return a date N months ago.
  1623. If the input day of month is greater than the last day of the resulting
  1624. month, the day is set to the last day of the resulting month.
  1625. Input time of day is copied to the DateTime result. }
  1626. function IncMonth(const DateTime: TDateTime; NumberOfMonths: Integer = 1): TDateTime;
  1627. { Optimized version of IncMonth that works with years, months and days
  1628. directly. See above comments for more detail as to what happens to the day
  1629. when incrementing months }
  1630. procedure IncAMonth(var Year, Month, Day: Word; NumberOfMonths: Integer = 1);
  1631. { ReplaceTime replaces the time portion of the DateTime parameter with the given
  1632. time value, adjusting the signs as needed if the date is prior to 1900
  1633. (Date value less than zero) }
  1634. procedure ReplaceTime(var DateTime: TDateTime; const NewTime: TDateTime);
  1635. { ReplaceDate replaces the date portion of the DateTime parameter with the given
  1636. date value, adjusting as needed for negative dates }
  1637. procedure ReplaceDate(var DateTime: TDateTime; const NewDate: TDateTime);
  1638. { IsLeapYear determines whether the given year is a leap year. }
  1639. function IsLeapYear(Year: Word): Boolean;
  1640. type
  1641. PDayTable = ^TDayTable;
  1642. TDayTable = array[1..12] of Word;
  1643. { The MonthDays array can be used to quickly find the number of
  1644. days in a month: MonthDays[IsLeapYear(Y), M] }
  1645. const
  1646. MonthDays: array [Boolean] of TDayTable =
  1647. ((31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31),
  1648. (31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31));
  1649. { Each of the date/time formatting routines that uses global variables
  1650. for formatting (separators, decimals, etc.), has an overloaded equivalent
  1651. requiring a parameter of type TFormatSettings. This additional parameter
  1652. provides the formatting information rather than the global variables. For
  1653. more information see the note at TFormatSettings. }
  1654. { DateToStr converts the date part of the given TDateTime value to a string.
  1655. The conversion uses the format specified by the ShortDateFormat global
  1656. variable. }
  1657. function DateToStr(const DateTime: TDateTime): string; overload; inline;
  1658. function DateToStr(const DateTime: TDateTime;
  1659. const FormatSettings: TFormatSettings): string; overload; inline;
  1660. { TimeToStr converts the time part of the given TDateTime value to a string.
  1661. The conversion uses the format specified by the LongTimeFormat global
  1662. variable. }
  1663. function TimeToStr(const DateTime: TDateTime): string; overload; inline;
  1664. function TimeToStr(const DateTime: TDateTime;
  1665. const FormatSettings: TFormatSettings): string; overload; inline;
  1666. { DateTimeToStr converts the given date and time to a string. The resulting
  1667. string consists of a date and time formatted using the ShortDateFormat and
  1668. LongTimeFormat global variables. Time information is included in the
  1669. resulting string only if the fractional part of the given date and time
  1670. value is non-zero. }
  1671. function DateTimeToStr(const DateTime: TDateTime): string; overload; inline;
  1672. function DateTimeToStr(const DateTime: TDateTime;
  1673. const FormatSettings: TFormatSettings): string; overload; inline;
  1674. { StrToDate converts the given string to a date value. The string must
  1675. consist of two or three numbers, separated by the character defined by
  1676. the DateSeparator global variable. The order for month, day, and year is
  1677. determined by the ShortDateFormat global variable--possible combinations
  1678. are m/d/y, d/m/y, and y/m/d. If the string contains only two numbers, it
  1679. is interpreted as a date (m/d or d/m) in the current year. Year values
  1680. between 0 and 99 are assumed to be in the current century. If the given
  1681. string does not contain a valid date, an EConvertError exception is
  1682. raised. }
  1683. function StrToDate(const S: string): TDateTime; overload;
  1684. function StrToDate(const S: string;
  1685. const FormatSettings: TFormatSettings): TDateTime; overload;
  1686. function StrToDateDef(const S: string;
  1687. const Default: TDateTime): TDateTime; overload;
  1688. function StrToDateDef(const S: string; const Default: TDateTime;
  1689. const FormatSettings: TFormatSettings): TDateTime; overload;
  1690. function TryStrToDate(const S: string; out Value: TDateTime): Boolean; overload;
  1691. function TryStrToDate(const S: string; out Value: TDateTime;
  1692. const FormatSettings: TFormatSettings): Boolean; overload;
  1693. { StrToTime converts the given string to a time value. The string must
  1694. consist of two or three numbers, separated by the character defined by
  1695. the TimeSeparator global variable, optionally followed by an AM or PM
  1696. indicator. The numbers represent hour, minute, and (optionally) second,
  1697. in that order. If the time is followed by AM or PM, it is assumed to be
  1698. in 12-hour clock format. If no AM or PM indicator is included, the time
  1699. is assumed to be in 24-hour clock format. If the given string does not
  1700. contain a valid time, an EConvertError exception is raised. }
  1701. function StrToTime(const S: string): TDateTime; overload;
  1702. function StrToTime(const S: string;
  1703. const FormatSettings: TFormatSettings): TDateTime; overload;
  1704. function StrToTimeDef(const S: string;
  1705. const Default: TDateTime): TDateTime; overload;
  1706. function StrToTimeDef(const S: string; const Default: TDateTime;
  1707. const FormatSettings: TFormatSettings): TDateTime; overload;
  1708. function TryStrToTime(const S: string; out Value: TDateTime): Boolean; overload;
  1709. function TryStrToTime(const S: string; out Value: TDateTime;
  1710. const FormatSettings: TFormatSettings): Boolean; overload;
  1711. { StrToDateTime converts the given string to a date and time value. The
  1712. string must contain a date optionally followed by a time. The date and
  1713. time parts of the string must follow the formats described for the
  1714. StrToDate and StrToTime functions. }
  1715. function StrToDateTime(const S: string): TDateTime; overload;
  1716. function StrToDateTime(const S: string;
  1717. const FormatSettings: TFormatSettings): TDateTime; overload;
  1718. function StrToDateTimeDef(const S: string;
  1719. const Default: TDateTime): TDateTime; overload;
  1720. function StrToDateTimeDef(const S: string; const Default: TDateTime;
  1721. const FormatSettings: TFormatSettings): TDateTime; overload;
  1722. function TryStrToDateTime(const S: string;
  1723. out Value: TDateTime): Boolean; overload;
  1724. function TryStrToDateTime(const S: string; out Value: TDateTime;
  1725. const FormatSettings: TFormatSettings): Boolean; overload;
  1726. { FormatDateTime formats the date-and-time value given by DateTime using the
  1727. format given by Format. The following format specifiers are supported:
  1728. c Displays the date using the format given by the ShortDateFormat
  1729. global variable, followed by the time using the format given by
  1730. the LongTimeFormat global variable. The time is not displayed if
  1731. the fractional part of the DateTime value is zero.
  1732. d Displays the day as a number without a leading zero (1-31).
  1733. dd Displays the day as a number with a leading zero (01-31).
  1734. ddd Displays the day as an abbreviation (Sun-Sat) using the strings
  1735. given by the ShortDayNames global variable.
  1736. dddd Displays the day as a full name (Sunday-Saturday) using the strings
  1737. given by the LongDayNames global variable.
  1738. ddddd Displays the date using the format given by the ShortDateFormat
  1739. global variable.
  1740. dddddd Displays the date using the format given by the LongDateFormat
  1741. global variable.
  1742. g Displays the period/era as an abbreviation (Japanese and
  1743. Taiwanese locales only).
  1744. gg Displays the period/era as a full name.
  1745. e Displays the year in the current period/era as a number without
  1746. a leading zero (Japanese, Korean and Taiwanese locales only).
  1747. ee Displays the year in the current period/era as a number with
  1748. a leading zero (Japanese, Korean and Taiwanese locales only).
  1749. m Displays the month as a number without a leading zero (1-12). If
  1750. the m specifier immediately follows an h or hh specifier, the
  1751. minute rather than the month is displayed.
  1752. mm Displays the month as a number with a leading zero (01-12). If
  1753. the mm specifier immediately follows an h or hh specifier, the
  1754. minute rather than the month is displayed.
  1755. mmm Displays the month as an abbreviation (Jan-Dec) using the strings
  1756. given by the ShortMonthNames global variable.
  1757. mmmm Displays the month as a full name (January-December) using the
  1758. strings given by the LongMonthNames global variable.
  1759. yy Displays the year as a two-digit number (00-99).
  1760. yyyy Displays the year as a four-digit number (0000-9999).
  1761. h Displays the hour without a leading zero (0-23).
  1762. hh Displays the hour with a leading zero (00-23).
  1763. n Displays the minute without a leading zero (0-59).
  1764. nn Displays the minute with a leading zero (00-59).
  1765. s Displays the second without a leading zero (0-59).
  1766. ss Displays the second with a leading zero (00-59).
  1767. z Displays the millisecond without a leading zero (0-999).
  1768. zzz Displays the millisecond with a leading zero (000-999).
  1769. t Displays the time using the format given by the ShortTimeFormat
  1770. global variable.
  1771. tt Displays the time using the format given by the LongTimeFormat
  1772. global variable.
  1773. am/pm Uses the 12-hour clock for the preceding h or hh specifier, and
  1774. displays 'am' for any hour before noon, and 'pm' for any hour
  1775. after noon. The am/pm specifier can use lower, upper, or mixed
  1776. case, and the result is displayed accordingly.
  1777. a/p Uses the 12-hour clock for the preceding h or hh specifier, and
  1778. displays 'a' for any hour before noon, and 'p' for any hour after
  1779. noon. The a/p specifier can use lower, upper, or mixed case, and
  1780. the result is displayed accordingly.
  1781. ampm Uses the 12-hour clock for the preceding h or hh specifier, and
  1782. displays the contents of the TimeAMString global variable for any
  1783. hour before noon, and the contents of the TimePMString global
  1784. variable for any hour after noon.
  1785. / Displays the date separator character given by the DateSeparator
  1786. global variable.
  1787. : Displays the time separator character given by the TimeSeparator
  1788. global variable.
  1789. 'xx' Characters enclosed in single or double quotes are displayed as-is,
  1790. "xx" and do not affect formatting.
  1791. Format specifiers may be written in upper case as well as in lower case
  1792. letters--both produce the same result.
  1793. If the string given by the Format parameter is empty, the date and time
  1794. value is formatted as if a 'c' format specifier had been given.
  1795. The following example:
  1796. S := FormatDateTime('"The meeting is on" dddd, mmmm d, yyyy, ' +
  1797. '"at" hh:mm AM/PM', StrToDateTime('2/15/95 10:30am'));
  1798. assigns 'The meeting is on Wednesday, February 15, 1995 at 10:30 AM' to
  1799. the string variable S. }
  1800. function FormatDateTime(const Format: string;
  1801. DateTime: TDateTime): string; overload; inline;
  1802. function FormatDateTime(const Format: string; DateTime: TDateTime;
  1803. const FormatSettings: TFormatSettings): string; overload;
  1804. { DateTimeToString converts the date and time value given by DateTime using
  1805. the format string given by Format into the string variable given by Result.
  1806. For further details, see the description of the FormatDateTime function. }
  1807. procedure DateTimeToString(var Result: string; const Format: string;
  1808. DateTime: TDateTime); overload;
  1809. procedure DateTimeToString(var Result: string; const Format: string;
  1810. DateTime: TDateTime; const FormatSettings: TFormatSettings); overload;
  1811. { FloatToDateTime will range validate a value to make sure it falls
  1812. within the acceptable date range }
  1813. const
  1814. MinDateTime: TDateTime = -657434.0; { 01/01/0100 12:00:00.000 AM }
  1815. MaxDateTime: TDateTime = 2958465.99999; { 12/31/9999 11:59:59.999 PM }
  1816. function FloatToDateTime(const Value: Extended): TDateTime;
  1817. function TryFloatToDateTime(const Value: Extended; out AResult: TDateTime): Boolean;
  1818. { System error messages }
  1819. function SysErrorMessage(ErrorCode: Integer): string;
  1820. { Initialization file support }
  1821. function GetLocaleStr(Locale, LocaleType: Integer; const Default: string): string; platform;
  1822. function GetLocaleChar(Locale, LocaleType: Integer; Default: Char): Char; platform;
  1823. { GetFormatSettings resets all locale-specific variables (date, time, number,
  1824. currency formats, system locale) to the values provided by the operating system. }
  1825. procedure GetFormatSettings;
  1826. { GetLocaleFormatSettings loads locale-specific variables (date, time, number,
  1827. currency formats) with values provided by the operating system for the
  1828. specified locale (LCID). The values are stored in the FormatSettings record. }
  1829. {$IFDEF MSWINDOWS}
  1830. procedure GetLocaleFormatSettings(LCID: Integer;
  1831. var FormatSettings: TFormatSettings);
  1832. {$ENDIF}
  1833. { Exception handling routines }
  1834. {$IFDEF LINUX}
  1835. { InquireSignal is used to determine the state of an OS signal handler.
  1836. Pass it one of the RTL_SIG* constants, and it will return a TSignalState
  1837. which will tell you if the signal has been hooked, not hooked, or overriden
  1838. by some other module. You can use this function to determine if some other
  1839. module has hijacked your signal handlers, should you wish to reinstall your
  1840. own. This is a risky proposition under Linux, and is only recommended as a
  1841. last resort. Do not pass RTL_SIGDEFAULT to this function.
  1842. }
  1843. function InquireSignal(RtlSigNum: Integer): TSignalState;
  1844. { AbandonSignalHandler tells the RTL to leave a signal handler
  1845. in place, even if we believe that we hooked it at startup time.
  1846. Once you have called AbandonSignalHandler with a specific signal number,
  1847. neither UnhookSignal nor the RTL will restore any previous signal handler
  1848. under any condition.
  1849. }
  1850. procedure AbandonSignalHandler(RtlSigNum: Integer);
  1851. { HookSignal is used to hook individual signals, or an RTL-defined default
  1852. set of signals. It does not test whether a signal has already been
  1853. hooked, so it should be used in conjunction with InquireSignal. It is
  1854. exposed to enable users to hook signals in standalone libraries, or in the
  1855. event that an external module hijacks the RTL installed signal handlers.
  1856. Pass RTL_SIGDEFAULT if you want to hook all the signals that the RTL
  1857. normally hooks at startup time.
  1858. }
  1859. procedure HookSignal(RtlSigNum: Integer);
  1860. { UnhookSignal is used to remove signal handlers installed by HookSignal.
  1861. It can remove individual signal handlers, or the RTL-defined default set
  1862. of signals. If OnlyIfHooked is True, then we will only unhook the signal
  1863. if the signal handler has been hooked, and has not since been overriden by
  1864. some foreign handler.
  1865. }
  1866. procedure UnhookSignal(RtlSigNum: Integer; OnlyIfHooked: Boolean = True);
  1867. { HookOSExceptions is used internally by thread support. DON'T call this
  1868. function yourself. }
  1869. procedure HookOSExceptions;
  1870. { MapSignal is used internally as well. It maps a signal and associated
  1871. context to an internal value that represents the type of Exception
  1872. class to raise. }
  1873. function MapSignal(SigNum: Integer; Context: PSigContext): LongWord;
  1874. { SignalConverter is used internally to properly reinit the FPU and properly
  1875. raise an external OS exception object. DON'T call this function yourself. }
  1876. procedure SignalConverter(ExceptionEIP: LongWord; FaultAddr: LongWord; ErrorCode: LongWord);
  1877. {
  1878. See the comment at the threadvar declarations for these below. The access
  1879. to these has been implemented through getter/setter functions because you
  1880. cannot use threadvars across packages.
  1881. }
  1882. procedure SetSafeCallExceptionMsg(const Msg: String);
  1883. procedure SetSafeCallExceptionAddr(Addr: Pointer);
  1884. function GetSafeCallExceptionMsg: String;
  1885. function GetSafeCallExceptionAddr: Pointer;
  1886. { HookOSExceptionsProc is used internally and cannot be used in a conventional
  1887. manner. DON'T ever set this variable. }
  1888. var
  1889. HookOSExceptionsProc: procedure = nil platform deprecated;
  1890. { LoadLibrary / FreeLibrary are defined here only for convenience. On Linux,
  1891. they map directly to dlopen / dlclose. Note that module loading semantics
  1892. on Linux are not identical to Windows. }
  1893. function LoadLibrary(ModuleName: PChar): HMODULE;
  1894. function FreeLibrary(Module: HMODULE): LongBool;
  1895. { GetProcAddress does what it implies. It performs the same function as the like
  1896. named function under Windows. dlsym does not quite have the same sematics as
  1897. GetProcAddress as it will return the address of a symbol in another module if
  1898. it was not found in the given HMODULE. This function will verify that the 'Proc'
  1899. is actually found within the 'Module', and if not returns nil }
  1900. function GetProcAddress(Module: HMODULE; Proc: PChar): Pointer;
  1901. { Given a module name, this function will return the module handle. There is no
  1902. direct equivalent in Linux so this function provides that capability. Also
  1903. note, this function is specific to glibc. }
  1904. function GetModuleHandle(ModuleName: PChar): HMODULE;
  1905. { This function works just like GetModuleHandle, except it will look for a module
  1906. that matches the given base package name. For example, given the base package
  1907. name 'package', the actual module name is, by default, 'bplpackage.so'. This
  1908. function will search for the string 'package' within the module name. }
  1909. function GetPackageModuleHandle(PackageName: PChar): HMODULE;
  1910. {$ENDIF}
  1911. { In Linux, the parameter to sleep() is in whole seconds. In Windows, the
  1912. parameter is in milliseconds. To ease headaches, we implement a version
  1913. of sleep here for Linux that takes milliseconds and calls a Linux system
  1914. function with sub-second resolution. This maps directly to the Windows
  1915. API on Windows. }
  1916. procedure Sleep(milliseconds: Cardinal);{$IFDEF MSWINDOWS} stdcall; {$ENDIF}
  1917. {$IFDEF MSWINDOWS}
  1918. (*$EXTERNALSYM Sleep*)
  1919. {$ENDIF}
  1920. function GetModuleName(Module: HMODULE): string;
  1921. function ExceptionErrorMessage(ExceptObject: TObject; ExceptAddr: Pointer;
  1922. Buffer: PChar; Size: Integer): Integer;
  1923. procedure ShowException(ExceptObject: TObject; ExceptAddr: Pointer);
  1924. procedure Abort;
  1925. procedure OutOfMemoryError;
  1926. procedure Beep;
  1927. { MBCS functions }
  1928. { LeadBytes is a char set that indicates which char values are lead bytes
  1929. in multibyte character sets (Japanese, Chinese, etc).
  1930. This set is always empty for western locales. }
  1931. var
  1932. LeadBytes: set of Char = [];
  1933. (*$EXTERNALSYM LeadBytes*)
  1934. (*$HPPEMIT 'namespace Sysutils {'*)
  1935. (*$HPPEMIT 'extern PACKAGE System::Set<Byte, 0, 255> LeadBytes;'*)
  1936. (*$HPPEMIT '} // namespace Sysutils'*)
  1937. { ByteType indicates what kind of byte exists at the Index'th byte in S.
  1938. Western locales always return mbSingleByte. Far East multibyte locales
  1939. may also return mbLeadByte, indicating the byte is the first in a multibyte
  1940. character sequence, and mbTrailByte, indicating that the byte is one of
  1941. a sequence of bytes following a lead byte. One or more trail bytes can
  1942. follow a lead byte, depending on locale charset encoding and OS platform.
  1943. Parameters are assumed to be valid. }
  1944. function ByteType(const S: string; Index: Integer): TMbcsByteType;
  1945. { StrByteType works the same as ByteType, but on null-terminated PChar strings }
  1946. function StrByteType(Str: PChar; Index: Cardinal): TMbcsByteType;
  1947. { ByteToCharLen returns the character length of a MBCS string, scanning the
  1948. string for up to MaxLen bytes. In multibyte character sets, the number of
  1949. characters in a string may be less than the number of bytes. }
  1950. function ByteToCharLen(const S: string; MaxLen: Integer): Integer;
  1951. { CharToByteLen returns the byte length of a MBCS string, scanning the string
  1952. for up to MaxLen characters. }
  1953. function CharToByteLen(const S: string; MaxLen: Integer): Integer;
  1954. { ByteToCharIndex returns the 1-based character index of the Index'th byte in
  1955. a MBCS string. Returns zero if Index is out of range:
  1956. (Index <= 0) or (Index > Length(S)) }
  1957. function ByteToCharIndex(const S: string; Index: Integer): Integer;
  1958. { CharToByteIndex returns the 1-based byte index of the Index'th character
  1959. in a MBCS string. Returns zero if Index or Result are out of range:
  1960. (Index <= 0) or (Index > Length(S)) or (Result would be > Length(S)) }
  1961. function CharToByteIndex(const S: string; Index: Integer): Integer;
  1962. { StrCharLength returns the number of bytes required by the first character
  1963. in Str. In Windows, multibyte characters can be up to two bytes in length.
  1964. In Linux, multibyte characters can be up to six bytes in length (UTF-8). }
  1965. function StrCharLength(const Str: PChar): Integer;
  1966. { StrNextChar returns a pointer to the first byte of the character following
  1967. the character pointed to by Str. }
  1968. function StrNextChar(const Str: PChar): PChar;
  1969. { CharLength returns the number of bytes required by the character starting
  1970. at bytes S[Index]. }
  1971. function CharLength(const S: String; Index: Integer): Integer;
  1972. { NextCharIndex returns the byte index of the first byte of the character
  1973. following the character starting at S[Index]. }
  1974. function NextCharIndex(const S: String; Index: Integer): Integer;
  1975. { IsPathDelimiter returns True if the character at byte S[Index]
  1976. is a PathDelimiter ('\' or '/'), and it is not a MBCS lead or trail byte. }
  1977. function IsPathDelimiter(const S: string; Index: Integer): Boolean;
  1978. { IsDelimiter returns True if the character at byte S[Index] matches any
  1979. character in the Delimiters string, and the character is not a MBCS lead or
  1980. trail byte. S may contain multibyte characters; Delimiters must contain
  1981. only single byte characters. }
  1982. function IsDelimiter(const Delimiters, S: string; Index: Integer): Boolean;
  1983. { IncludeTrailingPathDelimiter returns the path with a PathDelimiter
  1984. ('/' or '\') at the end. This function is MBCS enabled. }
  1985. function IncludeTrailingPathDelimiter(const S: string): string;
  1986. { IncludeTrailingBackslash is the old name for IncludeTrailingPathDelimiter. }
  1987. function IncludeTrailingBackslash(const S: string): string; platform; inline;
  1988. { ExcludeTrailingPathDelimiter returns the path without a PathDelimiter
  1989. ('\' or '/') at the end. This function is MBCS enabled. }
  1990. function ExcludeTrailingPathDelimiter(const S: string): string;
  1991. { ExcludeTrailingBackslash is the old name for ExcludeTrailingPathDelimiter. }
  1992. function ExcludeTrailingBackslash(const S: string): string; platform; inline;
  1993. { LastDelimiter returns the byte index in S of the rightmost whole
  1994. character that matches any character in Delimiters (except null (#0)).
  1995. S may contain multibyte characters; Delimiters must contain only single
  1996. byte non-null characters.
  1997. Example: LastDelimiter('\.:', 'c:\filename.ext') returns 12. }
  1998. function LastDelimiter(const Delimiters, S: string): Integer;
  1999. { AnsiCompareFileName supports DOS file name comparison idiosyncracies
  2000. in Far East locales (Zenkaku) on Windows.
  2001. In non-MBCS locales on Windows, AnsiCompareFileName is identical to
  2002. AnsiCompareText (case insensitive).
  2003. On Linux, AnsiCompareFileName is identical to AnsiCompareStr (case sensitive).
  2004. For general purpose file name comparisions, you should use this function
  2005. instead of AnsiCompareText. }
  2006. function AnsiCompareFileName(const S1, S2: string): Integer; inline;
  2007. function SameFileName(const S1, S2: string): Boolean; inline;
  2008. { AnsiLowerCaseFileName supports lowercase conversion idiosyncracies of
  2009. DOS file names in Far East locales (Zenkaku). In non-MBCS locales,
  2010. AnsiLowerCaseFileName is identical to AnsiLowerCase. }
  2011. function AnsiLowerCaseFileName(const S: string): string;
  2012. { AnsiUpperCaseFileName supports uppercase conversion idiosyncracies of
  2013. DOS file names in Far East locales (Zenkaku). In non-MBCS locales,
  2014. AnsiUpperCaseFileName is identical to AnsiUpperCase. }
  2015. function AnsiUpperCaseFileName(const S: string): string;
  2016. { AnsiPos: Same as Pos but supports MBCS strings }
  2017. function AnsiPos(const Substr, S: string): Integer;
  2018. { AnsiStrPos: Same as StrPos but supports MBCS strings }
  2019. function AnsiStrPos(Str, SubStr: PChar): PChar;
  2020. { AnsiStrRScan: Same as StrRScan but supports MBCS strings }
  2021. function AnsiStrRScan(Str: PChar; Chr: Char): PChar;
  2022. { AnsiStrScan: Same as StrScan but supports MBCS strings }
  2023. function AnsiStrScan(Str: PChar; Chr: Char): PChar;
  2024. { StringReplace replaces occurances of <oldpattern> with <newpattern> in a
  2025. given string. Assumes the string may contain Multibyte characters }
  2026. type
  2027. TReplaceFlags = set of (rfReplaceAll, rfIgnoreCase);
  2028. function StringReplace(const S, OldPattern, NewPattern: string;
  2029. Flags: TReplaceFlags): string;
  2030. { WrapText will scan a string for BreakChars and insert the BreakStr at the
  2031. last BreakChar position before MaxCol. Will not insert a break into an
  2032. embedded quoted string (both ''' and '"' supported) }
  2033. function WrapText(const Line, BreakStr: string; const BreakChars: TSysCharSet;
  2034. MaxCol: Integer): string; overload;
  2035. function WrapText(const Line: string; MaxCol: Integer = 45): string; overload;
  2036. { FindCmdLineSwitch determines whether the string in the Switch parameter
  2037. was passed as a command line argument to the application. SwitchChars
  2038. identifies valid argument-delimiter characters (i.e., "-" and "/" are
  2039. common delimiters). The IgnoreCase paramter controls whether a
  2040. case-sensistive or case-insensitive search is performed. }
  2041. const
  2042. SwitchChars = {$IFDEF MSWINDOWS} ['/','-']; {$ENDIF}
  2043. {$IFDEF LINUX} ['-']; {$ENDIF}
  2044. function FindCmdLineSwitch(const Switch: string; const Chars: TSysCharSet;
  2045. IgnoreCase: Boolean): Boolean; overload;
  2046. { These versions of FindCmdLineSwitch are convenient for writing portable
  2047. code. The characters that are valid to indicate command line switches vary
  2048. on different platforms. For example, '/' cannot be used as a switch char
  2049. on Linux because '/' is the path delimiter. }
  2050. { This version uses SwitchChars defined above, and IgnoreCase False. }
  2051. function FindCmdLineSwitch(const Switch: string): Boolean; overload;
  2052. { This version uses SwitchChars defined above. }
  2053. function FindCmdLineSwitch(const Switch: string; IgnoreCase: Boolean): Boolean; overload;
  2054. { FreeAndNil frees the given TObject instance and sets the variable reference
  2055. to nil. Be careful to only pass TObjects to this routine. }
  2056. procedure FreeAndNil(var Obj);
  2057. { Interface support routines }
  2058. function Supports(const Instance: IInterface; const IID: TGUID; out Intf): Boolean; overload;
  2059. function Supports(const Instance: TObject; const IID: TGUID; out Intf): Boolean; overload;
  2060. function Supports(const Instance: IInterface; const IID: TGUID): Boolean; overload;
  2061. function Supports(const Instance: TObject; const IID: TGUID): Boolean; overload;
  2062. function Supports(const AClass: TClass; const IID: TGUID): Boolean; overload;
  2063. function CreateGUID(out Guid: TGUID): HResult;
  2064. {$IFDEF MSWINDOWS}
  2065. stdcall;
  2066. {$ENDIF}
  2067. function StringToGUID(const S: string): TGUID;
  2068. function GUIDToString(const GUID: TGUID): string;
  2069. function IsEqualGUID(const guid1, guid2: TGUID): Boolean;
  2070. {$IFDEF MSWINDOWS}
  2071. stdcall; {$EXTERNALSYM IsEqualGUID}
  2072. {$ENDIF}
  2073. { Package support routines }
  2074. { Package Info flags }
  2075. const
  2076. pfNeverBuild = $00000001;
  2077. pfDesignOnly = $00000002;
  2078. pfRunOnly = $00000004;
  2079. pfIgnoreDupUnits = $00000008;
  2080. pfModuleTypeMask = $C0000000;
  2081. pfExeModule = $00000000;
  2082. pfPackageModule = $40000000;
  2083. pfProducerMask = $0C000000;
  2084. pfV3Produced = $00000000;
  2085. pfProducerUndefined = $04000000;
  2086. pfBCB4Produced = $08000000;
  2087. pfDelphi4Produced = $0C000000;
  2088. pfLibraryModule = $80000000;
  2089. { Unit info flags }
  2090. const
  2091. ufMainUnit = $01;
  2092. ufPackageUnit = $02;
  2093. ufWeakUnit = $04;
  2094. ufOrgWeakUnit = $08;
  2095. ufImplicitUnit = $10;
  2096. ufWeakPackageUnit = ufPackageUnit or ufWeakUnit;
  2097. {$IFDEF LINUX}
  2098. var
  2099. PkgLoadingMode: Integer = RTLD_LAZY;
  2100. {$ENDIF}
  2101. { Procedure type of the callback given to GetPackageInfo. Name is the actual
  2102. name of the package element. If IsUnit is True then Name is the name of
  2103. a contained unit; a required package if False. Param is the value passed
  2104. to GetPackageInfo }
  2105. type
  2106. TNameType = (ntContainsUnit, ntRequiresPackage, ntDcpBpiName);
  2107. TPackageInfoProc = procedure (const Name: string; NameType: TNameType; Flags: Byte; Param: Pointer);
  2108. { LoadPackage loads a given package DLL, checks for duplicate units and
  2109. calls the initialization blocks of all the contained units }
  2110. function LoadPackage(const Name: string): HMODULE;
  2111. { UnloadPackage does the opposite of LoadPackage by calling the finalization
  2112. blocks of all contained units, then unloading the package DLL }
  2113. procedure UnloadPackage(Module: HMODULE);
  2114. { GetPackageInfo accesses the given package's info table and enumerates
  2115. all the contained units and required packages }
  2116. procedure GetPackageInfo(Module: HMODULE; Param: Pointer; var Flags: Integer;
  2117. InfoProc: TPackageInfoProc);
  2118. { GetPackageDescription loads the description resource from the package
  2119. library. If the description resource does not exist,
  2120. an empty string is returned. }
  2121. function GetPackageDescription(ModuleName: PChar): string;
  2122. { InitializePackage validates and initializes the given package DLL }
  2123. procedure InitializePackage(Module: HMODULE);
  2124. { FinalizePackage finalizes the given package DLL }
  2125. procedure FinalizePackage(Module: HMODULE);
  2126. { RaiseLastOSError calls GetLastError to retrieve the code for
  2127. the last occuring error in a call to an OS or system library function.
  2128. If GetLastError returns an error code, RaiseLastOSError raises
  2129. an EOSError exception with the error code and a system-provided
  2130. message associated with with error. }
  2131. procedure RaiseLastOSError; overload;
  2132. procedure RaiseLastOSError(LastError: Integer); overload;
  2133. {$IFDEF MSWINDOWS}
  2134. procedure RaiseLastWin32Error; deprecated; // use RaiseLastOSError
  2135. { Win32Check is used to check the return value of a Win32 API function }
  2136. { which returns a BOOL to indicate success. If the Win32 API function }
  2137. { returns False (indicating failure), Win32Check calls RaiseLastOSError }
  2138. { to raise an exception. If the Win32 API function returns True, }
  2139. { Win32Check returns True. }
  2140. function Win32Check(RetVal: BOOL): BOOL; platform;
  2141. {$ENDIF}
  2142. { Termination procedure support }
  2143. type
  2144. TTerminateProc = function: Boolean;
  2145. { Call AddTerminateProc to add a terminate procedure to the system list of }
  2146. { termination procedures. Delphi will call all of the function in the }
  2147. { termination procedure list before an application terminates. The user- }
  2148. { defined TermProc function should return True if the application can }
  2149. { safely terminate or False if the application cannot safely terminate. }
  2150. { If one of the functions in the termination procedure list returns False, }
  2151. { the application will not terminate. }
  2152. procedure AddTerminateProc(TermProc: TTerminateProc);
  2153. { CallTerminateProcs is called by VCL when an application is about to }
  2154. { terminate. It returns True only if all of the functions in the }
  2155. { system's terminate procedure list return True. This function is }
  2156. { intended only to be called by Delphi, and it should not be called }
  2157. { directly. }
  2158. function CallTerminateProcs: Boolean;
  2159. function GDAL: LongWord;
  2160. procedure RCS;
  2161. procedure RPR;
  2162. { HexDisplayPrefix contains the prefix to display on hexadecimal
  2163. values - '$' for Pascal syntax, '0x' for C++ syntax. This is
  2164. for display only - this does not affect the string-to-integer
  2165. conversion routines. }
  2166. var
  2167. HexDisplayPrefix: string = '$';
  2168. {$IFDEF MSWINDOWS}
  2169. { The GetDiskFreeSpace Win32 API does not support partitions larger than 2GB
  2170. under Win95. A new Win32 function, GetDiskFreeSpaceEx, supports partitions
  2171. larger than 2GB but only exists on Win NT 4.0 and Win95 OSR2.
  2172. The GetDiskFreeSpaceEx function pointer variable below will be initialized
  2173. at startup to point to either the actual OS API function if it exists on
  2174. the system, or to an internal Delphi function if it does not. When running
  2175. on Win95 pre-OSR2, the output of this function will still be limited to
  2176. the 2GB range reported by Win95, but at least you don't have to worry
  2177. about which API function to call in code you write. }
  2178. var
  2179. GetDiskFreeSpaceEx: function (Directory: PChar; var FreeAvailable,
  2180. TotalSpace: TLargeInteger; TotalFree: PLargeInteger): Bool stdcall = nil;
  2181. { SafeLoadLibrary calls LoadLibrary, disabling normal Win32 error message
  2182. popup dialogs if the requested file can't be loaded. SafeLoadLibrary also
  2183. preserves the current FPU control word (precision, exception masks) across
  2184. the LoadLibrary call (in case the DLL you're loading hammers the FPU control
  2185. word in its initialization, as many MS DLLs do)}
  2186. function SafeLoadLibrary(const FileName: string;
  2187. ErrorMode: UINT = SEM_NOOPENFILEERRORBOX): HMODULE;
  2188. {$ENDIF}
  2189. {$IFDEF LINUX}
  2190. { SafeLoadLibrary calls LoadLibrary preserves the current FPU control
  2191. word (precision, exception masks) across the LoadLibrary call (in
  2192. case the shared object you're loading hammers the FPU control
  2193. word in its initialization, as many MS DLLs do) }
  2194. function SafeLoadLibrary(const FileName: string;
  2195. Dummy: LongWord = 0): HMODULE;
  2196. {$ENDIF}
  2197. { Thread synchronization }
  2198. { IReadWriteSync is an abstract interface for general read/write synchronization.
  2199. Some implementations may allow simultaneous readers, but writers always have
  2200. exclusive locks.
  2201. Worst case is that this class behaves identical to a TRTLCriticalSection -
  2202. that is, read and write locks block all other threads. }
  2203. type
  2204. IReadWriteSync = interface
  2205. ['{7B108C52-1D8F-4CDB-9CDF-57E071193D3F}']
  2206. procedure BeginRead;
  2207. procedure EndRead;
  2208. function BeginWrite: Boolean;
  2209. procedure EndWrite;
  2210. end;
  2211. TSimpleRWSync = class(TInterfacedObject, IReadWriteSync)
  2212. private
  2213. FLock: TRTLCriticalSection;
  2214. public
  2215. constructor Create;
  2216. destructor Destroy; override;
  2217. procedure BeginRead;
  2218. procedure EndRead;
  2219. function BeginWrite: Boolean;
  2220. procedure EndWrite;
  2221. end;
  2222. { TThreadLocalCounter
  2223. This class implements a lightweight non-blocking thread local storage
  2224. mechanism specifically built for tracking per-thread recursion counts
  2225. in TMultiReadExclusiveWriteSynchronizer. This class is intended for
  2226. Delphi RTL internal use only. In the future it may be generalized
  2227. and "hardened" for general application use, but until then leave it alone.
  2228. Rules of Use:
  2229. The tls object must be opened to gain access to the thread-specific data
  2230. structure. If a threadinfo block does not exist for the current thread,
  2231. Open will allocate one. Every call to Open must be matched with a call
  2232. to Close. The pointer returned by Open is invalid after the matching call
  2233. to Close (or Delete).
  2234. The thread info structure is unique to each thread. Once you have it, it's
  2235. yours. You don't need to guard against concurrent access to the thread
  2236. data by multiple threads - your thread is the only thread that will ever
  2237. have access to the structure that Open returns. The thread info structure
  2238. is allocated and owned by the tls object. If you put allocated pointers
  2239. in the thread info make sure you free them before you delete the threadinfo
  2240. node.
  2241. When thread data is no longer needed, call the Delete method on the pointer.
  2242. This must be done between calls to Open and Close. You should not use the
  2243. thread data after calling Delete.
  2244. Important: Do not keep the tls object open for long periods of time.
  2245. In particular, be careful not to wait on a thread synchronization event or
  2246. critical section while you have the tls object open. It's much better to
  2247. open and close the tls object before and after the blocking event than to
  2248. leave the tls object open while waiting.
  2249. Implementation Notes:
  2250. The main purpose of this storage class is to provide thread-local storage
  2251. without using limited / problematic OS tls slots and without requiring
  2252. expensive blocking thread synchronization. This class performs no
  2253. blocking waits or spin loops! (except for memory allocation)
  2254. Thread info is kept in linked lists to facilitate non-blocking threading
  2255. techniques. A hash table indexed by a hash of the current thread ID
  2256. reduces linear search times.
  2257. When a node is deleted, its thread ID is stripped and its Active field is
  2258. set to zero, meaning it is available to be recycled for other threads.
  2259. Nodes are never removed from the live list or freed while the class is in
  2260. use. All nodes are freed when the class is destroyed.
  2261. Nodes are only inserted at the front of the list (each list in the hash table).
  2262. The linked list management relies heavily on InterlockedExchange to perform
  2263. atomic node pointer replacements. There are brief windows of time where
  2264. the linked list may be circular while a two-step insertion takes place.
  2265. During that brief window, other threads traversing the lists may see
  2266. the same node more than once more than once. (pun!) This is fine for what this
  2267. implementation needs. Don't do anything silly like try to count the
  2268. nodes during a traversal.
  2269. }
  2270. type
  2271. PThreadInfo = ^TThreadInfo;
  2272. TThreadInfo = record
  2273. Next: PThreadInfo;
  2274. ThreadID: Cardinal;
  2275. Active: Integer;
  2276. RecursionCount: Cardinal;
  2277. end;
  2278. TThreadLocalCounter = class
  2279. private
  2280. FHashTable: array [0..15] of PThreadInfo;
  2281. function HashIndex: Byte;
  2282. function Recycle: PThreadInfo;
  2283. public
  2284. destructor Destroy; override;
  2285. procedure Open(var Thread: PThreadInfo);
  2286. procedure Delete(var Thread: PThreadInfo);
  2287. procedure Close(var Thread: PThreadInfo);
  2288. end;
  2289. {$IFDEF MSWINDOWS}
  2290. { TMultiReadExclusiveWriteSynchronizer minimizes thread serialization to gain
  2291. read access to a resource shared among threads while still providing complete
  2292. exclusivity to callers needing write access to the shared resource.
  2293. (multithread shared reads, single thread exclusive write)
  2294. Read locks are allowed while owning a write lock.
  2295. Read locks can be promoted to write locks within the same thread.
  2296. (BeginRead, BeginWrite, EndWrite, EndRead)
  2297. Note: Other threads have an opportunity to modify the protected resource
  2298. when you call BeginWrite before you are granted the write lock, even
  2299. if you already have a read lock open. Best policy is not to retain
  2300. any info about the protected resource (such as count or size) across a
  2301. write lock. Always reacquire samples of the protected resource after
  2302. acquiring or releasing a write lock.
  2303. The function result of BeginWrite indicates whether another thread got
  2304. the write lock while the current thread was waiting for the write lock.
  2305. Return value of True means that the write lock was acquired without
  2306. any intervening modifications by other threads. Return value of False
  2307. means another thread got the write lock while you were waiting, so the
  2308. resource protected by the MREWS object should be considered modified.
  2309. Any samples of the protected resource should be discarded.
  2310. In general, it's better to just always reacquire samples of the protected
  2311. resource after obtaining a write lock. The boolean result of BeginWrite
  2312. and the RevisionLevel property help cases where reacquiring the samples
  2313. is computationally expensive or time consuming.
  2314. RevisionLevel changes each time a write lock is granted. You can test
  2315. RevisionLevel for equality with a previously sampled value of the property
  2316. to determine if a write lock has been granted, implying that the protected
  2317. resource may be changed from its state when the original RevisionLevel
  2318. value was sampled. Do not rely on the sequentiality of the current
  2319. RevisionLevel implementation (it will wrap around to zero when it tops out).
  2320. Do not perform greater than / less than comparisons on RevisionLevel values.
  2321. RevisionLevel indicates only the stability of the protected resource since
  2322. your original sample. It should not be used to calculate how many
  2323. revisions have been made.
  2324. }
  2325. type
  2326. TMultiReadExclusiveWriteSynchronizer = class(TInterfacedObject, IReadWriteSync)
  2327. private
  2328. FSentinel: Integer;
  2329. FReadSignal: THandle;
  2330. FWriteSignal: THandle;
  2331. FWaitRecycle: Cardinal;
  2332. FWriteRecursionCount: Cardinal;
  2333. tls: TThreadLocalCounter;
  2334. FWriterID: Cardinal;
  2335. FRevisionLevel: Cardinal;
  2336. procedure BlockReaders;
  2337. procedure UnblockReaders;
  2338. procedure UnblockOneWriter;
  2339. procedure WaitForReadSignal;
  2340. procedure WaitForWriteSignal;
  2341. {$IFDEF DEBUG_MREWS}
  2342. procedure Debug(const Msg: string);
  2343. {$ENDIF}
  2344. public
  2345. constructor Create;
  2346. destructor Destroy; override;
  2347. procedure BeginRead;
  2348. procedure EndRead;
  2349. function BeginWrite: Boolean;
  2350. procedure EndWrite;
  2351. property RevisionLevel: Cardinal read FRevisionLevel;
  2352. end;
  2353. {$ELSE}
  2354. type
  2355. TMultiReadExclusiveWriteSynchronizer = TSimpleRWSync;
  2356. {$ENDIF}
  2357. type
  2358. TMREWSync = TMultiReadExclusiveWriteSynchronizer; // short form
  2359. function GetEnvironmentVariable(const Name: string): string; overload;
  2360. {$IFDEF LINUX}
  2361. function InterlockedIncrement(var I: Integer): Integer;
  2362. function InterlockedDecrement(var I: Integer): Integer;
  2363. function InterlockedExchange(var A: Integer; B: Integer): Integer;
  2364. function InterlockedExchangeAdd(var A: Integer; B: Integer): Integer;
  2365. {$ENDIF}
  2366. implementation
  2367. {$IFDEF LINUX}
  2368. {
  2369. Exceptions raised in methods that are safecall will be filtered
  2370. through the virtual method SafeCallException on the class. The
  2371. implementation of this method under Linux has the option of setting
  2372. the following thread vars: SafeCallExceptionMsg, SafeCallExceptionAddr.
  2373. If these are set, then the implementation of SafeCallError here will
  2374. reraise a generic exception based on these. One might consider actually
  2375. having the SafeCallException implementation store off the exception
  2376. object itself, but this raises the issue that the exception object
  2377. might have to live a long time (if an external application calls a
  2378. Delphi safecall method). Since an arbitrary exception object could
  2379. be holding large resources hostage, we hold only the string and
  2380. address as a hedge.
  2381. }
  2382. threadvar
  2383. SafeCallExceptionMsg: String;
  2384. SafeCallExceptionAddr: Pointer;
  2385. procedure SetSafeCallExceptionMsg(const Msg: String);
  2386. begin
  2387. SafeCallExceptionMsg := Msg;
  2388. end;
  2389. procedure SetSafeCallExceptionAddr(Addr: Pointer);
  2390. begin
  2391. SafeCallExceptionAddr := Addr;
  2392. end;
  2393. function GetSafeCallExceptionMsg: String;
  2394. begin
  2395. Result := SafeCallExceptionMsg;
  2396. end;
  2397. function GetSafeCallExceptionAddr: Pointer;
  2398. begin
  2399. Result := SafeCallExceptionAddr;
  2400. end;
  2401. {$ENDIF}
  2402. { Utility routines }
  2403. procedure DivMod(Dividend: Integer; Divisor: Word;
  2404. var Result, Remainder: Word);
  2405. asm
  2406. PUSH EBX
  2407. MOV EBX,EDX
  2408. MOV EDX,EAX
  2409. SHR EDX,16
  2410. DIV BX
  2411. MOV EBX,Remainder
  2412. MOV [ECX],AX
  2413. MOV [EBX],DX
  2414. POP EBX
  2415. end;
  2416. {$IFDEF PIC}
  2417. function GetGOT: Pointer; export;
  2418. begin
  2419. asm
  2420. MOV Result,EBX
  2421. end;
  2422. end;
  2423. {$ENDIF}
  2424. procedure ConvertError(const ResString: string); local;
  2425. begin
  2426. raise EConvertError.Create(ResString);
  2427. end;
  2428. procedure ConvertErrorFmt(const ResString: string; const Args: array of const); local;
  2429. begin
  2430. raise EConvertError.CreateFmt(ResString, Args);
  2431. end;
  2432. {$IFDEF MSWINDOWS}
  2433. {$EXTERNALSYM CoCreateGuid}
  2434. function CoCreateGuid(out guid: TGUID): HResult; stdcall; external 'ole32.dll' name 'CoCreateGuid';
  2435. function CreateGUID(out Guid: TGUID): HResult;
  2436. begin
  2437. Result := CoCreateGuid(Guid);
  2438. end;
  2439. //function CreateGUID; external 'ole32.dll' name 'CoCreateGuid';
  2440. {$ENDIF}
  2441. {$IFDEF LINUX}
  2442. { CreateGUID }
  2443. { libuuid.so implements the tricky code to create GUIDs using the
  2444. MAC address of the network adapter plus other flavor bits.
  2445. libuuid.so is currently distributed with the ext2 file system
  2446. package, but does not depend upon the ext2 file system libraries.
  2447. Ideally, libuuid.so should be distributed separately.
  2448. If you do not have libuuid.so.1 on your Linux distribution, you
  2449. can extract the library from the e2fsprogs RPM.
  2450. Note: Do not use the generic uuid_generate function in libuuid.so.
  2451. In the current implementation (e2fsprogs-1.19), uuid_generate
  2452. gives preference to generating guids entirely from random number
  2453. streams over generating guids based on the NIC MAC address.
  2454. No matter how "random" a random number generator is, it will
  2455. never produce guids that can be guaranteed unique across all
  2456. systems on the planet. MAC-address based guids are guaranteed
  2457. unique because the MAC address of the NIC is guaranteed unique
  2458. by the manufacturer.
  2459. For this reason, we call uuid_generate_time instead of the
  2460. generic uuid_generate. uuid_generate_time constructs the guid
  2461. using the MAC address, and falls back to randomness if no NIC
  2462. can be found. }
  2463. var
  2464. libuuidHandle: Pointer;
  2465. uuid_generate_time: procedure (out Guid: TGUID) cdecl;
  2466. function CreateGUID(out Guid: TGUID): HResult;
  2467. const
  2468. E_NOTIMPL = HRESULT($80004001);
  2469. begin
  2470. Result := E_NOTIMPL;
  2471. if libuuidHandle = nil then
  2472. begin
  2473. libuuidHandle := dlopen('libuuid.so.1', RTLD_LAZY);
  2474. if libuuidHandle = nil then Exit;
  2475. uuid_generate_time := dlsym(libuuidHandle, 'uuid_generate_time');
  2476. if @uuid_generate_time = nil then Exit;
  2477. end;
  2478. uuid_generate_time(Guid);
  2479. Result := 0;
  2480. end;
  2481. {$ENDIF}
  2482. {$IFDEF MSWINDOWS}
  2483. function StringFromCLSID(const clsid: TGUID; out psz: PWideChar): HResult; stdcall;
  2484. external 'ole32.dll' name 'StringFromCLSID';
  2485. procedure CoTaskMemFree(pv: Pointer); stdcall;
  2486. external 'ole32.dll' name 'CoTaskMemFree';
  2487. function CLSIDFromString(psz: PWideChar; out clsid: TGUID): HResult; stdcall;
  2488. external 'ole32.dll' name 'CLSIDFromString';
  2489. {$ENDIF MSWINDOWS}
  2490. function StringToGUID(const S: string): TGUID;
  2491. {$IFDEF MSWINDOWS}
  2492. begin
  2493. if not Succeeded(CLSIDFromString(PWideChar(WideString(S)), Result)) then
  2494. ConvertErrorFmt(SInvalidGUID, [s]);
  2495. end;
  2496. {$ENDIF}
  2497. {$IFDEF LINUX}
  2498. procedure InvalidGUID;
  2499. begin
  2500. ConvertErrorFmt(@SInvalidGUID, [s]);
  2501. end;
  2502. function HexChar(c: Char): Byte;
  2503. begin
  2504. case c of
  2505. '0'..'9': Result := Byte(c) - Byte('0');
  2506. 'a'..'f': Result := (Byte(c) - Byte('a')) + 10;
  2507. 'A'..'F': Result := (Byte(c) - Byte('A')) + 10;
  2508. else
  2509. InvalidGUID;
  2510. Result := 0;
  2511. end;
  2512. end;
  2513. function HexByte(p: PChar): Char;
  2514. begin
  2515. Result := Char((HexChar(p[0]) shl 4) + HexChar(p[1]));
  2516. end;
  2517. var
  2518. i: Integer;
  2519. src, dest: PChar;
  2520. begin
  2521. if ((Length(S) <> 38) or (s[1] <> '{')) then InvalidGUID;
  2522. dest := @Result;
  2523. src := PChar(s);
  2524. Inc(src);
  2525. for i := 0 to 3 do
  2526. dest[i] := HexByte(src+(3-i)*2);
  2527. Inc(src, 8);
  2528. Inc(dest, 4);
  2529. if src[0] <> '-' then InvalidGUID;
  2530. Inc(src);
  2531. for i := 0 to 1 do
  2532. begin
  2533. dest^ := HexByte(src+2);
  2534. Inc(dest);
  2535. dest^ := HexByte(src);
  2536. Inc(dest);
  2537. Inc(src, 4);
  2538. if src[0] <> '-' then InvalidGUID;
  2539. inc(src);
  2540. end;
  2541. dest^ := HexByte(src);
  2542. Inc(dest);
  2543. Inc(src, 2);
  2544. dest^ := HexByte(src);
  2545. Inc(dest);
  2546. Inc(src, 2);
  2547. if src[0] <> '-' then InvalidGUID;
  2548. Inc(src);
  2549. for i := 0 to 5 do
  2550. begin
  2551. dest^ := HexByte(src);
  2552. Inc(dest);
  2553. Inc(src, 2);
  2554. end;
  2555. end;
  2556. {$ENDIF LINUX}
  2557. {$IFDEF MSWINDOWS}
  2558. function GUIDToString(const GUID: TGUID): string;
  2559. var
  2560. P: PWideChar;
  2561. begin
  2562. if not Succeeded(StringFromCLSID(GUID, P)) then
  2563. ConvertError(SInvalidGUID);
  2564. Result := P;
  2565. CoTaskMemFree(P);
  2566. end;
  2567. {$ENDIF}
  2568. {$IFDEF LINUX}
  2569. function GUIDToString(const GUID: TGUID): string;
  2570. begin
  2571. SetLength(Result, 38);
  2572. StrLFmt(PChar(Result), 38,'{%.8x-%.4x-%.4x-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x}', // do not localize
  2573. [GUID.D1, GUID.D2, GUID.D3, GUID.D4[0], GUID.D4[1], GUID.D4[2], GUID.D4[3],
  2574. GUID.D4[4], GUID.D4[5], GUID.D4[6], GUID.D4[7]]);
  2575. end;
  2576. {$ENDIF}
  2577. {$IFDEF MSWINDOWS}
  2578. function IsEqualGUID; external 'ole32.dll' name 'IsEqualGUID';
  2579. {$ENDIF MSWINDOWS}
  2580. {$IFDEF LINUX}
  2581. function IsEqualGUID(const guid1, guid2: TGUID): Boolean;
  2582. var
  2583. a, b: PIntegerArray;
  2584. begin
  2585. a := PIntegerArray(@guid1);
  2586. b := PIntegerArray(@guid2);
  2587. Result := (a^[0] = b^[0]) and (a^[1] = b^[1]) and (a^[2] = b^[2]) and (a^[3] = b^[3]);
  2588. end;
  2589. {$ENDIF LINUX}
  2590. { Memory management routines }
  2591. function AllocMem(Size: Cardinal): Pointer;
  2592. begin
  2593. GetMem(Result, Size);
  2594. FillChar(Result^, Size, 0);
  2595. end;
  2596. { Exit procedure handling }
  2597. type
  2598. PExitProcInfo = ^TExitProcInfo;
  2599. TExitProcInfo = record
  2600. Next: PExitProcInfo;
  2601. SaveExit: Pointer;
  2602. Proc: TProcedure;
  2603. end;
  2604. var
  2605. ExitProcList: PExitProcInfo = nil;
  2606. procedure DoExitProc;
  2607. var
  2608. P: PExitProcInfo;
  2609. Proc: TProcedure;
  2610. begin
  2611. P := ExitProcList;
  2612. ExitProcList := P^.Next;
  2613. ExitProc := P^.SaveExit;
  2614. Proc := P^.Proc;
  2615. Dispose(P);
  2616. Proc;
  2617. end;
  2618. procedure AddExitProc(Proc: TProcedure);
  2619. var
  2620. P: PExitProcInfo;
  2621. begin
  2622. New(P);
  2623. P^.Next := ExitProcList;
  2624. P^.SaveExit := ExitProc;
  2625. P^.Proc := Proc;
  2626. ExitProcList := P;
  2627. ExitProc := @DoExitProc;
  2628. end;
  2629. { String handling routines }
  2630. function NewStr(const S: string): PString;
  2631. begin
  2632. if S = '' then Result := NullStr else
  2633. begin
  2634. New(Result);
  2635. Result^ := S;
  2636. end;
  2637. end;
  2638. procedure DisposeStr(P: PString);
  2639. begin
  2640. if (P <> nil) and (P^ <> '') then Dispose(P);
  2641. end;
  2642. procedure AssignStr(var P: PString; const S: string);
  2643. var
  2644. Temp: PString;
  2645. begin
  2646. Temp := P;
  2647. P := NewStr(S);
  2648. DisposeStr(Temp);
  2649. end;
  2650. procedure AppendStr(var Dest: string; const S: string);
  2651. begin
  2652. Dest := Dest + S;
  2653. end;
  2654. function UpperCase(const S: string): string;
  2655. var
  2656. Ch: Char;
  2657. L: Integer;
  2658. Source, Dest: PChar;
  2659. begin
  2660. L := Length(S);
  2661. SetLength(Result, L);
  2662. Source := Pointer(S);
  2663. Dest := Pointer(Result);
  2664. while L <> 0 do
  2665. begin
  2666. Ch := Source^;
  2667. if (Ch >= 'a') and (Ch <= 'z') then Dec(Ch, 32);
  2668. Dest^ := Ch;
  2669. Inc(Source);
  2670. Inc(Dest);
  2671. Dec(L);
  2672. end;
  2673. end;
  2674. function UpperCase(const S: string; LocaleOptions: TLocaleOptions): string;
  2675. begin
  2676. if LocaleOptions = loUserLocale then
  2677. Result := AnsiUpperCase(S)
  2678. else
  2679. Result := UpperCase(S);
  2680. end;
  2681. function LowerCase(const S: string): string;
  2682. var
  2683. Ch: Char;
  2684. L: Integer;
  2685. Source, Dest: PChar;
  2686. begin
  2687. L := Length(S);
  2688. SetLength(Result, L);
  2689. Source := Pointer(S);
  2690. Dest := Pointer(Result);
  2691. while L <> 0 do
  2692. begin
  2693. Ch := Source^;
  2694. if (Ch >= 'A') and (Ch <= 'Z') then Inc(Ch, 32);
  2695. Dest^ := Ch;
  2696. Inc(Source);
  2697. Inc(Dest);
  2698. Dec(L);
  2699. end;
  2700. end;
  2701. function LowerCase(const S: string; LocaleOptions: TLocaleOptions): string;
  2702. begin
  2703. if LocaleOptions = loUserLocale then
  2704. Result := AnsiLowerCase(S)
  2705. else
  2706. Result := LowerCase(S);
  2707. end;
  2708. function CompareStr(const S1, S2: string): Integer; assembler;
  2709. asm
  2710. PUSH ESI
  2711. PUSH EDI
  2712. MOV ESI,EAX
  2713. MOV EDI,EDX
  2714. OR EAX,EAX
  2715. JE @@1
  2716. MOV EAX,[EAX-4]
  2717. @@1: OR EDX,EDX
  2718. JE @@2
  2719. MOV EDX,[EDX-4]
  2720. @@2: MOV ECX,EAX
  2721. CMP ECX,EDX
  2722. JBE @@3
  2723. MOV ECX,EDX
  2724. @@3: CMP ECX,ECX
  2725. REPE CMPSB
  2726. JE @@4
  2727. MOVZX EAX,BYTE PTR [ESI-1]
  2728. MOVZX EDX,BYTE PTR [EDI-1]
  2729. @@4: SUB EAX,EDX
  2730. POP EDI
  2731. POP ESI
  2732. end;
  2733. function CompareStr(const S1, S2: string; LocaleOptions: TLocaleOptions): Integer;
  2734. begin
  2735. if LocaleOptions = loUserLocale then
  2736. Result := AnsiCompareStr(S1, S2)
  2737. else
  2738. Result := CompareStr(S1, S2);
  2739. end;
  2740. function SameStr(const S1, S2: string): Boolean;
  2741. asm
  2742. CMP EAX,EDX
  2743. JZ @1
  2744. OR EAX,EAX
  2745. JZ @2
  2746. OR EDX,EDX
  2747. JZ @3
  2748. MOV ECX,[EAX-4]
  2749. CMP ECX,[EDX-4]
  2750. JNE @3
  2751. CALL CompareStr
  2752. TEST EAX,EAX
  2753. JNZ @3
  2754. @1: MOV AL,1
  2755. @2: RET
  2756. @3: XOR EAX,EAX
  2757. end;
  2758. function SameStr(const S1, S2: string; LocaleOptions: TLocaleOptions): Boolean;
  2759. begin
  2760. if LocaleOptions = loUserLocale then
  2761. Result := AnsiSameStr(S1, S2)
  2762. else
  2763. Result := SameStr(S1, S2);
  2764. end;
  2765. function CompareMem(P1, P2: Pointer; Length: Integer): Boolean; assembler;
  2766. asm
  2767. PUSH ESI
  2768. PUSH EDI
  2769. MOV ESI,P1
  2770. MOV EDI,P2
  2771. MOV EDX,ECX
  2772. XOR EAX,EAX
  2773. AND EDX,3
  2774. SAR ECX,2
  2775. JS @@1 // Negative Length implies identity.
  2776. REPE CMPSD
  2777. JNE @@2
  2778. MOV ECX,EDX
  2779. REPE CMPSB
  2780. JNE @@2
  2781. @@1: INC EAX
  2782. @@2: POP EDI
  2783. POP ESI
  2784. end;
  2785. (* ***** BEGIN LICENSE BLOCK *****
  2786. * Version: MPL 1.1
  2787. *
  2788. * The implementation of function CompareText is subject to the
  2789. * Mozilla Public License Version 1.1 (the "License"); you may
  2790. * not use this file except in compliance with the License.
  2791. * You may obtain a copy of the License at http://www.mozilla.org/MPL/
  2792. *
  2793. * Software distributed under the License is distributed on an "AS IS" basis,
  2794. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  2795. * for the specific language governing rights and limitations under the
  2796. * License.
  2797. *
  2798. * The Original Code is Fastcode
  2799. *
  2800. * The Initial Developer of the Original Code is
  2801. * Fastcode
  2802. *
  2803. * Portions created by the Initial Developer are Copyright (C) 2002-2004
  2804. * the Initial Developer. All Rights Reserved.
  2805. *
  2806. * Contributor(s): John O'Harrow
  2807. *
  2808. * ***** END LICENSE BLOCK ***** *)
  2809. function CompareText(const S1, S2: string): Integer;
  2810. asm
  2811. TEST EAX, EAX
  2812. JNZ @@CheckS2
  2813. TEST EDX, EDX
  2814. JZ @@Ret
  2815. MOV EAX, [EDX-4]
  2816. NEG EAX
  2817. @@Ret:
  2818. RET
  2819. @@CheckS2:
  2820. TEST EDX, EDX
  2821. JNZ @@Compare
  2822. MOV EAX, [EAX-4]
  2823. RET
  2824. @@Compare:
  2825. PUSH EBX
  2826. PUSH EBP
  2827. PUSH ESI
  2828. MOV EBP, [EAX-4] // length(S1)
  2829. MOV EBX, [EDX-4] // length(S2)
  2830. SUB EBP, EBX // Result if All Compared Characters Match
  2831. SBB ECX, ECX
  2832. AND ECX, EBP
  2833. ADD ECX, EBX // min(length(S1),length(S2)) = Compare Length
  2834. LEA ESI, [EAX+ECX] // Last Compare Position in S1
  2835. ADD EDX, ECX // Last Compare Position in S2
  2836. NEG ECX
  2837. JZ @@SetResult // Exit if Smallest Length = 0
  2838. @@Loop: // Load Next 2 Chars from S1 and S2
  2839. // May Include Null Terminator}
  2840. MOVZX EAX, WORD PTR [ESI+ECX]
  2841. MOVZX EBX, WORD PTR [EDX+ECX]
  2842. CMP EAX, EBX
  2843. JE @@Next // Next 2 Chars Match
  2844. CMP AL, BL
  2845. JE @@SecondPair // First Char Matches
  2846. MOV AH, 0
  2847. MOV BH, 0
  2848. CMP AL, 'a'
  2849. JL @@UC1
  2850. CMP AL, 'z'
  2851. JG @@UC1
  2852. SUB EAX, 'a'-'A'
  2853. @@UC1:
  2854. CMP BL, 'a'
  2855. JL @@UC2
  2856. CMP BL, 'z'
  2857. JG @@UC2
  2858. SUB EBX, 'a'-'A'
  2859. @@UC2:
  2860. SUB EAX, EBX // Compare Both Uppercase Chars
  2861. JNE @@Done // Exit with Result in EAX if Not Equal
  2862. MOVZX EAX, WORD PTR [ESI+ECX] // Reload Same 2 Chars from S1
  2863. MOVZX EBX, WORD PTR [EDX+ECX] // Reload Same 2 Chars from S2
  2864. CMP AH, BH
  2865. JE @@Next // Second Char Matches
  2866. @@SecondPair:
  2867. SHR EAX, 8
  2868. SHR EBX, 8
  2869. CMP AL, 'a'
  2870. JL @@UC3
  2871. CMP AL, 'z'
  2872. JG @@UC3
  2873. SUB EAX, 'a'-'A'
  2874. @@UC3:
  2875. CMP BL, 'a'
  2876. JL @@UC4
  2877. CMP BL, 'z'
  2878. JG @@UC4
  2879. SUB EBX, 'a'-'A'
  2880. @@UC4:
  2881. SUB EAX, EBX // Compare Both Uppercase Chars
  2882. JNE @@Done // Exit with Result in EAX if Not Equal
  2883. @@Next:
  2884. ADD ECX, 2
  2885. JL @@Loop // Loop until All required Chars Compared
  2886. @@SetResult:
  2887. MOV EAX, EBP // All Matched, Set Result from Lengths
  2888. @@Done:
  2889. POP ESI
  2890. POP EBP
  2891. POP EBX
  2892. end;
  2893. function CompareText(const S1, S2: string; LocaleOptions: TLocaleOptions): Integer;
  2894. begin
  2895. if LocaleOptions = loUserLocale then
  2896. Result := AnsiCompareText(S1, S2)
  2897. else
  2898. Result := CompareText(S1, S2);
  2899. end;
  2900. function SameText(const S1, S2: string): Boolean; assembler;
  2901. asm
  2902. CMP EAX,EDX
  2903. JZ @1
  2904. OR EAX,EAX
  2905. JZ @2
  2906. OR EDX,EDX
  2907. JZ @3
  2908. MOV ECX,[EAX-4]
  2909. CMP ECX,[EDX-4]
  2910. JNE @3
  2911. CALL CompareText
  2912. TEST EAX,EAX
  2913. JNZ @3
  2914. @1: MOV AL,1
  2915. @2: RET
  2916. @3: XOR EAX,EAX
  2917. end;
  2918. function SameText(const S1, S2: string; LocaleOptions: TLocaleOptions): Boolean;
  2919. begin
  2920. if LocaleOptions = loUserLocale then
  2921. Result := AnsiSameText(S1, S2)
  2922. else
  2923. Result := SameText(S1, S2);
  2924. end;
  2925. function AnsiUpperCase(const S: string): string;
  2926. {$IFDEF MSWINDOWS}
  2927. var
  2928. Len: Integer;
  2929. begin
  2930. Len := Length(S);
  2931. SetString(Result, PChar(S), Len);
  2932. if Len > 0 then CharUpperBuff(Pointer(Result), Len);
  2933. end;
  2934. {$ENDIF}
  2935. {$IFDEF LINUX}
  2936. begin
  2937. Result := WideUpperCase(S);
  2938. end;
  2939. {$ENDIF}
  2940. function AnsiLowerCase(const S: string): string;
  2941. {$IFDEF MSWINDOWS}
  2942. var
  2943. Len: Integer;
  2944. begin
  2945. Len := Length(S);
  2946. SetString(Result, PChar(S), Len);
  2947. if Len > 0 then CharLowerBuff(Pointer(Result), Len);
  2948. end;
  2949. {$ENDIF}
  2950. {$IFDEF LINUX}
  2951. begin
  2952. Result := WideLowerCase(S);
  2953. end;
  2954. {$ENDIF}
  2955. function AnsiCompareStr(const S1, S2: string): Integer;
  2956. begin
  2957. {$IFDEF MSWINDOWS}
  2958. Result := CompareString(LOCALE_USER_DEFAULT, 0, PChar(S1), Length(S1),
  2959. PChar(S2), Length(S2)) - 2;
  2960. {$ENDIF}
  2961. {$IFDEF LINUX}
  2962. // glibc 2.1.2 / 2.1.3 implementations of strcoll() and strxfrm()
  2963. // have severe capacity limits. Comparing two 100k strings may
  2964. // exhaust the stack and kill the process.
  2965. // Fixed in glibc 2.1.91 and later.
  2966. Result := strcoll(PChar(S1), PChar(S2));
  2967. {$ENDIF}
  2968. end;
  2969. function AnsiSameStr(const S1, S2: string): Boolean;
  2970. begin
  2971. Result := AnsiCompareStr(S1, S2) = 0;
  2972. end;
  2973. function AnsiCompareText(const S1, S2: string): Integer;
  2974. begin
  2975. {$IFDEF MSWINDOWS}
  2976. Result := CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, PChar(S1),
  2977. Length(S1), PChar(S2), Length(S2)) - 2;
  2978. {$ENDIF}
  2979. {$IFDEF LINUX}
  2980. Result := WideCompareText(S1, S2);
  2981. {$ENDIF}
  2982. end;
  2983. function AnsiSameText(const S1, S2: string): Boolean;
  2984. begin
  2985. Result := AnsiCompareText(S1, S2) = 0;
  2986. end;
  2987. function AnsiStrComp(S1, S2: PChar): Integer;
  2988. begin
  2989. {$IFDEF MSWINDOWS}
  2990. Result := CompareString(LOCALE_USER_DEFAULT, 0, S1, -1, S2, -1) - 2;
  2991. {$ENDIF}
  2992. {$IFDEF LINUX}
  2993. Result := strcoll(S1, S2);
  2994. {$ENDIF}
  2995. end;
  2996. function AnsiStrIComp(S1, S2: PChar): Integer;
  2997. begin
  2998. {$IFDEF MSWINDOWS}
  2999. Result := CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, S1, -1,
  3000. S2, -1) - 2;
  3001. {$ENDIF}
  3002. {$IFDEF LINUX}
  3003. Result := AnsiCompareText(S1, S2);
  3004. {$ENDIF}
  3005. end;
  3006. // StrLenLimit: Scan Src for a null terminator up to MaxLen bytes
  3007. function StrLenLimit(Src: PChar; MaxLen: Cardinal): Cardinal;
  3008. begin
  3009. if Src = nil then
  3010. begin
  3011. Result := 0;
  3012. Exit;
  3013. end;
  3014. Result := MaxLen;
  3015. while (Src^ <> #0) and (Result > 0) do
  3016. begin
  3017. Inc(Src);
  3018. Dec(Result);
  3019. end;
  3020. Result := MaxLen - Result;
  3021. end;
  3022. { StrBufLimit: Return a pointer to a buffer that contains no more than MaxLen
  3023. bytes of Src, avoiding heap allocation if possible.
  3024. If clipped Src length is less than MaxLen, return Src. Allocated = False.
  3025. If clipped Src length is less than StaticBufLen, return StaticBuf with a
  3026. copy of Src. Allocated = False.
  3027. Otherwise, return a heap allocated buffer with a copy of Src. Allocated = True.
  3028. }
  3029. function StrBufLimit(Src: PChar; MaxLen: Cardinal; StaticBuf: PChar;
  3030. StaticBufLen: Cardinal; var Allocated: Boolean): PChar;
  3031. var
  3032. Len: Cardinal;
  3033. begin
  3034. Len := StrLenLimit(Src, MaxLen);
  3035. Allocated := False;
  3036. if Len < MaxLen then
  3037. Result := Src
  3038. else
  3039. begin
  3040. if Len < StaticBufLen then
  3041. Result := StaticBuf
  3042. else
  3043. begin
  3044. GetMem(Result, Len+1);
  3045. Allocated := True;
  3046. end;
  3047. Move(Src^, Result^, Len);
  3048. Result[Len] := #0;
  3049. end;
  3050. end;
  3051. function InternalAnsiStrLComp(S1, S2: PChar; MaxLen: Cardinal; CaseSensitive: Boolean): Integer;
  3052. var
  3053. Buf1, Buf2: array [0..4095] of Char;
  3054. P1, P2: PChar;
  3055. Allocated1, Allocated2: Boolean;
  3056. begin
  3057. // glibc has no length-limited strcoll!
  3058. P1 := nil;
  3059. P2 := nil;
  3060. Allocated1 := False;
  3061. Allocated2 := False;
  3062. try
  3063. P1 := StrBufLimit(S1, MaxLen, Buf1, High(Buf1), Allocated1);
  3064. P2 := StrBufLimit(S2, MaxLen, Buf2, High(Buf2), Allocated2);
  3065. if CaseSensitive then
  3066. Result := AnsiStrComp(P1, P2)
  3067. else
  3068. Result := AnsiStrIComp(P1, P2);
  3069. finally
  3070. if Allocated1 then
  3071. FreeMem(P1);
  3072. if Allocated2 then
  3073. FreeMem(P2);
  3074. end;
  3075. end;
  3076. function AnsiStrLComp(S1, S2: PChar; MaxLen: Cardinal): Integer;
  3077. {$IFDEF MSWINDOWS}
  3078. begin
  3079. Result := CompareString(LOCALE_USER_DEFAULT, 0,
  3080. S1, MaxLen, S2, MaxLen) - 2;
  3081. end;
  3082. {$ENDIF}
  3083. {$IFDEF LINUX}
  3084. begin
  3085. Result := InternalAnsiStrLComp(S1, S2, MaxLen, True);
  3086. end;
  3087. {$ENDIF}
  3088. function AnsiStrLIComp(S1, S2: PChar; MaxLen: Cardinal): Integer;
  3089. begin
  3090. {$IFDEF MSWINDOWS}
  3091. Result := CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE,
  3092. S1, MaxLen, S2, MaxLen) - 2;
  3093. {$ENDIF}
  3094. {$IFDEF LINUX}
  3095. Result := InternalAnsiStrLComp(S1, S2, MaxLen, False);
  3096. {$ENDIF}
  3097. end;
  3098. function AnsiStrLower(Str: PChar): PChar;
  3099. {$IFDEF MSWINDOWS}
  3100. begin
  3101. CharLower(Str);
  3102. Result := Str;
  3103. end;
  3104. {$ENDIF}
  3105. {$IFDEF LINUX}
  3106. var
  3107. Temp: WideString;
  3108. Squish: AnsiString;
  3109. I: Integer;
  3110. begin
  3111. Temp := Str; // expand and copy multibyte to widechar
  3112. for I := 1 to Length(Temp) do
  3113. Temp[I] := WideChar(towlower(UCS4Char(Temp[I])));
  3114. Squish := Temp; // reduce and copy widechar to multibyte
  3115. if Cardinal(Length(Squish)) > StrLen(Str) then
  3116. raise ERangeError.CreateRes(@SRangeError);
  3117. Move(Squish[1], Str^, Length(Squish));
  3118. Result := Str;
  3119. end;
  3120. {$ENDIF}
  3121. function AnsiStrUpper(Str: PChar): PChar;
  3122. {$IFDEF MSWINDOWS}
  3123. begin
  3124. CharUpper(Str);
  3125. Result := Str;
  3126. end;
  3127. {$ENDIF}
  3128. {$IFDEF LINUX}
  3129. var
  3130. Temp: WideString;
  3131. Squish: AnsiString;
  3132. I: Integer;
  3133. begin
  3134. Temp := Str; // expand and copy multibyte to widechar
  3135. for I := 1 to Length(Temp) do
  3136. Temp[I] := WideChar(towupper(UCS4Char(Temp[I])));
  3137. Squish := Temp; // reduce and copy widechar to multibyte
  3138. if Cardinal(Length(Squish)) > StrLen(Str) then
  3139. raise ERangeError.CreateRes(@SRangeError);
  3140. Move(Squish[1], Str^, Length(Squish));
  3141. Result := Str;
  3142. end;
  3143. {$ENDIF}
  3144. function WideUpperCase(const S: WideString): WideString;
  3145. {$IFDEF MSWINDOWS}
  3146. var
  3147. Len: Integer;
  3148. begin
  3149. Len := Length(S);
  3150. SetString(Result, PWideChar(S), Len);
  3151. if Len > 0 then CharUpperBuffW(Pointer(Result), Len);
  3152. end;
  3153. {$ENDIF}
  3154. {$IFDEF LINUX}
  3155. var
  3156. I: Integer;
  3157. P: PWideChar;
  3158. begin
  3159. SetLength(Result, Length(S));
  3160. P := @Result[1];
  3161. for I := 1 to Length(S) do
  3162. P[I-1] := WideChar(towupper(UCS4Char(S[I])));
  3163. end;
  3164. {$ENDIF}
  3165. function WideLowerCase(const S: WideString): WideString;
  3166. {$IFDEF MSWINDOWS}
  3167. var
  3168. Len: Integer;
  3169. begin
  3170. Len := Length(S);
  3171. SetString(Result, PWideChar(S), Len);
  3172. if Len > 0 then CharLowerBuffW(Pointer(Result), Len);
  3173. end;
  3174. {$ENDIF}
  3175. {$IFDEF LINUX}
  3176. var
  3177. I: Integer;
  3178. P: PWideChar;
  3179. begin
  3180. SetLength(Result, Length(S));
  3181. P := @Result[1];
  3182. for I := 1 to Length(S) do
  3183. P[I-1] := WideChar(towlower(UCS4Char(S[I])));
  3184. end;
  3185. {$ENDIF}
  3186. {$IFDEF MSWINDOWS}
  3187. function DumbItDownFor95(const S1, S2: WideString; CmpFlags: Integer): Integer;
  3188. var
  3189. a1, a2: AnsiString;
  3190. begin
  3191. a1 := s1;
  3192. a2 := s2;
  3193. Result := CompareStringA(LOCALE_USER_DEFAULT, CmpFlags, PChar(a1), Length(a1),
  3194. PChar(a2), Length(a2)) - 2;
  3195. end;
  3196. {$ENDIF}
  3197. function WideCompareStr(const S1, S2: WideString): Integer;
  3198. {$IFDEF MSWINDOWS}
  3199. begin
  3200. SetLastError(0);
  3201. Result := CompareStringW(LOCALE_USER_DEFAULT, 0, PWideChar(S1), Length(S1),
  3202. PWideChar(S2), Length(S2)) - 2;
  3203. case GetLastError of
  3204. 0: ;
  3205. ERROR_CALL_NOT_IMPLEMENTED: Result := DumbItDownFor95(S1, S2, 0);
  3206. else
  3207. RaiseLastOSError;
  3208. end;
  3209. end;
  3210. {$ENDIF}
  3211. {$IFDEF LINUX}
  3212. var
  3213. UCS4_S1, UCS4_S2: UCS4String;
  3214. begin
  3215. UCS4_S1 := WideStringToUCS4String(S1);
  3216. UCS4_S2 := WideStringToUCS4String(S2);
  3217. // glibc 2.1.2 / 2.1.3 implementations of wcscoll() and wcsxfrm()
  3218. // have severe capacity limits. Comparing two 100k strings may
  3219. // exhaust the stack and kill the process.
  3220. // Fixed in glibc 2.1.91 and later.
  3221. SetLastError(0);
  3222. Result := wcscoll(PUCS4Chars(UCS4_S1), PUCS4Chars(UCS4_S2));
  3223. if GetLastError <> 0 then
  3224. RaiseLastOSError;
  3225. end;
  3226. {$ENDIF}
  3227. function WideSameStr(const S1, S2: WideString): Boolean;
  3228. begin
  3229. Result := WideCompareStr(S1, S2) = 0;
  3230. end;
  3231. function WideCompareText(const S1, S2: WideString): Integer;
  3232. begin
  3233. {$IFDEF MSWINDOWS}
  3234. SetLastError(0);
  3235. Result := CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, PWideChar(S1),
  3236. Length(S1), PWideChar(S2), Length(S2)) - 2;
  3237. case GetLastError of
  3238. 0: ;
  3239. ERROR_CALL_NOT_IMPLEMENTED: Result := DumbItDownFor95(S1, S2, NORM_IGNORECASE);
  3240. else
  3241. RaiseLastOSError;
  3242. end;
  3243. {$ENDIF}
  3244. {$IFDEF LINUX}
  3245. Result := WideCompareStr(WideUpperCase(S1), WideUpperCase(S2));
  3246. {$ENDIF}
  3247. end;
  3248. function WideSameText(const S1, S2: WideString): Boolean;
  3249. begin
  3250. Result := WideCompareText(S1, S2) = 0;
  3251. end;
  3252. function Trim(const S: string): string;
  3253. var
  3254. I, L: Integer;
  3255. begin
  3256. L := Length(S);
  3257. I := 1;
  3258. while (I <= L) and (S[I] <= ' ') do Inc(I);
  3259. if I > L then Result := '' else
  3260. begin
  3261. while S[L] <= ' ' do Dec(L);
  3262. Result := Copy(S, I, L - I + 1);
  3263. end;
  3264. end;
  3265. function Trim(const S: WideString): WideString;
  3266. var
  3267. I, L: Integer;
  3268. begin
  3269. L := Length(S);
  3270. I := 1;
  3271. while (I <= L) and (S[I] <= ' ') do Inc(I);
  3272. if I > L then
  3273. Result := ''
  3274. else
  3275. begin
  3276. while S[L] <= ' ' do Dec(L);
  3277. Result := Copy(S, I, L - I + 1);
  3278. end;
  3279. end;
  3280. function TrimLeft(const S: string): string;
  3281. var
  3282. I, L: Integer;
  3283. begin
  3284. L := Length(S);
  3285. I := 1;
  3286. while (I <= L) and (S[I] <= ' ') do Inc(I);
  3287. Result := Copy(S, I, Maxint);
  3288. end;
  3289. function TrimLeft(const S: WideString): WideString;
  3290. var
  3291. I, L: Integer;
  3292. begin
  3293. L := Length(S);
  3294. I := 1;
  3295. while (I <= L) and (S[I] <= ' ') do Inc(I);
  3296. Result := Copy(S, I, Maxint);
  3297. end;
  3298. function TrimRight(const S: string): string;
  3299. var
  3300. I: Integer;
  3301. begin
  3302. I := Length(S);
  3303. while (I > 0) and (S[I] <= ' ') do Dec(I);
  3304. Result := Copy(S, 1, I);
  3305. end;
  3306. function TrimRight(const S: WideString): WideString;
  3307. var
  3308. I: Integer;
  3309. begin
  3310. I := Length(S);
  3311. while (I > 0) and (S[I] <= ' ') do Dec(I);
  3312. Result := Copy(S, 1, I);
  3313. end;
  3314. function QuotedStr(const S: string): string;
  3315. var
  3316. I: Integer;
  3317. begin
  3318. Result := S;
  3319. for I := Length(Result) downto 1 do
  3320. if Result[I] = '''' then Insert('''', Result, I);
  3321. Result := '''' + Result + '''';
  3322. end;
  3323. function AnsiQuotedStr(const S: string; Quote: Char): string;
  3324. var
  3325. P, Src, Dest: PChar;
  3326. AddCount: Integer;
  3327. begin
  3328. AddCount := 0;
  3329. P := AnsiStrScan(PChar(S), Quote);
  3330. while P <> nil do
  3331. begin
  3332. Inc(P);
  3333. Inc(AddCount);
  3334. P := AnsiStrScan(P, Quote);
  3335. end;
  3336. if AddCount = 0 then
  3337. begin
  3338. Result := Quote + S + Quote;
  3339. Exit;
  3340. end;
  3341. SetLength(Result, Length(S) + AddCount + 2);
  3342. Dest := Pointer(Result);
  3343. Dest^ := Quote;
  3344. Inc(Dest);
  3345. Src := Pointer(S);
  3346. P := AnsiStrScan(Src, Quote);
  3347. repeat
  3348. Inc(P);
  3349. Move(Src^, Dest^, P - Src);
  3350. Inc(Dest, P - Src);
  3351. Dest^ := Quote;
  3352. Inc(Dest);
  3353. Src := P;
  3354. P := AnsiStrScan(Src, Quote);
  3355. until P = nil;
  3356. P := StrEnd(Src);
  3357. Move(Src^, Dest^, P - Src);
  3358. Inc(Dest, P - Src);
  3359. Dest^ := Quote;
  3360. end;
  3361. function AnsiExtractQuotedStr(var Src: PChar; Quote: Char): string;
  3362. var
  3363. P, Dest: PChar;
  3364. DropCount: Integer;
  3365. begin
  3366. Result := '';
  3367. if (Src = nil) or (Src^ <> Quote) then Exit;
  3368. Inc(Src);
  3369. DropCount := 1;
  3370. P := Src;
  3371. Src := AnsiStrScan(Src, Quote);
  3372. while Src <> nil do // count adjacent pairs of quote chars
  3373. begin
  3374. Inc(Src);
  3375. if Src^ <> Quote then Break;
  3376. Inc(Src);
  3377. Inc(DropCount);
  3378. Src := AnsiStrScan(Src, Quote);
  3379. end;
  3380. if Src = nil then Src := StrEnd(P);
  3381. if ((Src - P) <= 1) then Exit;
  3382. if DropCount = 1 then
  3383. SetString(Result, P, Src - P - 1)
  3384. else
  3385. begin
  3386. SetLength(Result, Src - P - DropCount);
  3387. Dest := PChar(Result);
  3388. Src := AnsiStrScan(P, Quote);
  3389. while Src <> nil do
  3390. begin
  3391. Inc(Src);
  3392. if Src^ <> Quote then Break;
  3393. Move(P^, Dest^, Src - P);
  3394. Inc(Dest, Src - P);
  3395. Inc(Src);
  3396. P := Src;
  3397. Src := AnsiStrScan(Src, Quote);
  3398. end;
  3399. if Src = nil then Src := StrEnd(P);
  3400. Move(P^, Dest^, Src - P - 1);
  3401. end;
  3402. end;
  3403. function AnsiDequotedStr(const S: string; AQuote: Char): string;
  3404. var
  3405. LText: PChar;
  3406. begin
  3407. LText := PChar(S);
  3408. Result := AnsiExtractQuotedStr(LText, AQuote);
  3409. if Result = '' then
  3410. Result := S;
  3411. end;
  3412. function AdjustLineBreaks(const S: string; Style: TTextLineBreakStyle): string;
  3413. var
  3414. Source, SourceEnd, Dest: PChar;
  3415. DestLen: Integer;
  3416. L: Integer;
  3417. begin
  3418. Source := Pointer(S);
  3419. SourceEnd := Source + Length(S);
  3420. DestLen := Length(S);
  3421. while Source < SourceEnd do
  3422. begin
  3423. case Source^ of
  3424. #10:
  3425. if Style = tlbsCRLF then
  3426. Inc(DestLen);
  3427. #13:
  3428. if Style = tlbsCRLF then
  3429. if Source[1] = #10 then
  3430. Inc(Source)
  3431. else
  3432. Inc(DestLen)
  3433. else
  3434. if Source[1] = #10 then
  3435. Dec(DestLen);
  3436. else
  3437. if Source^ in LeadBytes then
  3438. begin
  3439. Source := StrNextChar(Source);
  3440. continue;
  3441. end;
  3442. end;
  3443. Inc(Source);
  3444. end;
  3445. if DestLen = Length(Source) then
  3446. Result := S
  3447. else
  3448. begin
  3449. Source := Pointer(S);
  3450. SetString(Result, nil, DestLen);
  3451. Dest := Pointer(Result);
  3452. while Source < SourceEnd do
  3453. case Source^ of
  3454. #10:
  3455. begin
  3456. if Style = tlbsCRLF then
  3457. begin
  3458. Dest^ := #13;
  3459. Inc(Dest);
  3460. end;
  3461. Dest^ := #10;
  3462. Inc(Dest);
  3463. Inc(Source);
  3464. end;
  3465. #13:
  3466. begin
  3467. if Style = tlbsCRLF then
  3468. begin
  3469. Dest^ := #13;
  3470. Inc(Dest);
  3471. end;
  3472. Dest^ := #10;
  3473. Inc(Dest);
  3474. Inc(Source);
  3475. if Source^ = #10 then Inc(Source);
  3476. end;
  3477. else
  3478. if Source^ in LeadBytes then
  3479. begin
  3480. L := StrCharLength(Source);
  3481. Move(Source^, Dest^, L);
  3482. Inc(Dest, L);
  3483. Inc(Source, L);
  3484. continue;
  3485. end;
  3486. Dest^ := Source^;
  3487. Inc(Dest);
  3488. Inc(Source);
  3489. end;
  3490. end;
  3491. end;
  3492. function IsValidIdent(const Ident: string; AllowDots: Boolean): Boolean;
  3493. const
  3494. Alpha = ['A'..'Z', 'a'..'z', '_'];
  3495. AlphaNumeric = Alpha + ['0'..'9'];
  3496. AlphaNumericDot = AlphaNumeric + ['.'];
  3497. var
  3498. I: Integer;
  3499. begin
  3500. Result := False;
  3501. if (Length(Ident) = 0) or not (Ident[1] in Alpha) then Exit;
  3502. if AllowDots then
  3503. for I := 2 to Length(Ident) do
  3504. begin
  3505. if not (Ident[I] in AlphaNumericDot) then Exit
  3506. end
  3507. else
  3508. for I := 2 to Length(Ident) do if not (Ident[I] in AlphaNumeric) then Exit;
  3509. Result := True;
  3510. end;
  3511. procedure CvtInt;
  3512. { IN:
  3513. EAX: The integer value to be converted to text
  3514. ESI: Ptr to the right-hand side of the output buffer: LEA ESI, StrBuf[16]
  3515. ECX: Base for conversion: 0 for signed decimal, 10 or 16 for unsigned
  3516. EDX: Precision: zero padded minimum field width
  3517. OUT:
  3518. ESI: Ptr to start of converted text (not start of buffer)
  3519. ECX: Length of converted text
  3520. }
  3521. asm
  3522. OR CL,CL
  3523. JNZ @CvtLoop
  3524. @C1: OR EAX,EAX
  3525. JNS @C2
  3526. NEG EAX
  3527. CALL @C2
  3528. MOV AL,'-'
  3529. INC ECX
  3530. DEC ESI
  3531. MOV [ESI],AL
  3532. RET
  3533. @C2: MOV ECX,10
  3534. @CvtLoop:
  3535. PUSH EDX
  3536. PUSH ESI
  3537. @D1: XOR EDX,EDX
  3538. DIV ECX
  3539. DEC ESI
  3540. ADD DL,'0'
  3541. CMP DL,'0'+10
  3542. JB @D2
  3543. ADD DL,('A'-'0')-10
  3544. @D2: MOV [ESI],DL
  3545. OR EAX,EAX
  3546. JNE @D1
  3547. POP ECX
  3548. POP EDX
  3549. SUB ECX,ESI
  3550. SUB EDX,ECX
  3551. JBE @D5
  3552. ADD ECX,EDX
  3553. MOV AL,'0'
  3554. SUB ESI,EDX
  3555. JMP @z
  3556. @zloop: MOV [ESI+EDX],AL
  3557. @z: DEC EDX
  3558. JNZ @zloop
  3559. MOV [ESI],AL
  3560. @D5:
  3561. end;
  3562. procedure CvtIntW;
  3563. { IN:
  3564. EAX: The integer value to be converted to text
  3565. ESI: Ptr to the right-hand side of the widechar output buffer: LEA ESI, WStrBuf[32]
  3566. ECX: Base for conversion: 0 for signed decimal, 10 or 16 for unsigned
  3567. EDX: Precision: zero padded minimum field width
  3568. OUT:
  3569. ESI: Ptr to start of converted widechar text (not start of buffer)
  3570. ECX: Character length of converted text
  3571. }
  3572. asm
  3573. OR CL,CL
  3574. JNZ @CvtLoop
  3575. @C1: OR EAX,EAX
  3576. JNS @C2
  3577. NEG EAX
  3578. CALL @C2
  3579. MOV AX,'-'
  3580. MOV [ESI-2],AX
  3581. SUB ESI, 2
  3582. INC ECX
  3583. RET
  3584. @C2: MOV ECX,10
  3585. @CvtLoop:
  3586. PUSH EDX
  3587. PUSH ESI
  3588. @D1: XOR EDX,EDX
  3589. DIV ECX
  3590. ADD DX,'0'
  3591. SUB ESI,2
  3592. CMP DX,'0'+10
  3593. JB @D2
  3594. ADD DX,('A'-'0')-10
  3595. @D2: MOV [ESI],DX
  3596. OR EAX,EAX
  3597. JNE @D1
  3598. POP ECX
  3599. POP EDX
  3600. SUB ECX,ESI
  3601. SHR ECX, 1
  3602. SUB EDX,ECX
  3603. JBE @D5
  3604. ADD ECX,EDX
  3605. SUB ESI,EDX
  3606. MOV AX,'0'
  3607. SUB ESI,EDX
  3608. JMP @z
  3609. @zloop: MOV [ESI+EDX*2],AX
  3610. @z: DEC EDX
  3611. JNZ @zloop
  3612. MOV [ESI],AX
  3613. @D5:
  3614. end;
  3615. function IntToStr(Value: Integer): string;
  3616. // FmtStr(Result, '%d', [Value]);
  3617. asm
  3618. PUSH ESI
  3619. MOV ESI, ESP
  3620. SUB ESP, 16
  3621. XOR ECX, ECX // base: 0 for signed decimal
  3622. PUSH EDX // result ptr
  3623. XOR EDX, EDX // zero filled field width: 0 for no leading zeros
  3624. CALL CvtInt
  3625. MOV EDX, ESI
  3626. POP EAX // result ptr
  3627. CALL System.@LStrFromPCharLen
  3628. ADD ESP, 16
  3629. POP ESI
  3630. end;
  3631. procedure CvtInt64W;
  3632. { IN:
  3633. EAX: Address of the int64 value to be converted to text
  3634. ESI: Ptr to the right-hand side of the widechar output buffer: LEA ESI, WStrBuf[32]
  3635. ECX: Base for conversion: 10 or 16
  3636. EDX: Precision: zero padded minimum field width
  3637. OUT:
  3638. ESI: Ptr to start of converted widechar text (not start of buffer)
  3639. ECX: Character length of converted text
  3640. }
  3641. asm
  3642. OR CL, CL
  3643. JNZ @start
  3644. MOV ECX, 10
  3645. TEST [EAX + 4], $80000000
  3646. JZ @start
  3647. PUSH [EAX + 4]
  3648. PUSH [EAX]
  3649. MOV EAX, ESP
  3650. NEG [ESP] // negate the value
  3651. ADC [ESP + 4],0
  3652. NEG [ESP + 4]
  3653. CALL @start
  3654. INC ECX
  3655. MOV [ESI-2].Word, '-'
  3656. SUB ESI, 2
  3657. ADD ESP, 8
  3658. JMP @done
  3659. @start:
  3660. PUSH ESI
  3661. SUB ESP, 4
  3662. FNSTCW [ESP+2].Word // save
  3663. FNSTCW [ESP].Word // scratch
  3664. OR [ESP].Word, $0F00 // trunc toward zero, full precision
  3665. FLDCW [ESP].Word
  3666. MOV [ESP].Word, CX
  3667. FLD1
  3668. TEST [EAX + 4], $80000000 // test for negative
  3669. JZ @ld1 // FPU doesn't understand unsigned ints
  3670. PUSH [EAX + 4] // copy value before modifying
  3671. PUSH [EAX]
  3672. AND [ESP + 4], $7FFFFFFF // clear the sign bit
  3673. PUSH $7FFFFFFF
  3674. PUSH $FFFFFFFF
  3675. FILD [ESP + 8].QWord // load value
  3676. FILD [ESP].QWord
  3677. FADD ST(0), ST(2) // Add 1. Produces unsigned $80000000 in ST(0)
  3678. FADDP ST(1), ST(0) // Add $80000000 to value to replace the sign bit
  3679. ADD ESP, 16
  3680. JMP @ld2
  3681. @ld1:
  3682. FILD [EAX].QWord // value
  3683. @ld2:
  3684. FILD [ESP].Word // base
  3685. FLD ST(1)
  3686. @loop:
  3687. SUB ESI, 2
  3688. FPREM // accumulator mod base
  3689. FISTP [ESI].Word
  3690. FDIV ST(1), ST(0) // accumulator := acumulator / base
  3691. MOV AX, [ESI].Word // overlap long division op with int ops
  3692. ADD AX, '0'
  3693. CMP AX, '0'+10
  3694. JB @store
  3695. ADD AX, ('A'-'0')-10
  3696. @store:
  3697. MOV [ESI].Word, AX
  3698. FLD ST(1) // copy accumulator
  3699. FCOM ST(3) // if accumulator >= 1.0 then loop
  3700. FSTSW AX
  3701. SAHF
  3702. JAE @loop
  3703. FLDCW [ESP+2].Word
  3704. ADD ESP,4
  3705. FFREE ST(3)
  3706. FFREE ST(2)
  3707. FFREE ST(1);
  3708. FFREE ST(0);
  3709. @zeropad:
  3710. POP ECX // original ESI
  3711. SUB ECX,ESI
  3712. SHR ECX, 1 // ECX = char length of converted string
  3713. OR EDX,EDX
  3714. JS @done
  3715. SUB EDX,ECX
  3716. JBE @done // output longer than field width = no pad
  3717. SUB ESI,EDX
  3718. MOV AX,'0'
  3719. SUB ESI,EDX
  3720. ADD ECX,EDX
  3721. JMP @z
  3722. @zloop: MOV [ESI+EDX*2].Word,AX
  3723. @z: DEC EDX
  3724. JNZ @zloop
  3725. MOV [ESI].Word,AX
  3726. @done:
  3727. end;
  3728. procedure CvtInt64;
  3729. { IN:
  3730. EAX: Address of the int64 value to be converted to text
  3731. ESI: Ptr to the right-hand side of the output buffer: LEA ESI, StrBuf[32]
  3732. ECX: Base for conversion: 0 for signed decimal, or 10 or 16 for unsigned
  3733. EDX: Precision: zero padded minimum field width
  3734. OUT:
  3735. ESI: Ptr to start of converted text (not start of buffer)
  3736. ECX: Byte length of converted text
  3737. }
  3738. asm
  3739. OR CL, CL
  3740. JNZ @start // CL = 0 => signed integer conversion
  3741. MOV ECX, 10
  3742. TEST [EAX + 4], $80000000
  3743. JZ @start
  3744. PUSH [EAX + 4]
  3745. PUSH [EAX]
  3746. MOV EAX, ESP
  3747. NEG [ESP] // negate the value
  3748. ADC [ESP + 4],0
  3749. NEG [ESP + 4]
  3750. CALL @start // perform unsigned conversion
  3751. MOV [ESI-1].Byte, '-' // tack on the negative sign
  3752. DEC ESI
  3753. INC ECX
  3754. ADD ESP, 8
  3755. RET
  3756. @start: // perform unsigned conversion
  3757. PUSH ESI
  3758. SUB ESP, 4
  3759. FNSTCW [ESP+2].Word // save
  3760. FNSTCW [ESP].Word // scratch
  3761. OR [ESP].Word, $0F00 // trunc toward zero, full precision
  3762. FLDCW [ESP].Word
  3763. MOV [ESP].Word, CX
  3764. FLD1
  3765. TEST [EAX + 4], $80000000 // test for negative
  3766. JZ @ld1 // FPU doesn't understand unsigned ints
  3767. PUSH [EAX + 4] // copy value before modifying
  3768. PUSH [EAX]
  3769. AND [ESP + 4], $7FFFFFFF // clear the sign bit
  3770. PUSH $7FFFFFFF
  3771. PUSH $FFFFFFFF
  3772. FILD [ESP + 8].QWord // load value
  3773. FILD [ESP].QWord
  3774. FADD ST(0), ST(2) // Add 1. Produces unsigned $80000000 in ST(0)
  3775. FADDP ST(1), ST(0) // Add $80000000 to value to replace the sign bit
  3776. ADD ESP, 16
  3777. JMP @ld2
  3778. @ld1:
  3779. FILD [EAX].QWord // value
  3780. @ld2:
  3781. FILD [ESP].Word // base
  3782. FLD ST(1)
  3783. @loop:
  3784. DEC ESI
  3785. FPREM // accumulator mod base
  3786. FISTP [ESP].Word
  3787. FDIV ST(1), ST(0) // accumulator := acumulator / base
  3788. MOV AL, [ESP].Byte // overlap long FPU division op with int ops
  3789. ADD AL, '0'
  3790. CMP AL, '0'+10
  3791. JB @store
  3792. ADD AL, ('A'-'0')-10
  3793. @store:
  3794. MOV [ESI].Byte, AL
  3795. FLD ST(1) // copy accumulator
  3796. FCOM ST(3) // if accumulator >= 1.0 then loop
  3797. FSTSW AX
  3798. SAHF
  3799. JAE @loop
  3800. FLDCW [ESP+2].Word
  3801. ADD ESP,4
  3802. FFREE ST(3)
  3803. FFREE ST(2)
  3804. FFREE ST(1);
  3805. FFREE ST(0);
  3806. POP ECX // original ESI
  3807. SUB ECX, ESI // ECX = length of converted string
  3808. SUB EDX,ECX
  3809. JBE @done // output longer than field width = no pad
  3810. SUB ESI,EDX
  3811. MOV AL,'0'
  3812. ADD ECX,EDX
  3813. JMP @z
  3814. @zloop: MOV [ESI+EDX].Byte,AL
  3815. @z: DEC EDX
  3816. JNZ @zloop
  3817. MOV [ESI].Byte,AL
  3818. @done:
  3819. end;
  3820. function IntToStr(Value: Int64): string;
  3821. // FmtStr(Result, '%d', [Value]);
  3822. asm
  3823. PUSH ESI
  3824. MOV ESI, ESP
  3825. SUB ESP, 32 // 32 chars
  3826. XOR ECX, ECX // base 10 signed
  3827. PUSH EAX // result ptr
  3828. XOR EDX, EDX // zero filled field width: 0 for no leading zeros
  3829. LEA EAX, Value;
  3830. CALL CvtInt64
  3831. MOV EDX, ESI
  3832. POP EAX // result ptr
  3833. CALL System.@LStrFromPCharLen
  3834. ADD ESP, 32
  3835. POP ESI
  3836. end;
  3837. function IntToHex(Value: Integer; Digits: Integer): string;
  3838. // FmtStr(Result, '%.*x', [Digits, Value]);
  3839. asm
  3840. CMP EDX, 32 // Digits < buffer length?
  3841. JBE @A1
  3842. XOR EDX, EDX
  3843. @A1: PUSH ESI
  3844. MOV ESI, ESP
  3845. SUB ESP, 32
  3846. PUSH ECX // result ptr
  3847. MOV ECX, 16 // base 16 EDX = Digits = field width
  3848. CALL CvtInt
  3849. MOV EDX, ESI
  3850. POP EAX // result ptr
  3851. CALL System.@LStrFromPCharLen
  3852. ADD ESP, 32
  3853. POP ESI
  3854. end;
  3855. function IntToHex(Value: Int64; Digits: Integer): string;
  3856. // FmtStr(Result, '%.*x', [Digits, Value]);
  3857. asm
  3858. CMP EAX, 32 // Digits < buffer length?
  3859. JLE @A1
  3860. XOR EAX, EAX
  3861. @A1: PUSH ESI
  3862. MOV ESI, ESP
  3863. SUB ESP, 32 // 32 chars
  3864. MOV ECX, 16 // base 16
  3865. PUSH EDX // result ptr
  3866. MOV EDX, EAX // zero filled field width: 0 for no leading zeros
  3867. LEA EAX, Value;
  3868. CALL CvtInt64
  3869. MOV EDX, ESI
  3870. POP EAX // result ptr
  3871. CALL System.@LStrFromPCharLen
  3872. ADD ESP, 32
  3873. POP ESI
  3874. end;
  3875. function StrToInt(const S: string): Integer;
  3876. var
  3877. E: Integer;
  3878. begin
  3879. Val(S, Result, E);
  3880. if E <> 0 then ConvertErrorFmt(SInvalidInteger, [S]);
  3881. end;
  3882. function StrToIntDef(const S: string; Default: Integer): Integer;
  3883. var
  3884. E: Integer;
  3885. begin
  3886. Val(S, Result, E);
  3887. if E <> 0 then Result := Default;
  3888. end;
  3889. function TryStrToInt(const S: string; out Value: Integer): Boolean;
  3890. var
  3891. E: Integer;
  3892. begin
  3893. Val(S, Value, E);
  3894. Result := E = 0;
  3895. end;
  3896. function StrToInt64(const S: string): Int64;
  3897. var
  3898. E: Integer;
  3899. begin
  3900. Val(S, Result, E);
  3901. if E <> 0 then ConvertErrorFmt(SInvalidInteger, [S]);
  3902. end;
  3903. function StrToInt64Def(const S: string; const Default: Int64): Int64;
  3904. var
  3905. E: Integer;
  3906. begin
  3907. Val(S, Result, E);
  3908. if E <> 0 then Result := Default;
  3909. end;
  3910. function TryStrToInt64(const S: string; out Value: Int64): Boolean;
  3911. var
  3912. E: Integer;
  3913. begin
  3914. Val(S, Value, E);
  3915. Result := E = 0;
  3916. end;
  3917. procedure VerifyBoolStrArray;
  3918. begin
  3919. if Length(TrueBoolStrs) = 0 then
  3920. begin
  3921. SetLength(TrueBoolStrs, 1);
  3922. TrueBoolStrs[0] := DefaultTrueBoolStr;
  3923. end;
  3924. if Length(FalseBoolStrs) = 0 then
  3925. begin
  3926. SetLength(FalseBoolStrs, 1);
  3927. FalseBoolStrs[0] := DefaultFalseBoolStr;
  3928. end;
  3929. end;
  3930. function StrToBool(const S: string): Boolean;
  3931. begin
  3932. if not TryStrToBool(S, Result) then
  3933. ConvertErrorFmt(SInvalidBoolean, [S]);
  3934. end;
  3935. function StrToBoolDef(const S: string; const Default: Boolean): Boolean;
  3936. begin
  3937. if not TryStrToBool(S, Result) then
  3938. Result := Default;
  3939. end;
  3940. function TryStrToBool(const S: string; out Value: Boolean): Boolean;
  3941. function CompareWith(const aArray: array of string): Boolean;
  3942. var
  3943. I: Integer;
  3944. begin
  3945. Result := False;
  3946. for I := Low(aArray) to High(aArray) do
  3947. if AnsiSameText(S, aArray[I]) then
  3948. begin
  3949. Result := True;
  3950. Break;
  3951. end;
  3952. end;
  3953. var
  3954. LResult: Extended;
  3955. begin
  3956. Result := TryStrToFloat(S, LResult);
  3957. if Result then
  3958. Value := LResult <> 0
  3959. else
  3960. begin
  3961. VerifyBoolStrArray;
  3962. Result := CompareWith(TrueBoolStrs);
  3963. if Result then
  3964. Value := True
  3965. else
  3966. begin
  3967. Result := CompareWith(FalseBoolStrs);
  3968. if Result then
  3969. Value := False;
  3970. end;
  3971. end;
  3972. end;
  3973. function BoolToStr(B: Boolean; UseBoolStrs: Boolean = False): string;
  3974. const
  3975. cSimpleBoolStrs: array [boolean] of String = ('0', '-1');
  3976. begin
  3977. if UseBoolStrs then
  3978. begin
  3979. VerifyBoolStrArray;
  3980. if B then
  3981. Result := TrueBoolStrs[0]
  3982. else
  3983. Result := FalseBoolStrs[0];
  3984. end
  3985. else
  3986. Result := cSimpleBoolStrs[B];
  3987. end;
  3988. type
  3989. PStrData = ^TStrData;
  3990. TStrData = record
  3991. Ident: Integer;
  3992. Str: string;
  3993. end;
  3994. function EnumStringModules(Instance: Longint; Data: Pointer): Boolean;
  3995. {$IFDEF MSWINDOWS}
  3996. var
  3997. Buffer: array [0..1023] of char;
  3998. begin
  3999. with PStrData(Data)^ do
  4000. begin
  4001. SetString(Str, Buffer,
  4002. LoadString(Instance, Ident, Buffer, sizeof(Buffer)));
  4003. Result := Str = '';
  4004. end;
  4005. end;
  4006. {$ENDIF}
  4007. {$IFDEF LINUX}
  4008. var
  4009. rs: TResStringRec;
  4010. Module: HModule;
  4011. begin
  4012. Module := Instance;
  4013. rs.Module := @Module;
  4014. with PStrData(Data)^ do
  4015. begin
  4016. rs.Identifier := Ident;
  4017. Str := LoadResString(@rs);
  4018. Result := Str = '';
  4019. end;
  4020. end;
  4021. {$ENDIF}
  4022. function FindStringResource(Ident: Integer): string;
  4023. var
  4024. StrData: TStrData;
  4025. begin
  4026. StrData.Ident := Ident;
  4027. StrData.Str := '';
  4028. EnumResourceModules(EnumStringModules, @StrData);
  4029. Result := StrData.Str;
  4030. end;
  4031. function LoadStr(Ident: Integer): string;
  4032. begin
  4033. Result := FindStringResource(Ident);
  4034. end;
  4035. function FmtLoadStr(Ident: Integer; const Args: array of const): string;
  4036. begin
  4037. FmtStr(Result, FindStringResource(Ident), Args);
  4038. end;
  4039. { File management routines }
  4040. function FileOpen(const FileName: string; Mode: LongWord): Integer;
  4041. {$IFDEF MSWINDOWS}
  4042. const
  4043. AccessMode: array[0..2] of LongWord = (
  4044. GENERIC_READ,
  4045. GENERIC_WRITE,
  4046. GENERIC_READ or GENERIC_WRITE);
  4047. ShareMode: array[0..4] of LongWord = (
  4048. 0,
  4049. 0,
  4050. FILE_SHARE_READ,
  4051. FILE_SHARE_WRITE,
  4052. FILE_SHARE_READ or FILE_SHARE_WRITE);
  4053. begin
  4054. Result := -1;
  4055. if ((Mode and 3) <= fmOpenReadWrite) and
  4056. ((Mode and $F0) <= fmShareDenyNone) then
  4057. Result := Integer(CreateFile(PChar(FileName), AccessMode[Mode and 3],
  4058. ShareMode[(Mode and $F0) shr 4], nil, OPEN_EXISTING,
  4059. FILE_ATTRIBUTE_NORMAL, 0));
  4060. end;
  4061. {$ENDIF}
  4062. {$IFDEF LINUX}
  4063. const
  4064. ShareMode: array[0..fmShareDenyNone shr 4] of Byte = (
  4065. 0, //No share mode specified
  4066. F_WRLCK, //fmShareExclusive
  4067. F_RDLCK, //fmShareDenyWrite
  4068. 0); //fmShareDenyNone
  4069. var
  4070. FileHandle, Tvar: Integer;
  4071. LockVar: TFlock;
  4072. smode: Byte;
  4073. begin
  4074. Result := -1;
  4075. if FileExists(FileName) and
  4076. ((Mode and 3) <= fmOpenReadWrite) and
  4077. ((Mode and $F0) <= fmShareDenyNone) then
  4078. begin
  4079. FileHandle := open(PChar(FileName), (Mode and 3), FileAccessRights);
  4080. if FileHandle = -1 then Exit;
  4081. smode := Mode and $F0 shr 4;
  4082. if ShareMode[smode] <> 0 then
  4083. begin
  4084. with LockVar do
  4085. begin
  4086. l_whence := SEEK_SET;
  4087. l_start := 0;
  4088. l_len := 0;
  4089. l_type := ShareMode[smode];
  4090. end;
  4091. Tvar := fcntl(FileHandle, F_SETLK, LockVar);
  4092. if Tvar = -1 then
  4093. begin
  4094. __close(FileHandle);
  4095. Exit;
  4096. end;
  4097. end;
  4098. Result := FileHandle;
  4099. end;
  4100. end;
  4101. {$ENDIF}
  4102. function FileCreate(const FileName: string): Integer;
  4103. {$IFDEF MSWINDOWS}
  4104. begin
  4105. Result := Integer(CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE,
  4106. 0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0));
  4107. end;
  4108. {$ENDIF}
  4109. {$IFDEF LINUX}
  4110. begin
  4111. Result := FileCreate(FileName, FileAccessRights);
  4112. end;
  4113. {$ENDIF}
  4114. function FileCreate(const FileName: string; Rights: Integer): Integer;
  4115. {$IFDEF MSWINDOWS}
  4116. begin
  4117. Result := FileCreate(FileName);
  4118. end;
  4119. {$ENDIF}
  4120. {$IFDEF LINUX}
  4121. begin
  4122. Result := Integer(open(PChar(FileName), O_RDWR or O_CREAT or O_TRUNC, Rights));
  4123. end;
  4124. {$ENDIF}
  4125. function FileRead(Handle: Integer; var Buffer; Count: LongWord): Integer;
  4126. begin
  4127. {$IFDEF MSWINDOWS}
  4128. if not ReadFile(THandle(Handle), Buffer, Count, LongWord(Result), nil) then
  4129. Result := -1;
  4130. {$ENDIF}
  4131. {$IFDEF LINUX}
  4132. Result := __read(Handle, Buffer, Count);
  4133. {$ENDIF}
  4134. end;
  4135. function FileWrite(Handle: Integer; const Buffer; Count: LongWord): Integer;
  4136. begin
  4137. {$IFDEF MSWINDOWS}
  4138. if not WriteFile(THandle(Handle), Buffer, Count, LongWord(Result), nil) then
  4139. Result := -1;
  4140. {$ENDIF}
  4141. {$IFDEF LINUX}
  4142. Result := __write(Handle, Buffer, Count);
  4143. {$ENDIF}
  4144. end;
  4145. function FileSeek(Handle, Offset, Origin: Integer): Integer;
  4146. begin
  4147. {$IFDEF MSWINDOWS}
  4148. Result := SetFilePointer(THandle(Handle), Offset, nil, Origin);
  4149. {$ENDIF}
  4150. {$IFDEF LINUX}
  4151. Result := __lseek(Handle, Offset, Origin);
  4152. {$ENDIF}
  4153. end;
  4154. function FileSeek(Handle: Integer; const Offset: Int64; Origin: Integer): Int64;
  4155. {$IFDEF MSWINDOWS}
  4156. begin
  4157. Result := Offset;
  4158. Int64Rec(Result).Lo := SetFilePointer(THandle(Handle), Int64Rec(Result).Lo,
  4159. @Int64Rec(Result).Hi, Origin);
  4160. end;
  4161. {$ENDIF}
  4162. {$IFDEF LINUX}
  4163. var
  4164. Temp: Integer;
  4165. begin
  4166. Temp := Offset; // allow for range-checking
  4167. Result := FileSeek(Handle, Temp, Origin);
  4168. end;
  4169. {$ENDIF}
  4170. procedure FileClose(Handle: Integer);
  4171. begin
  4172. {$IFDEF MSWINDOWS}
  4173. CloseHandle(THandle(Handle));
  4174. {$ENDIF}
  4175. {$IFDEF LINUX}
  4176. __close(Handle); // No need to unlock since all locks are released on close.
  4177. {$ENDIF}
  4178. end;
  4179. function FileAge(const FileName: string): Integer;
  4180. {$IFDEF MSWINDOWS}
  4181. var
  4182. Handle: THandle;
  4183. FindData: TWin32FindData;
  4184. LocalFileTime: TFileTime;
  4185. begin
  4186. Handle := FindFirstFile(PChar(FileName), FindData);
  4187. if Handle <> INVALID_HANDLE_VALUE then
  4188. begin
  4189. Windows.FindClose(Handle);
  4190. if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
  4191. begin
  4192. FileTimeToLocalFileTime(FindData.ftLastWriteTime, LocalFileTime);
  4193. if FileTimeToDosDateTime(LocalFileTime, LongRec(Result).Hi,
  4194. LongRec(Result).Lo) then Exit;
  4195. end;
  4196. end;
  4197. Result := -1;
  4198. end;
  4199. {$ENDIF}
  4200. {$IFDEF LINUX}
  4201. var
  4202. st: TStatBuf;
  4203. begin
  4204. if stat(PChar(FileName), st) = 0 then
  4205. Result := st.st_mtime
  4206. else
  4207. Result := -1;
  4208. end;
  4209. {$ENDIF}
  4210. function FileExists(const FileName: string): Boolean;
  4211. {$IFDEF MSWINDOWS}
  4212. begin
  4213. Result := FileAge(FileName) <> -1;
  4214. end;
  4215. {$ENDIF}
  4216. {$IFDEF LINUX}
  4217. begin
  4218. Result := euidaccess(PChar(FileName), F_OK) = 0;
  4219. end;
  4220. {$ENDIF}
  4221. function DirectoryExists(const Directory: string): Boolean;
  4222. {$IFDEF LINUX}
  4223. var
  4224. st: TStatBuf;
  4225. begin
  4226. if stat(PChar(Directory), st) = 0 then
  4227. Result := S_ISDIR(st.st_mode)
  4228. else
  4229. Result := False;
  4230. end;
  4231. {$ENDIF}
  4232. {$IFDEF MSWINDOWS}
  4233. var
  4234. Code: Integer;
  4235. begin
  4236. Code := GetFileAttributes(PChar(Directory));
  4237. Result := (Code <> -1) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0);
  4238. end;
  4239. {$ENDIF}
  4240. function ForceDirectories(Dir: string): Boolean;
  4241. var
  4242. E: EInOutError;
  4243. begin
  4244. Result := True;
  4245. if Dir = '' then
  4246. begin
  4247. E := EInOutError.CreateRes(SCannotCreateDir);
  4248. E.ErrorCode := 3;
  4249. raise E;
  4250. end;
  4251. Dir := ExcludeTrailingPathDelimiter(Dir);
  4252. {$IFDEF MSWINDOWS}
  4253. if (Length(Dir) < 3) or DirectoryExists(Dir)
  4254. or (ExtractFilePath(Dir) = Dir) then Exit; // avoid 'xyz:\' problem.
  4255. {$ENDIF}
  4256. {$IFDEF LINUX}
  4257. if (Dir = '') or DirectoryExists(Dir) then Exit;
  4258. {$ENDIF}
  4259. Result := ForceDirectories(ExtractFilePath(Dir)) and CreateDir(Dir);
  4260. end;
  4261. function FileGetDate(Handle: Integer): Integer;
  4262. {$IFDEF MSWINDOWS}
  4263. var
  4264. FileTime, LocalFileTime: TFileTime;
  4265. begin
  4266. if GetFileTime(THandle(Handle), nil, nil, @FileTime) and
  4267. FileTimeToLocalFileTime(FileTime, LocalFileTime) and
  4268. FileTimeToDosDateTime(LocalFileTime, LongRec(Result).Hi,
  4269. LongRec(Result).Lo) then Exit;
  4270. Result := -1;
  4271. end;
  4272. {$ENDIF}
  4273. {$IFDEF LINUX}
  4274. var
  4275. st: TStatBuf;
  4276. begin
  4277. if fstat(Handle, st) = 0 then
  4278. Result := st.st_mtime
  4279. else
  4280. Result := -1;
  4281. end;
  4282. {$ENDIF}
  4283. function FileSetDate(const FileName: string; Age: Integer): Integer;
  4284. {$IFDEF MSWINDOWS}
  4285. var
  4286. f: THandle;
  4287. begin
  4288. f := FileOpen(FileName, fmOpenWrite);
  4289. if f = THandle(-1) then
  4290. Result := GetLastError
  4291. else
  4292. begin
  4293. Result := FileSetDate(f, Age);
  4294. FileClose(f);
  4295. end;
  4296. end;
  4297. {$ENDIF}
  4298. {$IFDEF LINUX}
  4299. var
  4300. ut: TUTimeBuffer;
  4301. begin
  4302. Result := 0;
  4303. ut.actime := Age;
  4304. ut.modtime := Age;
  4305. if utime(PChar(FileName), @ut) = -1 then
  4306. Result := GetLastError;
  4307. end;
  4308. {$ENDIF}
  4309. {$IFDEF MSWINDOWS}
  4310. function FileSetDate(Handle: Integer; Age: Integer): Integer;
  4311. var
  4312. LocalFileTime, FileTime: TFileTime;
  4313. begin
  4314. Result := 0;
  4315. if DosDateTimeToFileTime(LongRec(Age).Hi, LongRec(Age).Lo, LocalFileTime) and
  4316. LocalFileTimeToFileTime(LocalFileTime, FileTime) and
  4317. SetFileTime(Handle, nil, nil, @FileTime) then Exit;
  4318. Result := GetLastError;
  4319. end;
  4320. function FileGetAttr(const FileName: string): Integer;
  4321. begin
  4322. Result := GetFileAttributes(PChar(FileName));
  4323. end;
  4324. function FileSetAttr(const FileName: string; Attr: Integer): Integer;
  4325. begin
  4326. Result := 0;
  4327. if not SetFileAttributes(PChar(FileName), Attr) then
  4328. Result := GetLastError;
  4329. end;
  4330. {$ENDIF}
  4331. function FileIsReadOnly(const FileName: string): Boolean;
  4332. begin
  4333. {$IFDEF MSWINDOWS}
  4334. Result := (GetFileAttributes(PChar(FileName)) and faReadOnly) <> 0;
  4335. {$ENDIF}
  4336. {$IFDEF LINUX}
  4337. Result := (euidaccess(PChar(FileName), R_OK) = 0) and
  4338. (euidaccess(PChar(FileName), W_OK) <> 0);
  4339. {$ENDIF}
  4340. end;
  4341. function FileSetReadOnly(const FileName: string; ReadOnly: Boolean): Boolean;
  4342. {$IFDEF MSWINDOWS}
  4343. var
  4344. Flags: Integer;
  4345. begin
  4346. Result := False;
  4347. Flags := GetFileAttributes(PChar(FileName));
  4348. if Flags = -1 then Exit;
  4349. if ReadOnly then
  4350. Flags := Flags or faReadOnly
  4351. else
  4352. Flags := Flags and not faReadOnly;
  4353. Result := SetFileAttributes(PChar(FileName), Flags);
  4354. end;
  4355. {$ENDIF}
  4356. {$IFDEF LINUX}
  4357. var
  4358. st: TStatBuf;
  4359. Flags: Integer;
  4360. begin
  4361. Result := False;
  4362. if stat(PChar(FileName), st) <> 0 then Exit;
  4363. if ReadOnly then
  4364. Flags := st.st_mode and not (S_IWUSR or S_IWGRP or S_IWOTH)
  4365. else
  4366. Flags := st.st_mode or (S_IWUSR or S_IWGRP or S_IWOTH);
  4367. Result := chmod(PChar(FileName), Flags) = 0;
  4368. end;
  4369. {$ENDIF}
  4370. function FindMatchingFile(var F: TSearchRec): Integer;
  4371. {$IFDEF MSWINDOWS}
  4372. var
  4373. LocalFileTime: TFileTime;
  4374. begin
  4375. with F do
  4376. begin
  4377. while FindData.dwFileAttributes and ExcludeAttr <> 0 do
  4378. if not FindNextFile(FindHandle, FindData) then
  4379. begin
  4380. Result := GetLastError;
  4381. Exit;
  4382. end;
  4383. FileTimeToLocalFileTime(FindData.ftLastWriteTime, LocalFileTime);
  4384. FileTimeToDosDateTime(LocalFileTime, LongRec(Time).Hi,
  4385. LongRec(Time).Lo);
  4386. Size := FindData.nFileSizeLow;
  4387. Attr := FindData.dwFileAttributes;
  4388. Name := FindData.cFileName;
  4389. end;
  4390. Result := 0;
  4391. end;
  4392. {$ENDIF}
  4393. {$IFDEF LINUX}
  4394. var
  4395. PtrDirEnt: PDirEnt;
  4396. Scratch: TDirEnt;
  4397. StatBuf: TStatBuf;
  4398. LinkStatBuf: TStatBuf;
  4399. FName: string;
  4400. Attr: Integer;
  4401. Mode: mode_t;
  4402. begin
  4403. Result := -1;
  4404. PtrDirEnt := nil;
  4405. if readdir_r(F.FindHandle, @Scratch, PtrDirEnt) <> 0 then
  4406. Exit;
  4407. while PtrDirEnt <> nil do
  4408. begin
  4409. if fnmatch(PChar(F.Pattern), PtrDirEnt.d_name, 0) = 0 then
  4410. begin // F.PathOnly must include trailing backslash
  4411. FName := F.PathOnly + string(PtrDirEnt.d_name);
  4412. if lstat(PChar(FName), StatBuf) = 0 then
  4413. begin
  4414. Attr := 0;
  4415. Mode := StatBuf.st_mode;
  4416. if S_ISDIR(Mode) then
  4417. Attr := Attr or faDirectory
  4418. else
  4419. if not S_ISREG(Mode) then // directories shouldn't be treated as system files
  4420. begin
  4421. if S_ISLNK(Mode) then
  4422. begin
  4423. Attr := Attr or faSymLink;
  4424. if (stat(PChar(FName), LinkStatBuf) = 0) and
  4425. S_ISDIR(LinkStatBuf.st_mode) then
  4426. Attr := Attr or faDirectory
  4427. end;
  4428. Attr := Attr or faSysFile;
  4429. end;
  4430. if (PtrDirEnt.d_name[0] = '.') and (PtrDirEnt.d_name[1] <> #0) then
  4431. begin
  4432. if not ((PtrDirEnt.d_name[1] = '.') and (PtrDirEnt.d_name[2] = #0)) then
  4433. Attr := Attr or faHidden;
  4434. end;
  4435. if euidaccess(PChar(FName), W_OK) <> 0 then
  4436. Attr := Attr or faReadOnly;
  4437. if Attr and F.ExcludeAttr = 0 then
  4438. begin
  4439. F.Size := StatBuf.st_size;
  4440. F.Attr := Attr;
  4441. F.Mode := StatBuf.st_mode;
  4442. F.Name := PtrDirEnt.d_name;
  4443. F.Time := StatBuf.st_mtime;
  4444. Result := 0;
  4445. Break;
  4446. end;
  4447. end;
  4448. end;
  4449. Result := -1;
  4450. if readdir_r(F.FindHandle, @Scratch, PtrDirEnt) <> 0 then
  4451. Break;
  4452. end // End of While
  4453. end;
  4454. {$ENDIF}
  4455. function FindFirst(const Path: string; Attr: Integer;
  4456. var F: TSearchRec): Integer;
  4457. const
  4458. faSpecial = faHidden or faSysFile or faDirectory;
  4459. {$IFDEF MSWINDOWS}
  4460. begin
  4461. F.ExcludeAttr := not Attr and faSpecial;
  4462. F.FindHandle := FindFirstFile(PChar(Path), F.FindData);
  4463. if F.FindHandle <> INVALID_HANDLE_VALUE then
  4464. begin
  4465. Result := FindMatchingFile(F);
  4466. if Result <> 0 then FindClose(F);
  4467. end else
  4468. Result := GetLastError;
  4469. end;
  4470. {$ENDIF}
  4471. {$IFDEF LINUX}
  4472. begin
  4473. F.ExcludeAttr := not Attr and faSpecial;
  4474. F.PathOnly := ExtractFilePath(Path);
  4475. F.Pattern := ExtractFileName(Path);
  4476. if F.PathOnly = '' then
  4477. F.PathOnly := IncludeTrailingPathDelimiter(GetCurrentDir);
  4478. F.FindHandle := opendir(PChar(F.PathOnly));
  4479. if F.FindHandle <> nil then
  4480. begin
  4481. Result := FindMatchingFile(F);
  4482. if Result <> 0 then
  4483. FindClose(F);
  4484. end
  4485. else
  4486. Result:= GetLastError;
  4487. end;
  4488. {$ENDIF}
  4489. function FindNext(var F: TSearchRec): Integer;
  4490. begin
  4491. {$IFDEF MSWINDOWS}
  4492. if FindNextFile(F.FindHandle, F.FindData) then
  4493. Result := FindMatchingFile(F) else
  4494. Result := GetLastError;
  4495. {$ENDIF}
  4496. {$IFDEF LINUX}
  4497. Result := FindMatchingFile(F);
  4498. {$ENDIF}
  4499. end;
  4500. procedure FindClose(var F: TSearchRec);
  4501. begin
  4502. {$IFDEF MSWINDOWS}
  4503. if F.FindHandle <> INVALID_HANDLE_VALUE then
  4504. begin
  4505. Windows.FindClose(F.FindHandle);
  4506. F.FindHandle := INVALID_HANDLE_VALUE;
  4507. end;
  4508. {$ENDIF}
  4509. {$IFDEF LINUX}
  4510. if F.FindHandle <> nil then
  4511. begin
  4512. closedir(F.FindHandle);
  4513. F.FindHandle := nil;
  4514. end;
  4515. {$ENDIF}
  4516. end;
  4517. function DeleteFile(const FileName: string): Boolean;
  4518. begin
  4519. {$IFDEF MSWINDOWS}
  4520. Result := Windows.DeleteFile(PChar(FileName));
  4521. {$ENDIF}
  4522. {$IFDEF LINUX}
  4523. Result := unlink(PChar(FileName)) <> -1;
  4524. {$ENDIF}
  4525. end;
  4526. function RenameFile(const OldName, NewName: string): Boolean;
  4527. begin
  4528. {$IFDEF MSWINDOWS}
  4529. Result := MoveFile(PChar(OldName), PChar(NewName));
  4530. {$ENDIF}
  4531. {$IFDEF LINUX}
  4532. Result := __rename(PChar(OldName), PChar(NewName)) = 0;
  4533. {$ENDIF}
  4534. end;
  4535. function AnsiStrLastChar(P: PChar): PChar;
  4536. var
  4537. LastByte: Integer;
  4538. begin
  4539. LastByte := StrLen(P) - 1;
  4540. Result := @P[LastByte];
  4541. {$IFDEF MSWINDOWS}
  4542. if StrByteType(P, LastByte) = mbTrailByte then Dec(Result);
  4543. {$ENDIF}
  4544. {$IFDEF LINUX}
  4545. while StrByteType(P, Result - P) = mbTrailByte do Dec(Result);
  4546. {$ENDIF}
  4547. end;
  4548. function AnsiLastChar(const S: string): PChar;
  4549. var
  4550. LastByte: Integer;
  4551. begin
  4552. LastByte := Length(S);
  4553. if LastByte <> 0 then
  4554. begin
  4555. while ByteType(S, LastByte) = mbTrailByte do Dec(LastByte);
  4556. Result := @S[LastByte];
  4557. end
  4558. else
  4559. Result := nil;
  4560. end;
  4561. function LastDelimiter(const Delimiters, S: string): Integer;
  4562. var
  4563. P: PChar;
  4564. begin
  4565. Result := Length(S);
  4566. P := PChar(Delimiters);
  4567. while Result > 0 do
  4568. begin
  4569. if (S[Result] <> #0) and (StrScan(P, S[Result]) <> nil) then
  4570. {$IFDEF MSWINDOWS}
  4571. if (ByteType(S, Result) = mbTrailByte) then
  4572. Dec(Result)
  4573. else
  4574. Exit;
  4575. {$ENDIF}
  4576. {$IFDEF LINUX}
  4577. begin
  4578. if (ByteType(S, Result) <> mbTrailByte) then
  4579. Exit;
  4580. Dec(Result);
  4581. while ByteType(S, Result) = mbTrailByte do Dec(Result);
  4582. end;
  4583. {$ENDIF}
  4584. Dec(Result);
  4585. end;
  4586. end;
  4587. function ChangeFileExt(const FileName, Extension: string): string;
  4588. var
  4589. I: Integer;
  4590. begin
  4591. I := LastDelimiter('.' + PathDelim + DriveDelim,Filename);
  4592. if (I = 0) or (FileName[I] <> '.') then I := MaxInt;
  4593. Result := Copy(FileName, 1, I - 1) + Extension;
  4594. end;
  4595. function ExtractFilePath(const FileName: string): string;
  4596. var
  4597. I: Integer;
  4598. begin
  4599. I := LastDelimiter(PathDelim + DriveDelim, FileName);
  4600. Result := Copy(FileName, 1, I);
  4601. end;
  4602. function ExtractFileDir(const FileName: string): string;
  4603. var
  4604. I: Integer;
  4605. begin
  4606. I := LastDelimiter(PathDelim + DriveDelim, Filename);
  4607. if (I > 1) and (FileName[I] = PathDelim) and
  4608. (not IsDelimiter( PathDelim + DriveDelim, FileName, I-1)) then Dec(I);
  4609. Result := Copy(FileName, 1, I);
  4610. end;
  4611. function ExtractFileDrive(const FileName: string): string;
  4612. {$IFDEF MSWINDOWS}
  4613. var
  4614. I, J: Integer;
  4615. begin
  4616. if (Length(FileName) >= 2) and (FileName[2] = DriveDelim) then
  4617. Result := Copy(FileName, 1, 2)
  4618. else if (Length(FileName) >= 2) and (FileName[1] = PathDelim) and
  4619. (FileName[2] = PathDelim) then
  4620. begin
  4621. J := 0;
  4622. I := 3;
  4623. While (I < Length(FileName)) and (J < 2) do
  4624. begin
  4625. if FileName[I] = PathDelim then Inc(J);
  4626. if J < 2 then Inc(I);
  4627. end;
  4628. if FileName[I] = PathDelim then Dec(I);
  4629. Result := Copy(FileName, 1, I);
  4630. end else Result := '';
  4631. end;
  4632. {$ENDIF}
  4633. {$IFDEF LINUX}
  4634. begin
  4635. Result := ''; // Linux doesn't support drive letters
  4636. end;
  4637. {$ENDIF}
  4638. function ExtractFileName(const FileName: string): string;
  4639. var
  4640. I: Integer;
  4641. begin
  4642. I := LastDelimiter(PathDelim + DriveDelim, FileName);
  4643. Result := Copy(FileName, I + 1, MaxInt);
  4644. end;
  4645. function ExtractFileExt(const FileName: string): string;
  4646. var
  4647. I: Integer;
  4648. begin
  4649. I := LastDelimiter('.' + PathDelim + DriveDelim, FileName);
  4650. if (I > 0) and (FileName[I] = '.') then
  4651. Result := Copy(FileName, I, MaxInt) else
  4652. Result := '';
  4653. end;
  4654. function ExpandFileName(const FileName: string): string;
  4655. {$IFDEF MSWINDOWS}
  4656. var
  4657. FName: PChar;
  4658. Buffer: array[0..MAX_PATH - 1] of Char;
  4659. begin
  4660. SetString(Result, Buffer, GetFullPathName(PChar(FileName), SizeOf(Buffer),
  4661. Buffer, FName));
  4662. end;
  4663. {$ENDIF}
  4664. {$IFDEF LINUX}
  4665. function ExpandTilde(const InString: string): string;
  4666. var
  4667. W: wordexp_t;
  4668. SpacePos: Integer;
  4669. PostSpaceStr: string;
  4670. begin
  4671. Result := InString;
  4672. SpacePos := AnsiPos(' ', Result); // only expand stuff up to the first space in the filename
  4673. if SpacePos > 0 then // then just add the space and the rest of the string
  4674. PostSpaceStr := Copy(Result, SpacePos, Length(Result) - (SpacePos-1));
  4675. case wordexp(PChar(Result), W, WRDE_NOCMD) of
  4676. 0: // success
  4677. begin
  4678. Result := PChar(W.we_wordv^);
  4679. wordfree(W);
  4680. end;
  4681. WRDE_NOSPACE: // error, but W may be partially allocated
  4682. wordfree(W);
  4683. end;
  4684. if PostSpaceStr <> '' then
  4685. Result := Result + PostSpaceStr;
  4686. end;
  4687. var
  4688. I, J: Integer;
  4689. LastWasPathDelim: Boolean;
  4690. TempName: string;
  4691. begin
  4692. Result := '';
  4693. if Length(Filename) = 0 then Exit;
  4694. if FileName[1] = PathDelim then
  4695. TempName := FileName
  4696. else
  4697. begin
  4698. TempName := FileName;
  4699. if FileName[1] = '~' then
  4700. TempName := ExpandTilde(TempName)
  4701. else
  4702. TempName := IncludeTrailingPathDelimiter(GetCurrentDir) + TempName;
  4703. end;
  4704. I := 1;
  4705. J := 1;
  4706. LastWasPathDelim := False;
  4707. while I <= Length(TempName) do
  4708. begin
  4709. case TempName[I] of
  4710. PathDelim:
  4711. if J < I then
  4712. begin
  4713. // Check for consecutive 'PathDelim' characters and skip them if present
  4714. if (I = 1) or (TempName[I - 1] <> PathDelim) then
  4715. Result := Result + Copy(TempName, J, I - J);
  4716. J := I;
  4717. // Set a flag indicating that we just processed a path delimiter
  4718. LastWasPathDelim := True;
  4719. end;
  4720. '.':
  4721. begin
  4722. // If the last character was a path delimiter then this '.' is
  4723. // possibly a relative path modifier
  4724. if LastWasPathDelim then
  4725. begin
  4726. // Check if the path ends in a '.'
  4727. if I < Length(TempName) then
  4728. begin
  4729. // If the next character is another '.' then this may be a relative path
  4730. // except if there is another '.' after that one. In this case simply
  4731. // treat this as just another filename.
  4732. if (TempName[I + 1] = '.') and
  4733. ((I + 1 >= Length(TempName)) or (TempName[I + 2] <> '.')) then
  4734. begin
  4735. // Don't attempt to backup past the Root dir
  4736. if Length(Result) > 1 then
  4737. // For the purpose of this excercise, treat the last dir as a
  4738. // filename so we can use this function to remove it
  4739. Result := ExtractFilePath(ExcludeTrailingPathDelimiter(Result));
  4740. J := I;
  4741. end
  4742. // Simply skip over and ignore any 'current dir' constrcucts, './'
  4743. // or the remaining './' from a ../ constrcut.
  4744. else if TempName[I + 1] = PathDelim then
  4745. begin
  4746. Result := IncludeTrailingPathDelimiter(Result);
  4747. if TempName[I] in LeadBytes then
  4748. Inc(I, StrCharLength(@TempName[I]))
  4749. else
  4750. Inc(I);
  4751. J := I + 1;
  4752. end else
  4753. // If any of the above tests fail, then this is not a 'current dir' or
  4754. // 'parent dir' construct so just clear the state and continue.
  4755. LastWasPathDelim := False;
  4756. end else
  4757. begin
  4758. // Don't let the expanded path end in a 'PathDelim' character
  4759. Result := ExcludeTrailingPathDelimiter(Result);
  4760. J := I + 1;
  4761. end;
  4762. end;
  4763. end;
  4764. else
  4765. LastWasPathDelim := False;
  4766. end;
  4767. if TempName[I] in LeadBytes then
  4768. Inc(I, StrCharLength(@TempName[I]))
  4769. else
  4770. Inc(I);
  4771. end;
  4772. // This will finally append what is left
  4773. if (I - J > 1) or (TempName[I] <> PathDelim) then
  4774. Result := Result + Copy(TempName, J, I - J);
  4775. end;
  4776. {$ENDIF}
  4777. function ExpandFileNameCase(const FileName: string;
  4778. out MatchFound: TFilenameCaseMatch): string;
  4779. var
  4780. SR: TSearchRec;
  4781. FullPath, Name: string;
  4782. Temp: Integer;
  4783. FoundOne: Boolean;
  4784. {$IFDEF LINUX}
  4785. Scans: Byte;
  4786. FirstLetter, TestLetter: string;
  4787. {$ENDIF}
  4788. begin
  4789. Result := ExpandFileName(FileName);
  4790. FullPath := ExtractFilePath(Result);
  4791. Name := ExtractFileName(Result);
  4792. MatchFound := mkNone;
  4793. // if FullPath is not the root directory (portable)
  4794. if not SameFileName(FullPath, IncludeTrailingPathDelimiter(ExtractFileDrive(FullPath))) then
  4795. begin // Does the path need case-sensitive work?
  4796. Temp := FindFirst(FullPath, faAnyFile, SR);
  4797. FindClose(SR); // close search before going recursive
  4798. if Temp <> 0 then
  4799. begin
  4800. FullPath := ExcludeTrailingPathDelimiter(FullPath);
  4801. FullPath := ExpandFileNameCase(FullPath, MatchFound);
  4802. if MatchFound = mkNone then
  4803. Exit; // if we can't find the path, we certainly can't find the file!
  4804. FullPath := IncludeTrailingPathDelimiter(FullPath);
  4805. end;
  4806. end;
  4807. // Path is validated / adjusted. Now for the file itself
  4808. try
  4809. if FindFirst(FullPath + Name, faAnyFile, SR)= 0 then // exact match on filename
  4810. begin
  4811. if not (MatchFound in [mkSingleMatch, mkAmbiguous]) then // path might have been inexact
  4812. MatchFound := mkExactMatch;
  4813. Result := FullPath + SR.Name;
  4814. Exit;
  4815. end;
  4816. finally
  4817. FindClose(SR);
  4818. end;
  4819. FoundOne := False; // Windows should never get to here except for file-not-found
  4820. {$IFDEF LINUX}
  4821. { Scan the directory.
  4822. To minimize the number of filenames tested, scan the directory
  4823. using upper/lowercase first letter + wildcard.
  4824. This results in two scans of the directory (particularly on Linux) but
  4825. vastly reduces the number of times we have to perform an expensive
  4826. locale-charset case-insensitive string compare. }
  4827. // First, scan for lowercase first letter
  4828. FirstLetter := AnsiLowerCase(Name[1]);
  4829. for Scans := 0 to 1 do
  4830. begin
  4831. Temp := FindFirst(FullPath + FirstLetter + '*', faAnyFile, SR);
  4832. while Temp = 0 do
  4833. begin
  4834. if AnsiSameText(SR.Name, Name) then
  4835. begin
  4836. if FoundOne then
  4837. begin // this is the second match
  4838. MatchFound := mkAmbiguous;
  4839. Exit;
  4840. end
  4841. else
  4842. begin
  4843. FoundOne := True;
  4844. Result := FullPath + SR.Name;
  4845. end;
  4846. end;
  4847. Temp := FindNext(SR);
  4848. end;
  4849. FindClose(SR);
  4850. TestLetter := AnsiUpperCase(Name[1]);
  4851. if TestLetter = FirstLetter then Break;
  4852. FirstLetter := TestLetter;
  4853. end;
  4854. {$ENDIF}
  4855. if MatchFound <> mkAmbiguous then
  4856. begin
  4857. if FoundOne then
  4858. MatchFound := mkSingleMatch
  4859. else
  4860. MatchFound := mkNone;
  4861. end;
  4862. end;
  4863. {$IFDEF MSWINDOWS}
  4864. function GetUniversalName(const FileName: string): string;
  4865. type
  4866. PNetResourceArray = ^TNetResourceArray;
  4867. TNetResourceArray = array[0..MaxInt div SizeOf(TNetResource) - 1] of TNetResource;
  4868. var
  4869. I, BufSize, NetResult: Integer;
  4870. Count, Size: LongWord;
  4871. Drive: Char;
  4872. NetHandle: THandle;
  4873. NetResources: PNetResourceArray;
  4874. RemoteNameInfo: array[0..1023] of Byte;
  4875. begin
  4876. Result := FileName;
  4877. if (Win32Platform <> VER_PLATFORM_WIN32_WINDOWS) or (Win32MajorVersion > 4) then
  4878. begin
  4879. Size := SizeOf(RemoteNameInfo);
  4880. if WNetGetUniversalName(PChar(FileName), UNIVERSAL_NAME_INFO_LEVEL,
  4881. @RemoteNameInfo, Size) <> NO_ERROR then Exit;
  4882. Result := PRemoteNameInfo(@RemoteNameInfo).lpUniversalName;
  4883. end else
  4884. begin
  4885. { The following works around a bug in WNetGetUniversalName under Windows 95 }
  4886. Drive := UpCase(FileName[1]);
  4887. if (Drive < 'A') or (Drive > 'Z') or (Length(FileName) < 3) or
  4888. (FileName[2] <> ':') or (FileName[3] <> '\') then
  4889. Exit;
  4890. if WNetOpenEnum(RESOURCE_CONNECTED, RESOURCETYPE_DISK, 0, nil,
  4891. NetHandle) <> NO_ERROR then Exit;
  4892. try
  4893. BufSize := 50 * SizeOf(TNetResource);
  4894. GetMem(NetResources, BufSize);
  4895. try
  4896. while True do
  4897. begin
  4898. Count := $FFFFFFFF;
  4899. Size := BufSize;
  4900. NetResult := WNetEnumResource(NetHandle, Count, NetResources, Size);
  4901. if NetResult = ERROR_MORE_DATA then
  4902. begin
  4903. BufSize := Size;
  4904. ReallocMem(NetResources, BufSize);
  4905. Continue;
  4906. end;
  4907. if NetResult <> NO_ERROR then Exit;
  4908. for I := 0 to Count - 1 do
  4909. with NetResources^[I] do
  4910. if (lpLocalName <> nil) and (Drive = UpCase(lpLocalName[0])) then
  4911. begin
  4912. Result := lpRemoteName + Copy(FileName, 3, Length(FileName) - 2);
  4913. Exit;
  4914. end;
  4915. end;
  4916. finally
  4917. FreeMem(NetResources, BufSize);
  4918. end;
  4919. finally
  4920. WNetCloseEnum(NetHandle);
  4921. end;
  4922. end;
  4923. end;
  4924. function ExpandUNCFileName(const FileName: string): string;
  4925. begin
  4926. { First get the local resource version of the file name }
  4927. Result := ExpandFileName(FileName);
  4928. if (Length(Result) >= 3) and (Result[2] = ':') and (Upcase(Result[1]) >= 'A')
  4929. and (Upcase(Result[1]) <= 'Z') then
  4930. Result := GetUniversalName(Result);
  4931. end;
  4932. {$ENDIF}
  4933. {$IFDEF LINUX}
  4934. function ExpandUNCFileName(const FileName: string): string;
  4935. begin
  4936. Result := ExpandFileName(FileName);
  4937. end;
  4938. {$ENDIF}
  4939. function ExtractRelativePath(const BaseName, DestName: string): string;
  4940. var
  4941. BasePath, DestPath: string;
  4942. BaseLead, DestLead: PChar;
  4943. BasePtr, DestPtr: PChar;
  4944. function ExtractFilePathNoDrive(const FileName: string): string;
  4945. begin
  4946. Result := ExtractFilePath(FileName);
  4947. Delete(Result, 1, Length(ExtractFileDrive(FileName)));
  4948. end;
  4949. function Next(var Lead: PChar): PChar;
  4950. begin
  4951. Result := Lead;
  4952. if Result = nil then Exit;
  4953. Lead := AnsiStrScan(Lead, PathDelim);
  4954. if Lead <> nil then
  4955. begin
  4956. Lead^ := #0;
  4957. Inc(Lead);
  4958. end;
  4959. end;
  4960. begin
  4961. if SameFilename(ExtractFileDrive(BaseName), ExtractFileDrive(DestName)) then
  4962. begin
  4963. BasePath := ExtractFilePathNoDrive(BaseName);
  4964. UniqueString(BasePath);
  4965. DestPath := ExtractFilePathNoDrive(DestName);
  4966. UniqueString(DestPath);
  4967. BaseLead := Pointer(BasePath);
  4968. BasePtr := Next(BaseLead);
  4969. DestLead := Pointer(DestPath);
  4970. DestPtr := Next(DestLead);
  4971. while (BasePtr <> nil) and (DestPtr <> nil) and SameFilename(BasePtr, DestPtr) do
  4972. begin
  4973. BasePtr := Next(BaseLead);
  4974. DestPtr := Next(DestLead);
  4975. end;
  4976. Result := '';
  4977. while BaseLead <> nil do
  4978. begin
  4979. Result := Result + '..' + PathDelim; { Do not localize }
  4980. Next(BaseLead);
  4981. end;
  4982. if (DestPtr <> nil) and (DestPtr^ <> #0) then
  4983. Result := Result + DestPtr + PathDelim;
  4984. if DestLead <> nil then
  4985. Result := Result + DestLead; // destlead already has a trailing backslash
  4986. Result := Result + ExtractFileName(DestName);
  4987. end
  4988. else
  4989. Result := DestName;
  4990. end;
  4991. {$IFDEF MSWINDOWS}
  4992. function ExtractShortPathName(const FileName: string): string;
  4993. var
  4994. Buffer: array[0..MAX_PATH - 1] of Char;
  4995. begin
  4996. SetString(Result, Buffer,
  4997. GetShortPathName(PChar(FileName), Buffer, SizeOf(Buffer)));
  4998. end;
  4999. {$ENDIF}
  5000. function FileSearch(const Name, DirList: string): string;
  5001. var
  5002. I, P, L: Integer;
  5003. C: Char;
  5004. begin
  5005. Result := Name;
  5006. P := 1;
  5007. L := Length(DirList);
  5008. while True do
  5009. begin
  5010. if FileExists(Result) then Exit;
  5011. while (P <= L) and (DirList[P] = PathSep) do Inc(P);
  5012. if P > L then Break;
  5013. I := P;
  5014. while (P <= L) and (DirList[P] <> PathSep) do
  5015. begin
  5016. if DirList[P] in LeadBytes then
  5017. P := NextCharIndex(DirList, P)
  5018. else
  5019. Inc(P);
  5020. end;
  5021. Result := Copy(DirList, I, P - I);
  5022. C := AnsiLastChar(Result)^;
  5023. if (C <> DriveDelim) and (C <> PathDelim) then
  5024. Result := Result + PathDelim;
  5025. Result := Result + Name;
  5026. end;
  5027. Result := '';
  5028. end;
  5029. {$IFDEF MSWINDOWS}
  5030. // This function is used if the OS doesn't support GetDiskFreeSpaceEx
  5031. function BackfillGetDiskFreeSpaceEx(Directory: PChar; var FreeAvailable,
  5032. TotalSpace: TLargeInteger; TotalFree: PLargeInteger): Bool; stdcall;
  5033. var
  5034. SectorsPerCluster, BytesPerSector, FreeClusters, TotalClusters: LongWord;
  5035. Temp: Int64;
  5036. Dir: PChar;
  5037. begin
  5038. if Directory <> nil then
  5039. Dir := Directory
  5040. else
  5041. Dir := nil;
  5042. Result := GetDiskFreeSpaceA(Dir, SectorsPerCluster, BytesPerSector,
  5043. FreeClusters, TotalClusters);
  5044. Temp := SectorsPerCluster * BytesPerSector;
  5045. FreeAvailable := Temp * FreeClusters;
  5046. TotalSpace := Temp * TotalClusters;
  5047. end;
  5048. function InternalGetDiskSpace(Drive: Byte;
  5049. var TotalSpace, FreeSpaceAvailable: Int64): Bool;
  5050. var
  5051. RootPath: array[0..4] of Char;
  5052. RootPtr: PChar;
  5053. begin
  5054. RootPtr := nil;
  5055. if Drive > 0 then
  5056. begin
  5057. RootPath[0] := Char(Drive + $40);
  5058. RootPath[1] := ':';
  5059. RootPath[2] := '\';
  5060. RootPath[3] := #0;
  5061. RootPtr := RootPath;
  5062. end;
  5063. Result := GetDiskFreeSpaceEx(RootPtr, FreeSpaceAvailable, TotalSpace, nil);
  5064. end;
  5065. function DiskFree(Drive: Byte): Int64;
  5066. var
  5067. TotalSpace: Int64;
  5068. begin
  5069. if not InternalGetDiskSpace(Drive, TotalSpace, Result) then
  5070. Result := -1;
  5071. end;
  5072. function DiskSize(Drive: Byte): Int64;
  5073. var
  5074. FreeSpace: Int64;
  5075. begin
  5076. if not InternalGetDiskSpace(Drive, Result, FreeSpace) then
  5077. Result := -1;
  5078. end;
  5079. {$ENDIF}
  5080. function FileDateToDateTime(FileDate: Integer): TDateTime;
  5081. {$IFDEF MSWINDOWS}
  5082. begin
  5083. Result :=
  5084. EncodeDate(
  5085. LongRec(FileDate).Hi shr 9 + 1980,
  5086. LongRec(FileDate).Hi shr 5 and 15,
  5087. LongRec(FileDate).Hi and 31) +
  5088. EncodeTime(
  5089. LongRec(FileDate).Lo shr 11,
  5090. LongRec(FileDate).Lo shr 5 and 63,
  5091. LongRec(FileDate).Lo and 31 shl 1, 0);
  5092. end;
  5093. {$ENDIF}
  5094. {$IFDEF LINUX}
  5095. var
  5096. UT: TUnixTime;
  5097. begin
  5098. localtime_r(@FileDate, UT);
  5099. Result := EncodeDate(UT.tm_year + 1900, UT.tm_mon + 1, UT.tm_mday) +
  5100. EncodeTime(UT.tm_hour, UT.tm_min, UT.tm_sec, 0);
  5101. end;
  5102. {$ENDIF}
  5103. function DateTimeToFileDate(DateTime: TDateTime): Integer;
  5104. {$IFDEF MSWINDOWS}
  5105. var
  5106. Year, Month, Day, Hour, Min, Sec, MSec: Word;
  5107. begin
  5108. DecodeDate(DateTime, Year, Month, Day);
  5109. if (Year < 1980) or (Year > 2107) then Result := 0 else
  5110. begin
  5111. DecodeTime(DateTime, Hour, Min, Sec, MSec);
  5112. LongRec(Result).Lo := (Sec shr 1) or (Min shl 5) or (Hour shl 11);
  5113. LongRec(Result).Hi := Day or (Month shl 5) or ((Year - 1980) shl 9);
  5114. end;
  5115. end;
  5116. {$ENDIF}
  5117. {$IFDEF LINUX}
  5118. var
  5119. tm: TUnixTime;
  5120. Year, Month, Day, Hour, Min, Sec, MSec: Word;
  5121. begin
  5122. DecodeDate(DateTime, Year, Month, Day);
  5123. { Valid range for 32 bit Unix time_t: 1970 through 2038 }
  5124. if (Year < 1970) or (Year > 2038) then
  5125. Result := 0
  5126. else
  5127. begin
  5128. DecodeTime(DateTime, Hour, Min, Sec, MSec);
  5129. FillChar(tm, sizeof(tm), 0);
  5130. with tm do
  5131. begin
  5132. tm_sec := Sec;
  5133. tm_min := Min;
  5134. tm_hour := Hour;
  5135. tm_mday := Day;
  5136. tm_mon := Month - 1;
  5137. tm_year := Year - 1900;
  5138. tm_isdst := -1;
  5139. end;
  5140. Result := mktime(tm);
  5141. end;
  5142. end;
  5143. {$ENDIF}
  5144. function GetCurrentDir: string;
  5145. begin
  5146. GetDir(0, Result);
  5147. end;
  5148. function SetCurrentDir(const Dir: string): Boolean;
  5149. begin
  5150. {$IFDEF MSWINDOWS}
  5151. Result := SetCurrentDirectory(PChar(Dir));
  5152. {$ENDIF}
  5153. {$IFDEF LINUX}
  5154. Result := __chdir(PChar(Dir)) = 0;
  5155. {$ENDIF}
  5156. end;
  5157. function CreateDir(const Dir: string): Boolean;
  5158. begin
  5159. {$IFDEF MSWINDOWS}
  5160. Result := CreateDirectory(PChar(Dir), nil);
  5161. {$ENDIF}
  5162. {$IFDEF LINUX}
  5163. Result := __mkdir(PChar(Dir), mode_t(-1)) = 0;
  5164. {$ENDIF}
  5165. end;
  5166. function RemoveDir(const Dir: string): Boolean;
  5167. begin
  5168. {$IFDEF MSWINDOWS}
  5169. Result := RemoveDirectory(PChar(Dir));
  5170. {$ENDIF}
  5171. {$IFDEF LINUX}
  5172. Result := __rmdir(PChar(Dir)) = 0;
  5173. {$ENDIF}
  5174. end;
  5175. { PChar routines }
  5176. function StrLen(const Str: PChar): Cardinal; assembler;
  5177. asm
  5178. MOV EDX,EDI
  5179. MOV EDI,EAX
  5180. MOV ECX,0FFFFFFFFH
  5181. XOR AL,AL
  5182. REPNE SCASB
  5183. MOV EAX,0FFFFFFFEH
  5184. SUB EAX,ECX
  5185. MOV EDI,EDX
  5186. end;
  5187. function StrEnd(const Str: PChar): PChar; assembler;
  5188. asm
  5189. MOV EDX,EDI
  5190. MOV EDI,EAX
  5191. MOV ECX,0FFFFFFFFH
  5192. XOR AL,AL
  5193. REPNE SCASB
  5194. LEA EAX,[EDI-1]
  5195. MOV EDI,EDX
  5196. end;
  5197. function StrMove(Dest: PChar; const Source: PChar; Count: Cardinal): PChar;
  5198. begin
  5199. Result := Dest;
  5200. Move(Source^, Dest^, Count);
  5201. end;
  5202. function StrCopy(Dest: PChar; const Source: PChar): PChar;
  5203. asm
  5204. PUSH EDI
  5205. PUSH ESI
  5206. MOV ESI,EAX
  5207. MOV EDI,EDX
  5208. MOV ECX,0FFFFFFFFH
  5209. XOR AL,AL
  5210. REPNE SCASB
  5211. NOT ECX
  5212. MOV EDI,ESI
  5213. MOV ESI,EDX
  5214. MOV EDX,ECX
  5215. MOV EAX,EDI
  5216. SHR ECX,2
  5217. REP MOVSD
  5218. MOV ECX,EDX
  5219. AND ECX,3
  5220. REP MOVSB
  5221. POP ESI
  5222. POP EDI
  5223. end;
  5224. function StrECopy(Dest: PChar; const Source: PChar): PChar; assembler;
  5225. asm
  5226. PUSH EDI
  5227. PUSH ESI
  5228. MOV ESI,EAX
  5229. MOV EDI,EDX
  5230. MOV ECX,0FFFFFFFFH
  5231. XOR AL,AL
  5232. REPNE SCASB
  5233. NOT ECX
  5234. MOV EDI,ESI
  5235. MOV ESI,EDX
  5236. MOV EDX,ECX
  5237. SHR ECX,2
  5238. REP MOVSD
  5239. MOV ECX,EDX
  5240. AND ECX,3
  5241. REP MOVSB
  5242. LEA EAX,[EDI-1]
  5243. POP ESI
  5244. POP EDI
  5245. end;
  5246. function StrLCopy(Dest: PChar; const Source: PChar; MaxLen: Cardinal): PChar; assembler;
  5247. asm
  5248. PUSH EDI
  5249. PUSH ESI
  5250. PUSH EBX
  5251. MOV ESI,EAX
  5252. MOV EDI,EDX
  5253. MOV EBX,ECX
  5254. XOR AL,AL
  5255. TEST ECX,ECX
  5256. JZ @@1
  5257. REPNE SCASB
  5258. JNE @@1
  5259. INC ECX
  5260. @@1: SUB EBX,ECX
  5261. MOV EDI,ESI
  5262. MOV ESI,EDX
  5263. MOV EDX,EDI
  5264. MOV ECX,EBX
  5265. SHR ECX,2
  5266. REP MOVSD
  5267. MOV ECX,EBX
  5268. AND ECX,3
  5269. REP MOVSB
  5270. STOSB
  5271. MOV EAX,EDX
  5272. POP EBX
  5273. POP ESI
  5274. POP EDI
  5275. end;
  5276. function StrPCopy(Dest: PChar; const Source: string): PChar;
  5277. begin
  5278. Result := StrLCopy(Dest, PChar(Source), Length(Source));
  5279. end;
  5280. function StrPLCopy(Dest: PChar; const Source: string;
  5281. MaxLen: Cardinal): PChar;
  5282. begin
  5283. Result := StrLCopy(Dest, PChar(Source), MaxLen);
  5284. end;
  5285. function StrCat(Dest: PChar; const Source: PChar): PChar;
  5286. begin
  5287. StrCopy(StrEnd(Dest), Source);
  5288. Result := Dest;
  5289. end;
  5290. function StrLCat(Dest: PChar; const Source: PChar; MaxLen: Cardinal): PChar; assembler;
  5291. asm
  5292. PUSH EDI
  5293. PUSH ESI
  5294. PUSH EBX
  5295. MOV EDI,Dest
  5296. MOV ESI,Source
  5297. MOV EBX,MaxLen
  5298. CALL StrEnd
  5299. MOV ECX,EDI
  5300. ADD ECX,EBX
  5301. SUB ECX,EAX
  5302. JBE @@1
  5303. MOV EDX,ESI
  5304. CALL StrLCopy
  5305. @@1: MOV EAX,EDI
  5306. POP EBX
  5307. POP ESI
  5308. POP EDI
  5309. end;
  5310. function StrComp(const Str1, Str2: PChar): Integer; assembler;
  5311. asm
  5312. PUSH EDI
  5313. PUSH ESI
  5314. MOV EDI,EDX
  5315. MOV ESI,EAX
  5316. MOV ECX,0FFFFFFFFH
  5317. XOR EAX,EAX
  5318. REPNE SCASB
  5319. NOT ECX
  5320. MOV EDI,EDX
  5321. XOR EDX,EDX
  5322. REPE CMPSB
  5323. MOV AL,[ESI-1]
  5324. MOV DL,[EDI-1]
  5325. SUB EAX,EDX
  5326. POP ESI
  5327. POP EDI
  5328. end;
  5329. function StrIComp(const Str1, Str2: PChar): Integer; assembler;
  5330. asm
  5331. PUSH EDI
  5332. PUSH ESI
  5333. MOV EDI,EDX
  5334. MOV ESI,EAX
  5335. MOV ECX,0FFFFFFFFH
  5336. XOR EAX,EAX
  5337. REPNE SCASB
  5338. NOT ECX
  5339. MOV EDI,EDX
  5340. XOR EDX,EDX
  5341. @@1: REPE CMPSB
  5342. JE @@4
  5343. MOV AL,[ESI-1]
  5344. CMP AL,'a'
  5345. JB @@2
  5346. CMP AL,'z'
  5347. JA @@2
  5348. SUB AL,20H
  5349. @@2: MOV DL,[EDI-1]
  5350. CMP DL,'a'
  5351. JB @@3
  5352. CMP DL,'z'
  5353. JA @@3
  5354. SUB DL,20H
  5355. @@3: SUB EAX,EDX
  5356. JE @@1
  5357. @@4: POP ESI
  5358. POP EDI
  5359. end;
  5360. function StrLComp(const Str1, Str2: PChar; MaxLen: Cardinal): Integer; assembler;
  5361. asm
  5362. PUSH EDI
  5363. PUSH ESI
  5364. PUSH EBX
  5365. MOV EDI,EDX
  5366. MOV ESI,EAX
  5367. MOV EBX,ECX
  5368. XOR EAX,EAX
  5369. OR ECX,ECX
  5370. JE @@1
  5371. REPNE SCASB
  5372. SUB EBX,ECX
  5373. MOV ECX,EBX
  5374. MOV EDI,EDX
  5375. XOR EDX,EDX
  5376. REPE CMPSB
  5377. MOV AL,[ESI-1]
  5378. MOV DL,[EDI-1]
  5379. SUB EAX,EDX
  5380. @@1: POP EBX
  5381. POP ESI
  5382. POP EDI
  5383. end;
  5384. function StrLIComp(const Str1, Str2: PChar; MaxLen: Cardinal): Integer; assembler;
  5385. asm
  5386. PUSH EDI
  5387. PUSH ESI
  5388. PUSH EBX
  5389. MOV EDI,EDX
  5390. MOV ESI,EAX
  5391. MOV EBX,ECX
  5392. XOR EAX,EAX
  5393. OR ECX,ECX
  5394. JE @@4
  5395. REPNE SCASB
  5396. SUB EBX,ECX
  5397. MOV ECX,EBX
  5398. MOV EDI,EDX
  5399. XOR EDX,EDX
  5400. @@1: REPE CMPSB
  5401. JE @@4
  5402. MOV AL,[ESI-1]
  5403. CMP AL,'a'
  5404. JB @@2
  5405. CMP AL,'z'
  5406. JA @@2
  5407. SUB AL,20H
  5408. @@2: MOV DL,[EDI-1]
  5409. CMP DL,'a'
  5410. JB @@3
  5411. CMP DL,'z'
  5412. JA @@3
  5413. SUB DL,20H
  5414. @@3: SUB EAX,EDX
  5415. JE @@1
  5416. @@4: POP EBX
  5417. POP ESI
  5418. POP EDI
  5419. end;
  5420. function StrScan(const Str: PChar; Chr: Char): PChar;
  5421. begin
  5422. Result := Str;
  5423. while Result^ <> Chr do
  5424. begin
  5425. if Result^ = #0 then
  5426. begin
  5427. Result := nil;
  5428. Exit;
  5429. end;
  5430. Inc(Result);
  5431. end;
  5432. end;
  5433. function StrRScan(const Str: PChar; Chr: Char): PChar;
  5434. var
  5435. MostRecentFound: PChar;
  5436. begin
  5437. if Chr = #0 then
  5438. Result := StrEnd(Str)
  5439. else
  5440. begin
  5441. Result := nil;
  5442. MostRecentFound := Str;
  5443. while True do
  5444. begin
  5445. while MostRecentFound^ <> Chr do
  5446. begin
  5447. if MostRecentFound^ = #0 then
  5448. Exit;
  5449. Inc(MostRecentFound);
  5450. end;
  5451. Result := MostRecentFound;
  5452. Inc(MostRecentFound);
  5453. end;
  5454. end;
  5455. end;
  5456. function StrPos(const Str1, Str2: PChar): PChar; assembler;
  5457. asm
  5458. PUSH EDI
  5459. PUSH ESI
  5460. PUSH EBX
  5461. OR EAX,EAX
  5462. JE @@2
  5463. OR EDX,EDX
  5464. JE @@2
  5465. MOV EBX,EAX
  5466. MOV EDI,EDX
  5467. XOR AL,AL
  5468. MOV ECX,0FFFFFFFFH
  5469. REPNE SCASB
  5470. NOT ECX
  5471. DEC ECX
  5472. JE @@2
  5473. MOV ESI,ECX
  5474. MOV EDI,EBX
  5475. MOV ECX,0FFFFFFFFH
  5476. REPNE SCASB
  5477. NOT ECX
  5478. SUB ECX,ESI
  5479. JBE @@2
  5480. MOV EDI,EBX
  5481. LEA EBX,[ESI-1]
  5482. @@1: MOV ESI,EDX
  5483. LODSB
  5484. REPNE SCASB
  5485. JNE @@2
  5486. MOV EAX,ECX
  5487. PUSH EDI
  5488. MOV ECX,EBX
  5489. REPE CMPSB
  5490. POP EDI
  5491. MOV ECX,EAX
  5492. JNE @@1
  5493. LEA EAX,[EDI-1]
  5494. JMP @@3
  5495. @@2: XOR EAX,EAX
  5496. @@3: POP EBX
  5497. POP ESI
  5498. POP EDI
  5499. end;
  5500. function StrUpper(Str: PChar): PChar; assembler;
  5501. asm
  5502. PUSH ESI
  5503. MOV ESI,Str
  5504. MOV EDX,Str
  5505. @@1: LODSB
  5506. OR AL,AL
  5507. JE @@2
  5508. CMP AL,'a'
  5509. JB @@1
  5510. CMP AL,'z'
  5511. JA @@1
  5512. SUB AL,20H
  5513. MOV [ESI-1],AL
  5514. JMP @@1
  5515. @@2: XCHG EAX,EDX
  5516. POP ESI
  5517. end;
  5518. function StrLower(Str: PChar): PChar; assembler;
  5519. asm
  5520. PUSH ESI
  5521. MOV ESI,Str
  5522. MOV EDX,Str
  5523. @@1: LODSB
  5524. OR AL,AL
  5525. JE @@2
  5526. CMP AL,'A'
  5527. JB @@1
  5528. CMP AL,'Z'
  5529. JA @@1
  5530. ADD AL,20H
  5531. MOV [ESI-1],AL
  5532. JMP @@1
  5533. @@2: XCHG EAX,EDX
  5534. POP ESI
  5535. end;
  5536. function StrPas(const Str: PChar): string;
  5537. begin
  5538. Result := Str;
  5539. end;
  5540. function StrAlloc(Size: Cardinal): PChar;
  5541. begin
  5542. Inc(Size, SizeOf(Cardinal));
  5543. GetMem(Result, Size);
  5544. Cardinal(Pointer(Result)^) := Size;
  5545. Inc(Result, SizeOf(Cardinal));
  5546. end;
  5547. function StrBufSize(const Str: PChar): Cardinal;
  5548. var
  5549. P: PChar;
  5550. begin
  5551. P := Str;
  5552. Dec(P, SizeOf(Cardinal));
  5553. Result := Cardinal(Pointer(P)^) - SizeOf(Cardinal);
  5554. end;
  5555. function StrNew(const Str: PChar): PChar;
  5556. var
  5557. Size: Cardinal;
  5558. begin
  5559. if Str = nil then Result := nil else
  5560. begin
  5561. Size := StrLen(Str) + 1;
  5562. Result := StrMove(StrAlloc(Size), Str, Size);
  5563. end;
  5564. end;
  5565. procedure StrDispose(Str: PChar);
  5566. begin
  5567. if Str <> nil then
  5568. begin
  5569. Dec(Str, SizeOf(Cardinal));
  5570. FreeMem(Str, Cardinal(Pointer(Str)^));
  5571. end;
  5572. end;
  5573. { String formatting routines }
  5574. procedure FormatError(ErrorCode: Integer; Format: PChar; FmtLen: Cardinal);
  5575. const
  5576. FormatErrorStrs: array[0..1] of string = (
  5577. SInvalidFormat, SArgumentMissing);
  5578. var
  5579. Buffer: array[0..31] of Char;
  5580. begin
  5581. if FmtLen > 31 then FmtLen := 31;
  5582. if StrByteType(Format, FmtLen-1) = mbLeadByte then Dec(FmtLen);
  5583. StrMove(Buffer, Format, FmtLen);
  5584. Buffer[FmtLen] := #0;
  5585. ConvertErrorFmt(FormatErrorStrs[ErrorCode], [PChar(@Buffer)]);
  5586. end;
  5587. procedure FormatVarToStr(var S: string; const V: Variant);
  5588. begin
  5589. {if Assigned(System.VarToLStr) then
  5590. System.is(S, V)
  5591. else
  5592. System.Error(reVarInvalidOp); }
  5593. S:='';
  5594. end;
  5595. procedure FormatClearStr(var S: string);
  5596. begin
  5597. S := '';
  5598. end;
  5599. function FloatToTextEx(BufferArg: PChar; const Value; ValueType: TFloatValue;
  5600. Format: TFloatFormat; Precision, Digits: Integer;
  5601. const FormatSettings: TFormatSettings): Integer;
  5602. begin
  5603. Result := FloatToText(BufferArg, Value, ValueType, Format, Precision, Digits,
  5604. FormatSettings);
  5605. end;
  5606. function FormatBuf(var Buffer; BufLen: Cardinal; const Format;
  5607. FmtLen: Cardinal; const Args: array of const): Cardinal;
  5608. var
  5609. ArgIndex, Width, Prec: Integer;
  5610. BufferOrg, FormatOrg, FormatPtr, TempStr: PChar;
  5611. JustFlag: Byte;
  5612. StrBuf: array[0..64] of Char;
  5613. TempAnsiStr: string;
  5614. SaveGOT: Integer;
  5615. { in: eax <-> Buffer }
  5616. { in: edx <-> BufLen }
  5617. { in: ecx <-> Format }
  5618. asm
  5619. PUSH EBX
  5620. PUSH ESI
  5621. PUSH EDI
  5622. MOV EDI,EAX
  5623. MOV ESI,ECX
  5624. {$IFDEF PIC}
  5625. PUSH ECX
  5626. CALL GetGOT
  5627. POP ECX
  5628. {$ELSE}
  5629. XOR EAX,EAX
  5630. {$ENDIF}
  5631. MOV SaveGOT,EAX
  5632. ADD ECX,FmtLen
  5633. MOV BufferOrg,EDI
  5634. XOR EAX,EAX
  5635. MOV ArgIndex,EAX
  5636. MOV TempStr,EAX
  5637. MOV TempAnsiStr,EAX
  5638. @Loop:
  5639. OR EDX,EDX
  5640. JE @Done
  5641. @NextChar:
  5642. CMP ESI,ECX
  5643. JE @Done
  5644. LODSB
  5645. CMP AL,'%'
  5646. JE @Format
  5647. @StoreChar:
  5648. STOSB
  5649. DEC EDX
  5650. JNE @NextChar
  5651. @Done:
  5652. MOV EAX,EDI
  5653. SUB EAX,BufferOrg
  5654. JMP @Exit
  5655. @Format:
  5656. CMP ESI,ECX
  5657. JE @Done
  5658. LODSB
  5659. CMP AL,'%'
  5660. JE @StoreChar
  5661. LEA EBX,[ESI-2]
  5662. MOV FormatOrg,EBX
  5663. @A0: MOV JustFlag,AL
  5664. CMP AL,'-'
  5665. JNE @A1
  5666. CMP ESI,ECX
  5667. JE @Done
  5668. LODSB
  5669. @A1: CALL @Specifier
  5670. CMP AL,':'
  5671. JNE @A2
  5672. MOV ArgIndex,EBX
  5673. CMP ESI,ECX
  5674. JE @Done
  5675. LODSB
  5676. JMP @A0
  5677. @A2: MOV Width,EBX
  5678. MOV EBX,-1
  5679. CMP AL,'.'
  5680. JNE @A3
  5681. CMP ESI,ECX
  5682. JE @Done
  5683. LODSB
  5684. CALL @Specifier
  5685. @A3: MOV Prec,EBX
  5686. MOV FormatPtr,ESI
  5687. PUSH ECX
  5688. PUSH EDX
  5689. CALL @Convert
  5690. POP EDX
  5691. MOV EBX,Width
  5692. SUB EBX,ECX //(* ECX <=> number of characters output *)
  5693. JAE @A4 //(* jump -> output smaller than width *)
  5694. XOR EBX,EBX
  5695. @A4: CMP JustFlag,'-'
  5696. JNE @A6
  5697. SUB EDX,ECX
  5698. JAE @A5
  5699. ADD ECX,EDX
  5700. XOR EDX,EDX
  5701. @A5: REP MOVSB
  5702. @A6: XCHG EBX,ECX
  5703. SUB EDX,ECX
  5704. JAE @A7
  5705. ADD ECX,EDX
  5706. XOR EDX,EDX
  5707. @A7: MOV AL,' '
  5708. REP STOSB
  5709. XCHG EBX,ECX
  5710. SUB EDX,ECX
  5711. JAE @A8
  5712. ADD ECX,EDX
  5713. XOR EDX,EDX
  5714. @A8: REP MOVSB
  5715. CMP TempStr,0
  5716. JE @A9
  5717. PUSH EDX
  5718. LEA EAX,TempStr
  5719. // PUSH EBX // GOT setup unnecessary for
  5720. // MOV EBX, SaveGOT // same-unit calls to Pascal procedures
  5721. CALL FormatClearStr
  5722. // POP EBX
  5723. POP EDX
  5724. @A9: POP ECX
  5725. MOV ESI,FormatPtr
  5726. JMP @Loop
  5727. @Specifier:
  5728. XOR EBX,EBX
  5729. CMP AL,'*'
  5730. JE @B3
  5731. @B1: CMP AL,'0'
  5732. JB @B5
  5733. CMP AL,'9'
  5734. JA @B5
  5735. IMUL EBX,EBX,10
  5736. SUB AL,'0'
  5737. MOVZX EAX,AL
  5738. ADD EBX,EAX
  5739. CMP ESI,ECX
  5740. JE @B2
  5741. LODSB
  5742. JMP @B1
  5743. @B2: POP EAX
  5744. JMP @Done
  5745. @B3: MOV EAX,ArgIndex
  5746. CMP EAX,Args.Integer[-4]
  5747. JG @B4
  5748. INC ArgIndex
  5749. MOV EBX,Args
  5750. CMP [EBX+EAX*8].Byte[4],vtInteger
  5751. MOV EBX,[EBX+EAX*8]
  5752. JE @B4
  5753. XOR EBX,EBX
  5754. @B4: CMP ESI,ECX
  5755. JE @B2
  5756. LODSB
  5757. @B5: RET
  5758. @Convert:
  5759. AND AL,0DFH
  5760. MOV CL,AL
  5761. MOV EAX,1
  5762. MOV EBX,ArgIndex
  5763. CMP EBX,Args.Integer[-4]
  5764. JG @ErrorExit
  5765. INC ArgIndex
  5766. MOV ESI,Args
  5767. LEA ESI,[ESI+EBX*8]
  5768. MOV EAX,[ESI].Integer[0] // TVarRec.data
  5769. MOVZX EDX,[ESI].Byte[4] // TVarRec.VType
  5770. {$IFDEF PIC}
  5771. MOV EBX, SaveGOT
  5772. ADD EBX, offset @CvtVector
  5773. MOV EBX, [EBX+EDX*4]
  5774. ADD EBX, SaveGOT
  5775. JMP EBX
  5776. {$ELSE}
  5777. JMP @CvtVector.Pointer[EDX*4]
  5778. {$ENDIF}
  5779. @CvtVector:
  5780. DD @CvtInteger // vtInteger
  5781. DD @CvtBoolean // vtBoolean
  5782. DD @CvtChar // vtChar
  5783. DD @CvtExtended // vtExtended
  5784. DD @CvtShortStr // vtString
  5785. DD @CvtPointer // vtPointer
  5786. DD @CvtPChar // vtPChar
  5787. DD @CvtObject // vtObject
  5788. DD @CvtClass // vtClass
  5789. DD @CvtWideChar // vtWideChar
  5790. DD @CvtPWideChar // vtPWideChar
  5791. DD @CvtAnsiStr // vtAnsiString
  5792. DD @CvtCurrency // vtCurrency
  5793. DD @CvtVariant // vtVariant
  5794. DD @CvtInterface // vtInterface
  5795. DD @CvtWideString // vtWideString
  5796. DD @CvtInt64 // vtInt64
  5797. @CvtBoolean:
  5798. @CvtObject:
  5799. @CvtClass:
  5800. @CvtWideChar:
  5801. @CvtInterface:
  5802. @CvtError:
  5803. XOR EAX,EAX
  5804. @ErrorExit:
  5805. CALL @ClearTmpAnsiStr
  5806. MOV EDX,FormatOrg
  5807. MOV ECX,FormatPtr
  5808. SUB ECX,EDX
  5809. {$IFDEF PC_MAPPED_EXCEPTIONS}
  5810. // Because of all the assembly code here, we can't call a routine
  5811. // that throws an exception if it looks like we're still on the
  5812. // stack. The static disassembler cannot give sufficient unwind
  5813. // frame info to unwind the confusion that is generated from the
  5814. // assembly code above. So before we throw the exception, we
  5815. // go to some lengths to excise ourselves from the stack chain.
  5816. // We were passed 12 bytes of parameters on the stack, and we have
  5817. // to make sure that we get rid of those, too.
  5818. MOV EBX, SaveGOT
  5819. MOV ESP, EBP // Ditch everthing to the frame
  5820. MOV EBP, [ESP + 4] // Get the return addr
  5821. MOV [ESP + 16], EBP // Move the ret addr up in the stack
  5822. POP EBP // Ditch the rest of the frame
  5823. ADD ESP, 12 // Ditch the space that was taken by params
  5824. JMP FormatError // Off to FormatErr
  5825. {$ELSE}
  5826. MOV EBX, SaveGOT
  5827. CALL FormatError
  5828. {$ENDIF}
  5829. // The above call raises an exception and does not return
  5830. @CvtInt64:
  5831. // CL <= format character
  5832. // EAX <= address of int64
  5833. // EBX <= TVarRec.VType
  5834. LEA ESI,StrBuf[32]
  5835. MOV EDX, Prec
  5836. CMP EDX, 32
  5837. JBE @I64_1 // zero padded field width > buffer => no padding
  5838. XOR EDX, EDX
  5839. @I64_1: MOV EBX, ECX
  5840. SUB CL, 'D'
  5841. JZ CvtInt64 // branch predict backward jump taken
  5842. MOV ECX, 16
  5843. CMP BL, 'X'
  5844. JE CvtInt64
  5845. MOV ECX, 10
  5846. CMP BL, 'U'
  5847. JE CvtInt64
  5848. JMP @CvtError
  5849. { LEA EBX, TempInt64 // (input is array of const; save original)
  5850. MOV EDX, [EAX]
  5851. MOV [EBX], EDX
  5852. MOV EDX, [EAX + 4]
  5853. MOV [EBX + 4], EDX
  5854. // EBX <= address of TempInt64
  5855. CMP CL,'D'
  5856. JE @DecI64
  5857. CMP CL,'U'
  5858. JE @DecI64_2
  5859. CMP CL,'X'
  5860. JNE @CvtError
  5861. @HexI64:
  5862. MOV ECX,16 // hex divisor
  5863. JMP @CvtI64
  5864. @DecI64:
  5865. TEST DWORD PTR [EBX + 4], $80000000 // sign bit set?
  5866. JE @DecI64_2 // no -> bypass '-' output
  5867. NEG DWORD PTR [EBX] // negate lo-order, then hi-order
  5868. ADC DWORD PTR [EBX+4], 0
  5869. NEG DWORD PTR [EBX+4]
  5870. CALL @DecI64_2
  5871. MOV AL,'-'
  5872. INC ECX
  5873. DEC ESI
  5874. MOV [ESI],AL
  5875. RET
  5876. @DecI64_2: // unsigned int64 output
  5877. MOV ECX,10 // decimal divisor
  5878. @CvtI64:
  5879. LEA ESI,StrBuf[32]
  5880. @CvtI64_1:
  5881. PUSH EBX
  5882. PUSH ECX // save radix
  5883. PUSH 0
  5884. PUSH ECX // radix divisor (10 or 16 only)
  5885. MOV EAX, [EBX]
  5886. MOV EDX, [EBX + 4]
  5887. MOV EBX, SaveGOT
  5888. CALL System.@_llumod
  5889. POP ECX // saved radix
  5890. POP EBX
  5891. XCHG EAX, EDX // lo-value to EDX for character output
  5892. ADD DL,'0'
  5893. CMP DL,'0'+10
  5894. JB @CvtI64_2
  5895. ADD DL,('A'-'0')-10
  5896. @CvtI64_2:
  5897. DEC ESI
  5898. MOV [ESI],DL
  5899. PUSH EBX
  5900. PUSH ECX // save radix
  5901. PUSH 0
  5902. PUSH ECX // radix divisor (10 or 16 only)
  5903. MOV EAX, [EBX] // value := value DIV radix
  5904. MOV EDX, [EBX + 4]
  5905. MOV EBX, SaveGOT
  5906. CALL System.@_lludiv
  5907. POP ECX // saved radix
  5908. POP EBX
  5909. MOV [EBX], EAX
  5910. MOV [EBX + 4], EDX
  5911. OR EAX,EDX // anything left to output?
  5912. JNE @CvtI64_1 // no jump => EDX:EAX = 0
  5913. LEA ECX,StrBuf[32]
  5914. SUB ECX,ESI
  5915. MOV EDX,Prec
  5916. CMP EDX,16
  5917. JBE @CvtI64_3
  5918. RET
  5919. @CvtI64_3:
  5920. SUB EDX,ECX
  5921. JBE @CvtI64_5
  5922. ADD ECX,EDX
  5923. MOV AL,'0'
  5924. @CvtI64_4:
  5925. DEC ESI
  5926. MOV [ESI],AL
  5927. DEC EDX
  5928. JNE @CvtI64_4
  5929. @CvtI64_5:
  5930. RET
  5931. }
  5932. ////////////////////////////////////////////////
  5933. @CvtInteger:
  5934. LEA ESI,StrBuf[16]
  5935. MOV EDX, Prec
  5936. MOV EBX, ECX
  5937. CMP EDX, 16
  5938. JBE @C1 // zero padded field width > buffer => no padding
  5939. XOR EDX, EDX
  5940. @C1: SUB CL, 'D'
  5941. JZ CvtInt // branch predict backward jump taken
  5942. MOV ECX, 16
  5943. CMP BL, 'X'
  5944. JE CvtInt
  5945. MOV ECX, 10
  5946. CMP BL, 'U'
  5947. JE CvtInt
  5948. JMP @CvtError
  5949. { CMP CL,'D'
  5950. JE @C1
  5951. CMP CL,'U'
  5952. JE @C2
  5953. CMP CL,'X'
  5954. JNE @CvtError
  5955. MOV ECX,16
  5956. JMP @CvtLong
  5957. @C1: OR EAX,EAX
  5958. JNS @C2
  5959. NEG EAX
  5960. CALL @C2
  5961. MOV AL,'-'
  5962. INC ECX
  5963. DEC ESI
  5964. MOV [ESI],AL
  5965. RET
  5966. @C2: MOV ECX,10
  5967. @CvtLong:
  5968. LEA ESI,StrBuf[16]
  5969. @D1: XOR EDX,EDX
  5970. DIV ECX
  5971. ADD DL,'0'
  5972. CMP DL,'0'+10
  5973. JB @D2
  5974. ADD DL,('A'-'0')-10
  5975. @D2: DEC ESI
  5976. MOV [ESI],DL
  5977. OR EAX,EAX
  5978. JNE @D1
  5979. LEA ECX,StrBuf[16]
  5980. SUB ECX,ESI
  5981. MOV EDX,Prec
  5982. CMP EDX,16
  5983. JBE @D3
  5984. RET
  5985. @D3: SUB EDX,ECX
  5986. JBE @D5
  5987. ADD ECX,EDX
  5988. MOV AL,'0'
  5989. @D4: DEC ESI
  5990. MOV [ESI],AL
  5991. DEC EDX
  5992. JNE @D4
  5993. @D5: RET
  5994. }
  5995. @CvtChar:
  5996. CMP CL,'S'
  5997. JNE @CvtError
  5998. MOV ECX,1
  5999. RET
  6000. @CvtVariant:
  6001. CMP CL,'S'
  6002. JNE @CvtError
  6003. CMP [EAX].TVarData.VType,varNull
  6004. JBE @CvtEmptyStr
  6005. MOV EDX,EAX
  6006. LEA EAX,TempStr
  6007. // PUSH EBX // GOT setup unnecessary for
  6008. // MOV EBX, SaveGOT // same-unit calls to Pascal procedures
  6009. CALL FormatVarToStr
  6010. // POP EBX
  6011. MOV ESI,TempStr
  6012. JMP @CvtStrRef
  6013. @CvtEmptyStr:
  6014. XOR ECX,ECX
  6015. RET
  6016. @CvtShortStr:
  6017. CMP CL,'S'
  6018. JNE @CvtError
  6019. MOV ESI,EAX
  6020. LODSB
  6021. MOVZX ECX,AL
  6022. JMP @CvtStrLen
  6023. @CvtPWideChar:
  6024. MOV ESI,OFFSET System.@LStrFromPWChar
  6025. JMP @CvtWideThing
  6026. @CvtWideString:
  6027. MOV ESI,OFFSET System.@LStrFromWStr
  6028. @CvtWideThing:
  6029. ADD ESI, SaveGOT
  6030. {$IFDEF PIC}
  6031. MOV ESI, [ESI]
  6032. {$ENDIF}
  6033. CMP CL,'S'
  6034. JNE @CvtError
  6035. MOV EDX,EAX
  6036. LEA EAX,TempAnsiStr
  6037. PUSH EBX
  6038. MOV EBX, SaveGOT
  6039. CALL ESI
  6040. POP EBX
  6041. MOV ESI,TempAnsiStr
  6042. MOV EAX,ESI
  6043. JMP @CvtStrRef
  6044. @CvtAnsiStr:
  6045. CMP CL,'S'
  6046. JNE @CvtError
  6047. MOV ESI,EAX
  6048. @CvtStrRef:
  6049. OR ESI,ESI
  6050. JE @CvtEmptyStr
  6051. MOV ECX,[ESI-4]
  6052. @CvtStrLen:
  6053. CMP ECX,Prec
  6054. JA @E1
  6055. RET
  6056. @E1: MOV ECX,Prec
  6057. RET
  6058. @CvtPChar:
  6059. CMP CL,'S'
  6060. JNE @CvtError
  6061. MOV ESI,EAX
  6062. PUSH EDI
  6063. MOV EDI,EAX
  6064. XOR AL,AL
  6065. MOV ECX,Prec
  6066. JECXZ @F1
  6067. REPNE SCASB
  6068. JNE @F1
  6069. DEC EDI
  6070. @F1: MOV ECX,EDI
  6071. SUB ECX,ESI
  6072. POP EDI
  6073. RET
  6074. @CvtPointer:
  6075. CMP CL,'P'
  6076. JNE @CvtError
  6077. MOV EDX,8
  6078. MOV ECX,16
  6079. LEA ESI,StrBuf[16]
  6080. JMP CvtInt
  6081. @CvtCurrency:
  6082. MOV BH,fvCurrency
  6083. JMP @CvtFloat
  6084. @CvtExtended:
  6085. MOV BH,fvExtended
  6086. @CvtFloat:
  6087. MOV ESI,EAX
  6088. MOV BL,ffGeneral
  6089. CMP CL,'G'
  6090. JE @G2
  6091. MOV BL,ffExponent
  6092. CMP CL,'E'
  6093. JE @G2
  6094. MOV BL,ffFixed
  6095. CMP CL,'F'
  6096. JE @G1
  6097. MOV BL,ffNumber
  6098. CMP CL,'N'
  6099. JE @G1
  6100. CMP CL,'M'
  6101. JNE @CvtError
  6102. MOV BL,ffCurrency
  6103. @G1: MOV EAX,18
  6104. MOV EDX,Prec
  6105. CMP EDX,EAX
  6106. JBE @G3
  6107. MOV EDX,2
  6108. CMP CL,'M'
  6109. JNE @G3
  6110. MOVZX EDX,CurrencyDecimals
  6111. JMP @G3
  6112. @G2: MOV EAX,Prec
  6113. MOV EDX,3
  6114. CMP EAX,18
  6115. JBE @G3
  6116. MOV EAX,15
  6117. @G3: PUSH EBX
  6118. PUSH EAX
  6119. PUSH EDX
  6120. LEA EAX,StrBuf
  6121. MOV EDX,ESI
  6122. MOVZX ECX,BH
  6123. MOV EBX, SaveGOT
  6124. CALL FloatToText
  6125. MOV ECX,EAX
  6126. LEA ESI,StrBuf
  6127. RET
  6128. @ClearTmpAnsiStr:
  6129. PUSH EBX
  6130. PUSH EAX
  6131. LEA EAX,TempAnsiStr
  6132. MOV EBX, SaveGOT
  6133. CALL System.@LStrClr
  6134. POP EAX
  6135. POP EBX
  6136. RET
  6137. @Exit:
  6138. CALL @ClearTmpAnsiStr
  6139. POP EDI
  6140. POP ESI
  6141. POP EBX
  6142. end;
  6143. function FormatBuf(var Buffer; BufLen: Cardinal; const Format;
  6144. FmtLen: Cardinal; const Args: array of const;
  6145. const FormatSettings: TFormatSettings): Cardinal;
  6146. var
  6147. ArgIndex, Width, Prec: Integer;
  6148. BufferOrg, FormatOrg, FormatPtr, TempStr: PChar;
  6149. JustFlag: Byte;
  6150. StrBuf: array[0..64] of Char;
  6151. TempAnsiStr: string;
  6152. SaveGOT: Integer;
  6153. { in: eax <-> Buffer }
  6154. { in: edx <-> BufLen }
  6155. { in: ecx <-> Format }
  6156. asm
  6157. PUSH EBX
  6158. PUSH ESI
  6159. PUSH EDI
  6160. MOV EDI,EAX
  6161. MOV ESI,ECX
  6162. {$IFDEF PIC}
  6163. PUSH ECX
  6164. CALL GetGOT
  6165. POP ECX
  6166. {$ELSE}
  6167. XOR EAX,EAX
  6168. {$ENDIF}
  6169. MOV SaveGOT,EAX
  6170. ADD ECX,FmtLen
  6171. MOV BufferOrg,EDI
  6172. XOR EAX,EAX
  6173. MOV ArgIndex,EAX
  6174. MOV TempStr,EAX
  6175. MOV TempAnsiStr,EAX
  6176. @Loop:
  6177. OR EDX,EDX
  6178. JE @Done
  6179. @NextChar:
  6180. CMP ESI,ECX
  6181. JE @Done
  6182. LODSB
  6183. CMP AL,'%'
  6184. JE @Format
  6185. @StoreChar:
  6186. STOSB
  6187. DEC EDX
  6188. JNE @NextChar
  6189. @Done:
  6190. MOV EAX,EDI
  6191. SUB EAX,BufferOrg
  6192. JMP @Exit
  6193. @Format:
  6194. CMP ESI,ECX
  6195. JE @Done
  6196. LODSB
  6197. CMP AL,'%'
  6198. JE @StoreChar
  6199. LEA EBX,[ESI-2]
  6200. MOV FormatOrg,EBX
  6201. @A0: MOV JustFlag,AL
  6202. CMP AL,'-'
  6203. JNE @A1
  6204. CMP ESI,ECX
  6205. JE @Done
  6206. LODSB
  6207. @A1: CALL @Specifier
  6208. CMP AL,':'
  6209. JNE @A2
  6210. MOV ArgIndex,EBX
  6211. CMP ESI,ECX
  6212. JE @Done
  6213. LODSB
  6214. JMP @A0
  6215. @A2: MOV Width,EBX
  6216. MOV EBX,-1
  6217. CMP AL,'.'
  6218. JNE @A3
  6219. CMP ESI,ECX
  6220. JE @Done
  6221. LODSB
  6222. CALL @Specifier
  6223. @A3: MOV Prec,EBX
  6224. MOV FormatPtr,ESI
  6225. PUSH ECX
  6226. PUSH EDX
  6227. CALL @Convert
  6228. POP EDX
  6229. MOV EBX,Width
  6230. SUB EBX,ECX //(* ECX <=> number of characters output *)
  6231. JAE @A4 //(* jump -> output smaller than width *)
  6232. XOR EBX,EBX
  6233. @A4: CMP JustFlag,'-'
  6234. JNE @A6
  6235. SUB EDX,ECX
  6236. JAE @A5
  6237. ADD ECX,EDX
  6238. XOR EDX,EDX
  6239. @A5: REP MOVSB
  6240. @A6: XCHG EBX,ECX
  6241. SUB EDX,ECX
  6242. JAE @A7
  6243. ADD ECX,EDX
  6244. XOR EDX,EDX
  6245. @A7: MOV AL,' '
  6246. REP STOSB
  6247. XCHG EBX,ECX
  6248. SUB EDX,ECX
  6249. JAE @A8
  6250. ADD ECX,EDX
  6251. XOR EDX,EDX
  6252. @A8: REP MOVSB
  6253. CMP TempStr,0
  6254. JE @A9
  6255. PUSH EDX
  6256. LEA EAX,TempStr
  6257. // PUSH EBX // GOT setup unnecessary for
  6258. // MOV EBX, SaveGOT // same-unit calls to Pascal procedures
  6259. CALL FormatClearStr
  6260. // POP EBX
  6261. POP EDX
  6262. @A9: POP ECX
  6263. MOV ESI,FormatPtr
  6264. JMP @Loop
  6265. @Specifier:
  6266. XOR EBX,EBX
  6267. CMP AL,'*'
  6268. JE @B3
  6269. @B1: CMP AL,'0'
  6270. JB @B5
  6271. CMP AL,'9'
  6272. JA @B5
  6273. IMUL EBX,EBX,10
  6274. SUB AL,'0'
  6275. MOVZX EAX,AL
  6276. ADD EBX,EAX
  6277. CMP ESI,ECX
  6278. JE @B2
  6279. LODSB
  6280. JMP @B1
  6281. @B2: POP EAX
  6282. JMP @Done
  6283. @B3: MOV EAX,ArgIndex
  6284. CMP EAX,Args.Integer[-4]
  6285. JG @B4
  6286. INC ArgIndex
  6287. MOV EBX,Args
  6288. CMP [EBX+EAX*8].Byte[4],vtInteger
  6289. MOV EBX,[EBX+EAX*8]
  6290. JE @B4
  6291. XOR EBX,EBX
  6292. @B4: CMP ESI,ECX
  6293. JE @B2
  6294. LODSB
  6295. @B5: RET
  6296. @Convert:
  6297. AND AL,0DFH
  6298. MOV CL,AL
  6299. MOV EAX,1
  6300. MOV EBX,ArgIndex
  6301. CMP EBX,Args.Integer[-4]
  6302. JG @ErrorExit
  6303. INC ArgIndex
  6304. MOV ESI,Args
  6305. LEA ESI,[ESI+EBX*8]
  6306. MOV EAX,[ESI].Integer[0] // TVarRec.data
  6307. MOVZX EDX,[ESI].Byte[4] // TVarRec.VType
  6308. {$IFDEF PIC}
  6309. MOV EBX, SaveGOT
  6310. ADD EBX, offset @CvtVector
  6311. MOV EBX, [EBX+EDX*4]
  6312. ADD EBX, SaveGOT
  6313. JMP EBX
  6314. {$ELSE}
  6315. JMP @CvtVector.Pointer[EDX*4]
  6316. {$ENDIF}
  6317. @CvtVector:
  6318. DD @CvtInteger // vtInteger
  6319. DD @CvtBoolean // vtBoolean
  6320. DD @CvtChar // vtChar
  6321. DD @CvtExtended // vtExtended
  6322. DD @CvtShortStr // vtString
  6323. DD @CvtPointer // vtPointer
  6324. DD @CvtPChar // vtPChar
  6325. DD @CvtObject // vtObject
  6326. DD @CvtClass // vtClass
  6327. DD @CvtWideChar // vtWideChar
  6328. DD @CvtPWideChar // vtPWideChar
  6329. DD @CvtAnsiStr // vtAnsiString
  6330. DD @CvtCurrency // vtCurrency
  6331. DD @CvtVariant // vtVariant
  6332. DD @CvtInterface // vtInterface
  6333. DD @CvtWideString // vtWideString
  6334. DD @CvtInt64 // vtInt64
  6335. @CvtBoolean:
  6336. @CvtObject:
  6337. @CvtClass:
  6338. @CvtWideChar:
  6339. @CvtInterface:
  6340. @CvtError:
  6341. XOR EAX,EAX
  6342. @ErrorExit:
  6343. CALL @ClearTmpAnsiStr
  6344. MOV EDX,FormatOrg
  6345. MOV ECX,FormatPtr
  6346. SUB ECX,EDX
  6347. {$IFDEF PC_MAPPED_EXCEPTIONS}
  6348. // Because of all the assembly code here, we can't call a routine
  6349. // that throws an exception if it looks like we're still on the
  6350. // stack. The static disassembler cannot give sufficient unwind
  6351. // frame info to unwind the confusion that is generated from the
  6352. // assembly code above. So before we throw the exception, we
  6353. // go to some lengths to excise ourselves from the stack chain.
  6354. // We were passed 12 bytes of parameters on the stack, and we have
  6355. // to make sure that we get rid of those, too.
  6356. MOV EBX, SaveGOT
  6357. MOV ESP, EBP // Ditch everthing to the frame
  6358. MOV EBP, [ESP + 4] // Get the return addr
  6359. MOV [ESP + 16], EBP // Move the ret addr up in the stack
  6360. POP EBP // Ditch the rest of the frame
  6361. ADD ESP, 12 // Ditch the space that was taken by params
  6362. JMP FormatError // Off to FormatErr
  6363. {$ELSE}
  6364. MOV EBX, SaveGOT
  6365. CALL FormatError
  6366. {$ENDIF}
  6367. // The above call raises an exception and does not return
  6368. @CvtInt64:
  6369. // CL <= format character
  6370. // EAX <= address of int64
  6371. // EBX <= TVarRec.VType
  6372. LEA ESI,StrBuf[32]
  6373. MOV EDX, Prec
  6374. CMP EDX, 32
  6375. JBE @I64_1 // zero padded field width > buffer => no padding
  6376. XOR EDX, EDX
  6377. @I64_1: MOV EBX, ECX
  6378. SUB CL, 'D'
  6379. JZ CvtInt64 // branch predict backward jump taken
  6380. MOV ECX, 16
  6381. CMP BL, 'X'
  6382. JE CvtInt64
  6383. MOV ECX, 10
  6384. CMP BL, 'U'
  6385. JE CvtInt64
  6386. JMP @CvtError
  6387. ////////////////////////////////////////////////
  6388. @CvtInteger:
  6389. LEA ESI,StrBuf[16]
  6390. MOV EDX, Prec
  6391. MOV EBX, ECX
  6392. CMP EDX, 16
  6393. JBE @C1 // zero padded field width > buffer => no padding
  6394. XOR EDX, EDX
  6395. @C1: SUB CL, 'D'
  6396. JZ CvtInt // branch predict backward jump taken
  6397. MOV ECX, 16
  6398. CMP BL, 'X'
  6399. JE CvtInt
  6400. MOV ECX, 10
  6401. CMP BL, 'U'
  6402. JE CvtInt
  6403. JMP @CvtError
  6404. @CvtChar:
  6405. CMP CL,'S'
  6406. JNE @CvtError
  6407. MOV ECX,1
  6408. RET
  6409. @CvtVariant:
  6410. CMP CL,'S'
  6411. JNE @CvtError
  6412. CMP [EAX].TVarData.VType,varNull
  6413. JBE @CvtEmptyStr
  6414. MOV EDX,EAX
  6415. LEA EAX,TempStr
  6416. // PUSH EBX // GOT setup unnecessary for
  6417. // MOV EBX, SaveGOT // same-unit calls to Pascal procedures
  6418. CALL FormatVarToStr
  6419. // POP EBX
  6420. MOV ESI,TempStr
  6421. JMP @CvtStrRef
  6422. @CvtEmptyStr:
  6423. XOR ECX,ECX
  6424. RET
  6425. @CvtShortStr:
  6426. CMP CL,'S'
  6427. JNE @CvtError
  6428. MOV ESI,EAX
  6429. LODSB
  6430. MOVZX ECX,AL
  6431. JMP @CvtStrLen
  6432. @CvtPWideChar:
  6433. MOV ESI,OFFSET System.@LStrFromPWChar
  6434. JMP @CvtWideThing
  6435. @CvtWideString:
  6436. MOV ESI,OFFSET System.@LStrFromWStr
  6437. @CvtWideThing:
  6438. ADD ESI, SaveGOT
  6439. {$IFDEF PIC}
  6440. MOV ESI, [ESI]
  6441. {$ENDIF}
  6442. CMP CL,'S'
  6443. JNE @CvtError
  6444. MOV EDX,EAX
  6445. LEA EAX,TempAnsiStr
  6446. PUSH EBX
  6447. MOV EBX, SaveGOT
  6448. CALL ESI
  6449. POP EBX
  6450. MOV ESI,TempAnsiStr
  6451. MOV EAX,ESI
  6452. JMP @CvtStrRef
  6453. @CvtAnsiStr:
  6454. CMP CL,'S'
  6455. JNE @CvtError
  6456. MOV ESI,EAX
  6457. @CvtStrRef:
  6458. OR ESI,ESI
  6459. JE @CvtEmptyStr
  6460. MOV ECX,[ESI-4]
  6461. @CvtStrLen:
  6462. CMP ECX,Prec
  6463. JA @E1
  6464. RET
  6465. @E1: MOV ECX,Prec
  6466. RET
  6467. @CvtPChar:
  6468. CMP CL,'S'
  6469. JNE @CvtError
  6470. MOV ESI,EAX
  6471. PUSH EDI
  6472. MOV EDI,EAX
  6473. XOR AL,AL
  6474. MOV ECX,Prec
  6475. JECXZ @F1
  6476. REPNE SCASB
  6477. JNE @F1
  6478. DEC EDI
  6479. @F1: MOV ECX,EDI
  6480. SUB ECX,ESI
  6481. POP EDI
  6482. RET
  6483. @CvtPointer:
  6484. CMP CL,'P'
  6485. JNE @CvtError
  6486. MOV EDX,8
  6487. MOV ECX,16
  6488. LEA ESI,StrBuf[16]
  6489. JMP CvtInt
  6490. @CvtCurrency:
  6491. MOV BH,fvCurrency
  6492. JMP @CvtFloat
  6493. @CvtExtended:
  6494. MOV BH,fvExtended
  6495. @CvtFloat:
  6496. MOV ESI,EAX
  6497. MOV BL,ffGeneral
  6498. CMP CL,'G'
  6499. JE @G2
  6500. MOV BL,ffExponent
  6501. CMP CL,'E'
  6502. JE @G2
  6503. MOV BL,ffFixed
  6504. CMP CL,'F'
  6505. JE @G1
  6506. MOV BL,ffNumber
  6507. CMP CL,'N'
  6508. JE @G1
  6509. CMP CL,'M'
  6510. JNE @CvtError
  6511. MOV BL,ffCurrency
  6512. @G1: MOV EAX,18
  6513. MOV EDX,Prec
  6514. CMP EDX,EAX
  6515. JBE @G3
  6516. MOV EDX,2
  6517. CMP CL,'M'
  6518. JNE @G3
  6519. MOV EDX,FormatSettings
  6520. MOVZX EDX,[EDX].TFormatSettings.CurrencyDecimals
  6521. JMP @G3
  6522. @G2: MOV EAX,Prec
  6523. MOV EDX,3
  6524. CMP EAX,18
  6525. JBE @G3
  6526. MOV EAX,15
  6527. @G3: PUSH EBX
  6528. PUSH EAX
  6529. PUSH EDX
  6530. MOV EDX,[FormatSettings]
  6531. PUSH EDX
  6532. LEA EAX,StrBuf
  6533. MOV EDX,ESI
  6534. MOVZX ECX,BH
  6535. MOV EBX, SaveGOT
  6536. CALL FloatToTextEx
  6537. MOV ECX,EAX
  6538. LEA ESI,StrBuf
  6539. RET
  6540. @ClearTmpAnsiStr:
  6541. PUSH EBX
  6542. PUSH EAX
  6543. LEA EAX,TempAnsiStr
  6544. MOV EBX, SaveGOT
  6545. CALL System.@LStrClr
  6546. POP EAX
  6547. POP EBX
  6548. RET
  6549. @Exit:
  6550. CALL @ClearTmpAnsiStr
  6551. POP EDI
  6552. POP ESI
  6553. POP EBX
  6554. end;
  6555. function StrFmt(Buffer, Format: PChar; const Args: array of const): PChar;
  6556. begin
  6557. if (Buffer <> nil) and (Format <> nil) then
  6558. begin
  6559. Buffer[FormatBuf(Buffer^, MaxInt, Format^, StrLen(Format), Args)] := #0;
  6560. Result := Buffer;
  6561. end
  6562. else
  6563. Result := nil;
  6564. end;
  6565. function StrFmt(Buffer, Format: PChar; const Args: array of const;
  6566. const FormatSettings: TFormatSettings): PChar;
  6567. begin
  6568. if (Buffer <> nil) and (Format <> nil) then
  6569. begin
  6570. Buffer[FormatBuf(Buffer^, MaxInt, Format^, StrLen(Format), Args,
  6571. FormatSettings)] := #0;
  6572. Result := Buffer;
  6573. end
  6574. else
  6575. Result := nil;
  6576. end;
  6577. function StrLFmt(Buffer: PChar; MaxBufLen: Cardinal; Format: PChar;
  6578. const Args: array of const): PChar;
  6579. begin
  6580. if (Buffer <> nil) and (Format <> nil) then
  6581. begin
  6582. Buffer[FormatBuf(Buffer^, MaxBufLen, Format^, StrLen(Format), Args)] := #0;
  6583. Result := Buffer;
  6584. end
  6585. else
  6586. Result := nil;
  6587. end;
  6588. function StrLFmt(Buffer: PChar; MaxBufLen: Cardinal; Format: PChar;
  6589. const Args: array of const; const FormatSettings: TFormatSettings): PChar;
  6590. begin
  6591. if (Buffer <> nil) and (Format <> nil) then
  6592. begin
  6593. Buffer[FormatBuf(Buffer^, MaxBufLen, Format^, StrLen(Format), Args,
  6594. FormatSettings)] := #0;
  6595. Result := Buffer;
  6596. end
  6597. else
  6598. Result := nil;
  6599. end;
  6600. function Format(const Format: string; const Args: array of const): string;
  6601. begin
  6602. FmtStr(Result, Format, Args);
  6603. end;
  6604. function Format(const Format: string; const Args: array of const;
  6605. const FormatSettings: TFormatSettings): string;
  6606. begin
  6607. FmtStr(Result, Format, Args, FormatSettings);
  6608. end;
  6609. procedure FmtStr(var Result: string; const Format: string;
  6610. const Args: array of const);
  6611. var
  6612. Len, BufLen: Integer;
  6613. Buffer: array[0..4095] of Char;
  6614. begin
  6615. BufLen := SizeOf(Buffer);
  6616. if Length(Format) < (sizeof(Buffer) - (sizeof(Buffer) div 4)) then
  6617. Len := FormatBuf(Buffer, sizeof(Buffer) - 1, Pointer(Format)^, Length(Format), Args)
  6618. else
  6619. begin
  6620. BufLen := Length(Format);
  6621. Len := BufLen;
  6622. end;
  6623. if Len >= BufLen - 1 then
  6624. begin
  6625. while Len >= BufLen - 1 do
  6626. begin
  6627. Inc(BufLen, BufLen);
  6628. Result := ''; // prevent copying of existing data, for speed
  6629. SetLength(Result, BufLen);
  6630. Len := FormatBuf(Pointer(Result)^, BufLen - 1, Pointer(Format)^,
  6631. Length(Format), Args);
  6632. end;
  6633. SetLength(Result, Len);
  6634. end
  6635. else
  6636. SetString(Result, Buffer, Len);
  6637. end;
  6638. procedure FmtStr(var Result: string; const Format: string;
  6639. const Args: array of const; const FormatSettings: TFormatSettings);
  6640. var
  6641. Len, BufLen: Integer;
  6642. Buffer: array[0..4095] of Char;
  6643. begin
  6644. BufLen := SizeOf(Buffer);
  6645. if Length(Format) < (sizeof(Buffer) - (sizeof(Buffer) div 4)) then
  6646. Len := FormatBuf(Buffer, sizeof(Buffer) - 1, Pointer(Format)^, Length(Format),
  6647. Args, FormatSettings)
  6648. else
  6649. begin
  6650. BufLen := Length(Format);
  6651. Len := BufLen;
  6652. end;
  6653. if Len >= BufLen - 1 then
  6654. begin
  6655. while Len >= BufLen - 1 do
  6656. begin
  6657. Inc(BufLen, BufLen);
  6658. Result := ''; // prevent copying of existing data, for speed
  6659. SetLength(Result, BufLen);
  6660. Len := FormatBuf(Pointer(Result)^, BufLen - 1, Pointer(Format)^,
  6661. Length(Format), Args, FormatSettings);
  6662. end;
  6663. SetLength(Result, Len);
  6664. end
  6665. else
  6666. SetString(Result, Buffer, Len);
  6667. end;
  6668. procedure WideFormatError(ErrorCode: Integer; Format: PWideChar;
  6669. FmtLen: Cardinal);
  6670. var
  6671. WideFormat: WideString;
  6672. FormatText: string;
  6673. begin
  6674. SetLength(WideFormat, FmtLen);
  6675. SetString(WideFormat, Format, FmtLen);
  6676. FormatText := WideFormat;
  6677. FormatError(ErrorCode, PChar(FormatText), FmtLen);
  6678. end;
  6679. procedure WideFormatVarToStr(var S: WideString; const V: TVarData);
  6680. begin
  6681. {if Assigned(System.VarToWStrProc) then
  6682. System.VarToWStrProc(S, V)
  6683. else
  6684. System.Error(reVarInvalidOp); }
  6685. S:='Cutted';
  6686. end;
  6687. function WideFormatBuf(var Buffer; BufLen: Cardinal; const Format;
  6688. FmtLen: Cardinal; const Args: array of const): Cardinal;
  6689. var
  6690. ArgIndex, Width, Prec: Integer;
  6691. BufferOrg, FormatOrg, FormatPtr: PWideChar;
  6692. JustFlag: WideChar;
  6693. StrBuf: array[0..64] of WideChar;
  6694. TempWideStr: WideString;
  6695. SaveGOT: Integer;
  6696. { in: eax <-> Buffer }
  6697. { in: edx <-> BufLen }
  6698. { in: ecx <-> Format }
  6699. asm
  6700. PUSH EBX
  6701. PUSH ESI
  6702. PUSH EDI
  6703. MOV EDI,EAX
  6704. MOV ESI,ECX
  6705. {$IFDEF PIC}
  6706. CALL GetGOT
  6707. {$ELSE}
  6708. XOR EAX,EAX
  6709. {$ENDIF}
  6710. MOV SaveGOT,EAX
  6711. MOV ECX,FmtLen
  6712. LEA ECX,[ECX*2+ESI]
  6713. MOV BufferOrg,EDI
  6714. XOR EAX,EAX
  6715. MOV ArgIndex,EAX
  6716. MOV TempWideStr,EAX
  6717. @Loop:
  6718. OR EDX,EDX
  6719. JE @Done
  6720. @NextChar:
  6721. CMP ESI,ECX
  6722. JE @Done
  6723. LODSW
  6724. CMP AX,'%'
  6725. JE @Format
  6726. @StoreChar:
  6727. STOSW
  6728. DEC EDX
  6729. JNE @NextChar
  6730. @Done:
  6731. MOV EAX,EDI
  6732. SUB EAX,BufferOrg
  6733. SHR EAX,1
  6734. JMP @Exit
  6735. @Format:
  6736. CMP ESI,ECX
  6737. JE @Done
  6738. LODSW
  6739. CMP AX,'%'
  6740. JE @StoreChar
  6741. LEA EBX,[ESI-4]
  6742. MOV FormatOrg,EBX
  6743. @A0: MOV JustFlag,AX
  6744. CMP AX,'-'
  6745. JNE @A1
  6746. CMP ESI,ECX
  6747. JE @Done
  6748. LODSW
  6749. @A1: CALL @Specifier
  6750. CMP AX,':'
  6751. JNE @A2
  6752. MOV ArgIndex,EBX
  6753. CMP ESI,ECX
  6754. JE @Done
  6755. LODSW
  6756. JMP @A0
  6757. @A2: MOV Width,EBX
  6758. MOV EBX,-1
  6759. CMP AX,'.'
  6760. JNE @A3
  6761. CMP ESI,ECX
  6762. JE @Done
  6763. LODSW
  6764. CALL @Specifier
  6765. @A3: MOV Prec,EBX
  6766. MOV FormatPtr,ESI
  6767. PUSH ECX
  6768. PUSH EDX
  6769. CALL @Convert
  6770. POP EDX
  6771. MOV EBX,Width
  6772. SUB EBX,ECX //(* ECX <=> number of characters output *)
  6773. JAE @A4 //(* jump -> output smaller than width *)
  6774. XOR EBX,EBX
  6775. @A4: CMP JustFlag,'-'
  6776. JNE @A6
  6777. SUB EDX,ECX
  6778. JAE @A5
  6779. ADD ECX,EDX
  6780. XOR EDX,EDX
  6781. @A5: REP MOVSW
  6782. @A6: XCHG EBX,ECX
  6783. SUB EDX,ECX
  6784. JAE @A7
  6785. ADD ECX,EDX
  6786. XOR EDX,EDX
  6787. @A7: MOV AX,' '
  6788. REP STOSW
  6789. XCHG EBX,ECX
  6790. SUB EDX,ECX
  6791. JAE @A8
  6792. ADD ECX,EDX
  6793. XOR EDX,EDX
  6794. @A8: REP MOVSW
  6795. POP ECX
  6796. MOV ESI,FormatPtr
  6797. JMP @Loop
  6798. @Specifier:
  6799. XOR EBX,EBX
  6800. CMP AX,'*'
  6801. JE @B3
  6802. @B1: CMP AX,'0'
  6803. JB @B5
  6804. CMP AX,'9'
  6805. JA @B5
  6806. IMUL EBX,EBX,10
  6807. SUB AX,'0'
  6808. MOVZX EAX,AX
  6809. ADD EBX,EAX
  6810. CMP ESI,ECX
  6811. JE @B2
  6812. LODSW
  6813. JMP @B1
  6814. @B2: POP EAX
  6815. JMP @Done
  6816. @B3: MOV EAX,ArgIndex
  6817. CMP EAX,Args.Integer[-4]
  6818. JG @B4
  6819. INC ArgIndex
  6820. MOV EBX,Args
  6821. CMP [EBX+EAX*8].Byte[4],vtInteger
  6822. MOV EBX,[EBX+EAX*8]
  6823. JE @B4
  6824. XOR EBX,EBX
  6825. @B4: CMP ESI,ECX
  6826. JE @B2
  6827. LODSW
  6828. @B5: RET
  6829. @Convert:
  6830. AND AL,0DFH
  6831. MOV CL,AL
  6832. MOV EAX,1
  6833. MOV EBX,ArgIndex
  6834. CMP EBX,Args.Integer[-4]
  6835. JG @ErrorExit
  6836. INC ArgIndex
  6837. MOV ESI,Args
  6838. LEA ESI,[ESI+EBX*8]
  6839. MOV EAX,[ESI].Integer[0] // TVarRec.data
  6840. MOVZX EDX,[ESI].Byte[4] // TVarRec.VType
  6841. {$IFDEF PIC}
  6842. MOV EBX, SaveGOT
  6843. ADD EBX, offset @CvtVector
  6844. MOV EBX, [EBX+EDX*4]
  6845. ADD EBX, SaveGOT
  6846. JMP EBX
  6847. {$ELSE}
  6848. JMP @CvtVector.Pointer[EDX*4]
  6849. {$ENDIF}
  6850. @CvtVector:
  6851. DD @CvtInteger // vtInteger
  6852. DD @CvtBoolean // vtBoolean
  6853. DD @CvtChar // vtChar
  6854. DD @CvtExtended // vtExtended
  6855. DD @CvtShortStr // vtString
  6856. DD @CvtPointer // vtPointer
  6857. DD @CvtPChar // vtPChar
  6858. DD @CvtObject // vtObject
  6859. DD @CvtClass // vtClass
  6860. DD @CvtWideChar // vtWideChar
  6861. DD @CvtPWideChar // vtPWideChar
  6862. DD @CvtAnsiStr // vtAnsiString
  6863. DD @CvtCurrency // vtCurrency
  6864. DD @CvtVariant // vtVariant
  6865. DD @CvtInterface // vtInterface
  6866. DD @CvtWideString // vtWideString
  6867. DD @CvtInt64 // vtInt64
  6868. @CvtBoolean:
  6869. @CvtObject:
  6870. @CvtClass:
  6871. @CvtInterface:
  6872. @CvtError:
  6873. XOR EAX,EAX
  6874. @ErrorExit:
  6875. CALL @ClearTmpWideStr
  6876. MOV EDX,FormatOrg
  6877. MOV ECX,FormatPtr
  6878. SUB ECX,EDX
  6879. SHR ECX,1
  6880. MOV EBX, SaveGOT
  6881. {$IFDEF PC_MAPPED_EXCEPTIONS}
  6882. // Because of all the assembly code here, we can't call a routine
  6883. // that throws an exception if it looks like we're still on the
  6884. // stack. The static disassembler cannot give sufficient unwind
  6885. // frame info to unwind the confusion that is generated from the
  6886. // assembly code above. So before we throw the exception, we
  6887. // go to some lengths to excise ourselves from the stack chain.
  6888. // We were passed 12 bytes of parameters on the stack, and we have
  6889. // to make sure that we get rid of those, too.
  6890. MOV ESP, EBP // Ditch everthing to the frame
  6891. MOV EBP, [ESP + 4] // Get the return addr
  6892. MOV [ESP + 16], EBP // Move the ret addr up in the stack
  6893. POP EBP // Ditch the rest of the frame
  6894. ADD ESP, 12 // Ditch the space that was taken by params
  6895. JMP WideFormatError // Off to FormatErr
  6896. {$ELSE}
  6897. CALL WideFormatError
  6898. {$ENDIF}
  6899. // The above call raises an exception and does not return
  6900. @CvtInt64:
  6901. // CL <= format character
  6902. // EAX <= address of int64
  6903. // EBX <= TVarRec.VType
  6904. LEA ESI,StrBuf[64]
  6905. MOV EDX, Prec
  6906. CMP EDX, 32
  6907. JBE @I64_1 // zero padded field width > buffer => no padding
  6908. XOR EDX, EDX
  6909. @I64_1: MOV EBX, ECX
  6910. SUB CL, 'D'
  6911. JZ CvtInt64W // branch predict backward jump taken
  6912. MOV ECX, 16
  6913. CMP BL, 'X'
  6914. JE CvtInt64W
  6915. MOV ECX, 10
  6916. CMP BL, 'U'
  6917. JE CvtInt64W
  6918. JMP @CvtError
  6919. @CvtInteger:
  6920. LEA ESI,StrBuf[32]
  6921. MOV EDX, Prec
  6922. MOV EBX, ECX
  6923. CMP EDX, 16
  6924. JBE @C1 // zero padded field width > buffer => no padding
  6925. XOR EDX, EDX
  6926. @C1: SUB CL, 'D'
  6927. JZ CvtIntW // branch predict backward jump taken
  6928. MOV ECX, 16
  6929. CMP BL, 'X'
  6930. JE CvtIntW
  6931. MOV ECX, 10
  6932. CMP BL, 'U'
  6933. JE CvtIntW
  6934. JMP @CvtError
  6935. @CvtChar:
  6936. CMP CL,'S'
  6937. JNE @CvtError
  6938. MOV EAX,ESI
  6939. MOV ECX,1
  6940. JMP @CvtAnsiThingLen
  6941. @CvtWideChar:
  6942. CMP CL,'S'
  6943. JNE @CvtError
  6944. MOV ECX,1
  6945. RET
  6946. @CvtVariant:
  6947. CMP CL,'S'
  6948. JNE @CvtError
  6949. CMP [EAX].TVarData.VType,varNull
  6950. JBE @CvtEmptyStr
  6951. MOV EDX,EAX
  6952. LEA EAX,TempWideStr
  6953. CALL WideFormatVarToStr
  6954. MOV ESI,TempWideStr
  6955. JMP @CvtWideStrRef
  6956. @CvtEmptyStr:
  6957. XOR ECX,ECX
  6958. RET
  6959. @CvtShortStr:
  6960. CMP CL,'S'
  6961. JNE @CvtError
  6962. MOVZX ECX,BYTE PTR [EAX]
  6963. INC EAX
  6964. @CvtAnsiThingLen:
  6965. MOV ESI,OFFSET System.@WStrFromPCharLen
  6966. JMP @CvtAnsiThing
  6967. @CvtPChar:
  6968. MOV ESI,OFFSET System.@WStrFromPChar
  6969. JMP @CvtAnsiThingTest
  6970. @CvtAnsiStr:
  6971. MOV ESI,OFFSET System.@WStrFromLStr
  6972. @CvtAnsiThingTest:
  6973. CMP CL,'S'
  6974. JNE @CvtError
  6975. @CvtAnsiThing:
  6976. ADD ESI, SaveGOT
  6977. {$IFDEF PIC}
  6978. MOV ESI, [ESI]
  6979. {$ENDIF}
  6980. MOV EDX,EAX
  6981. LEA EAX,TempWideStr
  6982. PUSH EBX
  6983. MOV EBX, SaveGOT
  6984. CALL ESI
  6985. POP EBX
  6986. MOV ESI,TempWideStr
  6987. JMP @CvtWideStrRef
  6988. @CvtWideString:
  6989. CMP CL,'S'
  6990. JNE @CvtError
  6991. MOV ESI,EAX
  6992. @CvtWideStrRef:
  6993. OR ESI,ESI
  6994. JE @CvtEmptyStr
  6995. MOV ECX,[ESI-4]
  6996. SHR ECX,1
  6997. @CvtWideStrLen:
  6998. CMP ECX,Prec
  6999. JA @E1
  7000. RET
  7001. @E1: MOV ECX,Prec
  7002. RET
  7003. @CvtPWideChar:
  7004. CMP CL,'S'
  7005. JNE @CvtError
  7006. MOV ESI,EAX
  7007. PUSH EDI
  7008. MOV EDI,EAX
  7009. XOR EAX,EAX
  7010. MOV ECX,Prec
  7011. JECXZ @F1
  7012. REPNE SCASW
  7013. JNE @F1
  7014. DEC EDI
  7015. DEC EDI
  7016. @F1: MOV ECX,EDI
  7017. SUB ECX,ESI
  7018. SHR ECX,1
  7019. POP EDI
  7020. RET
  7021. @CvtPointer:
  7022. CMP CL,'P'
  7023. JNE @CvtError
  7024. MOV EDX,8
  7025. MOV ECX,16
  7026. LEA ESI,StrBuf[32]
  7027. JMP CvtInt
  7028. @CvtCurrency:
  7029. MOV BH,fvCurrency
  7030. JMP @CvtFloat
  7031. @CvtExtended:
  7032. MOV BH,fvExtended
  7033. @CvtFloat:
  7034. MOV ESI,EAX
  7035. MOV BL,ffGeneral
  7036. CMP CL,'G'
  7037. JE @G2
  7038. MOV BL,ffExponent
  7039. CMP CL,'E'
  7040. JE @G2
  7041. MOV BL,ffFixed
  7042. CMP CL,'F'
  7043. JE @G1
  7044. MOV BL,ffNumber
  7045. CMP CL,'N'
  7046. JE @G1
  7047. CMP CL,'M'
  7048. JNE @CvtError
  7049. MOV BL,ffCurrency
  7050. @G1: MOV EAX,18
  7051. MOV EDX,Prec
  7052. CMP EDX,EAX
  7053. JBE @G3
  7054. MOV EDX,2
  7055. CMP CL,'M'
  7056. JNE @G3
  7057. MOVZX EDX,CurrencyDecimals
  7058. JMP @G3
  7059. @G2: MOV EAX,Prec
  7060. MOV EDX,3
  7061. CMP EAX,18
  7062. JBE @G3
  7063. MOV EAX,15
  7064. @G3: PUSH EBX
  7065. PUSH EAX
  7066. PUSH EDX
  7067. LEA EAX,StrBuf
  7068. MOV EDX,ESI
  7069. MOVZX ECX,BH
  7070. MOV EBX, SaveGOT
  7071. CALL FloatToText
  7072. MOV ECX,EAX
  7073. LEA EDX,StrBuf
  7074. LEA EAX,TempWideStr
  7075. MOV EBX, SaveGOT
  7076. CALL System.@WStrFromPCharLen
  7077. MOV ESI,TempWideStr
  7078. OR ESI,ESI
  7079. JE @CvtEmptyStr
  7080. MOV ECX,[ESI-4]
  7081. SHR ECX,1
  7082. RET
  7083. @ClearTmpWideStr:
  7084. PUSH EBX
  7085. PUSH EAX
  7086. LEA EAX,TempWideStr
  7087. MOV EBX, SaveGOT
  7088. CALL System.@WStrClr
  7089. POP EAX
  7090. POP EBX
  7091. RET
  7092. @Exit:
  7093. CALL @ClearTmpWideStr
  7094. POP EDI
  7095. POP ESI
  7096. POP EBX
  7097. end;
  7098. function WideFormatBuf(var Buffer; BufLen: Cardinal; const Format;
  7099. FmtLen: Cardinal; const Args: array of const;
  7100. const FormatSettings: TFormatSettings): Cardinal;
  7101. var
  7102. ArgIndex, Width, Prec: Integer;
  7103. BufferOrg, FormatOrg, FormatPtr: PWideChar;
  7104. JustFlag: WideChar;
  7105. StrBuf: array[0..64] of WideChar;
  7106. TempWideStr: WideString;
  7107. SaveGOT: Integer;
  7108. { in: eax <-> Buffer }
  7109. { in: edx <-> BufLen }
  7110. { in: ecx <-> Format }
  7111. asm
  7112. PUSH EBX
  7113. PUSH ESI
  7114. PUSH EDI
  7115. MOV EDI,EAX
  7116. MOV ESI,ECX
  7117. {$IFDEF PIC}
  7118. CALL GetGOT
  7119. {$ELSE}
  7120. XOR EAX,EAX
  7121. {$ENDIF}
  7122. MOV SaveGOT,EAX
  7123. MOV ECX,FmtLen
  7124. LEA ECX,[ECX*2+ESI]
  7125. MOV BufferOrg,EDI
  7126. XOR EAX,EAX
  7127. MOV ArgIndex,EAX
  7128. MOV TempWideStr,EAX
  7129. @Loop:
  7130. OR EDX,EDX
  7131. JE @Done
  7132. @NextChar:
  7133. CMP ESI,ECX
  7134. JE @Done
  7135. LODSW
  7136. CMP AX,'%'
  7137. JE @Format
  7138. @StoreChar:
  7139. STOSW
  7140. DEC EDX
  7141. JNE @NextChar
  7142. @Done:
  7143. MOV EAX,EDI
  7144. SUB EAX,BufferOrg
  7145. SHR EAX,1
  7146. JMP @Exit
  7147. @Format:
  7148. CMP ESI,ECX
  7149. JE @Done
  7150. LODSW
  7151. CMP AX,'%'
  7152. JE @StoreChar
  7153. LEA EBX,[ESI-4]
  7154. MOV FormatOrg,EBX
  7155. @A0: MOV JustFlag,AX
  7156. CMP AX,'-'
  7157. JNE @A1
  7158. CMP ESI,ECX
  7159. JE @Done
  7160. LODSW
  7161. @A1: CALL @Specifier
  7162. CMP AX,':'
  7163. JNE @A2
  7164. MOV ArgIndex,EBX
  7165. CMP ESI,ECX
  7166. JE @Done
  7167. LODSW
  7168. JMP @A0
  7169. @A2: MOV Width,EBX
  7170. MOV EBX,-1
  7171. CMP AX,'.'
  7172. JNE @A3
  7173. CMP ESI,ECX
  7174. JE @Done
  7175. LODSW
  7176. CALL @Specifier
  7177. @A3: MOV Prec,EBX
  7178. MOV FormatPtr,ESI
  7179. PUSH ECX
  7180. PUSH EDX
  7181. CALL @Convert
  7182. POP EDX
  7183. MOV EBX,Width
  7184. SUB EBX,ECX //(* ECX <=> number of characters output *)
  7185. JAE @A4 //(* jump -> output smaller than width *)
  7186. XOR EBX,EBX
  7187. @A4: CMP JustFlag,'-'
  7188. JNE @A6
  7189. SUB EDX,ECX
  7190. JAE @A5
  7191. ADD ECX,EDX
  7192. XOR EDX,EDX
  7193. @A5: REP MOVSW
  7194. @A6: XCHG EBX,ECX
  7195. SUB EDX,ECX
  7196. JAE @A7
  7197. ADD ECX,EDX
  7198. XOR EDX,EDX
  7199. @A7: MOV AX,' '
  7200. REP STOSW
  7201. XCHG EBX,ECX
  7202. SUB EDX,ECX
  7203. JAE @A8
  7204. ADD ECX,EDX
  7205. XOR EDX,EDX
  7206. @A8: REP MOVSW
  7207. POP ECX
  7208. MOV ESI,FormatPtr
  7209. JMP @Loop
  7210. @Specifier:
  7211. XOR EBX,EBX
  7212. CMP AX,'*'
  7213. JE @B3
  7214. @B1: CMP AX,'0'
  7215. JB @B5
  7216. CMP AX,'9'
  7217. JA @B5
  7218. IMUL EBX,EBX,10
  7219. SUB AX,'0'
  7220. MOVZX EAX,AX
  7221. ADD EBX,EAX
  7222. CMP ESI,ECX
  7223. JE @B2
  7224. LODSW
  7225. JMP @B1
  7226. @B2: POP EAX
  7227. JMP @Done
  7228. @B3: MOV EAX,ArgIndex
  7229. CMP EAX,Args.Integer[-4]
  7230. JG @B4
  7231. INC ArgIndex
  7232. MOV EBX,Args
  7233. CMP [EBX+EAX*8].Byte[4],vtInteger
  7234. MOV EBX,[EBX+EAX*8]
  7235. JE @B4
  7236. XOR EBX,EBX
  7237. @B4: CMP ESI,ECX
  7238. JE @B2
  7239. LODSW
  7240. @B5: RET
  7241. @Convert:
  7242. AND AL,0DFH
  7243. MOV CL,AL
  7244. MOV EAX,1
  7245. MOV EBX,ArgIndex
  7246. CMP EBX,Args.Integer[-4]
  7247. JG @ErrorExit
  7248. INC ArgIndex
  7249. MOV ESI,Args
  7250. LEA ESI,[ESI+EBX*8]
  7251. MOV EAX,[ESI].Integer[0] // TVarRec.data
  7252. MOVZX EDX,[ESI].Byte[4] // TVarRec.VType
  7253. {$IFDEF PIC}
  7254. MOV EBX, SaveGOT
  7255. ADD EBX, offset @CvtVector
  7256. MOV EBX, [EBX+EDX*4]
  7257. ADD EBX, SaveGOT
  7258. JMP EBX
  7259. {$ELSE}
  7260. JMP @CvtVector.Pointer[EDX*4]
  7261. {$ENDIF}
  7262. @CvtVector:
  7263. DD @CvtInteger // vtInteger
  7264. DD @CvtBoolean // vtBoolean
  7265. DD @CvtChar // vtChar
  7266. DD @CvtExtended // vtExtended
  7267. DD @CvtShortStr // vtString
  7268. DD @CvtPointer // vtPointer
  7269. DD @CvtPChar // vtPChar
  7270. DD @CvtObject // vtObject
  7271. DD @CvtClass // vtClass
  7272. DD @CvtWideChar // vtWideChar
  7273. DD @CvtPWideChar // vtPWideChar
  7274. DD @CvtAnsiStr // vtAnsiString
  7275. DD @CvtCurrency // vtCurrency
  7276. DD @CvtVariant // vtVariant
  7277. DD @CvtInterface // vtInterface
  7278. DD @CvtWideString // vtWideString
  7279. DD @CvtInt64 // vtInt64
  7280. @CvtBoolean:
  7281. @CvtObject:
  7282. @CvtClass:
  7283. @CvtInterface:
  7284. @CvtError:
  7285. XOR EAX,EAX
  7286. @ErrorExit:
  7287. CALL @ClearTmpWideStr
  7288. MOV EDX,FormatOrg
  7289. MOV ECX,FormatPtr
  7290. SUB ECX,EDX
  7291. SHR ECX,1
  7292. MOV EBX, SaveGOT
  7293. {$IFDEF PC_MAPPED_EXCEPTIONS}
  7294. // Because of all the assembly code here, we can't call a routine
  7295. // that throws an exception if it looks like we're still on the
  7296. // stack. The static disassembler cannot give sufficient unwind
  7297. // frame info to unwind the confusion that is generated from the
  7298. // assembly code above. So before we throw the exception, we
  7299. // go to some lengths to excise ourselves from the stack chain.
  7300. // We were passed 12 bytes of parameters on the stack, and we have
  7301. // to make sure that we get rid of those, too.
  7302. MOV ESP, EBP // Ditch everthing to the frame
  7303. MOV EBP, [ESP + 4] // Get the return addr
  7304. MOV [ESP + 16], EBP // Move the ret addr up in the stack
  7305. POP EBP // Ditch the rest of the frame
  7306. ADD ESP, 12 // Ditch the space that was taken by params
  7307. JMP WideFormatError // Off to FormatErr
  7308. {$ELSE}
  7309. CALL WideFormatError
  7310. {$ENDIF}
  7311. // The above call raises an exception and does not return
  7312. @CvtInt64:
  7313. // CL <= format character
  7314. // EAX <= address of int64
  7315. // EBX <= TVarRec.VType
  7316. LEA ESI,StrBuf[64]
  7317. MOV EDX,Prec
  7318. CMP EDX, 32
  7319. JBE @I64_1 // zero padded field width > buffer => no padding
  7320. XOR EDX, EDX
  7321. @I64_1: MOV EBX, ECX
  7322. SUB CL, 'D'
  7323. JZ CvtInt64W // branch predict backward jump taken
  7324. MOV ECX,16
  7325. CMP BL, 'X'
  7326. JE CvtInt64W
  7327. MOV ECX, 10
  7328. CMP BL, 'U'
  7329. JE CvtInt64W
  7330. JMP @CvtError
  7331. @CvtInteger:
  7332. LEA ESI,StrBuf[32]
  7333. MOV EDX,Prec
  7334. MOV EBX, ECX
  7335. CMP EDX,16
  7336. JBE @C1 // zero padded field width > buffer => no padding
  7337. XOR EDX, EDX
  7338. @C1: SUB CL, 'D'
  7339. JZ CvtIntW // branch predict backward jump taken
  7340. MOV ECX, 16
  7341. CMP BL, 'X'
  7342. JE CvtIntW
  7343. MOV ECX, 10
  7344. CMP BL, 'U'
  7345. JE CvtIntW
  7346. JMP @CvtError
  7347. @CvtChar:
  7348. CMP CL,'S'
  7349. JNE @CvtError
  7350. MOV EAX,ESI
  7351. MOV ECX,1
  7352. JMP @CvtAnsiThingLen
  7353. @CvtWideChar:
  7354. CMP CL,'S'
  7355. JNE @CvtError
  7356. MOV ECX,1
  7357. RET
  7358. @CvtVariant:
  7359. CMP CL,'S'
  7360. JNE @CvtError
  7361. CMP [EAX].TVarData.VType,varNull
  7362. JBE @CvtEmptyStr
  7363. MOV EDX,EAX
  7364. LEA EAX,TempWideStr
  7365. CALL WideFormatVarToStr
  7366. MOV ESI,TempWideStr
  7367. JMP @CvtWideStrRef
  7368. @CvtEmptyStr:
  7369. XOR ECX,ECX
  7370. RET
  7371. @CvtShortStr:
  7372. CMP CL,'S'
  7373. JNE @CvtError
  7374. MOVZX ECX,BYTE PTR [EAX]
  7375. INC EAX
  7376. @CvtAnsiThingLen:
  7377. MOV ESI,OFFSET System.@WStrFromPCharLen
  7378. JMP @CvtAnsiThing
  7379. @CvtPChar:
  7380. MOV ESI,OFFSET System.@WStrFromPChar
  7381. JMP @CvtAnsiThingTest
  7382. @CvtAnsiStr:
  7383. MOV ESI,OFFSET System.@WStrFromLStr
  7384. @CvtAnsiThingTest:
  7385. CMP CL,'S'
  7386. JNE @CvtError
  7387. @CvtAnsiThing:
  7388. ADD ESI, SaveGOT
  7389. {$IFDEF PIC}
  7390. MOV ESI, [ESI]
  7391. {$ENDIF}
  7392. MOV EDX,EAX
  7393. LEA EAX,TempWideStr
  7394. PUSH EBX
  7395. MOV EBX, SaveGOT
  7396. CALL ESI
  7397. POP EBX
  7398. MOV ESI,TempWideStr
  7399. JMP @CvtWideStrRef
  7400. @CvtWideString:
  7401. CMP CL,'S'
  7402. JNE @CvtError
  7403. MOV ESI,EAX
  7404. @CvtWideStrRef:
  7405. OR ESI,ESI
  7406. JE @CvtEmptyStr
  7407. MOV ECX,[ESI-4]
  7408. SHR ECX,1
  7409. @CvtWideStrLen:
  7410. CMP ECX,Prec
  7411. JA @E1
  7412. RET
  7413. @E1: MOV ECX,Prec
  7414. RET
  7415. @CvtPWideChar:
  7416. CMP CL,'S'
  7417. JNE @CvtError
  7418. MOV ESI,EAX
  7419. PUSH EDI
  7420. MOV EDI,EAX
  7421. XOR EAX,EAX
  7422. MOV ECX,Prec
  7423. JECXZ @F1
  7424. REPNE SCASW
  7425. JNE @F1
  7426. DEC EDI
  7427. DEC EDI
  7428. @F1: MOV ECX,EDI
  7429. SUB ECX,ESI
  7430. SHR ECX,1
  7431. POP EDI
  7432. RET
  7433. @CvtPointer:
  7434. CMP CL,'P'
  7435. JNE @CvtError
  7436. MOV EDX,8
  7437. MOV ECX,16
  7438. LEA ESI,StrBuf[32]
  7439. JMP CvtInt
  7440. @CvtCurrency:
  7441. MOV BH,fvCurrency
  7442. JMP @CvtFloat
  7443. @CvtExtended:
  7444. MOV BH,fvExtended
  7445. @CvtFloat:
  7446. MOV ESI,EAX
  7447. MOV BL,ffGeneral
  7448. CMP CL,'G'
  7449. JE @G2
  7450. MOV BL,ffExponent
  7451. CMP CL,'E'
  7452. JE @G2
  7453. MOV BL,ffFixed
  7454. CMP CL,'F'
  7455. JE @G1
  7456. MOV BL,ffNumber
  7457. CMP CL,'N'
  7458. JE @G1
  7459. CMP CL,'M'
  7460. JNE @CvtError
  7461. MOV BL,ffCurrency
  7462. @G1: MOV EAX,18
  7463. MOV EDX,Prec
  7464. CMP EDX,EAX
  7465. JBE @G3
  7466. MOV EDX,2
  7467. CMP CL,'M'
  7468. JNE @G3
  7469. MOV EDX,FormatSettings
  7470. MOVZX EDX,[EDX].TFormatSettings.CurrencyDecimals
  7471. JMP @G3
  7472. @G2: MOV EAX,Prec
  7473. MOV EDX,3
  7474. CMP EAX,18
  7475. JBE @G3
  7476. MOV EAX,15
  7477. @G3: PUSH EBX
  7478. PUSH EAX
  7479. PUSH EDX
  7480. MOV EDX,[FormatSettings]
  7481. PUSH EDX
  7482. LEA EAX,StrBuf
  7483. MOV EDX,ESI
  7484. MOVZX ECX,BH
  7485. MOV EBX, SaveGOT
  7486. CALL FloatToTextEx
  7487. MOV ECX,EAX
  7488. LEA EDX,StrBuf
  7489. LEA EAX,TempWideStr
  7490. MOV EBX, SaveGOT
  7491. CALL System.@WStrFromPCharLen
  7492. MOV ESI,TempWideStr
  7493. OR ESI,ESI
  7494. JE @CvtEmptyStr
  7495. MOV ECX,[ESI-4]
  7496. SHR ECX,1
  7497. RET
  7498. @ClearTmpWideStr:
  7499. PUSH EBX
  7500. PUSH EAX
  7501. LEA EAX,TempWideStr
  7502. MOV EBX, SaveGOT
  7503. CALL System.@WStrClr
  7504. POP EAX
  7505. POP EBX
  7506. RET
  7507. @Exit:
  7508. CALL @ClearTmpWideStr
  7509. POP EDI
  7510. POP ESI
  7511. POP EBX
  7512. end;
  7513. procedure WideFmtStr(var Result: WideString; const Format: WideString;
  7514. const Args: array of const);
  7515. const
  7516. BufSize = 2048;
  7517. var
  7518. Len, BufLen: Integer;
  7519. Buffer: array[0..BufSize-1] of WideChar;
  7520. begin
  7521. if Length(Format) < (BufSize - (BufSize div 4)) then
  7522. begin
  7523. BufLen := BufSize;
  7524. Len := WideFormatBuf(Buffer, BufSize - 1, Pointer(Format)^, Length(Format), Args);
  7525. if Len < BufLen - 1 then
  7526. begin
  7527. SetString(Result, Buffer, Len);
  7528. Exit;
  7529. end;
  7530. end
  7531. else
  7532. begin
  7533. BufLen := Length(Format);
  7534. Len := BufLen;
  7535. end;
  7536. while Len >= BufLen - 1 do
  7537. begin
  7538. Inc(BufLen, BufLen);
  7539. Result := ''; // prevent copying of existing data, for speed
  7540. SetLength(Result, BufLen);
  7541. Len := WideFormatBuf(Pointer(Result)^, BufLen - 1, Pointer(Format)^,
  7542. Length(Format), Args);
  7543. end;
  7544. SetLength(Result, Len);
  7545. end;
  7546. procedure WideFmtStr(var Result: WideString; const Format: WideString;
  7547. const Args: array of const; const FormatSettings: TFormatSettings);
  7548. const
  7549. BufSize = 2048;
  7550. var
  7551. Len, BufLen: Integer;
  7552. Buffer: array[0..BufSize-1] of WideChar;
  7553. begin
  7554. if Length(Format) < (BufSize - (BufSize div 4)) then
  7555. begin
  7556. BufLen := BufSize;
  7557. Len := WideFormatBuf(Buffer, BufSize - 1, Pointer(Format)^,
  7558. Length(Format), Args, FormatSettings);
  7559. if Len < BufLen - 1 then
  7560. begin
  7561. SetString(Result, Buffer, Len);
  7562. Exit;
  7563. end;
  7564. end
  7565. else
  7566. begin
  7567. BufLen := Length(Format);
  7568. Len := BufLen;
  7569. end;
  7570. while Len >= BufLen - 1 do
  7571. begin
  7572. Inc(BufLen, BufLen);
  7573. Result := ''; // prevent copying of existing data, for speed
  7574. SetLength(Result, BufLen);
  7575. Len := WideFormatBuf(Pointer(Result)^, BufLen - 1, Pointer(Format)^,
  7576. Length(Format), Args, FormatSettings);
  7577. end;
  7578. SetLength(Result, Len);
  7579. end;
  7580. function WideFormat(const Format: WideString; const Args: array of const): WideString;
  7581. begin
  7582. WideFmtStr(Result, Format, Args);
  7583. end;
  7584. function WideFormat(const Format: WideString; const Args: array of const;
  7585. const FormatSettings: TFormatSettings): WideString;
  7586. begin
  7587. WideFmtStr(Result, Format, Args, FormatSettings);
  7588. end;
  7589. { Floating point conversion routines }
  7590. const
  7591. // 1E18 as a 64-bit integer
  7592. Const1E18Lo = $0A7640000;
  7593. Const1E18Hi = $00DE0B6B3;
  7594. FCon1E18: Extended = 1E18;
  7595. DCon10: Integer = 10;
  7596. procedure PutExponent;
  7597. // Store exponent
  7598. // In AL = Exponent character ('E' or 'e')
  7599. // AH = Positive sign character ('+' or 0)
  7600. // BL = Zero indicator
  7601. // ECX = Minimum number of digits (0..4)
  7602. // EDX = Exponent
  7603. // EDI = Destination buffer
  7604. asm
  7605. PUSH ESI
  7606. {$IFDEF PIC}
  7607. PUSH EAX
  7608. PUSH ECX
  7609. CALL GetGOT
  7610. MOV ESI,EAX
  7611. POP ECX
  7612. POP EAX
  7613. {$ELSE}
  7614. XOR ESI,ESI
  7615. {$ENDIF}
  7616. STOSB
  7617. OR BL,BL
  7618. JNE @@0
  7619. XOR EDX,EDX
  7620. JMP @@1
  7621. @@0: OR EDX,EDX
  7622. JGE @@1
  7623. MOV AL,'-'
  7624. NEG EDX
  7625. JMP @@2
  7626. @@1: OR AH,AH
  7627. JE @@3
  7628. MOV AL,AH
  7629. @@2: STOSB
  7630. @@3: XCHG EAX,EDX
  7631. PUSH EAX
  7632. MOV EBX,ESP
  7633. @@4: XOR EDX,EDX
  7634. DIV [ESI].DCon10
  7635. ADD DL,'0'
  7636. MOV [EBX],DL
  7637. INC EBX
  7638. DEC ECX
  7639. OR EAX,EAX
  7640. JNE @@4
  7641. OR ECX,ECX
  7642. JG @@4
  7643. @@5: DEC EBX
  7644. MOV AL,[EBX]
  7645. STOSB
  7646. CMP EBX,ESP
  7647. JNE @@5
  7648. POP EAX
  7649. POP ESI
  7650. end;
  7651. function FloatToText(BufferArg: PChar; const Value; ValueType: TFloatValue;
  7652. Format: TFloatFormat; Precision, Digits: Integer): Integer;
  7653. var
  7654. Buffer: Cardinal;
  7655. FloatRec: TFloatRec;
  7656. SaveGOT: Integer;
  7657. DecimalSep: Char;
  7658. ThousandSep: Char;
  7659. CurrencyStr: Pointer;
  7660. CurrFmt: Byte;
  7661. NegCurrFmt: Byte;
  7662. asm
  7663. PUSH EDI
  7664. PUSH ESI
  7665. PUSH EBX
  7666. MOV Buffer,EAX
  7667. {$IFDEF PIC}
  7668. PUSH ECX
  7669. CALL GetGOT
  7670. MOV SaveGOT,EAX
  7671. MOV ECX,[EAX].OFFSET DecimalSeparator
  7672. MOV CL,[ECX]
  7673. MOV DecimalSep,CL
  7674. MOV ECX,[EAX].OFFSET ThousandSeparator
  7675. MOV CL,[ECX].Byte
  7676. MOV ThousandSep,CL
  7677. MOV ECX,[EAX].OFFSET CurrencyString
  7678. MOV ECX,[ECX].Integer
  7679. MOV CurrencyStr,ECX
  7680. MOV ECX,[EAX].OFFSET CurrencyFormat
  7681. MOV CL,[ECX].Byte
  7682. MOV CurrFmt,CL
  7683. MOV ECX,[EAX].OFFSET NegCurrFormat
  7684. MOV CL,[ECX].Byte
  7685. MOV NegCurrFmt,CL
  7686. POP ECX
  7687. {$ELSE}
  7688. MOV AL,DecimalSeparator
  7689. MOV DecimalSep,AL
  7690. MOV AL,ThousandSeparator
  7691. MOV ThousandSep,AL
  7692. MOV EAX,CurrencyString
  7693. MOV CurrencyStr,EAX
  7694. MOV AL,CurrencyFormat
  7695. MOV CurrFmt,AL
  7696. MOV AL,NegCurrFormat
  7697. MOV NegCurrFmt,AL
  7698. MOV SaveGOT,0
  7699. {$ENDIF}
  7700. MOV EAX,19
  7701. CMP CL,fvExtended
  7702. JNE @@2
  7703. MOV EAX,Precision
  7704. CMP EAX,2
  7705. JGE @@1
  7706. MOV EAX,2
  7707. @@1: CMP EAX,18
  7708. JLE @@2
  7709. MOV EAX,18
  7710. @@2: MOV Precision,EAX
  7711. PUSH EAX
  7712. MOV EAX,9999
  7713. CMP Format,ffFixed
  7714. JB @@3
  7715. MOV EAX,Digits
  7716. @@3: PUSH EAX
  7717. LEA EAX,FloatRec
  7718. CALL FloatToDecimal
  7719. MOV EDI,Buffer
  7720. MOVZX EAX,FloatRec.Exponent
  7721. SUB EAX,7FFFH
  7722. CMP EAX,2
  7723. JAE @@4
  7724. MOV ECX, EAX
  7725. CALL @@PutSign
  7726. LEA ESI,@@INFNAN[ECX+ECX*2]
  7727. ADD ESI,SaveGOT
  7728. MOV ECX,3
  7729. REP MOVSB
  7730. JMP @@7
  7731. @@4: LEA ESI,FloatRec.Digits
  7732. MOVZX EBX,Format
  7733. CMP BL,ffExponent
  7734. JE @@6
  7735. CMP BL,ffCurrency
  7736. JA @@5
  7737. MOVSX EAX,FloatRec.Exponent
  7738. CMP EAX,Precision
  7739. JLE @@6
  7740. @@5: MOV BL,ffGeneral
  7741. @@6: LEA EBX,@@FormatVector[EBX*4]
  7742. ADD EBX,SaveGOT
  7743. MOV EBX,[EBX]
  7744. ADD EBX,SaveGOT
  7745. CALL EBX
  7746. @@7: MOV EAX,EDI
  7747. SUB EAX,Buffer
  7748. POP EBX
  7749. POP ESI
  7750. POP EDI
  7751. JMP @@Exit
  7752. @@FormatVector:
  7753. DD @@PutFGeneral
  7754. DD @@PutFExponent
  7755. DD @@PutFFixed
  7756. DD @@PutFNumber
  7757. DD @@PutFCurrency
  7758. @@INFNAN: DB 'INFNAN'
  7759. // Get digit or '0' if at end of digit string
  7760. @@GetDigit:
  7761. LODSB
  7762. OR AL,AL
  7763. JNE @@a1
  7764. MOV AL,'0'
  7765. DEC ESI
  7766. @@a1: RET
  7767. // Store '-' if number is negative
  7768. @@PutSign:
  7769. CMP FloatRec.Negative,0
  7770. JE @@b1
  7771. MOV AL,'-'
  7772. STOSB
  7773. @@b1: RET
  7774. // Convert number using ffGeneral format
  7775. @@PutFGeneral:
  7776. CALL @@PutSign
  7777. MOVSX ECX,FloatRec.Exponent
  7778. XOR EDX,EDX
  7779. CMP ECX,Precision
  7780. JG @@c1
  7781. CMP ECX,-3
  7782. JL @@c1
  7783. OR ECX,ECX
  7784. JG @@c2
  7785. MOV AL,'0'
  7786. STOSB
  7787. CMP BYTE PTR [ESI],0
  7788. JE @@c6
  7789. MOV AL,DecimalSep
  7790. STOSB
  7791. NEG ECX
  7792. MOV AL,'0'
  7793. REP STOSB
  7794. JMP @@c3
  7795. @@c1: MOV ECX,1
  7796. INC EDX
  7797. @@c2: LODSB
  7798. OR AL,AL
  7799. JE @@c4
  7800. STOSB
  7801. LOOP @@c2
  7802. LODSB
  7803. OR AL,AL
  7804. JE @@c5
  7805. MOV AH,AL
  7806. MOV AL,DecimalSep
  7807. STOSW
  7808. @@c3: LODSB
  7809. OR AL,AL
  7810. JE @@c5
  7811. STOSB
  7812. JMP @@c3
  7813. @@c4: MOV AL,'0'
  7814. REP STOSB
  7815. @@c5: OR EDX,EDX
  7816. JE @@c6
  7817. XOR EAX,EAX
  7818. JMP @@PutFloatExpWithDigits
  7819. @@c6: RET
  7820. // Convert number using ffExponent format
  7821. @@PutFExponent:
  7822. CALL @@PutSign
  7823. CALL @@GetDigit
  7824. MOV AH,DecimalSep
  7825. STOSW
  7826. MOV ECX,Precision
  7827. DEC ECX
  7828. @@d1: CALL @@GetDigit
  7829. STOSB
  7830. LOOP @@d1
  7831. MOV AH,'+'
  7832. @@PutFloatExpWithDigits:
  7833. MOV ECX,Digits
  7834. CMP ECX,4
  7835. JBE @@PutFloatExp
  7836. XOR ECX,ECX
  7837. // Store exponent
  7838. // In AH = Positive sign character ('+' or 0)
  7839. // ECX = Minimum number of digits (0..4)
  7840. @@PutFloatExp:
  7841. MOV AL,'E'
  7842. MOV BL, FloatRec.Digits.Byte
  7843. MOVSX EDX,FloatRec.Exponent
  7844. DEC EDX
  7845. CALL PutExponent
  7846. RET
  7847. // Convert number using ffFixed or ffNumber format
  7848. @@PutFFixed:
  7849. @@PutFNumber:
  7850. CALL @@PutSign
  7851. // Store number in fixed point format
  7852. @@PutNumber:
  7853. MOV EDX,Digits
  7854. CMP EDX,18
  7855. JB @@f1
  7856. MOV EDX,18
  7857. @@f1: MOVSX ECX,FloatRec.Exponent
  7858. OR ECX,ECX
  7859. JG @@f2
  7860. MOV AL,'0'
  7861. STOSB
  7862. JMP @@f4
  7863. @@f2: XOR EBX,EBX
  7864. CMP Format,ffFixed
  7865. JE @@f3
  7866. MOV EAX,ECX
  7867. DEC EAX
  7868. MOV BL,3
  7869. DIV BL
  7870. MOV BL,AH
  7871. INC EBX
  7872. @@f3: CALL @@GetDigit
  7873. STOSB
  7874. DEC ECX
  7875. JE @@f4
  7876. DEC EBX
  7877. JNE @@f3
  7878. MOV AL,ThousandSep
  7879. TEST AL,AL
  7880. JZ @@f3
  7881. STOSB
  7882. MOV BL,3
  7883. JMP @@f3
  7884. @@f4: OR EDX,EDX
  7885. JE @@f7
  7886. MOV AL,DecimalSep
  7887. TEST AL,AL
  7888. JZ @@f4b
  7889. STOSB
  7890. @@f4b: JECXZ @@f6
  7891. MOV AL,'0'
  7892. @@f5: STOSB
  7893. DEC EDX
  7894. JE @@f7
  7895. INC ECX
  7896. JNE @@f5
  7897. @@f6: CALL @@GetDigit
  7898. STOSB
  7899. DEC EDX
  7900. JNE @@f6
  7901. @@f7: RET
  7902. // Convert number using ffCurrency format
  7903. @@PutFCurrency:
  7904. XOR EBX,EBX
  7905. MOV BL,CurrFmt.Byte
  7906. MOV ECX,0003H
  7907. CMP FloatRec.Negative,0
  7908. JE @@g1
  7909. MOV BL,NegCurrFmt.Byte
  7910. MOV ECX,040FH
  7911. @@g1: CMP BL,CL
  7912. JBE @@g2
  7913. MOV BL,CL
  7914. @@g2: ADD BL,CH
  7915. LEA EBX,@@MoneyFormats[EBX+EBX*4]
  7916. ADD EBX,SaveGOT
  7917. MOV ECX,5
  7918. @@g10: MOV AL,[EBX]
  7919. CMP AL,'@'
  7920. JE @@g14
  7921. PUSH ECX
  7922. PUSH EBX
  7923. CMP AL,'$'
  7924. JE @@g11
  7925. CMP AL,'*'
  7926. JE @@g12
  7927. STOSB
  7928. JMP @@g13
  7929. @@g11: CALL @@PutCurSym
  7930. JMP @@g13
  7931. @@g12: CALL @@PutNumber
  7932. @@g13: POP EBX
  7933. POP ECX
  7934. INC EBX
  7935. LOOP @@g10
  7936. @@g14: RET
  7937. // Store currency symbol string
  7938. @@PutCurSym:
  7939. PUSH ESI
  7940. MOV ESI,CurrencyStr
  7941. TEST ESI,ESI
  7942. JE @@h1
  7943. MOV ECX,[ESI-4]
  7944. REP MOVSB
  7945. @@h1: POP ESI
  7946. RET
  7947. // Currency formatting templates
  7948. @@MoneyFormats:
  7949. DB '$*@@@'
  7950. DB '*$@@@'
  7951. DB '$ *@@'
  7952. DB '* $@@'
  7953. DB '($*)@'
  7954. DB '-$*@@'
  7955. DB '$-*@@'
  7956. DB '$*-@@'
  7957. DB '(*$)@'
  7958. DB '-*$@@'
  7959. DB '*-$@@'
  7960. DB '*$-@@'
  7961. DB '-* $@'
  7962. DB '-$ *@'
  7963. DB '* $-@'
  7964. DB '$ *-@'
  7965. DB '$ -*@'
  7966. DB '*- $@'
  7967. DB '($ *)'
  7968. DB '(* $)'
  7969. @@Exit:
  7970. end;
  7971. function FloatToText(BufferArg: PChar; const Value; ValueType: TFloatValue;
  7972. Format: TFloatFormat; Precision, Digits: Integer;
  7973. const FormatSettings: TFormatSettings): Integer;
  7974. var
  7975. Buffer: Cardinal;
  7976. FloatRec: TFloatRec;
  7977. SaveGOT: Integer;
  7978. DecimalSep: Char;
  7979. ThousandSep: Char;
  7980. CurrencyStr: Pointer;
  7981. CurrFmt: Byte;
  7982. NegCurrFmt: Byte;
  7983. asm
  7984. PUSH EDI
  7985. PUSH ESI
  7986. PUSH EBX
  7987. MOV Buffer,EAX
  7988. {$IFDEF PIC}
  7989. PUSH ECX
  7990. CALL GetGOT
  7991. MOV SaveGOT,EAX
  7992. POP ECX
  7993. {$ENDIF}
  7994. MOV EAX,FormatSettings
  7995. MOV AL,[EAX].TFormatSettings.DecimalSeparator
  7996. MOV DecimalSep,AL
  7997. MOV EAX,FormatSettings
  7998. MOV AL,[EAX].TFormatSettings.ThousandSeparator
  7999. MOV ThousandSep,AL
  8000. MOV EAX,FormatSettings
  8001. MOV EAX,[EAX].TFormatSettings.CurrencyString
  8002. MOV CurrencyStr,EAX
  8003. MOV EAX,FormatSettings
  8004. MOV AL,[EAX].TFormatSettings.CurrencyFormat
  8005. MOV CurrFmt,AL
  8006. MOV EAX,FormatSettings
  8007. MOV AL,[EAX].TFormatSettings.NegCurrFormat
  8008. MOV NegCurrFmt,AL
  8009. MOV SaveGOT,0
  8010. MOV EAX,19
  8011. CMP CL,fvExtended
  8012. JNE @@2
  8013. MOV EAX,Precision
  8014. CMP EAX,2
  8015. JGE @@1
  8016. MOV EAX,2
  8017. @@1: CMP EAX,18
  8018. JLE @@2
  8019. MOV EAX,18
  8020. @@2: MOV Precision,EAX
  8021. PUSH EAX
  8022. MOV EAX,9999
  8023. CMP Format,ffFixed
  8024. JB @@3
  8025. MOV EAX,Digits
  8026. @@3: PUSH EAX
  8027. LEA EAX,FloatRec
  8028. CALL FloatToDecimal
  8029. MOV EDI,Buffer
  8030. MOVZX EAX,FloatRec.Exponent
  8031. SUB EAX,7FFFH
  8032. CMP EAX,2
  8033. JAE @@4
  8034. MOV ECX, EAX
  8035. CALL @@PutSign
  8036. LEA ESI,@@INFNAN[ECX+ECX*2]
  8037. ADD ESI,SaveGOT
  8038. MOV ECX,3
  8039. REP MOVSB
  8040. JMP @@7
  8041. @@4: LEA ESI,FloatRec.Digits
  8042. MOVZX EBX,Format
  8043. CMP BL,ffExponent
  8044. JE @@6
  8045. CMP BL,ffCurrency
  8046. JA @@5
  8047. MOVSX EAX,FloatRec.Exponent
  8048. CMP EAX,Precision
  8049. JLE @@6
  8050. @@5: MOV BL,ffGeneral
  8051. @@6: LEA EBX,@@FormatVector[EBX*4]
  8052. ADD EBX,SaveGOT
  8053. MOV EBX,[EBX]
  8054. ADD EBX,SaveGOT
  8055. CALL EBX
  8056. @@7: MOV EAX,EDI
  8057. SUB EAX,Buffer
  8058. POP EBX
  8059. POP ESI
  8060. POP EDI
  8061. JMP @@Exit
  8062. @@FormatVector:
  8063. DD @@PutFGeneral
  8064. DD @@PutFExponent
  8065. DD @@PutFFixed
  8066. DD @@PutFNumber
  8067. DD @@PutFCurrency
  8068. @@INFNAN: DB 'INFNAN'
  8069. // Get digit or '0' if at end of digit string
  8070. @@GetDigit:
  8071. LODSB
  8072. OR AL,AL
  8073. JNE @@a1
  8074. MOV AL,'0'
  8075. DEC ESI
  8076. @@a1: RET
  8077. // Store '-' if number is negative
  8078. @@PutSign:
  8079. CMP FloatRec.Negative,0
  8080. JE @@b1
  8081. MOV AL,'-'
  8082. STOSB
  8083. @@b1: RET
  8084. // Convert number using ffGeneral format
  8085. @@PutFGeneral:
  8086. CALL @@PutSign
  8087. MOVSX ECX,FloatRec.Exponent
  8088. XOR EDX,EDX
  8089. CMP ECX,Precision
  8090. JG @@c1
  8091. CMP ECX,-3
  8092. JL @@c1
  8093. OR ECX,ECX
  8094. JG @@c2
  8095. MOV AL,'0'
  8096. STOSB
  8097. CMP BYTE PTR [ESI],0
  8098. JE @@c6
  8099. MOV AL,DecimalSep
  8100. STOSB
  8101. NEG ECX
  8102. MOV AL,'0'
  8103. REP STOSB
  8104. JMP @@c3
  8105. @@c1: MOV ECX,1
  8106. INC EDX
  8107. @@c2: LODSB
  8108. OR AL,AL
  8109. JE @@c4
  8110. STOSB
  8111. LOOP @@c2
  8112. LODSB
  8113. OR AL,AL
  8114. JE @@c5
  8115. MOV AH,AL
  8116. MOV AL,DecimalSep
  8117. STOSW
  8118. @@c3: LODSB
  8119. OR AL,AL
  8120. JE @@c5
  8121. STOSB
  8122. JMP @@c3
  8123. @@c4: MOV AL,'0'
  8124. REP STOSB
  8125. @@c5: OR EDX,EDX
  8126. JE @@c6
  8127. XOR EAX,EAX
  8128. JMP @@PutFloatExpWithDigits
  8129. @@c6: RET
  8130. // Convert number using ffExponent format
  8131. @@PutFExponent:
  8132. CALL @@PutSign
  8133. CALL @@GetDigit
  8134. MOV AH,DecimalSep
  8135. STOSW
  8136. MOV ECX,Precision
  8137. DEC ECX
  8138. @@d1: CALL @@GetDigit
  8139. STOSB
  8140. LOOP @@d1
  8141. MOV AH,'+'
  8142. @@PutFloatExpWithDigits:
  8143. MOV ECX,Digits
  8144. CMP ECX,4
  8145. JBE @@PutFloatExp
  8146. XOR ECX,ECX
  8147. // Store exponent
  8148. // In AH = Positive sign character ('+' or 0)
  8149. // ECX = Minimum number of digits (0..4)
  8150. @@PutFloatExp:
  8151. MOV AL,'E'
  8152. MOV BL, FloatRec.Digits.Byte
  8153. MOVSX EDX,FloatRec.Exponent
  8154. DEC EDX
  8155. CALL PutExponent
  8156. RET
  8157. // Convert number using ffFixed or ffNumber format
  8158. @@PutFFixed:
  8159. @@PutFNumber:
  8160. CALL @@PutSign
  8161. // Store number in fixed point format
  8162. @@PutNumber:
  8163. MOV EDX,Digits
  8164. CMP EDX,18
  8165. JB @@f1
  8166. MOV EDX,18
  8167. @@f1: MOVSX ECX,FloatRec.Exponent
  8168. OR ECX,ECX
  8169. JG @@f2
  8170. MOV AL,'0'
  8171. STOSB
  8172. JMP @@f4
  8173. @@f2: XOR EBX,EBX
  8174. CMP Format,ffFixed
  8175. JE @@f3
  8176. MOV EAX,ECX
  8177. DEC EAX
  8178. MOV BL,3
  8179. DIV BL
  8180. MOV BL,AH
  8181. INC EBX
  8182. @@f3: CALL @@GetDigit
  8183. STOSB
  8184. DEC ECX
  8185. JE @@f4
  8186. DEC EBX
  8187. JNE @@f3
  8188. MOV AL,ThousandSep
  8189. TEST AL,AL
  8190. JZ @@f3
  8191. STOSB
  8192. MOV BL,3
  8193. JMP @@f3
  8194. @@f4: OR EDX,EDX
  8195. JE @@f7
  8196. MOV AL,DecimalSep
  8197. TEST AL,AL
  8198. JZ @@f4b
  8199. STOSB
  8200. @@f4b: JECXZ @@f6
  8201. MOV AL,'0'
  8202. @@f5: STOSB
  8203. DEC EDX
  8204. JE @@f7
  8205. INC ECX
  8206. JNE @@f5
  8207. @@f6: CALL @@GetDigit
  8208. STOSB
  8209. DEC EDX
  8210. JNE @@f6
  8211. @@f7: RET
  8212. // Convert number using ffCurrency format
  8213. @@PutFCurrency:
  8214. XOR EBX,EBX
  8215. MOV BL,CurrFmt.Byte
  8216. MOV ECX,0003H
  8217. CMP FloatRec.Negative,0
  8218. JE @@g1
  8219. MOV BL,NegCurrFmt.Byte
  8220. MOV ECX,040FH
  8221. @@g1: CMP BL,CL
  8222. JBE @@g2
  8223. MOV BL,CL
  8224. @@g2: ADD BL,CH
  8225. LEA EBX,@@MoneyFormats[EBX+EBX*4]
  8226. ADD EBX,SaveGOT
  8227. MOV ECX,5
  8228. @@g10: MOV AL,[EBX]
  8229. CMP AL,'@'
  8230. JE @@g14
  8231. PUSH ECX
  8232. PUSH EBX
  8233. CMP AL,'$'
  8234. JE @@g11
  8235. CMP AL,'*'
  8236. JE @@g12
  8237. STOSB
  8238. JMP @@g13
  8239. @@g11: CALL @@PutCurSym
  8240. JMP @@g13
  8241. @@g12: CALL @@PutNumber
  8242. @@g13: POP EBX
  8243. POP ECX
  8244. INC EBX
  8245. LOOP @@g10
  8246. @@g14: RET
  8247. // Store currency symbol string
  8248. @@PutCurSym:
  8249. PUSH ESI
  8250. MOV ESI,CurrencyStr
  8251. TEST ESI,ESI
  8252. JE @@h1
  8253. MOV ECX,[ESI-4]
  8254. REP MOVSB
  8255. @@h1: POP ESI
  8256. RET
  8257. // Currency formatting templates
  8258. @@MoneyFormats:
  8259. DB '$*@@@'
  8260. DB '*$@@@'
  8261. DB '$ *@@'
  8262. DB '* $@@'
  8263. DB '($*)@'
  8264. DB '-$*@@'
  8265. DB '$-*@@'
  8266. DB '$*-@@'
  8267. DB '(*$)@'
  8268. DB '-*$@@'
  8269. DB '*-$@@'
  8270. DB '*$-@@'
  8271. DB '-* $@'
  8272. DB '-$ *@'
  8273. DB '* $-@'
  8274. DB '$ *-@'
  8275. DB '$ -*@'
  8276. DB '*- $@'
  8277. DB '($ *)'
  8278. DB '(* $)'
  8279. @@Exit:
  8280. end;
  8281. function FloatToTextFmt(Buf: PChar; const Value; ValueType: TFloatValue;
  8282. Format: PChar): Integer;
  8283. var
  8284. Buffer: Pointer;
  8285. ThousandSep: Boolean;
  8286. DecimalSep: Char;
  8287. ThousandsSep: Char;
  8288. Scientific: Boolean;
  8289. Section: Integer;
  8290. DigitCount: Integer;
  8291. DecimalIndex: Integer;
  8292. FirstDigit: Integer;
  8293. LastDigit: Integer;
  8294. DigitPlace: Integer;
  8295. DigitDelta: Integer;
  8296. FloatRec: TFloatRec;
  8297. SaveGOT: Pointer;
  8298. asm
  8299. PUSH EDI
  8300. PUSH ESI
  8301. PUSH EBX
  8302. MOV Buffer,EAX
  8303. MOV EDI,EDX
  8304. MOV EBX,ECX
  8305. {$IFDEF PIC}
  8306. CALL GetGOT
  8307. MOV SaveGOT,EAX
  8308. MOV ECX,[EAX].OFFSET DecimalSeparator
  8309. MOV CL,[ECX].Byte
  8310. MOV DecimalSep,CL
  8311. MOV ECX,[EAX].OFFSET ThousandSeparator
  8312. MOV CL,[ECX].Byte
  8313. MOV ThousandsSep,CL
  8314. {$ELSE}
  8315. MOV SaveGOT,0
  8316. MOV AL,DecimalSeparator
  8317. MOV DecimalSep,AL
  8318. MOV AL,ThousandSeparator
  8319. MOV ThousandsSep,AL
  8320. {$ENDIF}
  8321. MOV ECX,2
  8322. CMP BL,fvExtended
  8323. JE @@1
  8324. MOV EAX,[EDI].Integer
  8325. OR EAX,[EDI].Integer[4]
  8326. JE @@2
  8327. MOV ECX,[EDI].Integer[4]
  8328. SHR ECX,31
  8329. JMP @@2
  8330. @@1: MOVZX EAX,[EDI].Word[8]
  8331. OR EAX,[EDI].Integer[0]
  8332. OR EAX,[EDI].Integer[4]
  8333. JE @@2
  8334. MOVZX ECX,[EDI].Word[8]
  8335. SHR ECX,15
  8336. @@2: CALL @@FindSection
  8337. JE @@5
  8338. CALL @@ScanSection
  8339. MOV EAX,DigitCount
  8340. MOV EDX,9999
  8341. CMP Scientific,0
  8342. JNE @@3
  8343. SUB EAX,DecimalIndex
  8344. MOV EDX,EAX
  8345. MOV EAX,18
  8346. @@3: PUSH EAX
  8347. PUSH EDX
  8348. LEA EAX,FloatRec
  8349. MOV EDX,EDI
  8350. MOV ECX,EBX
  8351. CALL FloatToDecimal
  8352. MOV AX,FloatRec.Exponent
  8353. CMP AX,8000H
  8354. JE @@5
  8355. CMP AX,7FFFH
  8356. JE @@5
  8357. CMP BL,fvExtended
  8358. JNE @@6
  8359. CMP AX,18
  8360. JLE @@6
  8361. CMP Scientific,0
  8362. JNE @@6
  8363. @@5: PUSH ffGeneral
  8364. PUSH 15
  8365. PUSH 0
  8366. MOV EAX,Buffer
  8367. MOV EDX,EDI
  8368. MOV ECX,EBX
  8369. CALL FloatToText
  8370. JMP @@Exit
  8371. @@6: CMP FloatRec.Digits.Byte,0
  8372. JNE @@7
  8373. MOV ECX,2
  8374. CALL @@FindSection
  8375. JE @@5
  8376. CMP ESI,Section
  8377. JE @@7
  8378. CALL @@ScanSection
  8379. @@7: CALL @@ApplyFormat
  8380. JMP @@Exit
  8381. // Find format section
  8382. // In ECX = Section index
  8383. // Out ESI = Section offset
  8384. // ZF = 1 if section is empty
  8385. @@FindSection:
  8386. MOV ESI,Format
  8387. JECXZ @@fs2
  8388. @@fs1: LODSB
  8389. CMP AL,"'"
  8390. JE @@fs4
  8391. CMP AL,'"'
  8392. JE @@fs4
  8393. OR AL,AL
  8394. JE @@fs2
  8395. CMP AL,';'
  8396. JNE @@fs1
  8397. LOOP @@fs1
  8398. MOV AL,byte ptr [ESI]
  8399. OR AL,AL
  8400. JE @@fs2
  8401. CMP AL,';'
  8402. JNE @@fs3
  8403. @@fs2: MOV ESI,Format
  8404. MOV AL,byte ptr [ESI]
  8405. OR AL,AL
  8406. JE @@fs3
  8407. CMP AL,';'
  8408. @@fs3: RET
  8409. @@fs4: MOV AH,AL
  8410. @@fs5: LODSB
  8411. CMP AL,AH
  8412. JE @@fs1
  8413. OR AL,AL
  8414. JNE @@fs5
  8415. JMP @@fs2
  8416. // Scan format section
  8417. @@ScanSection:
  8418. PUSH EBX
  8419. MOV Section,ESI
  8420. MOV EBX,32767
  8421. XOR ECX,ECX
  8422. XOR EDX,EDX
  8423. MOV DecimalIndex,-1
  8424. MOV ThousandSep,DL
  8425. MOV Scientific,DL
  8426. @@ss1: LODSB
  8427. @@ss2: CMP AL,'#'
  8428. JE @@ss10
  8429. CMP AL,'0'
  8430. JE @@ss11
  8431. CMP AL,'.'
  8432. JE @@ss13
  8433. CMP AL,','
  8434. JE @@ss14
  8435. CMP AL,"'"
  8436. JE @@ss15
  8437. CMP AL,'"'
  8438. JE @@ss15
  8439. CMP AL,'E'
  8440. JE @@ss20
  8441. CMP AL,'e'
  8442. JE @@ss20
  8443. CMP AL,';'
  8444. JE @@ss30
  8445. OR AL,AL
  8446. JNE @@ss1
  8447. JMP @@ss30
  8448. @@ss10: INC EDX
  8449. JMP @@ss1
  8450. @@ss11: CMP EDX,EBX
  8451. JGE @@ss12
  8452. MOV EBX,EDX
  8453. @@ss12: INC EDX
  8454. MOV ECX,EDX
  8455. JMP @@ss1
  8456. @@ss13: CMP DecimalIndex,-1
  8457. JNE @@ss1
  8458. MOV DecimalIndex,EDX
  8459. JMP @@ss1
  8460. @@ss14: MOV ThousandSep,1
  8461. JMP @@ss1
  8462. @@ss15: MOV AH,AL
  8463. @@ss16: LODSB
  8464. CMP AL,AH
  8465. JE @@ss1
  8466. OR AL,AL
  8467. JNE @@ss16
  8468. JMP @@ss30
  8469. @@ss20: LODSB
  8470. CMP AL,'-'
  8471. JE @@ss21
  8472. CMP AL,'+'
  8473. JNE @@ss2
  8474. @@ss21: MOV Scientific,1
  8475. @@ss22: LODSB
  8476. CMP AL,'0'
  8477. JE @@ss22
  8478. JMP @@ss2
  8479. @@ss30: MOV DigitCount,EDX
  8480. CMP DecimalIndex,-1
  8481. JNE @@ss31
  8482. MOV DecimalIndex,EDX
  8483. @@ss31: MOV EAX,DecimalIndex
  8484. SUB EAX,ECX
  8485. JLE @@ss32
  8486. XOR EAX,EAX
  8487. @@ss32: MOV LastDigit,EAX
  8488. MOV EAX,DecimalIndex
  8489. SUB EAX,EBX
  8490. JGE @@ss33
  8491. XOR EAX,EAX
  8492. @@ss33: MOV FirstDigit,EAX
  8493. POP EBX
  8494. RET
  8495. // Apply format string
  8496. @@ApplyFormat:
  8497. CMP Scientific,0
  8498. JE @@af1
  8499. MOV EAX,DecimalIndex
  8500. XOR EDX,EDX
  8501. JMP @@af3
  8502. @@af1: MOVSX EAX,FloatRec.Exponent
  8503. CMP EAX,DecimalIndex
  8504. JG @@af2
  8505. MOV EAX,DecimalIndex
  8506. @@af2: MOVSX EDX,FloatRec.Exponent
  8507. SUB EDX,DecimalIndex
  8508. @@af3: MOV DigitPlace,EAX
  8509. MOV DigitDelta,EDX
  8510. MOV ESI,Section
  8511. MOV EDI,Buffer
  8512. LEA EBX,FloatRec.Digits
  8513. CMP FloatRec.Negative,0
  8514. JE @@af10
  8515. CMP ESI,Format
  8516. JNE @@af10
  8517. MOV AL,'-'
  8518. STOSB
  8519. @@af10: LODSB
  8520. CMP AL,'#'
  8521. JE @@af20
  8522. CMP AL,'0'
  8523. JE @@af20
  8524. CMP AL,'.'
  8525. JE @@af10
  8526. CMP AL,','
  8527. JE @@af10
  8528. CMP AL,"'"
  8529. JE @@af25
  8530. CMP AL,'"'
  8531. JE @@af25
  8532. CMP AL,'E'
  8533. JE @@af30
  8534. CMP AL,'e'
  8535. JE @@af30
  8536. CMP AL,';'
  8537. JE @@af40
  8538. OR AL,AL
  8539. JE @@af40
  8540. @@af11: STOSB
  8541. JMP @@af10
  8542. @@af20: CALL @@PutFmtDigit
  8543. JMP @@af10
  8544. @@af25: MOV AH,AL
  8545. @@af26: LODSB
  8546. CMP AL,AH
  8547. JE @@af10
  8548. OR AL,AL
  8549. JE @@af40
  8550. STOSB
  8551. JMP @@af26
  8552. @@af30: MOV AH,[ESI]
  8553. CMP AH,'+'
  8554. JE @@af31
  8555. CMP AH,'-'
  8556. JNE @@af11
  8557. XOR AH,AH
  8558. @@af31: MOV ECX,-1
  8559. @@af32: INC ECX
  8560. INC ESI
  8561. CMP [ESI].Byte,'0'
  8562. JE @@af32
  8563. CMP ECX,4
  8564. JB @@af33
  8565. MOV ECX,4
  8566. @@af33: PUSH EBX
  8567. MOV BL,FloatRec.Digits.Byte
  8568. MOVSX EDX,FloatRec.Exponent
  8569. SUB EDX,DecimalIndex
  8570. CALL PutExponent
  8571. POP EBX
  8572. JMP @@af10
  8573. @@af40: MOV EAX,EDI
  8574. SUB EAX,Buffer
  8575. RET
  8576. // Store formatted digit
  8577. @@PutFmtDigit:
  8578. CMP DigitDelta,0
  8579. JE @@fd3
  8580. JL @@fd2
  8581. @@fd1: CALL @@fd3
  8582. DEC DigitDelta
  8583. JNE @@fd1
  8584. JMP @@fd3
  8585. @@fd2: INC DigitDelta
  8586. MOV EAX,DigitPlace
  8587. CMP EAX,FirstDigit
  8588. JLE @@fd4
  8589. JMP @@fd7
  8590. @@fd3: MOV AL,[EBX]
  8591. INC EBX
  8592. OR AL,AL
  8593. JNE @@fd5
  8594. DEC EBX
  8595. MOV EAX,DigitPlace
  8596. CMP EAX,LastDigit
  8597. JLE @@fd7
  8598. @@fd4: MOV AL,'0'
  8599. @@fd5: CMP DigitPlace,0
  8600. JNE @@fd6
  8601. MOV AH,AL
  8602. MOV AL,DecimalSep
  8603. STOSW
  8604. JMP @@fd7
  8605. @@fd6: STOSB
  8606. CMP ThousandSep,0
  8607. JE @@fd7
  8608. MOV EAX,DigitPlace
  8609. CMP EAX,1
  8610. JLE @@fd7
  8611. MOV DL,3
  8612. DIV DL
  8613. CMP AH,1
  8614. JNE @@fd7
  8615. MOV AL,ThousandsSep
  8616. TEST AL,AL
  8617. JZ @@fd7
  8618. STOSB
  8619. @@fd7: DEC DigitPlace
  8620. RET
  8621. @@exit:
  8622. POP EBX
  8623. POP ESI
  8624. POP EDI
  8625. end;
  8626. function FloatToTextFmt(Buf: PChar; const Value; ValueType: TFloatValue;
  8627. Format: PChar; const FormatSettings: TFormatSettings): Integer;
  8628. var
  8629. Buffer: Pointer;
  8630. ThousandSep: Boolean;
  8631. DecimalSep: Char;
  8632. ThousandsSep: Char;
  8633. Scientific: Boolean;
  8634. Section: Integer;
  8635. DigitCount: Integer;
  8636. DecimalIndex: Integer;
  8637. FirstDigit: Integer;
  8638. LastDigit: Integer;
  8639. DigitPlace: Integer;
  8640. DigitDelta: Integer;
  8641. FloatRec: TFloatRec;
  8642. SaveGOT: Pointer;
  8643. asm
  8644. PUSH EDI
  8645. PUSH ESI
  8646. PUSH EBX
  8647. MOV Buffer,EAX
  8648. MOV EDI,EDX
  8649. MOV EBX,ECX
  8650. {$IFDEF PIC}
  8651. CALL GetGOT
  8652. MOV SaveGOT,EAX
  8653. {$ELSE}
  8654. MOV SaveGOT,0
  8655. {$ENDIF}
  8656. MOV EAX,FormatSettings
  8657. MOV AL,[EAX].TFormatSettings.DecimalSeparator
  8658. MOV DecimalSep,AL
  8659. MOV EAX,FormatSettings
  8660. MOV AL,[EAX].TFormatSettings.ThousandSeparator
  8661. MOV ThousandsSep,AL
  8662. MOV ECX,2
  8663. CMP BL,fvExtended
  8664. JE @@1
  8665. MOV EAX,[EDI].Integer
  8666. OR EAX,[EDI].Integer[4]
  8667. JE @@2
  8668. MOV ECX,[EDI].Integer[4]
  8669. SHR ECX,31
  8670. JMP @@2
  8671. @@1: MOVZX EAX,[EDI].Word[8]
  8672. OR EAX,[EDI].Integer[0]
  8673. OR EAX,[EDI].Integer[4]
  8674. JE @@2
  8675. MOVZX ECX,[EDI].Word[8]
  8676. SHR ECX,15
  8677. @@2: CALL @@FindSection
  8678. JE @@5
  8679. CALL @@ScanSection
  8680. MOV EAX,DigitCount
  8681. MOV EDX,9999
  8682. CMP Scientific,0
  8683. JNE @@3
  8684. SUB EAX,DecimalIndex
  8685. MOV EDX,EAX
  8686. MOV EAX,18
  8687. @@3: PUSH EAX
  8688. PUSH EDX
  8689. LEA EAX,FloatRec
  8690. MOV EDX,EDI
  8691. MOV ECX,EBX
  8692. CALL FloatToDecimal
  8693. MOV AX,FloatRec.Exponent
  8694. CMP AX,8000H
  8695. JE @@5
  8696. CMP AX,7FFFH
  8697. JE @@5
  8698. CMP BL,fvExtended
  8699. JNE @@6
  8700. CMP AX,18
  8701. JLE @@6
  8702. CMP Scientific,0
  8703. JNE @@6
  8704. @@5: PUSH ffGeneral
  8705. PUSH 15
  8706. PUSH 0
  8707. MOV EAX,[FormatSettings]
  8708. PUSH EAX
  8709. MOV EAX,Buffer
  8710. MOV EDX,EDI
  8711. MOV ECX,EBX
  8712. CALL FloatToTextEx
  8713. JMP @@Exit
  8714. @@6: CMP FloatRec.Digits.Byte,0
  8715. JNE @@7
  8716. MOV ECX,2
  8717. CALL @@FindSection
  8718. JE @@5
  8719. CMP ESI,Section
  8720. JE @@7
  8721. CALL @@ScanSection
  8722. @@7: CALL @@ApplyFormat
  8723. JMP @@Exit
  8724. // Find format section
  8725. // In ECX = Section index
  8726. // Out ESI = Section offset
  8727. // ZF = 1 if section is empty
  8728. @@FindSection:
  8729. MOV ESI,Format
  8730. JECXZ @@fs2
  8731. @@fs1: LODSB
  8732. CMP AL,"'"
  8733. JE @@fs4
  8734. CMP AL,'"'
  8735. JE @@fs4
  8736. OR AL,AL
  8737. JE @@fs2
  8738. CMP AL,';'
  8739. JNE @@fs1
  8740. LOOP @@fs1
  8741. MOV AL,byte ptr [ESI]
  8742. OR AL,AL
  8743. JE @@fs2
  8744. CMP AL,';'
  8745. JNE @@fs3
  8746. @@fs2: MOV ESI,Format
  8747. MOV AL,byte ptr [ESI]
  8748. OR AL,AL
  8749. JE @@fs3
  8750. CMP AL,';'
  8751. @@fs3: RET
  8752. @@fs4: MOV AH,AL
  8753. @@fs5: LODSB
  8754. CMP AL,AH
  8755. JE @@fs1
  8756. OR AL,AL
  8757. JNE @@fs5
  8758. JMP @@fs2
  8759. // Scan format section
  8760. @@ScanSection:
  8761. PUSH EBX
  8762. MOV Section,ESI
  8763. MOV EBX,32767
  8764. XOR ECX,ECX
  8765. XOR EDX,EDX
  8766. MOV DecimalIndex,-1
  8767. MOV ThousandSep,DL
  8768. MOV Scientific,DL
  8769. @@ss1: LODSB
  8770. @@ss2: CMP AL,'#'
  8771. JE @@ss10
  8772. CMP AL,'0'
  8773. JE @@ss11
  8774. CMP AL,'.'
  8775. JE @@ss13
  8776. CMP AL,','
  8777. JE @@ss14
  8778. CMP AL,"'"
  8779. JE @@ss15
  8780. CMP AL,'"'
  8781. JE @@ss15
  8782. CMP AL,'E'
  8783. JE @@ss20
  8784. CMP AL,'e'
  8785. JE @@ss20
  8786. CMP AL,';'
  8787. JE @@ss30
  8788. OR AL,AL
  8789. JNE @@ss1
  8790. JMP @@ss30
  8791. @@ss10: INC EDX
  8792. JMP @@ss1
  8793. @@ss11: CMP EDX,EBX
  8794. JGE @@ss12
  8795. MOV EBX,EDX
  8796. @@ss12: INC EDX
  8797. MOV ECX,EDX
  8798. JMP @@ss1
  8799. @@ss13: CMP DecimalIndex,-1
  8800. JNE @@ss1
  8801. MOV DecimalIndex,EDX
  8802. JMP @@ss1
  8803. @@ss14: MOV ThousandSep,1
  8804. JMP @@ss1
  8805. @@ss15: MOV AH,AL
  8806. @@ss16: LODSB
  8807. CMP AL,AH
  8808. JE @@ss1
  8809. OR AL,AL
  8810. JNE @@ss16
  8811. JMP @@ss30
  8812. @@ss20: LODSB
  8813. CMP AL,'-'
  8814. JE @@ss21
  8815. CMP AL,'+'
  8816. JNE @@ss2
  8817. @@ss21: MOV Scientific,1
  8818. @@ss22: LODSB
  8819. CMP AL,'0'
  8820. JE @@ss22
  8821. JMP @@ss2
  8822. @@ss30: MOV DigitCount,EDX
  8823. CMP DecimalIndex,-1
  8824. JNE @@ss31
  8825. MOV DecimalIndex,EDX
  8826. @@ss31: MOV EAX,DecimalIndex
  8827. SUB EAX,ECX
  8828. JLE @@ss32
  8829. XOR EAX,EAX
  8830. @@ss32: MOV LastDigit,EAX
  8831. MOV EAX,DecimalIndex
  8832. SUB EAX,EBX
  8833. JGE @@ss33
  8834. XOR EAX,EAX
  8835. @@ss33: MOV FirstDigit,EAX
  8836. POP EBX
  8837. RET
  8838. // Apply format string
  8839. @@ApplyFormat:
  8840. CMP Scientific,0
  8841. JE @@af1
  8842. MOV EAX,DecimalIndex
  8843. XOR EDX,EDX
  8844. JMP @@af3
  8845. @@af1: MOVSX EAX,FloatRec.Exponent
  8846. CMP EAX,DecimalIndex
  8847. JG @@af2
  8848. MOV EAX,DecimalIndex
  8849. @@af2: MOVSX EDX,FloatRec.Exponent
  8850. SUB EDX,DecimalIndex
  8851. @@af3: MOV DigitPlace,EAX
  8852. MOV DigitDelta,EDX
  8853. MOV ESI,Section
  8854. MOV EDI,Buffer
  8855. LEA EBX,FloatRec.Digits
  8856. CMP FloatRec.Negative,0
  8857. JE @@af10
  8858. CMP ESI,Format
  8859. JNE @@af10
  8860. MOV AL,'-'
  8861. STOSB
  8862. @@af10: LODSB
  8863. CMP AL,'#'
  8864. JE @@af20
  8865. CMP AL,'0'
  8866. JE @@af20
  8867. CMP AL,'.'
  8868. JE @@af10
  8869. CMP AL,','
  8870. JE @@af10
  8871. CMP AL,"'"
  8872. JE @@af25
  8873. CMP AL,'"'
  8874. JE @@af25
  8875. CMP AL,'E'
  8876. JE @@af30
  8877. CMP AL,'e'
  8878. JE @@af30
  8879. CMP AL,';'
  8880. JE @@af40
  8881. OR AL,AL
  8882. JE @@af40
  8883. @@af11: STOSB
  8884. JMP @@af10
  8885. @@af20: CALL @@PutFmtDigit
  8886. JMP @@af10
  8887. @@af25: MOV AH,AL
  8888. @@af26: LODSB
  8889. CMP AL,AH
  8890. JE @@af10
  8891. OR AL,AL
  8892. JE @@af40
  8893. STOSB
  8894. JMP @@af26
  8895. @@af30: MOV AH,[ESI]
  8896. CMP AH,'+'
  8897. JE @@af31
  8898. CMP AH,'-'
  8899. JNE @@af11
  8900. XOR AH,AH
  8901. @@af31: MOV ECX,-1
  8902. @@af32: INC ECX
  8903. INC ESI
  8904. CMP [ESI].Byte,'0'
  8905. JE @@af32
  8906. CMP ECX,4
  8907. JB @@af33
  8908. MOV ECX,4
  8909. @@af33: PUSH EBX
  8910. MOV BL,FloatRec.Digits.Byte
  8911. MOVSX EDX,FloatRec.Exponent
  8912. SUB EDX,DecimalIndex
  8913. CALL PutExponent
  8914. POP EBX
  8915. JMP @@af10
  8916. @@af40: MOV EAX,EDI
  8917. SUB EAX,Buffer
  8918. RET
  8919. // Store formatted digit
  8920. @@PutFmtDigit:
  8921. CMP DigitDelta,0
  8922. JE @@fd3
  8923. JL @@fd2
  8924. @@fd1: CALL @@fd3
  8925. DEC DigitDelta
  8926. JNE @@fd1
  8927. JMP @@fd3
  8928. @@fd2: INC DigitDelta
  8929. MOV EAX,DigitPlace
  8930. CMP EAX,FirstDigit
  8931. JLE @@fd4
  8932. JMP @@fd7
  8933. @@fd3: MOV AL,[EBX]
  8934. INC EBX
  8935. OR AL,AL
  8936. JNE @@fd5
  8937. DEC EBX
  8938. MOV EAX,DigitPlace
  8939. CMP EAX,LastDigit
  8940. JLE @@fd7
  8941. @@fd4: MOV AL,'0'
  8942. @@fd5: CMP DigitPlace,0
  8943. JNE @@fd6
  8944. MOV AH,AL
  8945. MOV AL,DecimalSep
  8946. STOSW
  8947. JMP @@fd7
  8948. @@fd6: STOSB
  8949. CMP ThousandSep,0
  8950. JE @@fd7
  8951. MOV EAX,DigitPlace
  8952. CMP EAX,1
  8953. JLE @@fd7
  8954. MOV DL,3
  8955. DIV DL
  8956. CMP AH,1
  8957. JNE @@fd7
  8958. MOV AL,ThousandsSep
  8959. TEST AL,AL
  8960. JZ @@fd7
  8961. STOSB
  8962. @@fd7: DEC DigitPlace
  8963. RET
  8964. @@exit:
  8965. POP EBX
  8966. POP ESI
  8967. POP EDI
  8968. end;
  8969. const
  8970. // 8087 status word masks
  8971. mIE = $0001;
  8972. mDE = $0002;
  8973. mZE = $0004;
  8974. mOE = $0008;
  8975. mUE = $0010;
  8976. mPE = $0020;
  8977. mC0 = $0100;
  8978. mC1 = $0200;
  8979. mC2 = $0400;
  8980. mC3 = $4000;
  8981. procedure FloatToDecimal(var Result: TFloatRec; const Value;
  8982. ValueType: TFloatValue; Precision, Decimals: Integer);
  8983. var
  8984. StatWord: Word;
  8985. Exponent: Integer;
  8986. Temp: Double;
  8987. BCDValue: Extended;
  8988. SaveGOT: Pointer;
  8989. asm
  8990. PUSH EDI
  8991. PUSH ESI
  8992. PUSH EBX
  8993. MOV EBX,EAX
  8994. MOV ESI,EDX
  8995. {$IFDEF PIC}
  8996. PUSH ECX
  8997. CALL GetGOT
  8998. POP ECX
  8999. MOV SaveGOT,EAX
  9000. {$ELSE}
  9001. MOV SaveGOT,0
  9002. {$ENDIF}
  9003. CMP CL,fvExtended
  9004. JE @@1
  9005. CALL @@CurrToDecimal
  9006. JMP @@Exit
  9007. @@1: CALL @@ExtToDecimal
  9008. JMP @@Exit
  9009. // Convert Extended to decimal
  9010. @@ExtToDecimal:
  9011. MOV AX,[ESI].Word[8]
  9012. MOV EDX,EAX
  9013. AND EAX,7FFFH
  9014. JE @@ed1
  9015. CMP EAX,7FFFH
  9016. JNE @@ed10
  9017. // check for special values (INF, NAN)
  9018. TEST [ESI].Word[6],8000H
  9019. JZ @@ed2
  9020. // any significand bit set = NAN
  9021. // all significand bits clear = INF
  9022. CMP dword ptr [ESI], 0
  9023. JNZ @@ed0
  9024. CMP dword ptr [ESI+4], 80000000H
  9025. JZ @@ed2
  9026. @@ed0: INC EAX
  9027. @@ed1: XOR EDX,EDX
  9028. @@ed2: MOV [EBX].TFloatRec.Digits.Byte,0
  9029. JMP @@ed31
  9030. @@ed10: FLD TBYTE PTR [ESI]
  9031. SUB EAX,3FFFH
  9032. IMUL EAX,19728
  9033. SAR EAX,16
  9034. INC EAX
  9035. MOV Exponent,EAX
  9036. MOV EAX,18
  9037. SUB EAX,Exponent
  9038. FABS
  9039. PUSH EBX
  9040. MOV EBX,SaveGOT
  9041. CALL FPower10
  9042. POP EBX
  9043. FRNDINT
  9044. MOV EDI,SaveGOT
  9045. FLD [EDI].FCon1E18
  9046. FCOMP
  9047. FSTSW StatWord
  9048. FWAIT
  9049. TEST StatWord,mC0+mC3
  9050. JE @@ed11
  9051. FIDIV [EDI].DCon10
  9052. INC Exponent
  9053. @@ed11: FBSTP BCDValue
  9054. LEA EDI,[EBX].TFloatRec.Digits
  9055. MOV EDX,9
  9056. FWAIT
  9057. @@ed12: MOV AL,BCDValue[EDX-1].Byte
  9058. MOV AH,AL
  9059. SHR AL,4
  9060. AND AH,0FH
  9061. ADD AX,'00'
  9062. STOSW
  9063. DEC EDX
  9064. JNE @@ed12
  9065. XOR AL,AL
  9066. STOSB
  9067. @@ed20: MOV EDI,Exponent
  9068. ADD EDI,Decimals
  9069. JNS @@ed21
  9070. XOR EAX,EAX
  9071. JMP @@ed1
  9072. @@ed21: CMP EDI,Precision
  9073. JB @@ed22
  9074. MOV EDI,Precision
  9075. @@ed22: CMP EDI,18
  9076. JAE @@ed26
  9077. CMP [EBX].TFloatRec.Digits.Byte[EDI],'5'
  9078. JB @@ed25
  9079. @@ed23: MOV [EBX].TFloatRec.Digits.Byte[EDI],0
  9080. DEC EDI
  9081. JS @@ed24
  9082. INC [EBX].TFloatRec.Digits.Byte[EDI]
  9083. CMP [EBX].TFloatRec.Digits.Byte[EDI],'9'
  9084. JA @@ed23
  9085. JMP @@ed30
  9086. @@ed24: MOV [EBX].TFloatRec.Digits.Word,'1'
  9087. INC Exponent
  9088. JMP @@ed30
  9089. @@ed26: MOV EDI,18
  9090. @@ed25: MOV [EBX].TFloatRec.Digits.Byte[EDI],0
  9091. DEC EDI
  9092. JS @@ed32
  9093. CMP [EBX].TFloatRec.Digits.Byte[EDI],'0'
  9094. JE @@ed25
  9095. @@ed30: MOV DX,[ESI].Word[8]
  9096. @@ed30a:
  9097. MOV EAX,Exponent
  9098. @@ed31: SHR DX,15
  9099. MOV [EBX].TFloatRec.Exponent,AX
  9100. MOV [EBX].TFloatRec.Negative,DL
  9101. RET
  9102. @@ed32: XOR EDX,EDX
  9103. JMP @@ed30a
  9104. @@DecimalTable:
  9105. DD 10
  9106. DD 100
  9107. DD 1000
  9108. DD 10000
  9109. // Convert Currency to decimal
  9110. @@CurrToDecimal:
  9111. MOV EAX,[ESI].Integer[0]
  9112. MOV EDX,[ESI].Integer[4]
  9113. MOV ECX,EAX
  9114. OR ECX,EDX
  9115. JE @@cd20
  9116. OR EDX,EDX
  9117. JNS @@cd1
  9118. NEG EDX
  9119. NEG EAX
  9120. SBB EDX,0
  9121. @@cd1: XOR ECX,ECX
  9122. MOV EDI,Decimals
  9123. OR EDI,EDI
  9124. JGE @@cd2
  9125. XOR EDI,EDI
  9126. @@cd2: CMP EDI,4
  9127. JL @@cd4
  9128. MOV EDI,4
  9129. @@cd3: INC ECX
  9130. SUB EAX,Const1E18Lo
  9131. SBB EDX,Const1E18Hi
  9132. JNC @@cd3
  9133. DEC ECX
  9134. ADD EAX,Const1E18Lo
  9135. ADC EDX,Const1E18Hi
  9136. @@cd4: MOV Temp.Integer[0],EAX
  9137. MOV Temp.Integer[4],EDX
  9138. FILD Temp
  9139. MOV EDX,EDI
  9140. MOV EAX,4
  9141. SUB EAX,EDX
  9142. JE @@cd5
  9143. MOV EDI,SaveGOT
  9144. FIDIV @@DecimalTable.Integer[EDI+EAX*4-4]
  9145. @@cd5: FBSTP BCDValue
  9146. LEA EDI,[EBX].TFloatRec.Digits
  9147. FWAIT
  9148. OR ECX,ECX
  9149. JNE @@cd11
  9150. MOV ECX,9
  9151. @@cd10: MOV AL,BCDValue[ECX-1].Byte
  9152. MOV AH,AL
  9153. SHR AL,4
  9154. JNE @@cd13
  9155. MOV AL,AH
  9156. AND AL,0FH
  9157. JNE @@cd14
  9158. DEC ECX
  9159. JNE @@cd10
  9160. JMP @@cd20
  9161. @@cd11: MOV AL,CL
  9162. ADD AL,'0'
  9163. STOSB
  9164. MOV ECX,9
  9165. @@cd12: MOV AL,BCDValue[ECX-1].Byte
  9166. MOV AH,AL
  9167. SHR AL,4
  9168. @@cd13: ADD AL,'0'
  9169. STOSB
  9170. MOV AL,AH
  9171. AND AL,0FH
  9172. @@cd14: ADD AL,'0'
  9173. STOSB
  9174. DEC ECX
  9175. JNE @@cd12
  9176. MOV EAX,EDI
  9177. LEA ECX,[EBX].TFloatRec.Digits[EDX]
  9178. SUB EAX,ECX
  9179. @@cd15: MOV BYTE PTR [EDI],0
  9180. DEC EDI
  9181. CMP BYTE PTR [EDI],'0'
  9182. JE @@cd15
  9183. MOV EDX,[ESI].Integer[4]
  9184. SHR EDX,31
  9185. JMP @@cd21
  9186. @@cd20: XOR EAX,EAX
  9187. XOR EDX,EDX
  9188. MOV [EBX].TFloatRec.Digits.Byte[0],AL
  9189. @@cd21: MOV [EBX].TFloatRec.Exponent,AX
  9190. MOV [EBX].TFloatRec.Negative,DL
  9191. RET
  9192. @@Exit:
  9193. POP EBX
  9194. POP ESI
  9195. POP EDI
  9196. end;
  9197. function TextToFloat(Buffer: PChar; var Value;
  9198. ValueType: TFloatValue): Boolean;
  9199. const
  9200. // 8087 control word
  9201. // Infinity control = 1 Affine
  9202. // Rounding Control = 0 Round to nearest or even
  9203. // Precision Control = 3 64 bits
  9204. // All interrupts masked
  9205. CWNear: Word = $133F;
  9206. var
  9207. Temp: Integer;
  9208. CtrlWord: Word;
  9209. DecimalSep: Char;
  9210. SaveGOT: Integer;
  9211. asm
  9212. PUSH EDI
  9213. PUSH ESI
  9214. PUSH EBX
  9215. MOV ESI,EAX
  9216. MOV EDI,EDX
  9217. {$IFDEF PIC}
  9218. PUSH ECX
  9219. CALL GetGOT
  9220. POP EBX
  9221. MOV SaveGOT,EAX
  9222. MOV ECX,[EAX].OFFSET DecimalSeparator
  9223. MOV CL,[ECX].Byte
  9224. MOV DecimalSep,CL
  9225. {$ELSE}
  9226. MOV SaveGOT,0
  9227. MOV AL,DecimalSeparator
  9228. MOV DecimalSep,AL
  9229. MOV EBX,ECX
  9230. {$ENDIF}
  9231. FSTCW CtrlWord
  9232. FCLEX
  9233. {$IFDEF PIC}
  9234. FLDCW [EAX].CWNear
  9235. {$ELSE}
  9236. FLDCW CWNear
  9237. {$ENDIF}
  9238. FLDZ
  9239. CALL @@SkipBlanks
  9240. MOV BH, byte ptr [ESI]
  9241. CMP BH,'+'
  9242. JE @@1
  9243. CMP BH,'-'
  9244. JNE @@2
  9245. @@1: INC ESI
  9246. @@2: MOV ECX,ESI
  9247. CALL @@GetDigitStr
  9248. XOR EDX,EDX
  9249. MOV AL,[ESI]
  9250. CMP AL,DecimalSep
  9251. JNE @@3
  9252. INC ESI
  9253. CALL @@GetDigitStr
  9254. NEG EDX
  9255. @@3: CMP ECX,ESI
  9256. JE @@9
  9257. MOV AL, byte ptr [ESI]
  9258. AND AL,0DFH
  9259. CMP AL,'E'
  9260. JNE @@4
  9261. INC ESI
  9262. PUSH EDX
  9263. CALL @@GetExponent
  9264. POP EAX
  9265. ADD EDX,EAX
  9266. @@4: CALL @@SkipBlanks
  9267. CMP BYTE PTR [ESI],0
  9268. JNE @@9
  9269. MOV EAX,EDX
  9270. CMP BL,fvCurrency
  9271. JNE @@5
  9272. ADD EAX,4
  9273. @@5: PUSH EBX
  9274. MOV EBX,SaveGOT
  9275. CALL FPower10
  9276. POP EBX
  9277. CMP BH,'-'
  9278. JNE @@6
  9279. FCHS
  9280. @@6: CMP BL,fvExtended
  9281. JE @@7
  9282. FISTP QWORD PTR [EDI]
  9283. JMP @@8
  9284. @@7: FSTP TBYTE PTR [EDI]
  9285. @@8: FSTSW AX
  9286. TEST AX,mIE+mOE
  9287. JNE @@10
  9288. MOV AL,1
  9289. JMP @@11
  9290. @@9: FSTP ST(0)
  9291. @@10: XOR EAX,EAX
  9292. @@11: FCLEX
  9293. FLDCW CtrlWord
  9294. FWAIT
  9295. JMP @@Exit
  9296. @@SkipBlanks:
  9297. @@21: LODSB
  9298. OR AL,AL
  9299. JE @@22
  9300. CMP AL,' '
  9301. JE @@21
  9302. @@22: DEC ESI
  9303. RET
  9304. // Process string of digits
  9305. // Out EDX = Digit count
  9306. @@GetDigitStr:
  9307. XOR EAX,EAX
  9308. XOR EDX,EDX
  9309. @@31: LODSB
  9310. SUB AL,'0'+10
  9311. ADD AL,10
  9312. JNC @@32
  9313. {$IFDEF PIC}
  9314. XCHG SaveGOT,EBX
  9315. FIMUL [EBX].DCon10
  9316. XCHG SaveGOT,EBX
  9317. {$ELSE}
  9318. FIMUL DCon10
  9319. {$ENDIF}
  9320. MOV Temp,EAX
  9321. FIADD Temp
  9322. INC EDX
  9323. JMP @@31
  9324. @@32: DEC ESI
  9325. RET
  9326. // Get exponent
  9327. // Out EDX = Exponent (-4999..4999)
  9328. @@GetExponent:
  9329. XOR EAX,EAX
  9330. XOR EDX,EDX
  9331. MOV CL, byte ptr [ESI]
  9332. CMP CL,'+'
  9333. JE @@41
  9334. CMP CL,'-'
  9335. JNE @@42
  9336. @@41: INC ESI
  9337. @@42: MOV AL, byte ptr [ESI]
  9338. SUB AL,'0'+10
  9339. ADD AL,10
  9340. JNC @@43
  9341. INC ESI
  9342. IMUL EDX,10
  9343. ADD EDX,EAX
  9344. CMP EDX,500
  9345. JB @@42
  9346. @@43: CMP CL,'-'
  9347. JNE @@44
  9348. NEG EDX
  9349. @@44: RET
  9350. @@Exit:
  9351. POP EBX
  9352. POP ESI
  9353. POP EDI
  9354. end;
  9355. function TextToFloat(Buffer: PChar; var Value;
  9356. ValueType: TFloatValue; const FormatSettings: TFormatSettings): Boolean;
  9357. const
  9358. // 8087 control word
  9359. // Infinity control = 1 Affine
  9360. // Rounding Control = 0 Round to nearest or even
  9361. // Precision Control = 3 64 bits
  9362. // All interrupts masked
  9363. CWNear: Word = $133F;
  9364. var
  9365. Temp: Integer;
  9366. CtrlWord: Word;
  9367. DecimalSep: Char;
  9368. SaveGOT: Integer;
  9369. asm
  9370. PUSH EDI
  9371. PUSH ESI
  9372. PUSH EBX
  9373. MOV ESI,EAX
  9374. MOV EDI,EDX
  9375. {$IFDEF PIC}
  9376. PUSH ECX
  9377. CALL GetGOT
  9378. POP EBX
  9379. MOV SaveGOT,EAX
  9380. {$ELSE}
  9381. MOV SaveGOT,0
  9382. MOV EBX,ECX
  9383. {$ENDIF}
  9384. MOV EAX,FormatSettings
  9385. MOV AL,[EAX].TFormatSettings.DecimalSeparator
  9386. MOV DecimalSep,AL
  9387. FSTCW CtrlWord
  9388. FCLEX
  9389. {$IFDEF PIC}
  9390. FLDCW [EAX].CWNear
  9391. {$ELSE}
  9392. FLDCW CWNear
  9393. {$ENDIF}
  9394. FLDZ
  9395. CALL @@SkipBlanks
  9396. MOV BH, byte ptr [ESI]
  9397. CMP BH,'+'
  9398. JE @@1
  9399. CMP BH,'-'
  9400. JNE @@2
  9401. @@1: INC ESI
  9402. @@2: MOV ECX,ESI
  9403. CALL @@GetDigitStr
  9404. XOR EDX,EDX
  9405. MOV AL,[ESI]
  9406. CMP AL,DecimalSep
  9407. JNE @@3
  9408. INC ESI
  9409. CALL @@GetDigitStr
  9410. NEG EDX
  9411. @@3: CMP ECX,ESI
  9412. JE @@9
  9413. MOV AL, byte ptr [ESI]
  9414. AND AL,0DFH
  9415. CMP AL,'E'
  9416. JNE @@4
  9417. INC ESI
  9418. PUSH EDX
  9419. CALL @@GetExponent
  9420. POP EAX
  9421. ADD EDX,EAX
  9422. @@4: CALL @@SkipBlanks
  9423. CMP BYTE PTR [ESI],0
  9424. JNE @@9
  9425. MOV EAX,EDX
  9426. CMP BL,fvCurrency
  9427. JNE @@5
  9428. ADD EAX,4
  9429. @@5: PUSH EBX
  9430. MOV EBX,SaveGOT
  9431. CALL FPower10
  9432. POP EBX
  9433. CMP BH,'-'
  9434. JNE @@6
  9435. FCHS
  9436. @@6: CMP BL,fvExtended
  9437. JE @@7
  9438. FISTP QWORD PTR [EDI]
  9439. JMP @@8
  9440. @@7: FSTP TBYTE PTR [EDI]
  9441. @@8: FSTSW AX
  9442. TEST AX,mIE+mOE
  9443. JNE @@10
  9444. MOV AL,1
  9445. JMP @@11
  9446. @@9: FSTP ST(0)
  9447. @@10: XOR EAX,EAX
  9448. @@11: FCLEX
  9449. FLDCW CtrlWord
  9450. FWAIT
  9451. JMP @@Exit
  9452. @@SkipBlanks:
  9453. @@21: LODSB
  9454. OR AL,AL
  9455. JE @@22
  9456. CMP AL,' '
  9457. JE @@21
  9458. @@22: DEC ESI
  9459. RET
  9460. // Process string of digits
  9461. // Out EDX = Digit count
  9462. @@GetDigitStr:
  9463. XOR EAX,EAX
  9464. XOR EDX,EDX
  9465. @@31: LODSB
  9466. SUB AL,'0'+10
  9467. ADD AL,10
  9468. JNC @@32
  9469. {$IFDEF PIC}
  9470. XCHG SaveGOT,EBX
  9471. FIMUL [EBX].DCon10
  9472. XCHG SaveGOT,EBX
  9473. {$ELSE}
  9474. FIMUL DCon10
  9475. {$ENDIF}
  9476. MOV Temp,EAX
  9477. FIADD Temp
  9478. INC EDX
  9479. JMP @@31
  9480. @@32: DEC ESI
  9481. RET
  9482. // Get exponent
  9483. // Out EDX = Exponent (-4999..4999)
  9484. @@GetExponent:
  9485. XOR EAX,EAX
  9486. XOR EDX,EDX
  9487. MOV CL, byte ptr [ESI]
  9488. CMP CL,'+'
  9489. JE @@41
  9490. CMP CL,'-'
  9491. JNE @@42
  9492. @@41: INC ESI
  9493. @@42: MOV AL, byte ptr [ESI]
  9494. SUB AL,'0'+10
  9495. ADD AL,10
  9496. JNC @@43
  9497. INC ESI
  9498. IMUL EDX,10
  9499. ADD EDX,EAX
  9500. CMP EDX,500
  9501. JB @@42
  9502. @@43: CMP CL,'-'
  9503. JNE @@44
  9504. NEG EDX
  9505. @@44: RET
  9506. @@Exit:
  9507. POP EBX
  9508. POP ESI
  9509. POP EDI
  9510. end;
  9511. function FloatToStr(Value: Extended): string;
  9512. var
  9513. Buffer: array[0..63] of Char;
  9514. begin
  9515. SetString(Result, Buffer, FloatToText(Buffer, Value, fvExtended,
  9516. ffGeneral, 15, 0));
  9517. end;
  9518. function FloatToStr(Value: Extended;
  9519. const FormatSettings: TFormatSettings): string;
  9520. var
  9521. Buffer: array[0..63] of Char;
  9522. begin
  9523. SetString(Result, Buffer, FloatToText(Buffer, Value, fvExtended,
  9524. ffGeneral, 15, 0, FormatSettings));
  9525. end;
  9526. function CurrToStr(Value: Currency): string;
  9527. var
  9528. Buffer: array[0..63] of Char;
  9529. begin
  9530. SetString(Result, Buffer, FloatToText(Buffer, Value, fvCurrency,
  9531. ffGeneral, 0, 0));
  9532. end;
  9533. function CurrToStr(Value: Currency;
  9534. const FormatSettings: TFormatSettings): string;
  9535. var
  9536. Buffer: array[0..63] of Char;
  9537. begin
  9538. SetString(Result, Buffer, FloatToText(Buffer, Value, fvCurrency,
  9539. ffGeneral, 0, 0, FormatSettings));
  9540. end;
  9541. function TryFloatToCurr(const Value: Extended; out AResult: Currency): Boolean;
  9542. begin
  9543. Result := (Value >= MinCurrency) and (Value <= MaxCurrency);
  9544. if Result then
  9545. AResult := Value;
  9546. end;
  9547. function FloatToCurr(const Value: Extended): Currency;
  9548. begin
  9549. if not TryFloatToCurr(Value, Result) then
  9550. ConvertErrorFmt(SInvalidCurrency, [FloatToStr(Value)]);
  9551. end;
  9552. function FloatToStrF(Value: Extended; Format: TFloatFormat;
  9553. Precision, Digits: Integer): string;
  9554. var
  9555. Buffer: array[0..63] of Char;
  9556. begin
  9557. SetString(Result, Buffer, FloatToText(Buffer, Value, fvExtended,
  9558. Format, Precision, Digits));
  9559. end;
  9560. function FloatToStrF(Value: Extended; Format: TFloatFormat;
  9561. Precision, Digits: Integer; const FormatSettings: TFormatSettings): string;
  9562. var
  9563. Buffer: array[0..63] of Char;
  9564. begin
  9565. SetString(Result, Buffer, FloatToText(Buffer, Value, fvExtended,
  9566. Format, Precision, Digits, FormatSettings));
  9567. end;
  9568. function CurrToStrF(Value: Currency; Format: TFloatFormat;
  9569. Digits: Integer): string;
  9570. var
  9571. Buffer: array[0..63] of Char;
  9572. begin
  9573. SetString(Result, Buffer, FloatToText(Buffer, Value, fvCurrency,
  9574. Format, 0, Digits));
  9575. end;
  9576. function CurrToStrF(Value: Currency; Format: TFloatFormat;
  9577. Digits: Integer; const FormatSettings: TFormatSettings): string;
  9578. var
  9579. Buffer: array[0..63] of Char;
  9580. begin
  9581. SetString(Result, Buffer, FloatToText(Buffer, Value, fvCurrency,
  9582. Format, 0, Digits, FormatSettings));
  9583. end;
  9584. function FormatFloat(const Format: string; Value: Extended): string;
  9585. var
  9586. Buffer: array[0..255] of Char;
  9587. begin
  9588. if Length(Format) > SizeOf(Buffer) - 32 then ConvertError(SFormatTooLong);
  9589. SetString(Result, Buffer, FloatToTextFmt(Buffer, Value, fvExtended,
  9590. PChar(Format)));
  9591. end;
  9592. function FormatFloat(const Format: string; Value: Extended;
  9593. const FormatSettings: TFormatSettings): string;
  9594. var
  9595. Buffer: array[0..255] of Char;
  9596. begin
  9597. if Length(Format) > SizeOf(Buffer) - 32 then ConvertError(SFormatTooLong);
  9598. SetString(Result, Buffer, FloatToTextFmt(Buffer, Value, fvExtended,
  9599. PChar(Format), FormatSettings));
  9600. end;
  9601. function FormatCurr(const Format: string; Value: Currency): string;
  9602. var
  9603. Buffer: array[0..255] of Char;
  9604. begin
  9605. if Length(Format) > SizeOf(Buffer) - 32 then ConvertError(SFormatTooLong);
  9606. SetString(Result, Buffer, FloatToTextFmt(Buffer, Value, fvCurrency,
  9607. PChar(Format)));
  9608. end;
  9609. function FormatCurr(const Format: string; Value: Currency;
  9610. const FormatSettings: TFormatSettings): string;
  9611. var
  9612. Buffer: array[0..255] of Char;
  9613. begin
  9614. if Length(Format) > SizeOf(Buffer) - 32 then ConvertError(SFormatTooLong);
  9615. SetString(Result, Buffer, FloatToTextFmt(Buffer, Value, fvCurrency,
  9616. PChar(Format), FormatSettings));
  9617. end;
  9618. function StrToFloat(const S: string): Extended;
  9619. begin
  9620. if not TextToFloat(PChar(S), Result, fvExtended) then
  9621. ConvertErrorFmt(SInvalidFloat, [S]);
  9622. end;
  9623. function StrToFloat(const S: string;
  9624. const FormatSettings: TFormatSettings): Extended;
  9625. begin
  9626. if not TextToFloat(PChar(S), Result, fvExtended, FormatSettings) then
  9627. ConvertErrorFmt(SInvalidFloat, [S]);
  9628. end;
  9629. function StrToFloatDef(const S: string; const Default: Extended): Extended;
  9630. begin
  9631. if not TextToFloat(PChar(S), Result, fvExtended) then
  9632. Result := Default;
  9633. end;
  9634. function StrToFloatDef(const S: string; const Default: Extended;
  9635. const FormatSettings: TFormatSettings): Extended;
  9636. begin
  9637. if not TextToFloat(PChar(S), Result, fvExtended, FormatSettings) then
  9638. Result := Default;
  9639. end;
  9640. function TryStrToFloat(const S: string; out Value: Extended): Boolean;
  9641. begin
  9642. Result := TextToFloat(PChar(S), Value, fvExtended);
  9643. end;
  9644. function TryStrToFloat(const S: string; out Value: Extended;
  9645. const FormatSettings: TFormatSettings): Boolean;
  9646. begin
  9647. Result := TextToFloat(PChar(S), Value, fvExtended, FormatSettings);
  9648. end;
  9649. function TryStrToFloat(const S: string; out Value: Double): Boolean;
  9650. var
  9651. LValue: Extended;
  9652. begin
  9653. Result := TextToFloat(PChar(S), LValue, fvExtended);
  9654. if Result then
  9655. Value := LValue;
  9656. end;
  9657. function TryStrToFloat(const S: string; out Value: Double;
  9658. const FormatSettings: TFormatSettings): Boolean;
  9659. var
  9660. LValue: Extended;
  9661. begin
  9662. Result := TextToFloat(PChar(S), LValue, fvExtended, FormatSettings);
  9663. if Result then
  9664. Value := LValue;
  9665. end;
  9666. function TryStrToFloat(const S: string; out Value: Single): Boolean;
  9667. var
  9668. LValue: Extended;
  9669. begin
  9670. Result := TextToFloat(PChar(S), LValue, fvExtended);
  9671. if Result then
  9672. Value := LValue;
  9673. end;
  9674. function TryStrToFloat(const S: string; out Value: Single;
  9675. const FormatSettings: TFormatSettings): Boolean;
  9676. var
  9677. LValue: Extended;
  9678. begin
  9679. Result := TextToFloat(PChar(S), LValue, fvExtended, FormatSettings);
  9680. if Result then
  9681. Value := LValue;
  9682. end;
  9683. function StrToCurr(const S: string): Currency;
  9684. begin
  9685. if not TextToFloat(PChar(S), Result, fvCurrency) then
  9686. ConvertErrorFmt(SInvalidFloat, [S]);
  9687. end;
  9688. function StrToCurr(const S: string;
  9689. const FormatSettings: TFormatSettings): Currency;
  9690. begin
  9691. if not TextToFloat(PChar(S), Result, fvCurrency, FormatSettings) then
  9692. ConvertErrorFmt(SInvalidFloat, [S]);
  9693. end;
  9694. function StrToCurrDef(const S: string; const Default: Currency): Currency;
  9695. begin
  9696. if not TextToFloat(PChar(S), Result, fvCurrency) then
  9697. Result := Default;
  9698. end;
  9699. function StrToCurrDef(const S: string; const Default: Currency;
  9700. const FormatSettings: TFormatSettings): Currency;
  9701. begin
  9702. if not TextToFloat(PChar(S), Result, fvCurrency, FormatSettings) then
  9703. Result := Default;
  9704. end;
  9705. function TryStrToCurr(const S: string; out Value: Currency): Boolean;
  9706. begin
  9707. Result := TextToFloat(PChar(S), Value, fvCurrency);
  9708. end;
  9709. function TryStrToCurr(const S: string; out Value: Currency;
  9710. const FormatSettings: TFormatSettings): Boolean;
  9711. begin
  9712. Result := TextToFloat(PChar(S), Value, fvCurrency, FormatSettings);
  9713. end;
  9714. { Date/time support routines }
  9715. const
  9716. FMSecsPerDay: Single = MSecsPerDay;
  9717. IMSecsPerDay: Integer = MSecsPerDay;
  9718. function DateTimeToTimeStamp(DateTime: TDateTime): TTimeStamp;
  9719. asm
  9720. PUSH EBX
  9721. {$IFDEF PIC}
  9722. PUSH EAX
  9723. CALL GetGOT
  9724. MOV EBX,EAX
  9725. POP EAX
  9726. {$ELSE}
  9727. XOR EBX,EBX
  9728. {$ENDIF}
  9729. MOV ECX,EAX
  9730. FLD DateTime
  9731. FMUL [EBX].FMSecsPerDay
  9732. SUB ESP,8
  9733. FISTP QWORD PTR [ESP]
  9734. FWAIT
  9735. POP EAX
  9736. POP EDX
  9737. OR EDX,EDX
  9738. JNS @@1
  9739. NEG EDX
  9740. NEG EAX
  9741. SBB EDX,0
  9742. DIV [EBX].IMSecsPerDay
  9743. NEG EAX
  9744. JMP @@2
  9745. @@1: DIV [EBX].IMSecsPerDay
  9746. @@2: ADD EAX,DateDelta
  9747. MOV [ECX].TTimeStamp.Time,EDX
  9748. MOV [ECX].TTimeStamp.Date,EAX
  9749. POP EBX
  9750. end;
  9751. procedure ValidateTimeStamp(const TimeStamp: TTimeStamp);
  9752. begin
  9753. if (TimeStamp.Time < 0) or (TimeStamp.Date <= 0) then
  9754. ConvertErrorFmt(SInvalidTimeStamp, [TimeStamp.Date, TimeStamp.Time]);
  9755. end;
  9756. function TimeStampToDateTime(const TimeStamp: TTimeStamp): TDateTime;
  9757. asm
  9758. PUSH EBX
  9759. {$IFDEF PIC}
  9760. PUSH EAX
  9761. CALL GetGOT
  9762. MOV EBX,EAX
  9763. POP EAX
  9764. {$ELSE}
  9765. XOR EBX,EBX
  9766. {$ENDIF}
  9767. PUSH EAX
  9768. CALL ValidateTimeStamp
  9769. POP EAX
  9770. MOV ECX,[EAX].TTimeStamp.Time
  9771. MOV EAX,[EAX].TTimeStamp.Date
  9772. SUB EAX,DateDelta
  9773. IMUL [EBX].IMSecsPerDay
  9774. OR EDX,EDX
  9775. JNS @@1
  9776. SUB EAX,ECX
  9777. SBB EDX,0
  9778. JMP @@2
  9779. @@1: ADD EAX,ECX
  9780. ADC EDX,0
  9781. @@2: PUSH EDX
  9782. PUSH EAX
  9783. FILD QWORD PTR [ESP]
  9784. FDIV [EBX].FMSecsPerDay
  9785. ADD ESP,8
  9786. POP EBX
  9787. end;
  9788. function MSecsToTimeStamp(MSecs: Comp): TTimeStamp;
  9789. asm
  9790. PUSH EBX
  9791. {$IFDEF PIC}
  9792. PUSH EAX
  9793. CALL GetGOT
  9794. MOV EBX,EAX
  9795. POP EAX
  9796. {$ELSE}
  9797. XOR EBX,EBX
  9798. {$ENDIF}
  9799. MOV ECX,EAX
  9800. MOV EAX,MSecs.Integer[0]
  9801. MOV EDX,MSecs.Integer[4]
  9802. DIV [EBX].IMSecsPerDay
  9803. MOV [ECX].TTimeStamp.Time,EDX
  9804. MOV [ECX].TTimeStamp.Date,EAX
  9805. POP EBX
  9806. end;
  9807. function TimeStampToMSecs(const TimeStamp: TTimeStamp): Comp;
  9808. asm
  9809. PUSH EBX
  9810. {$IFDEF PIC}
  9811. PUSH EAX
  9812. CALL GetGOT
  9813. MOV EBX,EAX
  9814. POP EAX
  9815. {$ELSE}
  9816. XOR EBX,EBX
  9817. {$ENDIF}
  9818. PUSH EAX
  9819. CALL ValidateTimeStamp
  9820. POP EAX
  9821. FILD [EAX].TTimeStamp.Date
  9822. FMUL [EBX].FMSecsPerDay
  9823. FIADD [EAX].TTimeStamp.Time
  9824. POP EBX
  9825. end;
  9826. { Time encoding and decoding }
  9827. function TryEncodeTime(Hour, Min, Sec, MSec: Word; out Time: TDateTime): Boolean;
  9828. begin
  9829. Result := False;
  9830. if (Hour < HoursPerDay) and (Min < MinsPerHour) and (Sec < SecsPerMin) and (MSec < MSecsPerSec) then
  9831. begin
  9832. Time := (Hour * (MinsPerHour * SecsPerMin * MSecsPerSec) +
  9833. Min * (SecsPerMin * MSecsPerSec) +
  9834. Sec * MSecsPerSec +
  9835. MSec) / MSecsPerDay;
  9836. Result := True;
  9837. end;
  9838. end;
  9839. function EncodeTime(Hour, Min, Sec, MSec: Word): TDateTime;
  9840. begin
  9841. if not TryEncodeTime(Hour, Min, Sec, MSec, Result) then
  9842. ConvertError(STimeEncodeError);
  9843. end;
  9844. procedure DecodeTime(const DateTime: TDateTime; var Hour, Min, Sec, MSec: Word);
  9845. var
  9846. MinCount, MSecCount: Word;
  9847. begin
  9848. DivMod(DateTimeToTimeStamp(DateTime).Time, SecsPerMin * MSecsPerSec, MinCount, MSecCount);
  9849. DivMod(MinCount, MinsPerHour, Hour, Min);
  9850. DivMod(MSecCount, MSecsPerSec, Sec, MSec);
  9851. end;
  9852. { Date encoding and decoding }
  9853. function IsLeapYear(Year: Word): Boolean;
  9854. begin
  9855. Result := (Year mod 4 = 0) and ((Year mod 100 <> 0) or (Year mod 400 = 0));
  9856. end;
  9857. function TryEncodeDate(Year, Month, Day: Word; out Date: TDateTime): Boolean;
  9858. var
  9859. I: Integer;
  9860. DayTable: PDayTable;
  9861. begin
  9862. Result := False;
  9863. DayTable := @MonthDays[IsLeapYear(Year)];
  9864. if (Year >= 1) and (Year <= 9999) and (Month >= 1) and (Month <= 12) and
  9865. (Day >= 1) and (Day <= DayTable^[Month]) then
  9866. begin
  9867. for I := 1 to Month - 1 do Inc(Day, DayTable^[I]);
  9868. I := Year - 1;
  9869. Date := I * 365 + I div 4 - I div 100 + I div 400 + Day - DateDelta;
  9870. Result := True;
  9871. end;
  9872. end;
  9873. function EncodeDate(Year, Month, Day: Word): TDateTime;
  9874. begin
  9875. if not TryEncodeDate(Year, Month, Day, Result) then
  9876. ConvertError(SDateEncodeError);
  9877. end;
  9878. function DecodeDateFully(const DateTime: TDateTime; var Year, Month, Day, DOW: Word): Boolean;
  9879. const
  9880. D1 = 365;
  9881. D4 = D1 * 4 + 1;
  9882. D100 = D4 * 25 - 1;
  9883. D400 = D100 * 4 + 1;
  9884. var
  9885. Y, M, D, I: Word;
  9886. T: Integer;
  9887. DayTable: PDayTable;
  9888. begin
  9889. T := DateTimeToTimeStamp(DateTime).Date;
  9890. if T <= 0 then
  9891. begin
  9892. Year := 0;
  9893. Month := 0;
  9894. Day := 0;
  9895. DOW := 0;
  9896. Result := False;
  9897. end else
  9898. begin
  9899. DOW := T mod 7 + 1;
  9900. Dec(T);
  9901. Y := 1;
  9902. while T >= D400 do
  9903. begin
  9904. Dec(T, D400);
  9905. Inc(Y, 400);
  9906. end;
  9907. DivMod(T, D100, I, D);
  9908. if I = 4 then
  9909. begin
  9910. Dec(I);
  9911. Inc(D, D100);
  9912. end;
  9913. Inc(Y, I * 100);
  9914. DivMod(D, D4, I, D);
  9915. Inc(Y, I * 4);
  9916. DivMod(D, D1, I, D);
  9917. if I = 4 then
  9918. begin
  9919. Dec(I);
  9920. Inc(D, D1);
  9921. end;
  9922. Inc(Y, I);
  9923. Result := IsLeapYear(Y);
  9924. DayTable := @MonthDays[Result];
  9925. M := 1;
  9926. while True do
  9927. begin
  9928. I := DayTable^[M];
  9929. if D < I then Break;
  9930. Dec(D, I);
  9931. Inc(M);
  9932. end;
  9933. Year := Y;
  9934. Month := M;
  9935. Day := D + 1;
  9936. end;
  9937. end;
  9938. function InternalDecodeDate(const DateTime: TDateTime; var Year, Month, Day, DOW: Word): Boolean;
  9939. begin
  9940. Result := DecodeDateFully(DateTime, Year, Month, Day, DOW);
  9941. Dec(DOW);
  9942. end;
  9943. procedure DecodeDate(const DateTime: TDateTime; var Year, Month, Day: Word);
  9944. var
  9945. Dummy: Word;
  9946. begin
  9947. DecodeDateFully(DateTime, Year, Month, Day, Dummy);
  9948. end;
  9949. {$IFDEF MSWINDOWS}
  9950. procedure DateTimeToSystemTime(const DateTime: TDateTime; var SystemTime: TSystemTime);
  9951. begin
  9952. with SystemTime do
  9953. begin
  9954. DecodeDateFully(DateTime, wYear, wMonth, wDay, wDayOfWeek);
  9955. Dec(wDayOfWeek);
  9956. DecodeTime(DateTime, wHour, wMinute, wSecond, wMilliseconds);
  9957. end;
  9958. end;
  9959. function SystemTimeToDateTime(const SystemTime: TSystemTime): TDateTime;
  9960. begin
  9961. with SystemTime do
  9962. begin
  9963. Result := EncodeDate(wYear, wMonth, wDay);
  9964. if Result >= 0 then
  9965. Result := Result + EncodeTime(wHour, wMinute, wSecond, wMilliSeconds)
  9966. else
  9967. Result := Result - EncodeTime(wHour, wMinute, wSecond, wMilliSeconds);
  9968. end;
  9969. end;
  9970. {$ENDIF}
  9971. function DayOfWeek(const DateTime: TDateTime): Word;
  9972. begin
  9973. Result := DateTimeToTimeStamp(DateTime).Date mod 7 + 1;
  9974. end;
  9975. function Date: TDateTime;
  9976. {$IFDEF MSWINDOWS}
  9977. var
  9978. SystemTime: TSystemTime;
  9979. begin
  9980. GetLocalTime(SystemTime);
  9981. with SystemTime do Result := EncodeDate(wYear, wMonth, wDay);
  9982. end;
  9983. {$ENDIF}
  9984. {$IFDEF LINUX}
  9985. var
  9986. T: TTime_T;
  9987. UT: TUnixTime;
  9988. begin
  9989. __time(@T);
  9990. localtime_r(@T, UT);
  9991. Result := EncodeDate(UT.tm_year + 1900, UT.tm_mon + 1, UT.tm_mday);
  9992. end;
  9993. {$ENDIF}
  9994. function Time: TDateTime;
  9995. {$IFDEF MSWINDOWS}
  9996. var
  9997. SystemTime: TSystemTime;
  9998. begin
  9999. GetLocalTime(SystemTime);
  10000. with SystemTime do
  10001. Result := EncodeTime(wHour, wMinute, wSecond, wMilliSeconds);
  10002. end;
  10003. {$ENDIF}
  10004. {$IFDEF LINUX}
  10005. var
  10006. T: TTime_T;
  10007. TV: TTimeVal;
  10008. UT: TUnixTime;
  10009. begin
  10010. gettimeofday(TV, nil);
  10011. T := TV.tv_sec;
  10012. localtime_r(@T, UT);
  10013. Result := EncodeTime(UT.tm_hour, UT.tm_min, UT.tm_sec, TV.tv_usec div 1000);
  10014. end;
  10015. {$ENDIF}
  10016. function GetTime: TDateTime;
  10017. begin
  10018. Result := Time;
  10019. end;
  10020. function Now: TDateTime;
  10021. {$IFDEF MSWINDOWS}
  10022. var
  10023. SystemTime: TSystemTime;
  10024. begin
  10025. GetLocalTime(SystemTime);
  10026. with SystemTime do
  10027. Result := EncodeDate(wYear, wMonth, wDay) +
  10028. EncodeTime(wHour, wMinute, wSecond, wMilliseconds);
  10029. end;
  10030. {$ENDIF}
  10031. {$IFDEF LINUX}
  10032. var
  10033. T: TTime_T;
  10034. TV: TTimeVal;
  10035. UT: TUnixTime;
  10036. begin
  10037. gettimeofday(TV, nil);
  10038. T := TV.tv_sec;
  10039. localtime_r(@T, UT);
  10040. Result := EncodeDate(UT.tm_year + 1900, UT.tm_mon + 1, UT.tm_mday) +
  10041. EncodeTime(UT.tm_hour, UT.tm_min, UT.tm_sec, TV.tv_usec div 1000);
  10042. end;
  10043. {$ENDIF}
  10044. function IncMonth(const DateTime: TDateTime; NumberOfMonths: Integer): TDateTime;
  10045. var
  10046. Year, Month, Day: Word;
  10047. begin
  10048. DecodeDate(DateTime, Year, Month, Day);
  10049. IncAMonth(Year, Month, Day, NumberOfMonths);
  10050. Result := EncodeDate(Year, Month, Day);
  10051. ReplaceTime(Result, DateTime);
  10052. end;
  10053. procedure IncAMonth(var Year, Month, Day: Word; NumberOfMonths: Integer = 1);
  10054. var
  10055. DayTable: PDayTable;
  10056. Sign: Integer;
  10057. begin
  10058. if NumberOfMonths >= 0 then Sign := 1 else Sign := -1;
  10059. Year := Year + (NumberOfMonths div 12);
  10060. NumberOfMonths := NumberOfMonths mod 12;
  10061. Inc(Month, NumberOfMonths);
  10062. if Word(Month-1) > 11 then // if Month <= 0, word(Month-1) > 11)
  10063. begin
  10064. Inc(Year, Sign);
  10065. Inc(Month, -12 * Sign);
  10066. end;
  10067. DayTable := @MonthDays[IsLeapYear(Year)];
  10068. if Day > DayTable^[Month] then Day := DayTable^[Month];
  10069. end;
  10070. procedure ReplaceTime(var DateTime: TDateTime; const NewTime: TDateTime);
  10071. begin
  10072. DateTime := Trunc(DateTime);
  10073. if DateTime >= 0 then
  10074. DateTime := DateTime + Abs(Frac(NewTime))
  10075. else
  10076. DateTime := DateTime - Abs(Frac(NewTime));
  10077. end;
  10078. procedure ReplaceDate(var DateTime: TDateTime; const NewDate: TDateTime);
  10079. var
  10080. Temp: TDateTime;
  10081. begin
  10082. Temp := NewDate;
  10083. ReplaceTime(Temp, DateTime);
  10084. DateTime := Temp;
  10085. end;
  10086. function CurrentYear: Word;
  10087. {$IFDEF MSWINDOWS}
  10088. var
  10089. SystemTime: TSystemTime;
  10090. begin
  10091. GetLocalTime(SystemTime);
  10092. Result := SystemTime.wYear;
  10093. end;
  10094. {$ENDIF}
  10095. {$IFDEF LINUX}
  10096. var
  10097. T: TTime_T;
  10098. UT: TUnixTime;
  10099. begin
  10100. __time(@T);
  10101. localtime_r(@T, UT);
  10102. Result := UT.tm_year + 1900;
  10103. end;
  10104. {$ENDIF}
  10105. { Date/time to string conversions }
  10106. procedure DateTimeToString(var Result: string; const Format: string;
  10107. DateTime: TDateTime);
  10108. var
  10109. BufPos, AppendLevel: Integer;
  10110. Buffer: array[0..255] of Char;
  10111. procedure AppendChars(P: PChar; Count: Integer);
  10112. var
  10113. N: Integer;
  10114. begin
  10115. N := SizeOf(Buffer) - BufPos;
  10116. if N > Count then N := Count;
  10117. if N <> 0 then Move(P[0], Buffer[BufPos], N);
  10118. Inc(BufPos, N);
  10119. end;
  10120. procedure AppendString(const S: string);
  10121. begin
  10122. AppendChars(Pointer(S), Length(S));
  10123. end;
  10124. procedure AppendNumber(Number, Digits: Integer);
  10125. const
  10126. Format: array[0..3] of Char = '%.*d';
  10127. var
  10128. NumBuf: array[0..15] of Char;
  10129. begin
  10130. AppendChars(NumBuf, FormatBuf(NumBuf, SizeOf(NumBuf), Format,
  10131. SizeOf(Format), [Digits, Number]));
  10132. end;
  10133. procedure AppendFormat(Format: PChar);
  10134. var
  10135. Starter, Token, LastToken: Char;
  10136. DateDecoded, TimeDecoded, Use12HourClock,
  10137. BetweenQuotes: Boolean;
  10138. P: PChar;
  10139. Count: Integer;
  10140. Year, Month, Day, Hour, Min, Sec, MSec, H: Word;
  10141. procedure GetCount;
  10142. var
  10143. P: PChar;
  10144. begin
  10145. P := Format;
  10146. while Format^ = Starter do Inc(Format);
  10147. Count := Format - P + 1;
  10148. end;
  10149. procedure GetDate;
  10150. begin
  10151. if not DateDecoded then
  10152. begin
  10153. DecodeDate(DateTime, Year, Month, Day);
  10154. DateDecoded := True;
  10155. end;
  10156. end;
  10157. procedure GetTime;
  10158. begin
  10159. if not TimeDecoded then
  10160. begin
  10161. DecodeTime(DateTime, Hour, Min, Sec, MSec);
  10162. TimeDecoded := True;
  10163. end;
  10164. end;
  10165. {$IFDEF MSWINDOWS}
  10166. function ConvertEraString(const Count: Integer) : string;
  10167. var
  10168. FormatStr: string;
  10169. SystemTime: TSystemTime;
  10170. Buffer: array[Byte] of Char;
  10171. P: PChar;
  10172. begin
  10173. Result := '';
  10174. with SystemTime do
  10175. begin
  10176. wYear := Year;
  10177. wMonth := Month;
  10178. wDay := Day;
  10179. end;
  10180. FormatStr := 'gg';
  10181. if GetDateFormat(GetThreadLocale, DATE_USE_ALT_CALENDAR, @SystemTime,
  10182. PChar(FormatStr), Buffer, SizeOf(Buffer)) <> 0 then
  10183. begin
  10184. Result := Buffer;
  10185. if Count = 1 then
  10186. begin
  10187. case SysLocale.PriLangID of
  10188. LANG_JAPANESE:
  10189. Result := Copy(Result, 1, CharToBytelen(Result, 1));
  10190. LANG_CHINESE:
  10191. if (SysLocale.SubLangID = SUBLANG_CHINESE_TRADITIONAL)
  10192. and (ByteToCharLen(Result, Length(Result)) = 4) then
  10193. begin
  10194. P := Buffer + CharToByteIndex(Result, 3) - 1;
  10195. SetString(Result, P, CharToByteLen(P, 2));
  10196. end;
  10197. end;
  10198. end;
  10199. end;
  10200. end;
  10201. function ConvertYearString(const Count: Integer): string;
  10202. var
  10203. FormatStr: string;
  10204. SystemTime: TSystemTime;
  10205. Buffer: array[Byte] of Char;
  10206. begin
  10207. Result := '';
  10208. with SystemTime do
  10209. begin
  10210. wYear := Year;
  10211. wMonth := Month;
  10212. wDay := Day;
  10213. end;
  10214. if Count <= 2 then
  10215. FormatStr := 'yy' // avoid Win95 bug.
  10216. else
  10217. FormatStr := 'yyyy';
  10218. if GetDateFormat(GetThreadLocale, DATE_USE_ALT_CALENDAR, @SystemTime,
  10219. PChar(FormatStr), Buffer, SizeOf(Buffer)) <> 0 then
  10220. begin
  10221. Result := Buffer;
  10222. if (Count = 1) and (Result[1] = '0') then
  10223. Result := Copy(Result, 2, Length(Result)-1);
  10224. end;
  10225. end;
  10226. {$ENDIF}
  10227. {$IFDEF LINUX}
  10228. function FindEra(Date: Integer): Byte;
  10229. var
  10230. I : Byte;
  10231. begin
  10232. Result := 0;
  10233. for I := 1 to EraCount do
  10234. begin
  10235. if (EraRanges[I].StartDate <= Date) and
  10236. (EraRanges[I].EndDate >= Date) then
  10237. begin
  10238. Result := I;
  10239. Exit;
  10240. end;
  10241. end;
  10242. end;
  10243. function ConvertEraString(const Count: Integer) : String;
  10244. var
  10245. I : Byte;
  10246. begin
  10247. Result := '';
  10248. I := FindEra(Trunc(DateTime));
  10249. if I > 0 then
  10250. Result := EraNames[I];
  10251. end;
  10252. function ConvertYearString(const Count: Integer) : String;
  10253. var
  10254. I : Byte;
  10255. S : string;
  10256. begin
  10257. I := FindEra(Trunc(DateTime));
  10258. if I > 0 then
  10259. S := IntToStr(Year - EraYearOffsets[I])
  10260. else
  10261. S := IntToStr(Year);
  10262. while Length(S) < Count do
  10263. S := '0' + S;
  10264. if Length(S) > Count then
  10265. S := Copy(S, Length(S) - (Count - 1), Count);
  10266. Result := S;
  10267. end;
  10268. {$ENDIF}
  10269. begin
  10270. if (Format <> nil) and (AppendLevel < 2) then
  10271. begin
  10272. Inc(AppendLevel);
  10273. LastToken := ' ';
  10274. DateDecoded := False;
  10275. TimeDecoded := False;
  10276. Use12HourClock := False;
  10277. while Format^ <> #0 do
  10278. begin
  10279. Starter := Format^;
  10280. if Starter in LeadBytes then
  10281. begin
  10282. AppendChars(Format, StrCharLength(Format));
  10283. Format := StrNextChar(Format);
  10284. LastToken := ' ';
  10285. Continue;
  10286. end;
  10287. Format := StrNextChar(Format);
  10288. Token := Starter;
  10289. if Token in ['a'..'z'] then Dec(Token, 32);
  10290. if Token in ['A'..'Z'] then
  10291. begin
  10292. if (Token = 'M') and (LastToken = 'H') then Token := 'N';
  10293. LastToken := Token;
  10294. end;
  10295. case Token of
  10296. 'Y':
  10297. begin
  10298. GetCount;
  10299. GetDate;
  10300. if Count <= 2 then
  10301. AppendNumber(Year mod 100, 2) else
  10302. AppendNumber(Year, 4);
  10303. end;
  10304. 'G':
  10305. begin
  10306. GetCount;
  10307. GetDate;
  10308. AppendString(ConvertEraString(Count));
  10309. end;
  10310. 'E':
  10311. begin
  10312. GetCount;
  10313. GetDate;
  10314. AppendString(ConvertYearString(Count));
  10315. end;
  10316. 'M':
  10317. begin
  10318. GetCount;
  10319. GetDate;
  10320. case Count of
  10321. 1, 2: AppendNumber(Month, Count);
  10322. 3: AppendString(ShortMonthNames[Month]);
  10323. else
  10324. AppendString(LongMonthNames[Month]);
  10325. end;
  10326. end;
  10327. 'D':
  10328. begin
  10329. GetCount;
  10330. case Count of
  10331. 1, 2:
  10332. begin
  10333. GetDate;
  10334. AppendNumber(Day, Count);
  10335. end;
  10336. 3: AppendString(ShortDayNames[DayOfWeek(DateTime)]);
  10337. 4: AppendString(LongDayNames[DayOfWeek(DateTime)]);
  10338. 5: AppendFormat(Pointer(ShortDateFormat));
  10339. else
  10340. AppendFormat(Pointer(LongDateFormat));
  10341. end;
  10342. end;
  10343. 'H':
  10344. begin
  10345. GetCount;
  10346. GetTime;
  10347. BetweenQuotes := False;
  10348. P := Format;
  10349. while P^ <> #0 do
  10350. begin
  10351. if P^ in LeadBytes then
  10352. begin
  10353. P := StrNextChar(P);
  10354. Continue;
  10355. end;
  10356. case P^ of
  10357. 'A', 'a':
  10358. if not BetweenQuotes then
  10359. begin
  10360. if ( (StrLIComp(P, 'AM/PM', 5) = 0)
  10361. or (StrLIComp(P, 'A/P', 3) = 0)
  10362. or (StrLIComp(P, 'AMPM', 4) = 0) ) then
  10363. Use12HourClock := True;
  10364. Break;
  10365. end;
  10366. 'H', 'h':
  10367. Break;
  10368. '''', '"': BetweenQuotes := not BetweenQuotes;
  10369. end;
  10370. Inc(P);
  10371. end;
  10372. H := Hour;
  10373. if Use12HourClock then
  10374. if H = 0 then H := 12 else if H > 12 then Dec(H, 12);
  10375. if Count > 2 then Count := 2;
  10376. AppendNumber(H, Count);
  10377. end;
  10378. 'N':
  10379. begin
  10380. GetCount;
  10381. GetTime;
  10382. if Count > 2 then Count := 2;
  10383. AppendNumber(Min, Count);
  10384. end;
  10385. 'S':
  10386. begin
  10387. GetCount;
  10388. GetTime;
  10389. if Count > 2 then Count := 2;
  10390. AppendNumber(Sec, Count);
  10391. end;
  10392. 'T':
  10393. begin
  10394. GetCount;
  10395. if Count = 1 then
  10396. AppendFormat(Pointer(ShortTimeFormat)) else
  10397. AppendFormat(Pointer(LongTimeFormat));
  10398. end;
  10399. 'Z':
  10400. begin
  10401. GetCount;
  10402. GetTime;
  10403. if Count > 3 then Count := 3;
  10404. AppendNumber(MSec, Count);
  10405. end;
  10406. 'A':
  10407. begin
  10408. GetTime;
  10409. P := Format - 1;
  10410. if StrLIComp(P, 'AM/PM', 5) = 0 then
  10411. begin
  10412. if Hour >= 12 then Inc(P, 3);
  10413. AppendChars(P, 2);
  10414. Inc(Format, 4);
  10415. Use12HourClock := TRUE;
  10416. end else
  10417. if StrLIComp(P, 'A/P', 3) = 0 then
  10418. begin
  10419. if Hour >= 12 then Inc(P, 2);
  10420. AppendChars(P, 1);
  10421. Inc(Format, 2);
  10422. Use12HourClock := TRUE;
  10423. end else
  10424. if StrLIComp(P, 'AMPM', 4) = 0 then
  10425. begin
  10426. if Hour < 12 then
  10427. AppendString(TimeAMString) else
  10428. AppendString(TimePMString);
  10429. Inc(Format, 3);
  10430. Use12HourClock := TRUE;
  10431. end else
  10432. if StrLIComp(P, 'AAAA', 4) = 0 then
  10433. begin
  10434. GetDate;
  10435. AppendString(LongDayNames[DayOfWeek(DateTime)]);
  10436. Inc(Format, 3);
  10437. end else
  10438. if StrLIComp(P, 'AAA', 3) = 0 then
  10439. begin
  10440. GetDate;
  10441. AppendString(ShortDayNames[DayOfWeek(DateTime)]);
  10442. Inc(Format, 2);
  10443. end else
  10444. AppendChars(@Starter, 1);
  10445. end;
  10446. 'C':
  10447. begin
  10448. GetCount;
  10449. AppendFormat(Pointer(ShortDateFormat));
  10450. GetTime;
  10451. if (Hour <> 0) or (Min <> 0) or (Sec <> 0) then
  10452. begin
  10453. AppendChars(' ', 1);
  10454. AppendFormat(Pointer(LongTimeFormat));
  10455. end;
  10456. end;
  10457. '/':
  10458. if DateSeparator <> #0 then
  10459. AppendChars(@DateSeparator, 1);
  10460. ':':
  10461. if TimeSeparator <> #0 then
  10462. AppendChars(@TimeSeparator, 1);
  10463. '''', '"':
  10464. begin
  10465. P := Format;
  10466. while (Format^ <> #0) and (Format^ <> Starter) do
  10467. begin
  10468. if Format^ in LeadBytes then
  10469. Format := StrNextChar(Format)
  10470. else
  10471. Inc(Format);
  10472. end;
  10473. AppendChars(P, Format - P);
  10474. if Format^ <> #0 then Inc(Format);
  10475. end;
  10476. else
  10477. AppendChars(@Starter, 1);
  10478. end;
  10479. end;
  10480. Dec(AppendLevel);
  10481. end;
  10482. end;
  10483. begin
  10484. BufPos := 0;
  10485. AppendLevel := 0;
  10486. if Format <> '' then AppendFormat(Pointer(Format)) else AppendFormat('C');
  10487. SetString(Result, Buffer, BufPos);
  10488. end;
  10489. procedure DateTimeToString(var Result: string; const Format: string;
  10490. DateTime: TDateTime; const FormatSettings: TFormatSettings);
  10491. var
  10492. BufPos, AppendLevel: Integer;
  10493. Buffer: array[0..255] of Char;
  10494. procedure AppendChars(P: PChar; Count: Integer);
  10495. var
  10496. N: Integer;
  10497. begin
  10498. N := SizeOf(Buffer) - BufPos;
  10499. if N > Count then N := Count;
  10500. if N <> 0 then Move(P[0], Buffer[BufPos], N);
  10501. Inc(BufPos, N);
  10502. end;
  10503. procedure AppendString(const S: string);
  10504. begin
  10505. AppendChars(Pointer(S), Length(S));
  10506. end;
  10507. procedure AppendNumber(Number, Digits: Integer);
  10508. const
  10509. Format: array[0..3] of Char = '%.*d';
  10510. var
  10511. NumBuf: array[0..15] of Char;
  10512. begin
  10513. AppendChars(NumBuf, FormatBuf(NumBuf, SizeOf(NumBuf), Format,
  10514. SizeOf(Format), [Digits, Number]));
  10515. end;
  10516. procedure AppendFormat(Format: PChar);
  10517. var
  10518. Starter, Token, LastToken: Char;
  10519. DateDecoded, TimeDecoded, Use12HourClock,
  10520. BetweenQuotes: Boolean;
  10521. P: PChar;
  10522. Count: Integer;
  10523. Year, Month, Day, Hour, Min, Sec, MSec, H: Word;
  10524. procedure GetCount;
  10525. var
  10526. P: PChar;
  10527. begin
  10528. P := Format;
  10529. while Format^ = Starter do Inc(Format);
  10530. Count := Format - P + 1;
  10531. end;
  10532. procedure GetDate;
  10533. begin
  10534. if not DateDecoded then
  10535. begin
  10536. DecodeDate(DateTime, Year, Month, Day);
  10537. DateDecoded := True;
  10538. end;
  10539. end;
  10540. procedure GetTime;
  10541. begin
  10542. if not TimeDecoded then
  10543. begin
  10544. DecodeTime(DateTime, Hour, Min, Sec, MSec);
  10545. TimeDecoded := True;
  10546. end;
  10547. end;
  10548. {$IFDEF MSWINDOWS}
  10549. function ConvertEraString(const Count: Integer) : string;
  10550. var
  10551. FormatStr: string;
  10552. SystemTime: TSystemTime;
  10553. Buffer: array[Byte] of Char;
  10554. P: PChar;
  10555. begin
  10556. Result := '';
  10557. with SystemTime do
  10558. begin
  10559. wYear := Year;
  10560. wMonth := Month;
  10561. wDay := Day;
  10562. end;
  10563. FormatStr := 'gg';
  10564. if GetDateFormat(GetThreadLocale, DATE_USE_ALT_CALENDAR, @SystemTime,
  10565. PChar(FormatStr), Buffer, SizeOf(Buffer)) <> 0 then
  10566. begin
  10567. Result := Buffer;
  10568. if Count = 1 then
  10569. begin
  10570. case SysLocale.PriLangID of
  10571. LANG_JAPANESE:
  10572. Result := Copy(Result, 1, CharToBytelen(Result, 1));
  10573. LANG_CHINESE:
  10574. if (SysLocale.SubLangID = SUBLANG_CHINESE_TRADITIONAL)
  10575. and (ByteToCharLen(Result, Length(Result)) = 4) then
  10576. begin
  10577. P := Buffer + CharToByteIndex(Result, 3) - 1;
  10578. SetString(Result, P, CharToByteLen(P, 2));
  10579. end;
  10580. end;
  10581. end;
  10582. end;
  10583. end;
  10584. function ConvertYearString(const Count: Integer): string;
  10585. var
  10586. FormatStr: string;
  10587. SystemTime: TSystemTime;
  10588. Buffer: array[Byte] of Char;
  10589. begin
  10590. Result := '';
  10591. with SystemTime do
  10592. begin
  10593. wYear := Year;
  10594. wMonth := Month;
  10595. wDay := Day;
  10596. end;
  10597. if Count <= 2 then
  10598. FormatStr := 'yy' // avoid Win95 bug.
  10599. else
  10600. FormatStr := 'yyyy';
  10601. if GetDateFormat(GetThreadLocale, DATE_USE_ALT_CALENDAR, @SystemTime,
  10602. PChar(FormatStr), Buffer, SizeOf(Buffer)) <> 0 then
  10603. begin
  10604. Result := Buffer;
  10605. if (Count = 1) and (Result[1] = '0') then
  10606. Result := Copy(Result, 2, Length(Result)-1);
  10607. end;
  10608. end;
  10609. {$ENDIF}
  10610. {$IFDEF LINUX}
  10611. function FindEra(Date: Integer): Byte;
  10612. var
  10613. I : Byte;
  10614. begin
  10615. Result := 0;
  10616. for I := 1 to EraCount do
  10617. begin
  10618. if (EraRanges[I].StartDate <= Date) and
  10619. (EraRanges[I].EndDate >= Date) then
  10620. begin
  10621. Result := I;
  10622. Exit;
  10623. end;
  10624. end;
  10625. end;
  10626. function ConvertEraString(const Count: Integer) : String;
  10627. var
  10628. I : Byte;
  10629. begin
  10630. Result := '';
  10631. I := FindEra(Trunc(DateTime));
  10632. if I > 0 then
  10633. Result := EraNames[I];
  10634. end;
  10635. function ConvertYearString(const Count: Integer) : String;
  10636. var
  10637. I : Byte;
  10638. S : string;
  10639. begin
  10640. I := FindEra(Trunc(DateTime));
  10641. if I > 0 then
  10642. S := IntToStr(Year - EraYearOffsets[I])
  10643. else
  10644. S := IntToStr(Year);
  10645. while Length(S) < Count do
  10646. S := '0' + S;
  10647. if Length(S) > Count then
  10648. S := Copy(S, Length(S) - (Count - 1), Count);
  10649. Result := S;
  10650. end;
  10651. {$ENDIF}
  10652. begin
  10653. if (Format <> nil) and (AppendLevel < 2) then
  10654. begin
  10655. Inc(AppendLevel);
  10656. LastToken := ' ';
  10657. DateDecoded := False;
  10658. TimeDecoded := False;
  10659. Use12HourClock := False;
  10660. while Format^ <> #0 do
  10661. begin
  10662. Starter := Format^;
  10663. if Starter in LeadBytes then
  10664. begin
  10665. AppendChars(Format, StrCharLength(Format));
  10666. Format := StrNextChar(Format);
  10667. LastToken := ' ';
  10668. Continue;
  10669. end;
  10670. Format := StrNextChar(Format);
  10671. Token := Starter;
  10672. if Token in ['a'..'z'] then Dec(Token, 32);
  10673. if Token in ['A'..'Z'] then
  10674. begin
  10675. if (Token = 'M') and (LastToken = 'H') then Token := 'N';
  10676. LastToken := Token;
  10677. end;
  10678. case Token of
  10679. 'Y':
  10680. begin
  10681. GetCount;
  10682. GetDate;
  10683. if Count <= 2 then
  10684. AppendNumber(Year mod 100, 2) else
  10685. AppendNumber(Year, 4);
  10686. end;
  10687. 'G':
  10688. begin
  10689. GetCount;
  10690. GetDate;
  10691. AppendString(ConvertEraString(Count));
  10692. end;
  10693. 'E':
  10694. begin
  10695. GetCount;
  10696. GetDate;
  10697. AppendString(ConvertYearString(Count));
  10698. end;
  10699. 'M':
  10700. begin
  10701. GetCount;
  10702. GetDate;
  10703. case Count of
  10704. 1, 2: AppendNumber(Month, Count);
  10705. 3: AppendString(FormatSettings.ShortMonthNames[Month]);
  10706. else
  10707. AppendString(FormatSettings.LongMonthNames[Month]);
  10708. end;
  10709. end;
  10710. 'D':
  10711. begin
  10712. GetCount;
  10713. case Count of
  10714. 1, 2:
  10715. begin
  10716. GetDate;
  10717. AppendNumber(Day, Count);
  10718. end;
  10719. 3: AppendString(FormatSettings.ShortDayNames[DayOfWeek(DateTime)]);
  10720. 4: AppendString(FormatSettings.LongDayNames[DayOfWeek(DateTime)]);
  10721. 5: AppendFormat(Pointer(FormatSettings.ShortDateFormat));
  10722. else
  10723. AppendFormat(Pointer(FormatSettings.LongDateFormat));
  10724. end;
  10725. end;
  10726. 'H':
  10727. begin
  10728. GetCount;
  10729. GetTime;
  10730. BetweenQuotes := False;
  10731. P := Format;
  10732. while P^ <> #0 do
  10733. begin
  10734. if P^ in LeadBytes then
  10735. begin
  10736. P := StrNextChar(P);
  10737. Continue;
  10738. end;
  10739. case P^ of
  10740. 'A', 'a':
  10741. if not BetweenQuotes then
  10742. begin
  10743. if ( (StrLIComp(P, 'AM/PM', 5) = 0)
  10744. or (StrLIComp(P, 'A/P', 3) = 0)
  10745. or (StrLIComp(P, 'AMPM', 4) = 0) ) then
  10746. Use12HourClock := True;
  10747. Break;
  10748. end;
  10749. 'H', 'h':
  10750. Break;
  10751. '''', '"': BetweenQuotes := not BetweenQuotes;
  10752. end;
  10753. Inc(P);
  10754. end;
  10755. H := Hour;
  10756. if Use12HourClock then
  10757. if H = 0 then H := 12 else if H > 12 then Dec(H, 12);
  10758. if Count > 2 then Count := 2;
  10759. AppendNumber(H, Count);
  10760. end;
  10761. 'N':
  10762. begin
  10763. GetCount;
  10764. GetTime;
  10765. if Count > 2 then Count := 2;
  10766. AppendNumber(Min, Count);
  10767. end;
  10768. 'S':
  10769. begin
  10770. GetCount;
  10771. GetTime;
  10772. if Count > 2 then Count := 2;
  10773. AppendNumber(Sec, Count);
  10774. end;
  10775. 'T':
  10776. begin
  10777. GetCount;
  10778. if Count = 1 then
  10779. AppendFormat(Pointer(FormatSettings.ShortTimeFormat)) else
  10780. AppendFormat(Pointer(FormatSettings.LongTimeFormat));
  10781. end;
  10782. 'Z':
  10783. begin
  10784. GetCount;
  10785. GetTime;
  10786. if Count > 3 then Count := 3;
  10787. AppendNumber(MSec, Count);
  10788. end;
  10789. 'A':
  10790. begin
  10791. GetTime;
  10792. P := Format - 1;
  10793. if StrLIComp(P, 'AM/PM', 5) = 0 then
  10794. begin
  10795. if Hour >= 12 then Inc(P, 3);
  10796. AppendChars(P, 2);
  10797. Inc(Format, 4);
  10798. Use12HourClock := TRUE;
  10799. end else
  10800. if StrLIComp(P, 'A/P', 3) = 0 then
  10801. begin
  10802. if Hour >= 12 then Inc(P, 2);
  10803. AppendChars(P, 1);
  10804. Inc(Format, 2);
  10805. Use12HourClock := TRUE;
  10806. end else
  10807. if StrLIComp(P, 'AMPM', 4) = 0 then
  10808. begin
  10809. if Hour < 12 then
  10810. AppendString(FormatSettings.TimeAMString) else
  10811. AppendString(FormatSettings.TimePMString);
  10812. Inc(Format, 3);
  10813. Use12HourClock := TRUE;
  10814. end else
  10815. if StrLIComp(P, 'AAAA', 4) = 0 then
  10816. begin
  10817. GetDate;
  10818. AppendString(FormatSettings.LongDayNames[DayOfWeek(DateTime)]);
  10819. Inc(Format, 3);
  10820. end else
  10821. if StrLIComp(P, 'AAA', 3) = 0 then
  10822. begin
  10823. GetDate;
  10824. AppendString(FormatSettings.ShortDayNames[DayOfWeek(DateTime)]);
  10825. Inc(Format, 2);
  10826. end else
  10827. AppendChars(@Starter, 1);
  10828. end;
  10829. 'C':
  10830. begin
  10831. GetCount;
  10832. AppendFormat(Pointer(FormatSettings.ShortDateFormat));
  10833. GetTime;
  10834. if (Hour <> 0) or (Min <> 0) or (Sec <> 0) then
  10835. begin
  10836. AppendChars(' ', 1);
  10837. AppendFormat(Pointer(FormatSettings.LongTimeFormat));
  10838. end;
  10839. end;
  10840. '/':
  10841. if DateSeparator <> #0 then
  10842. AppendChars(@FormatSettings.DateSeparator, 1);
  10843. ':':
  10844. if TimeSeparator <> #0 then
  10845. AppendChars(@FormatSettings.TimeSeparator, 1);
  10846. '''', '"':
  10847. begin
  10848. P := Format;
  10849. while (Format^ <> #0) and (Format^ <> Starter) do
  10850. begin
  10851. if Format^ in LeadBytes then
  10852. Format := StrNextChar(Format)
  10853. else
  10854. Inc(Format);
  10855. end;
  10856. AppendChars(P, Format - P);
  10857. if Format^ <> #0 then Inc(Format);
  10858. end;
  10859. else
  10860. AppendChars(@Starter, 1);
  10861. end;
  10862. end;
  10863. Dec(AppendLevel);
  10864. end;
  10865. end;
  10866. begin
  10867. BufPos := 0;
  10868. AppendLevel := 0;
  10869. if Format <> '' then AppendFormat(Pointer(Format)) else AppendFormat('C');
  10870. SetString(Result, Buffer, BufPos);
  10871. end;
  10872. function TryFloatToDateTime(const Value: Extended; out AResult: TDateTime): Boolean;
  10873. begin
  10874. Result := not ((Value < MinDateTime) or (Value >= Int(MaxDateTime) + 1.0));
  10875. if Result then
  10876. AResult := Value;
  10877. end;
  10878. function FloatToDateTime(const Value: Extended): TDateTime;
  10879. begin
  10880. if not TryFloatToDateTime(Value, Result) then
  10881. ConvertErrorFmt(SInvalidDateTimeFloat, [Value]);
  10882. end;
  10883. function DateToStr(const DateTime: TDateTime): string;
  10884. begin
  10885. DateTimeToString(Result, ShortDateFormat, DateTime);
  10886. end;
  10887. function DateToStr(const DateTime: TDateTime;
  10888. const FormatSettings: TFormatSettings): string;
  10889. begin
  10890. DateTimeToString(Result, FormatSettings.ShortDateFormat, DateTime,
  10891. FormatSettings);
  10892. end;
  10893. function TimeToStr(const DateTime: TDateTime): string;
  10894. begin
  10895. DateTimeToString(Result, LongTimeFormat, DateTime);
  10896. end;
  10897. function TimeToStr(const DateTime: TDateTime;
  10898. const FormatSettings: TFormatSettings): string;
  10899. begin
  10900. DateTimeToString(Result, FormatSettings.LongTimeFormat, DateTime,
  10901. FormatSettings);
  10902. end;
  10903. function DateTimeToStr(const DateTime: TDateTime): string;
  10904. begin
  10905. DateTimeToString(Result, '', DateTime);
  10906. end;
  10907. function DateTimeToStr(const DateTime: TDateTime;
  10908. const FormatSettings: TFormatSettings): string;
  10909. begin
  10910. DateTimeToString(Result, '', DateTime, FormatSettings);
  10911. end;
  10912. function FormatDateTime(const Format: string; DateTime: TDateTime): string;
  10913. begin
  10914. DateTimeToString(Result, Format, DateTime);
  10915. end;
  10916. function FormatDateTime(const Format: string; DateTime: TDateTime;
  10917. const FormatSettings: TFormatSettings): string;
  10918. begin
  10919. DateTimeToString(Result, Format, DateTime, FormatSettings);
  10920. end;
  10921. { String to date/time conversions }
  10922. type
  10923. TDateOrder = (doMDY, doDMY, doYMD);
  10924. procedure ScanBlanks(const S: string; var Pos: Integer);
  10925. var
  10926. I: Integer;
  10927. begin
  10928. I := Pos;
  10929. while (I <= Length(S)) and (S[I] = ' ') do Inc(I);
  10930. Pos := I;
  10931. end;
  10932. function ScanNumber(const S: string; var Pos: Integer;
  10933. var Number: Word; var CharCount: Byte): Boolean;
  10934. var
  10935. I: Integer;
  10936. N: Word;
  10937. begin
  10938. Result := False;
  10939. CharCount := 0;
  10940. ScanBlanks(S, Pos);
  10941. I := Pos;
  10942. N := 0;
  10943. while (I <= Length(S)) and (S[I] in ['0'..'9']) and (N < 1000) do
  10944. begin
  10945. N := N * 10 + (Ord(S[I]) - Ord('0'));
  10946. Inc(I);
  10947. end;
  10948. if I > Pos then
  10949. begin
  10950. CharCount := I - Pos;
  10951. Pos := I;
  10952. Number := N;
  10953. Result := True;
  10954. end;
  10955. end;
  10956. function ScanString(const S: string; var Pos: Integer;
  10957. const Symbol: string): Boolean;
  10958. begin
  10959. Result := False;
  10960. if Symbol <> '' then
  10961. begin
  10962. ScanBlanks(S, Pos);
  10963. if AnsiCompareText(Symbol, Copy(S, Pos, Length(Symbol))) = 0 then
  10964. begin
  10965. Inc(Pos, Length(Symbol));
  10966. Result := True;
  10967. end;
  10968. end;
  10969. end;
  10970. function ScanChar(const S: string; var Pos: Integer; Ch: Char): Boolean;
  10971. begin
  10972. Result := False;
  10973. ScanBlanks(S, Pos);
  10974. if (Pos <= Length(S)) and (S[Pos] = Ch) then
  10975. begin
  10976. Inc(Pos);
  10977. Result := True;
  10978. end;
  10979. end;
  10980. function GetDateOrder(const DateFormat: string): TDateOrder;
  10981. var
  10982. I: Integer;
  10983. begin
  10984. Result := doMDY;
  10985. I := 1;
  10986. while I <= Length(DateFormat) do
  10987. begin
  10988. case Chr(Ord(DateFormat[I]) and $DF) of
  10989. 'E': Result := doYMD;
  10990. 'Y': Result := doYMD;
  10991. 'M': Result := doMDY;
  10992. 'D': Result := doDMY;
  10993. else
  10994. Inc(I);
  10995. Continue;
  10996. end;
  10997. Exit;
  10998. end;
  10999. Result := doMDY;
  11000. end;
  11001. procedure ScanToNumber(const S: string; var Pos: Integer);
  11002. begin
  11003. while (Pos <= Length(S)) and not (S[Pos] in ['0'..'9']) do
  11004. begin
  11005. if S[Pos] in LeadBytes then
  11006. Pos := NextCharIndex(S, Pos)
  11007. else
  11008. Inc(Pos);
  11009. end;
  11010. end;
  11011. function GetEraYearOffset(const Name: string): Integer;
  11012. var
  11013. I: Integer;
  11014. begin
  11015. Result := 0;
  11016. for I := Low(EraNames) to High(EraNames) do
  11017. begin
  11018. if EraNames[I] = '' then Break;
  11019. if AnsiStrPos(PChar(EraNames[I]), PChar(Name)) <> nil then
  11020. begin
  11021. Result := EraYearOffsets[I];
  11022. Exit;
  11023. end;
  11024. end;
  11025. end;
  11026. function ScanDate(const S: string; var Pos: Integer;
  11027. var Date: TDateTime): Boolean; overload;
  11028. var
  11029. DateOrder: TDateOrder;
  11030. N1, N2, N3, Y, M, D: Word;
  11031. L1, L2, L3, YearLen: Byte;
  11032. CenturyBase: Integer;
  11033. EraName : string;
  11034. EraYearOffset: Integer;
  11035. function EraToYear(Year: Integer): Integer;
  11036. begin
  11037. {$IFDEF MSWINDOWS}
  11038. if SysLocale.PriLangID = LANG_KOREAN then
  11039. begin
  11040. if Year <= 99 then
  11041. Inc(Year, (CurrentYear + Abs(EraYearOffset)) div 100 * 100);
  11042. if EraYearOffset > 0 then
  11043. EraYearOffset := -EraYearOffset;
  11044. end
  11045. else
  11046. Dec(EraYearOffset);
  11047. {$ENDIF}
  11048. Result := Year + EraYearOffset;
  11049. end;
  11050. begin
  11051. Y := 0;
  11052. M := 0;
  11053. D := 0;
  11054. YearLen := 0;
  11055. Result := False;
  11056. DateOrder := GetDateOrder(ShortDateFormat);
  11057. EraYearOffset := 0;
  11058. if ShortDateFormat[1] = 'g' then // skip over prefix text
  11059. begin
  11060. ScanToNumber(S, Pos);
  11061. EraName := Trim(Copy(S, 1, Pos-1));
  11062. EraYearOffset := GetEraYearOffset(EraName);
  11063. end
  11064. else
  11065. if AnsiPos('e', ShortDateFormat) > 0 then
  11066. EraYearOffset := EraYearOffsets[1];
  11067. if not (ScanNumber(S, Pos, N1, L1) and ScanChar(S, Pos, DateSeparator) and
  11068. ScanNumber(S, Pos, N2, L2)) then Exit;
  11069. if ScanChar(S, Pos, DateSeparator) then
  11070. begin
  11071. if not ScanNumber(S, Pos, N3, L3) then Exit;
  11072. case DateOrder of
  11073. doMDY: begin Y := N3; YearLen := L3; M := N1; D := N2; end;
  11074. doDMY: begin Y := N3; YearLen := L3; M := N2; D := N1; end;
  11075. doYMD: begin Y := N1; YearLen := L1; M := N2; D := N3; end;
  11076. end;
  11077. if EraYearOffset > 0 then
  11078. Y := EraToYear(Y)
  11079. else
  11080. if (YearLen <= 2) then
  11081. begin
  11082. CenturyBase := CurrentYear - TwoDigitYearCenturyWindow;
  11083. Inc(Y, CenturyBase div 100 * 100);
  11084. if (TwoDigitYearCenturyWindow > 0) and (Y < CenturyBase) then
  11085. Inc(Y, 100);
  11086. end;
  11087. end else
  11088. begin
  11089. Y := CurrentYear;
  11090. if DateOrder = doDMY then
  11091. begin
  11092. D := N1; M := N2;
  11093. end else
  11094. begin
  11095. M := N1; D := N2;
  11096. end;
  11097. end;
  11098. ScanChar(S, Pos, DateSeparator);
  11099. ScanBlanks(S, Pos);
  11100. if SysLocale.FarEast and (System.Pos('ddd', ShortDateFormat) <> 0) then
  11101. begin // ignore trailing text
  11102. if ShortTimeFormat[1] in ['0'..'9'] then // stop at time digit
  11103. ScanToNumber(S, Pos)
  11104. else // stop at time prefix
  11105. repeat
  11106. while (Pos <= Length(S)) and (S[Pos] <> ' ') do Inc(Pos);
  11107. ScanBlanks(S, Pos);
  11108. until (Pos > Length(S)) or
  11109. (AnsiCompareText(TimeAMString, Copy(S, Pos, Length(TimeAMString))) = 0) or
  11110. (AnsiCompareText(TimePMString, Copy(S, Pos, Length(TimePMString))) = 0);
  11111. end;
  11112. Result := TryEncodeDate(Y, M, D, Date);
  11113. end;
  11114. function ScanDate(const S: string; var Pos: Integer; var Date: TDateTime;
  11115. const FormatSettings: TFormatSettings): Boolean; overload;
  11116. var
  11117. DateOrder: TDateOrder;
  11118. N1, N2, N3, Y, M, D: Word;
  11119. L1, L2, L3, YearLen: Byte;
  11120. CenturyBase: Integer;
  11121. EraName : string;
  11122. EraYearOffset: Integer;
  11123. function EraToYear(Year: Integer): Integer;
  11124. begin
  11125. {$IFDEF MSWINDOWS}
  11126. if SysLocale.PriLangID = LANG_KOREAN then
  11127. begin
  11128. if Year <= 99 then
  11129. Inc(Year, (CurrentYear + Abs(EraYearOffset)) div 100 * 100);
  11130. if EraYearOffset > 0 then
  11131. EraYearOffset := -EraYearOffset;
  11132. end
  11133. else
  11134. Dec(EraYearOffset);
  11135. {$ENDIF}
  11136. Result := Year + EraYearOffset;
  11137. end;
  11138. begin
  11139. Y := 0;
  11140. M := 0;
  11141. D := 0;
  11142. YearLen := 0;
  11143. Result := False;
  11144. DateOrder := GetDateOrder(FormatSettings.ShortDateFormat);
  11145. EraYearOffset := 0;
  11146. if FormatSettings.ShortDateFormat[1] = 'g' then // skip over prefix text
  11147. begin
  11148. ScanToNumber(S, Pos);
  11149. EraName := Trim(Copy(S, 1, Pos-1));
  11150. EraYearOffset := GetEraYearOffset(EraName);
  11151. end
  11152. else
  11153. if AnsiPos('e', FormatSettings.ShortDateFormat) > 0 then
  11154. EraYearOffset := EraYearOffsets[1];
  11155. if not (ScanNumber(S, Pos, N1, L1) and ScanChar(S, Pos, FormatSettings.DateSeparator) and
  11156. ScanNumber(S, Pos, N2, L2)) then Exit;
  11157. if ScanChar(S, Pos, FormatSettings.DateSeparator) then
  11158. begin
  11159. if not ScanNumber(S, Pos, N3, L3) then Exit;
  11160. case DateOrder of
  11161. doMDY: begin Y := N3; YearLen := L3; M := N1; D := N2; end;
  11162. doDMY: begin Y := N3; YearLen := L3; M := N2; D := N1; end;
  11163. doYMD: begin Y := N1; YearLen := L1; M := N2; D := N3; end;
  11164. end;
  11165. if EraYearOffset > 0 then
  11166. Y := EraToYear(Y)
  11167. else
  11168. if (YearLen <= 2) then
  11169. begin
  11170. CenturyBase := CurrentYear - FormatSettings.TwoDigitYearCenturyWindow;
  11171. Inc(Y, CenturyBase div 100 * 100);
  11172. if (FormatSettings.TwoDigitYearCenturyWindow > 0) and (Y < CenturyBase) then
  11173. Inc(Y, 100);
  11174. end;
  11175. end else
  11176. begin
  11177. Y := CurrentYear;
  11178. if DateOrder = doDMY then
  11179. begin
  11180. D := N1; M := N2;
  11181. end else
  11182. begin
  11183. M := N1; D := N2;
  11184. end;
  11185. end;
  11186. ScanChar(S, Pos, FormatSettings.DateSeparator);
  11187. ScanBlanks(S, Pos);
  11188. if SysLocale.FarEast and (System.Pos('ddd', FormatSettings.ShortDateFormat) <> 0) then
  11189. begin // ignore trailing text
  11190. if FormatSettings.ShortTimeFormat[1] in ['0'..'9'] then // stop at time digit
  11191. ScanToNumber(S, Pos)
  11192. else // stop at time prefix
  11193. repeat
  11194. while (Pos <= Length(S)) and (S[Pos] <> ' ') do Inc(Pos);
  11195. ScanBlanks(S, Pos);
  11196. until (Pos > Length(S)) or
  11197. (AnsiCompareText(FormatSettings.TimeAMString,
  11198. Copy(S, Pos, Length(FormatSettings.TimeAMString))) = 0) or
  11199. (AnsiCompareText(FormatSettings.TimePMString,
  11200. Copy(S, Pos, Length(FormatSettings.TimePMString))) = 0);
  11201. end;
  11202. Result := TryEncodeDate(Y, M, D, Date);
  11203. end;
  11204. function ScanTime(const S: string; var Pos: Integer;
  11205. var Time: TDateTime): Boolean; overload;
  11206. var
  11207. BaseHour: Integer;
  11208. Hour, Min, Sec, MSec: Word;
  11209. Junk: Byte;
  11210. begin
  11211. Result := False;
  11212. BaseHour := -1;
  11213. if ScanString(S, Pos, TimeAMString) or ScanString(S, Pos, 'AM') then
  11214. BaseHour := 0
  11215. else if ScanString(S, Pos, TimePMString) or ScanString(S, Pos, 'PM') then
  11216. BaseHour := 12;
  11217. if BaseHour >= 0 then ScanBlanks(S, Pos);
  11218. if not ScanNumber(S, Pos, Hour, Junk) then Exit;
  11219. Min := 0;
  11220. Sec := 0;
  11221. MSec := 0;
  11222. if ScanChar(S, Pos, TimeSeparator) then
  11223. begin
  11224. if not ScanNumber(S, Pos, Min, Junk) then Exit;
  11225. if ScanChar(S, Pos, TimeSeparator) then
  11226. begin
  11227. if not ScanNumber(S, Pos, Sec, Junk) then Exit;
  11228. if ScanChar(S, Pos, DecimalSeparator) then
  11229. if not ScanNumber(S, Pos, MSec, Junk) then Exit;
  11230. end;
  11231. end;
  11232. if BaseHour < 0 then
  11233. if ScanString(S, Pos, TimeAMString) or ScanString(S, Pos, 'AM') then
  11234. BaseHour := 0
  11235. else
  11236. if ScanString(S, Pos, TimePMString) or ScanString(S, Pos, 'PM') then
  11237. BaseHour := 12;
  11238. if BaseHour >= 0 then
  11239. begin
  11240. if (Hour = 0) or (Hour > 12) then Exit;
  11241. if Hour = 12 then Hour := 0;
  11242. Inc(Hour, BaseHour);
  11243. end;
  11244. ScanBlanks(S, Pos);
  11245. Result := TryEncodeTime(Hour, Min, Sec, MSec, Time);
  11246. end;
  11247. function ScanTime(const S: string; var Pos: Integer; var Time: TDateTime;
  11248. const FormatSettings: TFormatSettings): Boolean; overload;
  11249. var
  11250. BaseHour: Integer;
  11251. Hour, Min, Sec, MSec: Word;
  11252. Junk: Byte;
  11253. begin
  11254. Result := False;
  11255. BaseHour := -1;
  11256. if ScanString(S, Pos, FormatSettings.TimeAMString) or ScanString(S, Pos, 'AM') then
  11257. BaseHour := 0
  11258. else if ScanString(S, Pos, FormatSettings.TimePMString) or ScanString(S, Pos, 'PM') then
  11259. BaseHour := 12;
  11260. if BaseHour >= 0 then ScanBlanks(S, Pos);
  11261. if not ScanNumber(S, Pos, Hour, Junk) then Exit;
  11262. Min := 0;
  11263. Sec := 0;
  11264. MSec := 0;
  11265. if ScanChar(S, Pos, FormatSettings.TimeSeparator) then
  11266. begin
  11267. if not ScanNumber(S, Pos, Min, Junk) then Exit;
  11268. if ScanChar(S, Pos, FormatSettings.TimeSeparator) then
  11269. begin
  11270. if not ScanNumber(S, Pos, Sec, Junk) then Exit;
  11271. if ScanChar(S, Pos, FormatSettings.DecimalSeparator) then
  11272. if not ScanNumber(S, Pos, MSec, Junk) then Exit;
  11273. end;
  11274. end;
  11275. if BaseHour < 0 then
  11276. if ScanString(S, Pos, FormatSettings.TimeAMString) or ScanString(S, Pos, 'AM') then
  11277. BaseHour := 0
  11278. else
  11279. if ScanString(S, Pos, FormatSettings.TimePMString) or ScanString(S, Pos, 'PM') then
  11280. BaseHour := 12;
  11281. if BaseHour >= 0 then
  11282. begin
  11283. if (Hour = 0) or (Hour > 12) then Exit;
  11284. if Hour = 12 then Hour := 0;
  11285. Inc(Hour, BaseHour);
  11286. end;
  11287. ScanBlanks(S, Pos);
  11288. Result := TryEncodeTime(Hour, Min, Sec, MSec, Time);
  11289. end;
  11290. function StrToDate(const S: string): TDateTime;
  11291. begin
  11292. if not TryStrToDate(S, Result) then
  11293. ConvertErrorFmt(SInvalidDate, [S]);
  11294. end;
  11295. function StrToDate(const S: string;
  11296. const FormatSettings: TFormatSettings): TDateTime;
  11297. begin
  11298. if not TryStrToDate(S, Result, FormatSettings) then
  11299. ConvertErrorFmt(SInvalidDate, [S]);
  11300. end;
  11301. function StrToDateDef(const S: string; const Default: TDateTime): TDateTime;
  11302. begin
  11303. if not TryStrToDate(S, Result) then
  11304. Result := Default;
  11305. end;
  11306. function StrToDateDef(const S: string; const Default: TDateTime;
  11307. const FormatSettings: TFormatSettings): TDateTime;
  11308. begin
  11309. if not TryStrToDate(S, Result, FormatSettings) then
  11310. Result := Default;
  11311. end;
  11312. function TryStrToDate(const S: string; out Value: TDateTime): Boolean;
  11313. var
  11314. Pos: Integer;
  11315. begin
  11316. Pos := 1;
  11317. Result := ScanDate(S, Pos, Value) and (Pos > Length(S));
  11318. end;
  11319. function TryStrToDate(const S: string; out Value: TDateTime;
  11320. const FormatSettings: TFormatSettings): Boolean;
  11321. var
  11322. Pos: Integer;
  11323. begin
  11324. Pos := 1;
  11325. Result := ScanDate(S, Pos, Value, FormatSettings) and (Pos > Length(S));
  11326. end;
  11327. function StrToTime(const S: string): TDateTime;
  11328. begin
  11329. if not TryStrToTime(S, Result) then
  11330. ConvertErrorFmt(SInvalidTime, [S]);
  11331. end;
  11332. function StrToTime(const S: string;
  11333. const FormatSettings: TFormatSettings): TDateTime;
  11334. begin
  11335. if not TryStrToTime(S, Result, FormatSettings) then
  11336. ConvertErrorFmt(SInvalidTime, [S]);
  11337. end;
  11338. function StrToTimeDef(const S: string; const Default: TDateTime): TDateTime;
  11339. begin
  11340. if not TryStrToTime(S, Result) then
  11341. Result := Default;
  11342. end;
  11343. function StrToTimeDef(const S: string; const Default: TDateTime;
  11344. const FormatSettings: TFormatSettings): TDateTime;
  11345. begin
  11346. if not TryStrToTime(S, Result, FormatSettings) then
  11347. Result := Default;
  11348. end;
  11349. function TryStrToTime(const S: string; out Value: TDateTime): Boolean;
  11350. var
  11351. Pos: Integer;
  11352. begin
  11353. Pos := 1;
  11354. Result := ScanTime(S, Pos, Value) and (Pos > Length(S));
  11355. end;
  11356. function TryStrToTime(const S: string; out Value: TDateTime;
  11357. const FormatSettings: TFormatSettings): Boolean;
  11358. var
  11359. Pos: Integer;
  11360. begin
  11361. Pos := 1;
  11362. Result := ScanTime(S, Pos, Value, FormatSettings) and (Pos > Length(S));
  11363. end;
  11364. function StrToDateTime(const S: string): TDateTime;
  11365. begin
  11366. if not TryStrToDateTime(S, Result) then
  11367. ConvertErrorFmt(SInvalidDateTime, [S]);
  11368. end;
  11369. function StrToDateTime(const S: string;
  11370. const FormatSettings: TFormatSettings): TDateTime;
  11371. begin
  11372. if not TryStrToDateTime(S, Result, FormatSettings) then
  11373. ConvertErrorFmt(SInvalidDateTime, [S]);
  11374. end;
  11375. function StrToDateTimeDef(const S: string; const Default: TDateTime): TDateTime;
  11376. begin
  11377. if not TryStrToDateTime(S, Result) then
  11378. Result := Default;
  11379. end;
  11380. function StrToDateTimeDef(const S: string; const Default: TDateTime;
  11381. const FormatSettings: TFormatSettings): TDateTime;
  11382. begin
  11383. if not TryStrToDateTime(S, Result, FormatSettings) then
  11384. Result := Default;
  11385. end;
  11386. function TryStrToDateTime(const S: string; out Value: TDateTime): Boolean;
  11387. var
  11388. Pos: Integer;
  11389. Date, Time: TDateTime;
  11390. begin
  11391. Result := True;
  11392. Pos := 1;
  11393. Time := 0;
  11394. if not ScanDate(S, Pos, Date) or
  11395. not ((Pos > Length(S)) or ScanTime(S, Pos, Time)) then
  11396. // Try time only
  11397. Result := TryStrToTime(S, Value)
  11398. else
  11399. if Date >= 0 then
  11400. Value := Date + Time
  11401. else
  11402. Value := Date - Time;
  11403. end;
  11404. function TryStrToDateTime(const S: string; out Value: TDateTime;
  11405. const FormatSettings: TFormatSettings): Boolean;
  11406. var
  11407. Pos: Integer;
  11408. Date, Time: TDateTime;
  11409. begin
  11410. Result := True;
  11411. Pos := 1;
  11412. Time := 0;
  11413. if not ScanDate(S, Pos, Date, FormatSettings) or
  11414. not ((Pos > Length(S)) or ScanTime(S, Pos, Time, FormatSettings)) then
  11415. // Try time only
  11416. Result := TryStrToTime(S, Value, FormatSettings)
  11417. else
  11418. if Date >= 0 then
  11419. Value := Date + Time
  11420. else
  11421. Value := Date - Time;
  11422. end;
  11423. { System error messages }
  11424. function SysErrorMessage(ErrorCode: Integer): string;
  11425. var
  11426. Buffer: array[0..255] of Char;
  11427. {$IFDEF MSWINDOWS}
  11428. var
  11429. Len: Integer;
  11430. begin
  11431. Len := FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_IGNORE_INSERTS or
  11432. FORMAT_MESSAGE_ARGUMENT_ARRAY, nil, ErrorCode, 0, Buffer,
  11433. SizeOf(Buffer), nil);
  11434. while (Len > 0) and (Buffer[Len - 1] in [#0..#32, '.']) do Dec(Len);
  11435. SetString(Result, Buffer, Len);
  11436. end;
  11437. {$ENDIF}
  11438. {$IFDEF LINUX}
  11439. begin
  11440. //Result := Format('System error: %4x',[ErrorCode]);
  11441. Result := strerror_r(ErrorCode, Buffer, sizeof(Buffer));
  11442. end;
  11443. {$ENDIF}
  11444. { Initialization file support }
  11445. function GetLocaleStr(Locale, LocaleType: Integer; const Default: string): string;
  11446. {$IFDEF MSWINDOWS}
  11447. var
  11448. L: Integer;
  11449. Buffer: array[0..255] of Char;
  11450. begin
  11451. L := GetLocaleInfo(Locale, LocaleType, Buffer, SizeOf(Buffer));
  11452. if L > 0 then SetString(Result, Buffer, L - 1) else Result := Default;
  11453. end;
  11454. {$ENDIF}
  11455. {$IFDEF LINUX}
  11456. begin
  11457. Result := Default;
  11458. end;
  11459. {$ENDIF}
  11460. function GetLocaleChar(Locale, LocaleType: Integer; Default: Char): Char;
  11461. {$IFDEF MSWINDOWS}
  11462. var
  11463. Buffer: array[0..1] of Char;
  11464. begin
  11465. if GetLocaleInfo(Locale, LocaleType, Buffer, 2) > 0 then
  11466. Result := Buffer[0] else
  11467. Result := Default;
  11468. end;
  11469. {$ENDIF}
  11470. {$IFDEF LINUX}
  11471. begin
  11472. Result := Default;
  11473. end;
  11474. {$ENDIF}
  11475. {var
  11476. DefShortMonthNames: array[1..12] of Pointer = (@SShortMonthNameJan,
  11477. @SShortMonthNameFeb, @SShortMonthNameMar, @SShortMonthNameApr,
  11478. @SShortMonthNameMay, @SShortMonthNameJun, @SShortMonthNameJul,
  11479. @SShortMonthNameAug, @SShortMonthNameSep, @SShortMonthNameOct,
  11480. @SShortMonthNameNov, @SShortMonthNameDec);
  11481. DefLongMonthNames: array[1..12] of Pointer = (@SLongMonthNameJan,
  11482. @SLongMonthNameFeb, @SLongMonthNameMar, @SLongMonthNameApr,
  11483. @SLongMonthNameMay, @SLongMonthNameJun, @SLongMonthNameJul,
  11484. @SLongMonthNameAug, @SLongMonthNameSep, @SLongMonthNameOct,
  11485. @SLongMonthNameNov, @SLongMonthNameDec);
  11486. DefShortDayNames: array[1..7] of Pointer = (@SShortDayNameSun,
  11487. @SShortDayNameMon, @SShortDayNameTue, @SShortDayNameWed,
  11488. @SShortDayNameThu, @SShortDayNameFri, @SShortDayNameSat);
  11489. DefLongDayNames: array[1..7] of Pointer = (@SLongDayNameSun,
  11490. @SLongDayNameMon, @SLongDayNameTue, @SLongDayNameWed,
  11491. @SLongDayNameThu, @SLongDayNameFri, @SLongDayNameSat);
  11492. }
  11493. procedure GetMonthDayNames;
  11494. {$IFDEF MSWINDOWS}
  11495. var
  11496. I, Day: Integer;
  11497. DefaultLCID: LCID;
  11498. function LocalGetLocaleStr(LocaleType: Integer): string;
  11499. begin
  11500. Result := GetLocaleStr(DefaultLCID, LocaleType, '');
  11501. if Result = '' then Result := GetLocaleStr($409, LocaleType, '');
  11502. //Result := LoadResString(DefValues[Index]);
  11503. end;
  11504. begin
  11505. DefaultLCID := GetThreadLocale;
  11506. for I := 1 to 12 do
  11507. begin
  11508. ShortMonthNames[I] := LocalGetLocaleStr(LOCALE_SABBREVMONTHNAME1 + I - 1);
  11509. LongMonthNames[I] := LocalGetLocaleStr(LOCALE_SMONTHNAME1 + I - 1);
  11510. end;
  11511. for I := 1 to 7 do
  11512. begin
  11513. Day := (I + 5) mod 7;
  11514. ShortDayNames[I] := LocalGetLocaleStr(LOCALE_SABBREVDAYNAME1 + Day);
  11515. LongDayNames[I] := LocalGetLocaleStr(LOCALE_SDAYNAME1 + Day);
  11516. end;
  11517. end;
  11518. {$ELSE}
  11519. {$IFDEF LINUX}
  11520. function GetLocaleStr(LocaleIndex, Index: Integer;
  11521. const DefValues: array of Pointer): string;
  11522. var
  11523. temp: PChar;
  11524. begin
  11525. temp := nl_langinfo(LocaleIndex);
  11526. if (temp = nil) or (temp^ = #0) then
  11527. Result := LoadResString(DefValues[Index])
  11528. else
  11529. Result := temp;
  11530. end;
  11531. var
  11532. I: Integer;
  11533. begin
  11534. for I := 1 to 12 do
  11535. begin
  11536. ShortMonthNames[I] := GetLocaleStr(ABMON_1 + I - 1,
  11537. I - Low(DefShortMonthNames), DefShortMonthNames);
  11538. LongMonthNames[I] := GetLocaleStr(MON_1 + I - 1,
  11539. I - Low(DefLongMonthNames), DefLongMonthNames);
  11540. end;
  11541. for I := 1 to 7 do
  11542. begin
  11543. ShortDayNames[I] := GetLocaleStr(ABDAY_1 + I - 1,
  11544. I - Low(DefShortDayNames), DefShortDayNames);
  11545. LongDayNames[I] := GetLocaleStr(DAY_1 + I - 1,
  11546. I - Low(DefLongDayNames), DefLongDayNames);
  11547. end;
  11548. end;
  11549. {$ELSE}
  11550. var
  11551. I: Integer;
  11552. begin
  11553. for I := 1 to 12 do
  11554. begin
  11555. ShortMonthNames[I] := LoadResString(DefShortMonthNames[I]);
  11556. LongMonthNames[I] := LoadResString(DefLongMonthNames[I]);
  11557. end;
  11558. for I := 1 to 7 do
  11559. begin
  11560. ShortDayNames[I] := LoadResString(DefShortDayNames[I]);
  11561. LongDayNames[I] := LoadResString(DefLongDayNames[I]);
  11562. end;
  11563. end;
  11564. {$ENDIF}
  11565. {$ENDIF}
  11566. {$IFDEF MSWINDOWS}
  11567. procedure GetLocaleMonthDayNames(DefaultLCID: Integer;
  11568. var FormatSettings: TFormatSettings);
  11569. var
  11570. I, Day: Integer;
  11571. function LocalGetLocaleStr(LocaleType: Integer): string;
  11572. begin
  11573. Result := GetLocaleStr(DefaultLCID, LocaleType, '');
  11574. if Result = '' then Result := GetLocaleStr($409, LocaleType, '');
  11575. end;
  11576. begin
  11577. for I := 1 to 12 do
  11578. begin
  11579. FormatSettings.ShortMonthNames[I] := LocalGetLocaleStr(LOCALE_SABBREVMONTHNAME1 + I - 1);
  11580. FormatSettings.LongMonthNames[I] := LocalGetLocaleStr(LOCALE_SMONTHNAME1 + I - 1);
  11581. end;
  11582. for I := 1 to 7 do
  11583. begin
  11584. Day := (I + 5) mod 7;
  11585. FormatSettings.ShortDayNames[I] := LocalGetLocaleStr(LOCALE_SABBREVDAYNAME1 + Day);
  11586. FormatSettings.LongDayNames[I] := LocalGetLocaleStr(LOCALE_SDAYNAME1 + Day);
  11587. end;
  11588. end;
  11589. {$ENDIF}
  11590. {$IFDEF MSWINDOWS}
  11591. function EnumEraNames(Names: PChar): Integer; stdcall;
  11592. var
  11593. I: Integer;
  11594. begin
  11595. Result := 0;
  11596. I := Low(EraNames);
  11597. while EraNames[I] <> '' do
  11598. if (I = High(EraNames)) then
  11599. Exit
  11600. else Inc(I);
  11601. EraNames[I] := Names;
  11602. Result := 1;
  11603. end;
  11604. function EnumEraYearOffsets(YearOffsets: PChar): Integer; stdcall;
  11605. var
  11606. I: Integer;
  11607. begin
  11608. Result := 0;
  11609. I := Low(EraYearOffsets);
  11610. while EraYearOffsets[I] <> -1 do
  11611. if (I = High(EraYearOffsets)) then
  11612. Exit
  11613. else Inc(I);
  11614. EraYearOffsets[I] := StrToIntDef(YearOffsets, 0);
  11615. Result := 1;
  11616. end;
  11617. procedure GetEraNamesAndYearOffsets;
  11618. var
  11619. J: Integer;
  11620. CalendarType: CALTYPE;
  11621. begin
  11622. CalendarType := StrToIntDef(GetLocaleStr(GetThreadLocale,
  11623. LOCALE_IOPTIONALCALENDAR, '1'), 1);
  11624. if CalendarType in [CAL_JAPAN, CAL_TAIWAN, CAL_KOREA] then
  11625. begin
  11626. EnumCalendarInfoA(@EnumEraNames, GetThreadLocale, CalendarType,
  11627. CAL_SERASTRING);
  11628. for J := Low(EraYearOffsets) to High(EraYearOffsets) do
  11629. EraYearOffsets[J] := -1;
  11630. EnumCalendarInfoA(@EnumEraYearOffsets, GetThreadLocale, CalendarType,
  11631. CAL_IYEAROFFSETRANGE);
  11632. end;
  11633. end;
  11634. function TranslateDateFormat(const FormatStr: string): string;
  11635. var
  11636. I: Integer;
  11637. L: Integer;
  11638. CalendarType: CALTYPE;
  11639. RemoveEra: Boolean;
  11640. begin
  11641. I := 1;
  11642. Result := '';
  11643. CalendarType := StrToIntDef(GetLocaleStr(GetThreadLocale,
  11644. LOCALE_ICALENDARTYPE, '1'), 1);
  11645. if not (CalendarType in [CAL_JAPAN, CAL_TAIWAN, CAL_KOREA]) then
  11646. begin
  11647. RemoveEra := SysLocale.PriLangID in [LANG_JAPANESE, LANG_CHINESE, LANG_KOREAN];
  11648. if RemoveEra then
  11649. begin
  11650. While I <= Length(FormatStr) do
  11651. begin
  11652. if not (FormatStr[I] in ['g', 'G']) then
  11653. Result := Result + FormatStr[I];
  11654. Inc(I);
  11655. end;
  11656. end
  11657. else
  11658. Result := FormatStr;
  11659. Exit;
  11660. end;
  11661. while I <= Length(FormatStr) do
  11662. begin
  11663. if FormatStr[I] in LeadBytes then
  11664. begin
  11665. L := CharLength(FormatStr, I);
  11666. Result := Result + Copy(FormatStr, I, L);
  11667. Inc(I, L);
  11668. end else
  11669. begin
  11670. if StrLIComp(@FormatStr[I], 'gg', 2) = 0 then
  11671. begin
  11672. Result := Result + 'ggg';
  11673. Inc(I, 1);
  11674. end
  11675. else if StrLIComp(@FormatStr[I], 'yyyy', 4) = 0 then
  11676. begin
  11677. Result := Result + 'eeee';
  11678. Inc(I, 4-1);
  11679. end
  11680. else if StrLIComp(@FormatStr[I], 'yy', 2) = 0 then
  11681. begin
  11682. Result := Result + 'ee';
  11683. Inc(I, 2-1);
  11684. end
  11685. else if FormatStr[I] in ['y', 'Y'] then
  11686. Result := Result + 'e'
  11687. else
  11688. Result := Result + FormatStr[I];
  11689. Inc(I);
  11690. end;
  11691. end;
  11692. end;
  11693. {$ENDIF}
  11694. {$IFDEF LINUX}
  11695. procedure InitEras;
  11696. var
  11697. Count : Byte;
  11698. I, J, Pos : Integer;
  11699. Number : Word;
  11700. S : string;
  11701. Year, Month, Day: Word;
  11702. begin
  11703. EraCount := 0;
  11704. S := nl_langinfo(ERA);
  11705. if S = '' then
  11706. S := LoadResString(@SEraEntries);
  11707. Pos := 1;
  11708. for I := 1 to MaxEraCount do
  11709. begin
  11710. if Pos > Length(S) then Break;
  11711. if not(ScanChar(S, Pos, '+') or ScanChar(S, Pos, '-')) then Break;
  11712. // Eras in which year increases with negative time (eg Christian BC era)
  11713. // are not currently supported.
  11714. // EraRanges[I].Direction := S[Pos - 1];
  11715. // Era offset, in years from Gregorian calendar year
  11716. if not ScanChar(S, Pos, ':') then Break;
  11717. if ScanChar(S, Pos, '-') then
  11718. J := -1
  11719. else
  11720. J := 1;
  11721. if not ScanNumber(S, Pos, Number, Count) then Break;
  11722. EraYearOffsets[I] := J * Number; // apply sign to Number
  11723. // Era start date, in Gregorian year/month/day format
  11724. if not ScanChar(S, Pos, ':') then Break;
  11725. if not ScanNumber(S, Pos, Year, Count) then Break;
  11726. if not ScanChar(S, Pos, '/') then Break;
  11727. if not ScanNumber(S, Pos, Month, Count) then Break;
  11728. if not ScanChar(S, Pos, '/') then Break;
  11729. if not ScanNumber(S, Pos, Day, Count) then Break;
  11730. EraRanges[I].StartDate := Trunc(EncodeDate(Year, Month, Day));
  11731. EraYearOffsets[I] := Year - EraYearOffsets[I];
  11732. // Era end date, in Gregorian year/month/day format
  11733. if not ScanChar(S, Pos, ':') then Break;
  11734. if ScanString(S, Pos, '+*') then // positive infinity
  11735. EraRanges[I].EndDate := High(EraRanges[I].EndDate)
  11736. else if ScanString(S, Pos, '-*') then // negative infinity
  11737. EraRanges[I].EndDate := Low(EraRanges[I].EndDate)
  11738. else if not ScanNumber(S, Pos, Year, Count) then
  11739. Break
  11740. else
  11741. begin
  11742. if not ScanChar(S, Pos, '/') then Break;
  11743. if not ScanNumber(S, Pos, Month, Count) then Break;
  11744. if not ScanChar(S, Pos, '/') then Break;
  11745. if not ScanNumber(S, Pos, Day, Count) then Break;
  11746. EraRanges[I].EndDate := Trunc(EncodeDate(Year, Month, Day));
  11747. end;
  11748. // Era name, in locale charset
  11749. if not ScanChar(S, Pos, ':') then Break;
  11750. J := AnsiPos(':', Copy(S, Pos, Length(S) + 1 - Pos));
  11751. if J = 0 then Break;
  11752. EraNames[I] := Copy(S, Pos, J - 1);
  11753. Inc(Pos, J - 1);
  11754. // Optional Era format string for era year, in locale charset
  11755. if not ScanChar(S, Pos, ':') then Break;
  11756. J := AnsiPos(';', Copy(S, Pos, Length(S) + 1 - Pos));
  11757. if J = 0 then
  11758. J := 1 + Length(S) + 1 - Pos;
  11759. {if J = 0 then Break;}
  11760. EraYearFormats[I] := Copy(S, Pos, J - 1);
  11761. Inc(Pos, J - 1);
  11762. Inc(EraCount);
  11763. if not((Pos > Length(S)) or ScanChar(S, Pos, ';')) then Break;
  11764. end;
  11765. // Clear the rest of the era slots, including partial entry from failed parse
  11766. for I := EraCount+1 to MaxEraCount do
  11767. begin
  11768. EraNames[I] := '';
  11769. EraYearOffsets[I] := -1;
  11770. EraRanges[I].StartDate := High(EraRanges[I].StartDate);
  11771. EraRanges[I].EndDate := High(EraRanges[I].EndDate);
  11772. EraYearFormats[I] := '';
  11773. end;
  11774. end;
  11775. {$ENDIF}
  11776. { Exception handling routines }
  11777. var
  11778. OutOfMemory: EOutOfMemory;
  11779. InvalidPointer: EInvalidPointer;
  11780. { Convert physical address to logical address }
  11781. { Format and return an exception error message }
  11782. function ExceptionErrorMessage(ExceptObject: TObject; ExceptAddr: Pointer;
  11783. Buffer: PChar; Size: Integer): Integer;
  11784. {$IFDEF MSWINDOWS}
  11785. function ConvertAddr(Address: Pointer): Pointer; assembler;
  11786. asm
  11787. TEST EAX,EAX { Always convert nil to nil }
  11788. JE @@1
  11789. SUB EAX, $1000 { offset from code start; code start set by linker to $1000 }
  11790. @@1:
  11791. end;
  11792. var
  11793. MsgPtr: PChar;
  11794. MsgEnd: PChar;
  11795. MsgLen: Integer;
  11796. ModuleName: array[0..MAX_PATH] of Char;
  11797. Temp: array[0..MAX_PATH] of Char;
  11798. //Format: array[0..255] of Char;
  11799. Info: TMemoryBasicInformation;
  11800. ConvertedAddress: Pointer;
  11801. begin
  11802. VirtualQuery(ExceptAddr, Info, sizeof(Info));
  11803. if (Info.State <> MEM_COMMIT) or
  11804. (GetModuleFilename(THandle(Info.AllocationBase), Temp, SizeOf(Temp)) = 0) then
  11805. begin
  11806. GetModuleFileName(HInstance, Temp, SizeOf(Temp));
  11807. ConvertedAddress := ConvertAddr(ExceptAddr);
  11808. end
  11809. else
  11810. Integer(ConvertedAddress) := Integer(ExceptAddr) - Integer(Info.AllocationBase);
  11811. StrLCopy(ModuleName, AnsiStrRScan(Temp, '\') + 1, SizeOf(ModuleName) - 1);
  11812. MsgPtr := '';
  11813. MsgEnd := '';
  11814. if ExceptObject is Exception then
  11815. begin
  11816. MsgPtr := PChar(Exception(ExceptObject).Message);
  11817. MsgLen := StrLen(MsgPtr);
  11818. if (MsgLen <> 0) and (MsgPtr[MsgLen - 1] <> '.') then MsgEnd := '.';
  11819. end;
  11820. {LoadString(FindResourceHInstance(HInstance),
  11821. PResStringRec(@SException).Identifier, Format, SizeOf(Format));
  11822. StrLFmt(Buffer, Size, Format, [ExceptObject.ClassName, ModuleName,
  11823. ConvertedAddress, MsgPtr, MsgEnd]); }
  11824. StrPCopy(Buffer, kol.Format(SException, [ExceptObject.ClassName, ModuleName,
  11825. ConvertedAddress, MsgPtr, MsgEnd]) );
  11826. Result := StrLen(Buffer);
  11827. end;
  11828. {$ENDIF}
  11829. {$IFDEF LINUX}
  11830. const
  11831. UnknownModuleName = '<unknown>';
  11832. var
  11833. MsgPtr: PChar;
  11834. MsgEnd: PChar;
  11835. MsgLen: Integer;
  11836. ModuleName: array[0..MAX_PATH] of Char;
  11837. Info: TDLInfo;
  11838. begin
  11839. MsgPtr := '';
  11840. MsgEnd := '';
  11841. if ExceptObject is Exception then
  11842. begin
  11843. MsgPtr := PChar(Exception(ExceptObject).Message);
  11844. MsgLen := StrLen(MsgPtr);
  11845. if (MsgLen <> 0) and (MsgPtr[MsgLen - 1] <> '.') then MsgEnd := '.';
  11846. end;
  11847. if (dladdr(ExceptAddr, Info) <> 0) and (Info.dli_fname <> nil) then
  11848. StrLCopy(ModuleName, AnsiStrRScan(Info.dli_fname, PathDelim) + 1, SizeOf(ModuleName) - 1)
  11849. else
  11850. StrLCopy(ModuleName, UnknownModuleName, SizeOf(ModuleName) - 1);
  11851. StrLFmt(Buffer, Size, PChar(SException), [ExceptObject.ClassName, ModuleName,
  11852. ExceptAddr, MsgPtr, MsgEnd]);
  11853. Result := StrLen(Buffer);
  11854. end;
  11855. {$ENDIF}
  11856. { Display exception message box }
  11857. procedure ShowException(ExceptObject: TObject; ExceptAddr: Pointer);
  11858. {$IFDEF MSWINDOWS}
  11859. var
  11860. //Title: array[0..63] of Char;
  11861. Buffer: array[0..1023] of Char;
  11862. Dummy: Cardinal;
  11863. begin
  11864. ExceptionErrorMessage(ExceptObject, ExceptAddr, Buffer, SizeOf(Buffer));
  11865. if IsConsole then
  11866. begin
  11867. Flush(Output);
  11868. CharToOemA(Buffer, Buffer);
  11869. WriteFile(GetStdHandle(STD_ERROR_HANDLE), Buffer, StrLen(Buffer), Dummy, nil);
  11870. WriteFile(GetStdHandle(STD_ERROR_HANDLE), sLineBreak, 2, Dummy, nil);
  11871. end
  11872. else
  11873. begin
  11874. { LoadString(FindResourceHInstance(HInstance), PResStringRec(@SExceptTitle).Identifier,
  11875. Title, SizeOf(Title));
  11876. MessageBox(0, Buffer, Title, MB_OK or MB_ICONSTOP or MB_TASKMODAL); }
  11877. MessageBox(0, Buffer, PChar(SExceptTitle), MB_OK or MB_ICONSTOP or MB_TASKMODAL);
  11878. end;
  11879. end;
  11880. {$ENDIF}
  11881. {$IFDEF LINUX}
  11882. var
  11883. Buffer: array[0..1023] of Char;
  11884. begin
  11885. ExceptionErrorMessage(ExceptObject, ExceptAddr, Buffer, Sizeof(Buffer));
  11886. if TTextRec(ErrOutput).Mode = fmOutput then
  11887. Flush(ErrOutput);
  11888. __write(STDERR_FILENO, Buffer, StrLen(Buffer));
  11889. end;
  11890. {$ENDIF}
  11891. { Raise abort exception }
  11892. procedure Abort;
  11893. function ReturnAddr: Pointer;
  11894. asm
  11895. MOV EAX,[EBP + 4]
  11896. end;
  11897. begin
  11898. raise EAbort.CreateRes(SOperationAborted) at ReturnAddr;
  11899. end;
  11900. { Raise out of memory exception }
  11901. procedure OutOfMemoryError;
  11902. begin
  11903. raise OutOfMemory;
  11904. end;
  11905. { Exception class }
  11906. constructor Exception.Create(const Msg: string);
  11907. begin
  11908. FMessage := Msg;
  11909. end;
  11910. constructor Exception.CreateFmt(const Msg: string;
  11911. const Args: array of const);
  11912. begin
  11913. FMessage := Format(Msg, Args);
  11914. end;
  11915. constructor Exception.CreateRes(Ident: Integer);
  11916. begin
  11917. FMessage := LoadStr(Ident);
  11918. end;
  11919. constructor Exception.CreateRes(const ResStringRec: string);
  11920. begin
  11921. FMessage := ResStringRec;
  11922. end;
  11923. constructor Exception.CreateResFmt(Ident: Integer;
  11924. const Args: array of const);
  11925. begin
  11926. FMessage := Format(LoadStr(Ident), Args);
  11927. end;
  11928. constructor Exception.CreateResFmt(const ResStringRec: string;
  11929. const Args: array of const);
  11930. begin
  11931. FMessage := Format(ResStringRec, Args);
  11932. end;
  11933. constructor Exception.CreateHelp(const Msg: string; AHelpContext: Integer);
  11934. begin
  11935. FMessage := Msg;
  11936. FHelpContext := AHelpContext;
  11937. end;
  11938. constructor Exception.CreateFmtHelp(const Msg: string; const Args: array of const;
  11939. AHelpContext: Integer);
  11940. begin
  11941. FMessage := Format(Msg, Args);
  11942. FHelpContext := AHelpContext;
  11943. end;
  11944. constructor Exception.CreateResHelp(Ident: Integer; AHelpContext: Integer);
  11945. begin
  11946. FMessage := LoadStr(Ident);
  11947. FHelpContext := AHelpContext;
  11948. end;
  11949. constructor Exception.CreateResHelp(ResStringRec: PResStringRec;
  11950. AHelpContext: Integer);
  11951. begin
  11952. FMessage := LoadResString(ResStringRec);
  11953. FHelpContext := AHelpContext;
  11954. end;
  11955. constructor Exception.CreateResFmtHelp(Ident: Integer;
  11956. const Args: array of const;
  11957. AHelpContext: Integer);
  11958. begin
  11959. FMessage := Format(LoadStr(Ident), Args);
  11960. FHelpContext := AHelpContext;
  11961. end;
  11962. constructor Exception.CreateResFmtHelp(ResStringRec: PResStringRec;
  11963. const Args: array of const;
  11964. AHelpContext: Integer);
  11965. begin
  11966. FMessage := Format(LoadResString(ResStringRec), Args);
  11967. FHelpContext := AHelpContext;
  11968. end;
  11969. { EHeapException class }
  11970. procedure EHeapException.FreeInstance;
  11971. begin
  11972. if AllowFree then
  11973. inherited FreeInstance;
  11974. end;
  11975. { Create I/O exception }
  11976. function CreateInOutError: EInOutError;
  11977. type
  11978. TErrorRec = record
  11979. Code: Integer;
  11980. Ident: string;
  11981. end;
  11982. const
  11983. ErrorMap: array[0..6] of TErrorRec = (
  11984. (Code: 2; Ident: SFileNotFound),
  11985. (Code: 3; Ident: SInvalidFilename),
  11986. (Code: 4; Ident: STooManyOpenFiles),
  11987. (Code: 5; Ident: SAccessDenied),
  11988. (Code: 100; Ident: SEndOfFile),
  11989. (Code: 101; Ident: SDiskFull),
  11990. (Code: 106; Ident: SInvalidInput));
  11991. var
  11992. I: Integer;
  11993. InOutRes: Integer;
  11994. begin
  11995. I := Low(ErrorMap);
  11996. InOutRes := IOResult; // resets IOResult to zero
  11997. while (I <= High(ErrorMap)) and (ErrorMap[I].Code <> InOutRes) do Inc(I);
  11998. if I <= High(ErrorMap) then
  11999. Result := EInOutError.Create(ErrorMap[I].Ident) else
  12000. Result := EInOutError.CreateResFmt(SInOutError, [InOutRes]);
  12001. Result.ErrorCode := InOutRes;
  12002. end;
  12003. { RTL error handler }
  12004. type
  12005. TExceptRec = record
  12006. EClass: ExceptClass;
  12007. EIdent: string;
  12008. end;
  12009. const
  12010. ExceptMap: array[Ord(reDivByZero)..Ord(High(TRuntimeError))] of TExceptRec = (
  12011. (EClass: EDivByZero; EIdent: SDivByZero),
  12012. (EClass: ERangeError; EIdent: SRangeError),
  12013. (EClass: EIntOverflow; EIdent: SIntOverflow),
  12014. (EClass: EInvalidOp; EIdent: SInvalidOp),
  12015. (EClass: EZeroDivide; EIdent: SZeroDivide),
  12016. (EClass: EOverflow; EIdent: SOverflow),
  12017. (EClass: EUnderflow; EIdent: SUnderflow),
  12018. (EClass: EInvalidCast; EIdent: SInvalidCast),
  12019. (EClass: EAccessViolation; EIdent: SAccessViolationNoArg),
  12020. (EClass: EPrivilege; EIdent: SPrivilege),
  12021. (EClass: EControlC; EIdent: SControlC),
  12022. (EClass: EStackOverflow; EIdent: SStackOverflow),
  12023. (EClass: EVariantError; EIdent: SInvalidVarCast),
  12024. (EClass: EVariantError; EIdent: SInvalidVarOp),
  12025. (EClass: EVariantError; EIdent: SDispatchError),
  12026. (EClass: EVariantError; EIdent: SVarArrayCreate),
  12027. (EClass: EVariantError; EIdent: SVarInvalid),
  12028. (EClass: EVariantError; EIdent: SVarArrayBounds),
  12029. (EClass: EAssertionFailed; EIdent: SAssertionFailed),
  12030. (EClass: EExternalException; EIdent: SExternalException),
  12031. (EClass: EIntfCastError; EIdent: SIntfCastError),
  12032. (EClass: ESafecallException; EIdent: SSafecallException)
  12033. {$IFDEF LINUX}
  12034. ,
  12035. (EClass: EQuit; EIdent: SQuit),
  12036. (EClass: ECodesetConversion; EIdent: SCodesetConversionError)
  12037. {$ENDIF}
  12038. );
  12039. procedure ErrorHandler(ErrorCode: Byte; ErrorAddr: Pointer); export;
  12040. var
  12041. E: Exception;
  12042. begin
  12043. case ErrorCode of
  12044. Ord(reOutOfMemory):
  12045. E := OutOfMemory;
  12046. Ord(reInvalidPtr):
  12047. E := InvalidPointer;
  12048. Ord(reDivByZero)..Ord(High(TRuntimeError)):
  12049. begin
  12050. with ExceptMap[ErrorCode] do
  12051. E := EClass.Create(EIdent);
  12052. end;
  12053. else
  12054. E := CreateInOutError;
  12055. end;
  12056. raise E at ErrorAddr;
  12057. end;
  12058. { Assertion error handler }
  12059. { This is complicated by the desire to make it look like the exception }
  12060. { happened in the user routine, so the debugger can give a decent stack }
  12061. { trace. To make that feasible, AssertErrorHandler calls a helper function }
  12062. { to create the exception object, so that AssertErrorHandler itself does }
  12063. { not need any temps. After the exception object is created, the asm }
  12064. { routine RaiseAssertException sets up the registers just as if the user }
  12065. { code itself had raised the exception. }
  12066. function CreateAssertException(const Message, Filename: string;
  12067. LineNumber: Integer): Exception;
  12068. var
  12069. S: string;
  12070. begin
  12071. if Message <> '' then S := Message else S := SAssertionFailed;
  12072. Result := EAssertionFailed.CreateFmt(SAssertError,
  12073. [S, Filename, LineNumber]);
  12074. end;
  12075. { This code is based on the following assumptions: }
  12076. { - Our direct caller (AssertErrorHandler) has an EBP frame }
  12077. { - ErrorStack points to where the return address would be if the }
  12078. { user program had called System.@RaiseExcept directly }
  12079. procedure RaiseAssertException(const E: Exception; const ErrorAddr, ErrorStack: Pointer);
  12080. asm
  12081. MOV ESP,ECX
  12082. MOV [ESP],EDX
  12083. MOV EBP,[EBP]
  12084. JMP System.@RaiseExcept
  12085. end;
  12086. { If you change this procedure, make sure it does not have any local variables }
  12087. { or temps that need cleanup - they won't get cleaned up due to the way }
  12088. { RaiseAssertException frame works. Also, it can not have an exception frame. }
  12089. procedure AssertErrorHandler(const Message, Filename: string;
  12090. LineNumber: Integer; ErrorAddr: Pointer);
  12091. var
  12092. E: Exception;
  12093. begin
  12094. E := CreateAssertException(Message, Filename, LineNumber);
  12095. {$IF Defined(LINUX)}
  12096. RaiseAssertException(E, ErrorAddr, PChar(@ErrorAddr)+8);
  12097. {$ELSEIF Defined(MSWINDOWS)}
  12098. RaiseAssertException(E, ErrorAddr, PChar(@ErrorAddr)+4);
  12099. {$ELSE}
  12100. {$MESSAGE ERROR 'AssertErrorHandler not implemented'}
  12101. {$IFEND}
  12102. end;
  12103. {$IFNDEF PC_MAPPED_EXCEPTIONS}
  12104. { Abstract method invoke error handler }
  12105. procedure AbstractErrorHandler;
  12106. begin
  12107. raise EAbstractError.CreateRes(SAbstractError);
  12108. end;
  12109. {$ENDIF}
  12110. {$IFDEF LINUX}
  12111. const
  12112. TRAP_ZERODIVIDE = 0;
  12113. TRAP_SINGLESTEP = 1;
  12114. TRAP_NMI = 2;
  12115. TRAP_BREAKPOINT = 3;
  12116. TRAP_OVERFLOW = 4;
  12117. TRAP_BOUND = 5;
  12118. TRAP_INVINSTR = 6;
  12119. TRAP_DEVICENA = 7;
  12120. TRAP_DOUBLEFAULT = 8;
  12121. TRAP_FPOVERRUN = 9;
  12122. TRAP_BADTSS = 10;
  12123. TRAP_SEGMENTNP = 11;
  12124. TRAP_STACKFAULT = 12;
  12125. TRAP_GPFAULT = 13;
  12126. TRAP_PAGEFAULT = 14;
  12127. TRAP_RESERVED = 15;
  12128. TRAP_FPE = 16;
  12129. TRAP_ALIGNMENT = 17;
  12130. TRAP_MACHINECHECK = 18;
  12131. TRAP_CACHEFAULT = 19;
  12132. TRAP_UNKNOWN = -1;
  12133. function MapFPUStatus(Status: LongWord): TRuntimeError;
  12134. begin
  12135. if (Status and 1) = 1 then Result := System.reInvalidOp // STACK_CHECK or INVALID_OPERATION
  12136. else if (Status and 2) = 2 then Result := System.reInvalidOp // DENORMAL_OPERAND
  12137. else if (Status and 4) = 4 then Result := System.reZeroDivide // DIVIDE_BY_ZERO
  12138. else if (Status and 8) = 8 then Result := System.reOverflow // OVERFLOW
  12139. else if (Status and $10) = $10 then Result := System.reUnderflow // UNDERFLOW
  12140. else if (Status and $20) = $20 then Result := System.reInvalidOp // INEXACT_RESULT
  12141. else Result := System.reInvalidOp;
  12142. end;
  12143. function MapFPE(Context: PSigContext): TRuntimeError;
  12144. begin
  12145. case Context^.trapno of
  12146. TRAP_ZERODIVIDE:
  12147. Result := System.reDivByZero;
  12148. TRAP_FPOVERRUN:
  12149. Result := System.reInvalidOp;
  12150. TRAP_FPE:
  12151. Result := MapFPUStatus(Context^.fpstate^.sw);
  12152. else
  12153. Result := System.reInvalidOp;
  12154. end;
  12155. end;
  12156. function MapFault(Context: PSigContext): TRuntimeError;
  12157. begin
  12158. case Context^.trapno of
  12159. TRAP_OVERFLOW:
  12160. Result := System.reIntOverflow;
  12161. TRAP_BOUND:
  12162. Result := System.reRangeError;
  12163. TRAP_INVINSTR:
  12164. Result := System.rePrivInstruction; // This doesn't seem right, but we don't
  12165. // have an external exception to match!
  12166. TRAP_STACKFAULT:
  12167. Result := System.reStackOverflow;
  12168. TRAP_SEGMENTNP,
  12169. TRAP_GPFAULT:
  12170. Result := System.reAccessViolation;
  12171. TRAP_PAGEFAULT:
  12172. Result := System.reAccessViolation;
  12173. else
  12174. Result := System.reAccessViolation;
  12175. end;
  12176. end;
  12177. function MapSignal(SigNum: Integer; Context: PSigContext): LongWord;
  12178. var
  12179. Err: TRuntimeError;
  12180. begin
  12181. case SigNum of
  12182. SIGINT: { Control-C }
  12183. Err := System.reControlBreak;
  12184. SIGQUIT: { Quit key (Control-\) }
  12185. Err := System.reQuit;
  12186. SIGFPE: { Floating Point Error }
  12187. Err := MapFPE(Context);
  12188. SIGSEGV: { Segmentation Violation }
  12189. Err := MapFault(Context);
  12190. SIGILL: { Illegal Instruction }
  12191. Err := MapFault(Context);
  12192. SIGBUS: { Bus Error }
  12193. Err := MapFault(Context);
  12194. else
  12195. Err := System.reExternalException;
  12196. end;
  12197. Result := LongWord(Err) or (LongWord(SigNum) shl 16);
  12198. end;
  12199. {$ENDIF}
  12200. {$IFDEF MSWINDOWS}
  12201. function MapException(P: PExceptionRecord): TRuntimeError;
  12202. begin
  12203. case P.ExceptionCode of
  12204. STATUS_INTEGER_DIVIDE_BY_ZERO:
  12205. Result := System.reDivByZero;
  12206. STATUS_ARRAY_BOUNDS_EXCEEDED:
  12207. Result := System.reRangeError;
  12208. STATUS_INTEGER_OVERFLOW:
  12209. Result := System.reIntOverflow;
  12210. STATUS_FLOAT_INEXACT_RESULT,
  12211. STATUS_FLOAT_INVALID_OPERATION,
  12212. STATUS_FLOAT_STACK_CHECK:
  12213. Result := System.reInvalidOp;
  12214. STATUS_FLOAT_DIVIDE_BY_ZERO:
  12215. Result := System.reZeroDivide;
  12216. STATUS_FLOAT_OVERFLOW:
  12217. Result := System.reOverflow;
  12218. STATUS_FLOAT_UNDERFLOW,
  12219. STATUS_FLOAT_DENORMAL_OPERAND:
  12220. Result := System.reUnderflow;
  12221. STATUS_ACCESS_VIOLATION:
  12222. Result := System.reAccessViolation;
  12223. STATUS_PRIVILEGED_INSTRUCTION:
  12224. Result := System.rePrivInstruction;
  12225. STATUS_CONTROL_C_EXIT:
  12226. Result := System.reControlBreak;
  12227. STATUS_STACK_OVERFLOW:
  12228. Result := System.reStackOverflow;
  12229. else
  12230. Result := System.reExternalException;
  12231. end;
  12232. end;
  12233. function GetExceptionClass(P: PExceptionRecord): ExceptClass;
  12234. var
  12235. ErrorCode: Byte;
  12236. begin
  12237. ErrorCode := Byte(MapException(P));
  12238. Result := ExceptMap[ErrorCode].EClass;
  12239. end;
  12240. function GetExceptionObject(P: PExceptionRecord): Exception;
  12241. var
  12242. ErrorCode: Integer;
  12243. function CreateAVObject: Exception;
  12244. var
  12245. AccessOp: string; // string ID indicating the access type READ or WRITE
  12246. AccessAddress: Pointer;
  12247. MemInfo: TMemoryBasicInformation;
  12248. ModName: array[0..MAX_PATH] of Char;
  12249. begin
  12250. with P^ do
  12251. begin
  12252. if ExceptionInformation[0] = 0 then
  12253. AccessOp := SReadAccess
  12254. else
  12255. AccessOp := SWriteAccess;
  12256. AccessAddress := Pointer(ExceptionInformation[1]);
  12257. VirtualQuery(ExceptionAddress, MemInfo, SizeOf(MemInfo));
  12258. if (MemInfo.State = MEM_COMMIT) and
  12259. (GetModuleFileName(THandle(MemInfo.AllocationBase), ModName, SizeOf(ModName)) <> 0) then
  12260. Result := EAccessViolation.CreateFmt(sModuleAccessViolation,
  12261. [ExceptionAddress, ExtractFileName(ModName), AccessOp,
  12262. AccessAddress])
  12263. else
  12264. Result := EAccessViolation.CreateFmt(SAccessViolationArg3,
  12265. [ExceptionAddress, AccessOp, AccessAddress]);
  12266. end;
  12267. end;
  12268. begin
  12269. ErrorCode := Byte(MapException(P));
  12270. case ErrorCode of
  12271. 3..10, 12..21:
  12272. with ExceptMap[ErrorCode] do Result := EClass.Create(EIdent);
  12273. 11: Result := CreateAVObject;
  12274. else
  12275. Result := EExternalException.CreateFmt(SExternalException, [P.ExceptionCode]);
  12276. end;
  12277. if Result is EExternal then EExternal(Result).ExceptionRecord := P;
  12278. end;
  12279. {$ENDIF} { WIN32 }
  12280. {$IFDEF LINUX}
  12281. {
  12282. The ErrorCode has the translated error code in the low byte and the
  12283. original signal number in the high word.
  12284. }
  12285. function GetExceptionObject(ExceptionAddress: LongWord; AccessAddress: LongWord; ErrorCode: LongWord): Exception;
  12286. begin
  12287. case (ErrorCode and $ff) of
  12288. 3..10, 12..21, 25:
  12289. begin
  12290. with ExceptMap[ErrorCode and $ff] do
  12291. Result := EClass.Create(EIdent);
  12292. end;
  12293. 11:
  12294. Result := EAccessViolation.CreateFmt(SAccessViolationArg2, [Pointer(ExceptionAddress), Pointer(AccessAddress)]);
  12295. else
  12296. // Result := EExternalException.CreateFmt(SExternalException, [P.ExceptionCode]);
  12297. { Not quite right - we need the original trap code, but that's lost }
  12298. Result := EExternalException.CreateFmt(SExternalException, [ErrorCode and $ff]);
  12299. end;
  12300. EExternal(Result).ExceptionAddress := ExceptionAddress;
  12301. EExternal(Result).AccessAddress := AccessAddress;
  12302. EExternal(Result).SignalNumber := ErrorCode shr 16;
  12303. end;
  12304. {$ENDIF}
  12305. { RTL exception handler }
  12306. procedure ExceptHandler(ExceptObject: TObject; ExceptAddr: Pointer); far;
  12307. begin
  12308. ShowException(ExceptObject, ExceptAddr);
  12309. Halt(1);
  12310. end;
  12311. {$IFDEF LINUX}
  12312. {$IFDEF DEBUG}
  12313. {
  12314. Used for debugging the signal handlers.
  12315. }
  12316. procedure DumpContext(SigNum: Integer; context : PSigContext);
  12317. var
  12318. Buff: array [0..128] of char;
  12319. begin
  12320. StrFmt(Buff, 'Context for signal: %d', [SigNum]);
  12321. Writeln(Buff);
  12322. StrFmt(Buff, 'CS = %04X DS = %04X ES = %04X FS = %04X GS = %04X SS = %04X',
  12323. [context^.cs, context^.ds, context^.es, context^.fs, context^.gs, context^.ss]);
  12324. WriteLn(Buff);
  12325. StrFmt(Buff, 'EAX = %08X EBX = %08X ECX = %08X EDX = %08X',
  12326. [context^.eax, context^.ebx, context^.ecx, context^.edx]);
  12327. WriteLn(Buff);
  12328. StrFmt(Buff, 'EDI = %08X ESI = %08X EBP = %08X ESP = %08X',
  12329. [context^.edi, context^.esi, context^.ebp, context^.esp]);
  12330. WriteLn(Buff);
  12331. StrFmt(Buff, 'EIP = %08X EFLAGS = %08X ESP(signal) = %08X CR2 = %08X',
  12332. [context^.eip, context^.eflags, context^.esp_at_signal, context^.cr2]);
  12333. WriteLn(Buff);
  12334. StrFmt(Buff, 'trapno = %d, err = %08x', [context^.trapno, context^.err]);
  12335. WriteLn(Buff);
  12336. end;
  12337. {$ENDIF}
  12338. {
  12339. RaiseSignalException is called from SignalConverter, once we've made things look
  12340. like there's a legitimate stack frame above us. Now we will just create
  12341. an exception object, and raise it via a software raise.
  12342. }
  12343. procedure RaiseSignalException(ExceptionEIP: LongWord; FaultAddr: LongWord; ErrorCode: LongWord);
  12344. begin
  12345. raise GetExceptionObject(ExceptionEIP, FaultAddr, ErrorCode);
  12346. end;
  12347. {
  12348. SignalConverter is where we come when a signal is raised that we want to convert
  12349. to an exception. This function stands the best chance of being called with a
  12350. useable stack frame behind it for the purpose of stack unwinding. We can't
  12351. guarantee that, though. The stack was modified by the baseline signal handler
  12352. to make it look as though we were called by the faulting instruction. That way
  12353. the unwinder stands a chance of being able to clean things up.
  12354. }
  12355. procedure SignalConverter(ExceptionEIP: LongWord; FaultAddr: LongWord; ErrorCode: LongWord);
  12356. asm
  12357. {
  12358. Here's the tricky part. We arrived here directly by virtue of our
  12359. signal handler tweaking the execution context with our address. That
  12360. means there's no return address on the stack. The unwinder needs to
  12361. have a return address so that it can unwind past this function when
  12362. we raise the Delphi exception. We will use the faulting instruction
  12363. pointer as a fake return address. Because of the fencepost conditions
  12364. in the Delphi unwinder, we need to have an address that is strictly
  12365. greater than the actual faulting instruction, so we increment that
  12366. address by one. This may be in the middle of an instruction, but we
  12367. don't care, because we will never be returning to that address.
  12368. Finally, the way that we get this address onto the stack is important.
  12369. The compiler will generate unwind information for SignalConverter that
  12370. will attempt to undo any stack modifications that are made by this
  12371. function when unwinding past it. In this particular case, we don't want
  12372. that to happen, so we use some assembly language tricks to get around
  12373. the compiler noticing the stack modification.
  12374. }
  12375. MOV EBX, ESP // Get the current stack pointer
  12376. SUB EBX, 4 // Effectively decrement the stack by 4
  12377. MOV ESP, EBX // by doing a move to ESP with a register value
  12378. MOV [ESP], EAX // Store the instruction pointer into the new stack loc
  12379. INC [ESP] // Increment by one to keep the unwinder happy
  12380. { Reset the FPU, or things can go south down the line from here }
  12381. FNINIT
  12382. FWAIT
  12383. {$IFDEF PIC}
  12384. PUSH EAX
  12385. PUSH ECX
  12386. CALL GetGOT
  12387. MOV EAX, [EAX].offset Default8087CW
  12388. FLDCW [EAX]
  12389. POP ECX
  12390. POP EAX
  12391. {$ELSE}
  12392. FLDCW Default8087CW
  12393. {$ENDIF}
  12394. PUSH EBP
  12395. MOV EBP, ESP
  12396. CALL RaiseSignalException
  12397. end;
  12398. function TlsGetValue(Key: Integer): Pointer; cdecl;
  12399. external libpthreadmodulename name 'pthread_getspecific';
  12400. {
  12401. Under Linux, we crawl out from underneath the OS signal handler before
  12402. we attempt to do anything with the signal. This is because the stack
  12403. has a bunch of OS frames on there that we cannot possibly unwind from.
  12404. So we use this routine to accomplish the dispatch, and then another
  12405. routine to handle the language level of the exception handling.
  12406. }
  12407. procedure SignalDispatcher(SigNum: Integer; SigInfo: PSigInfo; UContext: PUserContext); cdecl;
  12408. type
  12409. PGeneralRegisters = ^gregset_t;
  12410. var
  12411. GeneralRegisters: PGeneralRegisters;
  12412. begin
  12413. //DumpContext(SigNum, @context);
  12414. {
  12415. Some of the ways that we get here are can lead us to big trouble. For
  12416. example, if the signal is SIGINT or SIGQUIT, these will commonly be raised
  12417. to all threads in the process if the user generated them from the
  12418. keyboard. This is handled well by the Delphi threads, but if a non-Delphi
  12419. thread lets one of these get by unhandled, terrible things will happen.
  12420. So we look for that case, and eat SIGINT and SIGQUIT that have been issued
  12421. on threads that are not Delphi threads. If the signal is a SIGSEGV, or
  12422. other fatal sort of signal, and the thread that we're running on is not
  12423. a Delphi thread, then we are completely without options. We have no
  12424. recovery means, and we have to take the app down hard, right away.
  12425. }
  12426. if TlsGetValue(TlsIndex) = nil then
  12427. begin
  12428. if (SigNum = SIGINT) or (SigNum = SIGQUIT) then
  12429. Exit;
  12430. RunError(232);
  12431. end;
  12432. {
  12433. If we are processing another exception right now, we definitely do not
  12434. want to be dispatching any exceptions that are async, like SIGINT and
  12435. SIGQUIT. So we have check to see if OS signals are blocked. If they are,
  12436. we have to eat this signal right now.
  12437. }
  12438. if AreOSExceptionsBlocked and ((SigNum = SIGINT) or (SigNum = SIGQUIT)) then
  12439. Exit;
  12440. {
  12441. If someone wants to delay the handling of SIGINT or SIGQUIT until such
  12442. time as it's safe to handle it, they set DeferUserInterrupts to True.
  12443. Then we just set a global variable saying that a SIGINT or SIGQUIT was
  12444. issued. It is the responsibility of some other body of code at this
  12445. point to poll for changes to SIG(INT/QUIT)Issued
  12446. }
  12447. if DeferUserInterrupts then
  12448. begin
  12449. if SigNum = SIGINT then
  12450. begin
  12451. SIGINTIssued := True;
  12452. Exit;
  12453. end;
  12454. if SigNum = SIGQUIT then
  12455. begin
  12456. SIGQUITIssued := True;
  12457. Exit;
  12458. end;
  12459. end;
  12460. BlockOSExceptions;
  12461. GeneralRegisters := @UContext^.uc_mcontext.gregs;
  12462. GeneralRegisters^[REG_EAX] := GeneralRegisters^[REG_EIP];
  12463. GeneralRegisters^[REG_EDX] := UContext^.uc_mcontext.cr2;
  12464. GeneralRegisters^[REG_ECX] := MapSignal(SigNum, PSigContext(GeneralRegisters));
  12465. GeneralRegisters^[REG_EIP] := LongWord(@SignalConverter);
  12466. end;
  12467. type
  12468. TSignalMap = packed record
  12469. SigNum: Integer;
  12470. Abandon: Boolean;
  12471. OldAction: TSigAction;
  12472. Hooked: Boolean;
  12473. end;
  12474. var
  12475. Signals: array [0..RTL_SIGLAST] of TSignalMap =
  12476. ( (SigNum: SIGINT;),
  12477. (SigNum: SIGFPE;),
  12478. (SigNum: SIGSEGV;),
  12479. (SigNum: SIGILL;),
  12480. (SigNum: SIGBUS;),
  12481. (SigNum: SIGQUIT;) );
  12482. function InquireSignal(RtlSigNum: Integer): TSignalState;
  12483. var
  12484. Action: TSigAction;
  12485. begin
  12486. if sigaction(Signals[RtlSigNum].SigNum, nil, @Action) = -1 then
  12487. raise Exception.CreateRes(@SSigactionFailed);
  12488. if (@Action.__sigaction_handler <> @SignalDispatcher) then
  12489. begin
  12490. if Signals[RtlSigNum].Hooked then
  12491. Result := ssOverridden
  12492. else
  12493. Result := ssNotHooked;
  12494. end
  12495. else
  12496. Result := ssHooked;
  12497. end;
  12498. procedure AbandonSignalHandler(RtlSigNum: Integer);
  12499. var
  12500. I: Integer;
  12501. begin
  12502. if RtlSigNum = RTL_SIGDEFAULT then
  12503. begin
  12504. for I := 0 to RTL_SIGLAST do
  12505. AbandonSignalHandler(I);
  12506. Exit;
  12507. end;
  12508. Signals[RtlSigNum].Abandon := True;
  12509. end;
  12510. procedure HookSignal(RtlSigNum: Integer);
  12511. var
  12512. Action: TSigAction;
  12513. I: Integer;
  12514. begin
  12515. if RtlSigNum = RTL_SIGDEFAULT then
  12516. begin
  12517. for I := 0 to RTL_SIGLAST do
  12518. HookSignal(I);
  12519. Exit;
  12520. end;
  12521. FillChar(Action, SizeOf(Action), 0);
  12522. Action.__sigaction_handler := @SignalDispatcher;
  12523. Action.sa_flags := SA_SIGINFO;
  12524. sigaddset(Action.sa_mask, SIGINT);
  12525. sigaddset(Action.sa_mask, SIGQUIT);
  12526. if sigaction(Signals[RtlSigNum].SigNum, @Action, @Signals[RtlSigNum].OldAction) = -1 then
  12527. raise Exception.CreateRes(@SSigactionFailed);
  12528. Signals[RtlSigNum].Hooked := True;
  12529. end;
  12530. procedure UnhookSignal(RtlSigNum: Integer; OnlyIfHooked: Boolean);
  12531. var
  12532. I: Integer;
  12533. begin
  12534. if RtlSigNum = RTL_SIGDEFAULT then
  12535. begin
  12536. for I := 0 to RTL_SIGLAST do
  12537. UnhookSignal(I, OnlyIfHooked);
  12538. Exit;
  12539. end;
  12540. if not Signals[RtlSigNum].Abandon then
  12541. begin
  12542. if OnlyIfHooked and (InquireSignal(RtlSigNum) <> ssHooked) then
  12543. Exit;
  12544. if sigaction(Signals[RtlSigNum].SigNum, @Signals[RtlSigNum].OldAction, Nil) = -1 then
  12545. raise Exception.CreateRes(@SSigactionFailed);
  12546. Signals[RtlSigNum].Hooked := False;
  12547. end;
  12548. end;
  12549. procedure UnhookOSExceptions;
  12550. begin
  12551. if not Assigned(HookOSExceptionsProc) then
  12552. UnhookSignal(RTL_SIGDEFAULT, True);
  12553. end;
  12554. procedure HookOSExceptions;
  12555. begin
  12556. if Assigned(HookOSExceptionsProc) then
  12557. HookOSExceptionsProc
  12558. else
  12559. begin
  12560. HookSignal(RTL_SIGDEFAULT);
  12561. end;
  12562. end;
  12563. {$ENDIF} // LINUX
  12564. procedure InitExceptions;
  12565. begin
  12566. OutOfMemory := EOutOfMemory.CreateRes(SOutOfMemory);
  12567. InvalidPointer := EInvalidPointer.CreateRes(SInvalidPointer);
  12568. ErrorProc := ErrorHandler;
  12569. ExceptProc := @ExceptHandler;
  12570. ExceptionClass := Exception;
  12571. {$IFDEF MSWINDOWS}
  12572. ExceptClsProc := @GetExceptionClass;
  12573. ExceptObjProc := @GetExceptionObject;
  12574. {$ENDIF}
  12575. AssertErrorProc := @AssertErrorHandler;
  12576. {$IFNDEF PC_MAPPED_EXCEPTIONS}
  12577. // We don't hook this under PC mapped exceptions, because
  12578. // we have no idea what the parameters were to the procedure
  12579. // in question. Hence we cannot hope to unwind the stack in
  12580. // our handler. Since we just throw an exception from our
  12581. // handler, that pretty much rules out using this without
  12582. // exorbitant compiler support. If you do hook AbstractErrorProc,
  12583. // you must make sure that you never throw an exception from
  12584. // your handler if PC_MAPPED_EXCEPTIONS is defined.
  12585. AbstractErrorProc := @AbstractErrorHandler;
  12586. {$ENDIF}
  12587. {$IFDEF LINUX}
  12588. if not IsLibrary then
  12589. HookOSExceptions;
  12590. {$ENDIF}
  12591. end;
  12592. procedure DoneExceptions;
  12593. begin
  12594. if Assigned(OutOfMemory) then
  12595. begin
  12596. OutOfMemory.AllowFree := True;
  12597. OutOfMemory.FreeInstance;
  12598. OutOfMemory := nil;
  12599. end;
  12600. if Assigned(InvalidPointer) then
  12601. begin
  12602. InvalidPointer.AllowFree := True;
  12603. InvalidPointer.Free;
  12604. InvalidPointer := nil;
  12605. end;
  12606. ErrorProc := nil;
  12607. ExceptProc := nil;
  12608. ExceptionClass := nil;
  12609. {$IFDEF MSWINDOWS}
  12610. ExceptClsProc := nil;
  12611. ExceptObjProc := nil;
  12612. {$ENDIF}
  12613. AssertErrorProc := nil;
  12614. {$IFDEF LINUX}
  12615. if not IsLibrary then
  12616. UnhookOSExceptions;
  12617. {$ENDIF}
  12618. end;
  12619. {$IFDEF MSWINDOWS}
  12620. procedure InitPlatformId;
  12621. var
  12622. OSVersionInfo: TOSVersionInfo;
  12623. begin
  12624. OSVersionInfo.dwOSVersionInfoSize := SizeOf(OSVersionInfo);
  12625. if GetVersionEx(OSVersionInfo) then
  12626. with OSVersionInfo do
  12627. begin
  12628. Win32Platform := dwPlatformId;
  12629. Win32MajorVersion := dwMajorVersion;
  12630. Win32MinorVersion := dwMinorVersion;
  12631. if Win32Platform = VER_PLATFORM_WIN32_WINDOWS then
  12632. Win32BuildNumber := dwBuildNumber and $FFFF
  12633. else
  12634. Win32BuildNumber := dwBuildNumber;
  12635. Win32CSDVersion := szCSDVersion;
  12636. end;
  12637. end;
  12638. function CheckWin32Version(AMajor: Integer; AMinor: Integer = 0): Boolean;
  12639. begin
  12640. Result := (Win32MajorVersion > AMajor) or
  12641. ((Win32MajorVersion = AMajor) and
  12642. (Win32MinorVersion >= AMinor));
  12643. end;
  12644. function GetFileVersion(const AFileName: string): Cardinal;
  12645. var
  12646. FileName: string;
  12647. InfoSize, Wnd: DWORD;
  12648. VerBuf: Pointer;
  12649. FI: PVSFixedFileInfo;
  12650. VerSize: DWORD;
  12651. begin
  12652. Result := Cardinal(-1);
  12653. // GetFileVersionInfo modifies the filename parameter data while parsing.
  12654. // Copy the string const into a local variable to create a writeable copy.
  12655. FileName := AFileName;
  12656. UniqueString(FileName);
  12657. InfoSize := GetFileVersionInfoSize(PChar(FileName), Wnd);
  12658. if InfoSize <> 0 then
  12659. begin
  12660. GetMem(VerBuf, InfoSize);
  12661. try
  12662. if GetFileVersionInfo(PChar(FileName), Wnd, InfoSize, VerBuf) then
  12663. if VerQueryValue(VerBuf, '\', Pointer(FI), VerSize) then
  12664. Result:= FI.dwFileVersionMS;
  12665. finally
  12666. FreeMem(VerBuf);
  12667. end;
  12668. end;
  12669. end;
  12670. procedure Beep;
  12671. begin
  12672. MessageBeep(0);
  12673. end;
  12674. {$ENDIF}
  12675. {$IFDEF LINUX}
  12676. procedure Beep;
  12677. var
  12678. ch: Char;
  12679. FileDes: Integer;
  12680. begin
  12681. if isatty(STDERR_FILENO) = 1 then
  12682. FileDes := STDERR_FILENO
  12683. else
  12684. if isatty(STDOUT_FILENO) = 1 then
  12685. FileDes := STDOUT_FILENO
  12686. else
  12687. begin
  12688. // Neither STDERR_FILENO nor STDOUT_FILENO are open
  12689. // terminals (TTYs). It is not possible to safely
  12690. // write the beep character.
  12691. Exit;
  12692. end;
  12693. ch := #7;
  12694. __write(FileDes, ch, 1);
  12695. end;
  12696. {$ENDIF}
  12697. { MBCS functions }
  12698. function ByteTypeTest(P: PChar; Index: Integer): TMbcsByteType;
  12699. {$IFDEF MSWINDOWS}
  12700. var
  12701. I: Integer;
  12702. begin
  12703. Result := mbSingleByte;
  12704. if (P = nil) or (P[Index] = #$0) then Exit;
  12705. if (Index = 0) then
  12706. begin
  12707. if P[0] in LeadBytes then Result := mbLeadByte;
  12708. end
  12709. else
  12710. begin
  12711. I := Index - 1;
  12712. while (I >= 0) and (P[I] in LeadBytes) do Dec(I);
  12713. if ((Index - I) mod 2) = 0 then Result := mbTrailByte
  12714. else if P[Index] in LeadBytes then Result := mbLeadByte;
  12715. end;
  12716. end;
  12717. {$ENDIF}
  12718. {$IFDEF LINUX}
  12719. var
  12720. I, L: Integer;
  12721. begin
  12722. Result := mbSingleByte;
  12723. if (P = nil) or (P[Index] = #$0) then Exit;
  12724. I := 0;
  12725. repeat
  12726. if P[I] in LeadBytes then
  12727. L := StrCharLength(P + I)
  12728. else
  12729. L := 1;
  12730. Inc(I, L);
  12731. until (I > Index);
  12732. if (L <> 1) then
  12733. if (I - L = Index) then
  12734. Result := mbLeadByte
  12735. else
  12736. Result := mbTrailByte;
  12737. end;
  12738. {$ENDIF}
  12739. function ByteType(const S: string; Index: Integer): TMbcsByteType;
  12740. begin
  12741. Result := mbSingleByte;
  12742. if SysLocale.FarEast then
  12743. Result := ByteTypeTest(PChar(S), Index-1);
  12744. end;
  12745. function StrByteType(Str: PChar; Index: Cardinal): TMbcsByteType;
  12746. begin
  12747. Result := mbSingleByte;
  12748. if SysLocale.FarEast then
  12749. Result := ByteTypeTest(Str, Index);
  12750. end;
  12751. function ByteToCharLen(const S: string; MaxLen: Integer): Integer;
  12752. begin
  12753. if Length(S) < MaxLen then MaxLen := Length(S);
  12754. Result := ByteToCharIndex(S, MaxLen);
  12755. end;
  12756. function ByteToCharIndex(const S: string; Index: Integer): Integer;
  12757. var
  12758. I: Integer;
  12759. begin
  12760. Result := 0;
  12761. if (Index <= 0) or (Index > Length(S)) then Exit;
  12762. Result := Index;
  12763. if not SysLocale.FarEast then Exit;
  12764. I := 1;
  12765. Result := 0;
  12766. while I <= Index do
  12767. begin
  12768. if S[I] in LeadBytes then
  12769. I := NextCharIndex(S, I)
  12770. else
  12771. Inc(I);
  12772. Inc(Result);
  12773. end;
  12774. end;
  12775. procedure CountChars(const S: string; MaxChars: Integer; var CharCount, ByteCount: Integer);
  12776. var
  12777. C, L, B: Integer;
  12778. begin
  12779. L := Length(S);
  12780. C := 1;
  12781. B := 1;
  12782. while (B < L) and (C < MaxChars) do
  12783. begin
  12784. Inc(C);
  12785. if S[B] in LeadBytes then
  12786. B := NextCharIndex(S, B)
  12787. else
  12788. Inc(B);
  12789. end;
  12790. if (C = MaxChars) and (B < L) and (S[B] in LeadBytes) then
  12791. B := NextCharIndex(S, B) - 1;
  12792. CharCount := C;
  12793. ByteCount := B;
  12794. end;
  12795. function CharToByteIndex(const S: string; Index: Integer): Integer;
  12796. var
  12797. Chars: Integer;
  12798. begin
  12799. Result := 0;
  12800. if (Index <= 0) or (Index > Length(S)) then Exit;
  12801. if (Index > 1) and SysLocale.FarEast then
  12802. begin
  12803. CountChars(S, Index-1, Chars, Result);
  12804. if (Chars < (Index-1)) or (Result >= Length(S)) then
  12805. Result := 0 // Char index out of range
  12806. else
  12807. Inc(Result);
  12808. end
  12809. else
  12810. Result := Index;
  12811. end;
  12812. function CharToByteLen(const S: string; MaxLen: Integer): Integer;
  12813. var
  12814. Chars: Integer;
  12815. begin
  12816. Result := 0;
  12817. if MaxLen <= 0 then Exit;
  12818. if MaxLen > Length(S) then MaxLen := Length(S);
  12819. if SysLocale.FarEast then
  12820. begin
  12821. CountChars(S, MaxLen, Chars, Result);
  12822. if Result > Length(S) then
  12823. Result := Length(S);
  12824. end
  12825. else
  12826. Result := MaxLen;
  12827. end;
  12828. { MBCS Helper functions }
  12829. function StrCharLength(const Str: PChar): Integer;
  12830. begin
  12831. {$IFDEF LINUX}
  12832. Result := mblen(Str, MB_CUR_MAX);
  12833. if (Result = -1) then Result := 1;
  12834. {$ENDIF}
  12835. {$IFDEF MSWINDOWS}
  12836. if SysLocale.FarEast then
  12837. Result := Integer(CharNext(Str)) - Integer(Str)
  12838. else
  12839. Result := 1;
  12840. {$ENDIF}
  12841. end;
  12842. function StrNextChar(const Str: PChar): PChar;
  12843. begin
  12844. {$IFDEF LINUX}
  12845. Result := Str + StrCharLength(Str);
  12846. {$ENDIF}
  12847. {$IFDEF MSWINDOWS}
  12848. Result := CharNext(Str);
  12849. {$ENDIF}
  12850. end;
  12851. function CharLength(const S: string; Index: Integer): Integer;
  12852. begin
  12853. Result := 1;
  12854. assert((Index > 0) and (Index <= Length(S)));
  12855. if SysLocale.FarEast and (S[Index] in LeadBytes) then
  12856. Result := StrCharLength(PChar(S) + Index - 1);
  12857. end;
  12858. function NextCharIndex(const S: string; Index: Integer): Integer;
  12859. begin
  12860. Result := Index + 1;
  12861. assert((Index > 0) and (Index <= Length(S)));
  12862. if SysLocale.FarEast and (S[Index] in LeadBytes) then
  12863. Result := Index + StrCharLength(PChar(S) + Index - 1);
  12864. end;
  12865. function IsPathDelimiter(const S: string; Index: Integer): Boolean;
  12866. begin
  12867. Result := (Index > 0) and (Index <= Length(S)) and (S[Index] = PathDelim)
  12868. and (ByteType(S, Index) = mbSingleByte);
  12869. end;
  12870. function IsDelimiter(const Delimiters, S: string; Index: Integer): Boolean;
  12871. begin
  12872. Result := False;
  12873. if (Index <= 0) or (Index > Length(S)) or (ByteType(S, Index) <> mbSingleByte) then exit;
  12874. Result := StrScan(PChar(Delimiters), S[Index]) <> nil;
  12875. end;
  12876. function IncludeTrailingBackslash(const S: string): string;
  12877. begin
  12878. Result := IncludeTrailingPathDelimiter(S);
  12879. end;
  12880. function IncludeTrailingPathDelimiter(const S: string): string;
  12881. begin
  12882. Result := S;
  12883. if not IsPathDelimiter(Result, Length(Result)) then
  12884. Result := Result + PathDelim;
  12885. end;
  12886. function ExcludeTrailingBackslash(const S: string): string;
  12887. begin
  12888. Result := ExcludeTrailingPathDelimiter(S);
  12889. end;
  12890. function ExcludeTrailingPathDelimiter(const S: string): string;
  12891. begin
  12892. Result := S;
  12893. if IsPathDelimiter(Result, Length(Result)) then
  12894. SetLength(Result, Length(Result)-1);
  12895. end;
  12896. function AnsiPos(const Substr, S: string): Integer;
  12897. var
  12898. P: PChar;
  12899. begin
  12900. Result := 0;
  12901. P := AnsiStrPos(PChar(S), PChar(SubStr));
  12902. if P <> nil then
  12903. Result := Integer(P) - Integer(PChar(S)) + 1;
  12904. end;
  12905. function AnsiCompareFileName(const S1, S2: string): Integer;
  12906. begin
  12907. {$IFDEF MSWINDOWS}
  12908. Result := AnsiCompareStr(AnsiLowerCaseFileName(S1), AnsiLowerCaseFileName(S2));
  12909. {$ENDIF}
  12910. {$IFDEF LINUX}
  12911. Result := AnsiCompareStr(S1, S2);
  12912. {$ENDIF}
  12913. end;
  12914. function SameFileName(const S1, S2: string): Boolean;
  12915. begin
  12916. Result := AnsiCompareFileName(S1, S2) = 0;
  12917. end;
  12918. function AnsiLowerCaseFileName(const S: string): string;
  12919. {$IFDEF MSWINDOWS}
  12920. var
  12921. I,L: Integer;
  12922. begin
  12923. if SysLocale.FarEast then
  12924. begin
  12925. L := Length(S);
  12926. SetLength(Result, L);
  12927. I := 1;
  12928. while I <= L do
  12929. begin
  12930. Result[I] := S[I];
  12931. if S[I] in LeadBytes then
  12932. begin
  12933. Inc(I);
  12934. Result[I] := S[I];
  12935. end
  12936. else
  12937. if Result[I] in ['A'..'Z'] then Inc(Byte(Result[I]), 32);
  12938. Inc(I);
  12939. end;
  12940. end
  12941. else
  12942. Result := AnsiLowerCase(S);
  12943. end;
  12944. {$ENDIF}
  12945. {$IFDEF LINUX}
  12946. begin
  12947. Result := AnsiLowerCase(S);
  12948. end;
  12949. {$ENDIF}
  12950. function AnsiUpperCaseFileName(const S: string): string;
  12951. {$IFDEF MSWINDOWS}
  12952. var
  12953. I,L: Integer;
  12954. begin
  12955. if SysLocale.FarEast then
  12956. begin
  12957. L := Length(S);
  12958. SetLength(Result, L);
  12959. I := 1;
  12960. while I <= L do
  12961. begin
  12962. Result[I] := S[I];
  12963. if S[I] in LeadBytes then
  12964. begin
  12965. Inc(I);
  12966. Result[I] := S[I];
  12967. end
  12968. else
  12969. if Result[I] in ['a'..'z'] then Dec(Byte(Result[I]), 32);
  12970. Inc(I);
  12971. end;
  12972. end
  12973. else
  12974. Result := AnsiUpperCase(S);
  12975. end;
  12976. {$ENDIF}
  12977. {$IFDEF LINUX}
  12978. begin
  12979. Result := AnsiUpperCase(S);
  12980. end;
  12981. {$ENDIF}
  12982. function AnsiStrPos(Str, SubStr: PChar): PChar;
  12983. var
  12984. L1, L2: Cardinal;
  12985. ByteType : TMbcsByteType;
  12986. begin
  12987. Result := nil;
  12988. if (Str = nil) or (Str^ = #0) or (SubStr = nil) or (SubStr^ = #0) then Exit;
  12989. L1 := StrLen(Str);
  12990. L2 := StrLen(SubStr);
  12991. Result := StrPos(Str, SubStr);
  12992. while (Result <> nil) and ((L1 - Cardinal(Result - Str)) >= L2) do
  12993. begin
  12994. ByteType := StrByteType(Str, Integer(Result-Str));
  12995. {$IFDEF MSWINDOWS}
  12996. if (ByteType <> mbTrailByte) and
  12997. (CompareString(LOCALE_USER_DEFAULT, 0, Result, L2, SubStr, L2) = CSTR_EQUAL) then Exit;
  12998. if (ByteType = mbLeadByte) then Inc(Result);
  12999. {$ENDIF}
  13000. {$IFDEF LINUX}
  13001. if (ByteType <> mbTrailByte) and
  13002. (strncmp(Result, SubStr, L2) = 0) then Exit;
  13003. {$ENDIF}
  13004. Inc(Result);
  13005. Result := StrPos(Result, SubStr);
  13006. end;
  13007. Result := nil;
  13008. end;
  13009. function AnsiStrRScan(Str: PChar; Chr: Char): PChar;
  13010. begin
  13011. Str := AnsiStrScan(Str, Chr);
  13012. Result := Str;
  13013. if Chr <> #$0 then
  13014. begin
  13015. while Str <> nil do
  13016. begin
  13017. Result := Str;
  13018. Inc(Str);
  13019. Str := AnsiStrScan(Str, Chr);
  13020. end;
  13021. end
  13022. end;
  13023. function AnsiStrScan(Str: PChar; Chr: Char): PChar;
  13024. begin
  13025. Result := StrScan(Str, Chr);
  13026. while Result <> nil do
  13027. begin
  13028. {$IFDEF MSWINDOWS}
  13029. case StrByteType(Str, Integer(Result-Str)) of
  13030. mbSingleByte: Exit;
  13031. mbLeadByte: Inc(Result);
  13032. end;
  13033. {$ENDIF}
  13034. {$IFDEF LINUX}
  13035. if StrByteType(Str, Integer(Result-Str)) = mbSingleByte then Exit;
  13036. {$ENDIF}
  13037. Inc(Result);
  13038. Result := StrScan(Result, Chr);
  13039. end;
  13040. end;
  13041. {$IFDEF MSWINDOWS}
  13042. function LCIDToCodePage(ALcid: LCID): Integer;
  13043. var
  13044. Buffer: array [0..6] of Char;
  13045. begin
  13046. GetLocaleInfo(ALcid, LOCALE_IDEFAULTANSICODEPAGE, Buffer, SizeOf(Buffer));
  13047. Result:= StrToIntDef(Buffer, GetACP);
  13048. end;
  13049. {$ENDIF}
  13050. procedure InitSysLocale;
  13051. {$IFDEF MSWINDOWS}
  13052. var
  13053. DefaultLCID: LCID;
  13054. DefaultLangID: LANGID;
  13055. AnsiCPInfo: TCPInfo;
  13056. // I: Integer;
  13057. // BufferA: array [128..255] of Char;
  13058. // BufferW: array [128..256] of Word;
  13059. // PCharA: PChar;
  13060. procedure InitLeadBytes;
  13061. var
  13062. I: Integer;
  13063. J: Byte;
  13064. begin
  13065. GetCPInfo(CP_ACP, AnsiCPInfo);
  13066. with AnsiCPInfo do
  13067. begin
  13068. I := 0;
  13069. while (I < MAX_LEADBYTES) and ((LeadByte[I] or LeadByte[I + 1]) <> 0) do
  13070. begin
  13071. for J := LeadByte[I] to LeadByte[I + 1] do
  13072. Include(LeadBytes, Char(J));
  13073. Inc(I, 2);
  13074. end;
  13075. end;
  13076. end;
  13077. begin
  13078. { Set default to English (US). }
  13079. SysLocale.DefaultLCID := $0409;
  13080. SysLocale.PriLangID := LANG_ENGLISH;
  13081. SysLocale.SubLangID := SUBLANG_ENGLISH_US;
  13082. DefaultLCID := GetThreadLocale;
  13083. if DefaultLCID <> 0 then SysLocale.DefaultLCID := DefaultLCID;
  13084. DefaultLangID := Word(DefaultLCID);
  13085. if DefaultLangID <> 0 then
  13086. begin
  13087. SysLocale.PriLangID := DefaultLangID and $3ff;
  13088. SysLocale.SubLangID := DefaultLangID shr 10;
  13089. end;
  13090. LeadBytes := [];
  13091. if (Win32MajorVersion > 4) and (Win32Platform = VER_PLATFORM_WIN32_NT) then
  13092. SysLocale.MiddleEast := True
  13093. else
  13094. SysLocale.MiddleEast := GetSystemMetrics(SM_MIDEASTENABLED) <> 0;
  13095. SysLocale.FarEast := GetSystemMetrics(SM_DBCSENABLED) <> 0;
  13096. if SysLocale.FarEast then
  13097. InitLeadBytes;
  13098. end;
  13099. {$ENDIF}
  13100. {$IFDEF LINUX}
  13101. var
  13102. I: Integer;
  13103. buf: array [0..3] of char;
  13104. begin
  13105. FillChar(SysLocale, sizeof(SysLocale), 0);
  13106. SysLocale.FarEast := MB_CUR_MAX <> 1;
  13107. if not SysLocale.FarEast then Exit;
  13108. buf[1] := #0;
  13109. for I := 1 to 255 do
  13110. begin
  13111. buf[0] := Chr(I);
  13112. if mblen(buf, 1) <> 1 then Include(LeadBytes, Char(I));
  13113. end;
  13114. end;
  13115. {$ENDIF}
  13116. procedure GetFormatSettings;
  13117. {$IFDEF MSWINDOWS}
  13118. var
  13119. HourFormat, TimePrefix, TimePostfix: string;
  13120. DefaultLCID: Integer;
  13121. begin
  13122. InitSysLocale;
  13123. GetMonthDayNames;
  13124. if SysLocale.FarEast then GetEraNamesAndYearOffsets;
  13125. DefaultLCID := GetThreadLocale;
  13126. CurrencyString := GetLocaleStr(DefaultLCID, LOCALE_SCURRENCY, '');
  13127. CurrencyFormat := StrToIntDef(GetLocaleStr(DefaultLCID, LOCALE_ICURRENCY, '0'), 0);
  13128. NegCurrFormat := StrToIntDef(GetLocaleStr(DefaultLCID, LOCALE_INEGCURR, '0'), 0);
  13129. ThousandSeparator := GetLocaleChar(DefaultLCID, LOCALE_STHOUSAND, ',');
  13130. DecimalSeparator := GetLocaleChar(DefaultLCID, LOCALE_SDECIMAL, '.');
  13131. CurrencyDecimals := StrToIntDef(GetLocaleStr(DefaultLCID, LOCALE_ICURRDIGITS, '0'), 0);
  13132. DateSeparator := GetLocaleChar(DefaultLCID, LOCALE_SDATE, '/');
  13133. ShortDateFormat := TranslateDateFormat(GetLocaleStr(DefaultLCID, LOCALE_SSHORTDATE, 'm/d/yy'));
  13134. LongDateFormat := TranslateDateFormat(GetLocaleStr(DefaultLCID, LOCALE_SLONGDATE, 'mmmm d, yyyy'));
  13135. TimeSeparator := GetLocaleChar(DefaultLCID, LOCALE_STIME, ':');
  13136. TimeAMString := GetLocaleStr(DefaultLCID, LOCALE_S1159, 'am');
  13137. TimePMString := GetLocaleStr(DefaultLCID, LOCALE_S2359, 'pm');
  13138. TimePrefix := '';
  13139. TimePostfix := '';
  13140. if StrToIntDef(GetLocaleStr(DefaultLCID, LOCALE_ITLZERO, '0'), 0) = 0 then
  13141. HourFormat := 'h' else
  13142. HourFormat := 'hh';
  13143. if StrToIntDef(GetLocaleStr(DefaultLCID, LOCALE_ITIME, '0'), 0) = 0 then
  13144. if StrToIntDef(GetLocaleStr(DefaultLCID, LOCALE_ITIMEMARKPOSN, '0'), 0) = 0 then
  13145. TimePostfix := ' AMPM'
  13146. else
  13147. TimePrefix := 'AMPM ';
  13148. ShortTimeFormat := TimePrefix + HourFormat + ':mm' + TimePostfix;
  13149. LongTimeFormat := TimePrefix + HourFormat + ':mm:ss' + TimePostfix;
  13150. ListSeparator := GetLocaleChar(DefaultLCID, LOCALE_SLIST, ',');
  13151. end;
  13152. {$ELSE}
  13153. {$IFDEF LINUX}
  13154. const
  13155. //first boolean is p_cs_precedes, second is p_sep_by_space
  13156. CurrencyFormats: array[boolean, boolean] of byte = ((1, 3),(0, 2));
  13157. //first boolean is n_cs_precedes, second is n_sep_by_space and finally n_sign_posn
  13158. NegCurrFormats: array[boolean, boolean, 0..4] of byte =
  13159. (((4,5,7,6,7),(15,8,10,13,10)),((0,1,3,1,2),(14,9,11,9,12)));
  13160. function TranslateFormat(s: PChar; const Default: string): string;
  13161. begin
  13162. Result := '';
  13163. while s^ <> #0 do
  13164. begin
  13165. if s^ = '%' then
  13166. begin
  13167. inc(s);
  13168. case s^ of
  13169. 'a': Result := Result + 'ddd';
  13170. 'A': Result := Result + 'dddd';
  13171. 'b': Result := Result + 'MMM';
  13172. 'B': Result := Result + 'MMMM';
  13173. 'c': Result := Result + 'c';
  13174. // 'C': year / 100 not supported
  13175. 'd': Result := Result + 'dd';
  13176. 'D': Result := Result + 'MM/dd/yy';
  13177. 'e': Result := Result + 'd';
  13178. // 'E': alternate format not supported
  13179. 'g': Result := Result + 'yy';
  13180. 'G': Result := Result + 'yyyy';
  13181. 'h': Result := Result + 'MMM';
  13182. 'H': Result := Result + 'HH';
  13183. 'I': Result := Result + 'hh';
  13184. // 'j': day of year not supported
  13185. 'k': Result := Result + 'H';
  13186. 'l': Result := Result + 'h';
  13187. 'm': Result := Result + 'MM';
  13188. 'M': Result := Result + 'nn'; // minutes! not months!
  13189. 'n': Result := Result + sLineBreak; // line break
  13190. // 'O': alternate format not supported
  13191. 'P', // P's implied lowercasing of locale string is not supported
  13192. 'p': Result := Result + 'AMPM';
  13193. 'r': Result := Result + TranslateFormat(nl_langInfo(T_FMT_AMPM),'');
  13194. 'R': Result := Result + 'HH:mm';
  13195. // 's': number of seconds since Epoch not supported
  13196. 'S': Result := Result + 'ss';
  13197. 't': Result := Result + #9; // tab char
  13198. 'T': Result := Result + 'HH:mm:ss';
  13199. // 'u': day of week 1..7 not supported
  13200. // 'U': week number of the year not supported
  13201. // 'V': week number of the year not supported
  13202. // 'w': day of week 0..6 not supported
  13203. // 'W': week number of the year not supported
  13204. 'x': Result := Result + TranslateFormat(nl_langInfo(D_FMT),'');
  13205. 'X': Result := Result + TranslateFormat(nl_langinfo(T_FMT),'');
  13206. 'y': Result := Result + 'yy';
  13207. 'Y': Result := Result + 'yyyy';
  13208. // 'z': GMT offset is not supported
  13209. '%': Result := Result + '%';
  13210. end;
  13211. end
  13212. else
  13213. Result := Result + s^;
  13214. Inc(s);
  13215. end;
  13216. if Result = '' then
  13217. Result := Default;
  13218. end;
  13219. function GetFirstCharacter(const SrcString, match: string): char;
  13220. var
  13221. i, p: integer;
  13222. begin
  13223. result := match[1];
  13224. for i := 1 to length(SrcString) do begin
  13225. p := Pos(SrcString[i], match);
  13226. if p > 0 then
  13227. begin
  13228. result := match[p];
  13229. break;
  13230. end;
  13231. end;
  13232. end;
  13233. var
  13234. P: PLConv;
  13235. begin
  13236. InitSysLocale;
  13237. GetMonthDayNames;
  13238. if SysLocale.FarEast then InitEras;
  13239. CurrencyString := '';
  13240. CurrencyFormat := 0;
  13241. NegCurrFormat := 0;
  13242. ThousandSeparator := ',';
  13243. DecimalSeparator := '.';
  13244. CurrencyDecimals := 0;
  13245. P := localeconv;
  13246. if P <> nil then
  13247. begin
  13248. if P^.currency_symbol <> nil then
  13249. CurrencyString := P^.currency_symbol;
  13250. if (Byte(P^.p_cs_precedes) in [0..1]) and
  13251. (Byte(P^.p_sep_by_space) in [0..1]) then
  13252. begin
  13253. CurrencyFormat := CurrencyFormats[P^.p_cs_precedes, P^.p_sep_by_space];
  13254. if P^.p_sign_posn in [0..4] then
  13255. NegCurrFormat := NegCurrFormats[P^.n_cs_precedes, P^.n_sep_by_space,
  13256. P^.n_sign_posn];
  13257. end;
  13258. // #0 is valid for ThousandSeparator. Indicates no thousand separator.
  13259. ThousandSeparator := P^.thousands_sep^;
  13260. // #0 is not valid for DecimalSeparator.
  13261. if P^.decimal_point <> #0 then
  13262. DecimalSeparator := P^.decimal_point^;
  13263. CurrencyDecimals := P^.frac_digits;
  13264. end;
  13265. ShortDateFormat := TranslateFormat(nl_langinfo(D_FMT),'m/d/yy');
  13266. LongDateFormat := TranslateFormat(nl_langinfo(D_T_FMT), ShortDateFormat);
  13267. ShortTimeFormat := TranslateFormat(nl_langinfo(T_FMT), 'hh:mm AMPM');
  13268. LongTimeFormat := TranslateFormat(nl_langinfo(T_FMT_AMPM), ShortTimeFormat);
  13269. DateSeparator := GetFirstCharacter(ShortDateFormat, '/.-');
  13270. TimeSeparator := GetFirstCharacter(ShortTimeFormat, ':.');
  13271. TimeAMString := nl_langinfo(AM_STR);
  13272. TimePMString := nl_langinfo(PM_STR);
  13273. ListSeparator := ',';
  13274. end;
  13275. {$ELSE}
  13276. var
  13277. HourFormat, TimePrefix, TimePostfix: string;
  13278. begin
  13279. InitSysLocale;
  13280. GetMonthDayNames;
  13281. CurrencyString := '';
  13282. CurrencyFormat := 0;
  13283. NegCurrFormat := 0;
  13284. ThousandSeparator := ',';
  13285. DecimalSeparator := '.';
  13286. CurrencyDecimals := 0;
  13287. DateSeparator := '/';
  13288. ShortDateFormat := 'm/d/yy';
  13289. LongDateFormat := 'mmmm d, yyyy';
  13290. TimeSeparator := ':';
  13291. TimeAMString := 'am';
  13292. TimePMString := 'pm';
  13293. TimePrefix := '';
  13294. TimePostfix := '';
  13295. HourFormat := 'h';
  13296. TimePostfix := ' AMPM';
  13297. ShortTimeFormat := TimePrefix + HourFormat + ':mm' + TimePostfix;
  13298. LongTimeFormat := TimePrefix + HourFormat + ':mm:ss' + TimePostfix;
  13299. ListSeparator := ',';
  13300. end;
  13301. {$ENDIF}
  13302. {$ENDIF}
  13303. {$IFDEF MSWINDOWS}
  13304. procedure GetLocaleFormatSettings(LCID: Integer;
  13305. var FormatSettings: TFormatSettings);
  13306. var
  13307. HourFormat, TimePrefix, TimePostfix: string;
  13308. DefaultLCID: Integer;
  13309. begin
  13310. if IsValidLocale(LCID, LCID_INSTALLED) then
  13311. DefaultLCID := LCID
  13312. else
  13313. DefaultLCID := GetThreadLocale;
  13314. GetLocaleMonthDayNames(LCID, FormatSettings);
  13315. with FormatSettings do
  13316. begin
  13317. CurrencyString := GetLocaleStr(DefaultLCID, LOCALE_SCURRENCY, '');
  13318. CurrencyFormat := StrToIntDef(GetLocaleStr(DefaultLCID, LOCALE_ICURRENCY, '0'), 0);
  13319. NegCurrFormat := StrToIntDef(GetLocaleStr(DefaultLCID, LOCALE_INEGCURR, '0'), 0);
  13320. ThousandSeparator := GetLocaleChar(DefaultLCID, LOCALE_STHOUSAND, ',');
  13321. DecimalSeparator := GetLocaleChar(DefaultLCID, LOCALE_SDECIMAL, '.');
  13322. CurrencyDecimals := StrToIntDef(GetLocaleStr(DefaultLCID, LOCALE_ICURRDIGITS, '0'), 0);
  13323. DateSeparator := GetLocaleChar(DefaultLCID, LOCALE_SDATE, '/');
  13324. ShortDateFormat := TranslateDateFormat(GetLocaleStr(DefaultLCID, LOCALE_SSHORTDATE, 'm/d/yy'));
  13325. LongDateFormat := TranslateDateFormat(GetLocaleStr(DefaultLCID, LOCALE_SLONGDATE, 'mmmm d, yyyy'));
  13326. TimeSeparator := GetLocaleChar(DefaultLCID, LOCALE_STIME, ':');
  13327. TimeAMString := GetLocaleStr(DefaultLCID, LOCALE_S1159, 'am');
  13328. TimePMString := GetLocaleStr(DefaultLCID, LOCALE_S2359, 'pm');
  13329. TimePrefix := '';
  13330. TimePostfix := '';
  13331. if StrToIntDef(GetLocaleStr(DefaultLCID, LOCALE_ITLZERO, '0'), 0) = 0 then
  13332. HourFormat := 'h' else
  13333. HourFormat := 'hh';
  13334. if StrToIntDef(GetLocaleStr(DefaultLCID, LOCALE_ITIME, '0'), 0) = 0 then
  13335. if StrToIntDef(GetLocaleStr(DefaultLCID, LOCALE_ITIMEMARKPOSN, '0'), 0) = 0 then
  13336. TimePostfix := ' AMPM'
  13337. else
  13338. TimePrefix := 'AMPM ';
  13339. ShortTimeFormat := TimePrefix + HourFormat + ':mm' + TimePostfix;
  13340. LongTimeFormat := TimePrefix + HourFormat + ':mm:ss' + TimePostfix;
  13341. ListSeparator := GetLocaleChar(DefaultLCID, LOCALE_SLIST, ',');
  13342. end;
  13343. end;
  13344. {$ENDIF}
  13345. function StringReplace(const S, OldPattern, NewPattern: string;
  13346. Flags: TReplaceFlags): string;
  13347. var
  13348. SearchStr, Patt, NewStr: string;
  13349. Offset: Integer;
  13350. begin
  13351. if rfIgnoreCase in Flags then
  13352. begin
  13353. SearchStr := AnsiUpperCase(S);
  13354. Patt := AnsiUpperCase(OldPattern);
  13355. end else
  13356. begin
  13357. SearchStr := S;
  13358. Patt := OldPattern;
  13359. end;
  13360. NewStr := S;
  13361. Result := '';
  13362. while SearchStr <> '' do
  13363. begin
  13364. Offset := AnsiPos(Patt, SearchStr);
  13365. if Offset = 0 then
  13366. begin
  13367. Result := Result + NewStr;
  13368. Break;
  13369. end;
  13370. Result := Result + Copy(NewStr, 1, Offset - 1) + NewPattern;
  13371. NewStr := Copy(NewStr, Offset + Length(OldPattern), MaxInt);
  13372. if not (rfReplaceAll in Flags) then
  13373. begin
  13374. Result := Result + NewStr;
  13375. Break;
  13376. end;
  13377. SearchStr := Copy(SearchStr, Offset + Length(Patt), MaxInt);
  13378. end;
  13379. end;
  13380. function WrapText(const Line, BreakStr: string; const BreakChars: TSysCharSet;
  13381. MaxCol: Integer): string;
  13382. const
  13383. QuoteChars = ['''', '"'];
  13384. var
  13385. Col, Pos: Integer;
  13386. LinePos, LineLen: Integer;
  13387. BreakLen, BreakPos: Integer;
  13388. QuoteChar, CurChar: Char;
  13389. ExistingBreak: Boolean;
  13390. L: Integer;
  13391. begin
  13392. Col := 1;
  13393. Pos := 1;
  13394. LinePos := 1;
  13395. BreakPos := 0;
  13396. QuoteChar := #0;
  13397. ExistingBreak := False;
  13398. LineLen := Length(Line);
  13399. BreakLen := Length(BreakStr);
  13400. Result := '';
  13401. while Pos <= LineLen do
  13402. begin
  13403. CurChar := Line[Pos];
  13404. if CurChar in LeadBytes then
  13405. begin
  13406. L := CharLength(Line, Pos) - 1;
  13407. Inc(Pos, L);
  13408. Inc(Col, L);
  13409. end
  13410. else
  13411. begin
  13412. if CurChar in QuoteChars then
  13413. if QuoteChar = #0 then
  13414. QuoteChar := CurChar
  13415. else if CurChar = QuoteChar then
  13416. QuoteChar := #0;
  13417. if QuoteChar = #0 then
  13418. begin
  13419. if CurChar = BreakStr[1] then
  13420. begin
  13421. ExistingBreak := StrLComp(Pointer(BreakStr), Pointer(@Line[Pos]), BreakLen) = 0;
  13422. if ExistingBreak then
  13423. begin
  13424. Inc(Pos, BreakLen-1);
  13425. BreakPos := Pos;
  13426. end;
  13427. end;
  13428. if not ExistingBreak then
  13429. if CurChar in BreakChars then
  13430. BreakPos := Pos;
  13431. end;
  13432. end;
  13433. Inc(Pos);
  13434. Inc(Col);
  13435. if not (QuoteChar in QuoteChars) and (ExistingBreak or
  13436. ((Col > MaxCol) and (BreakPos > LinePos))) then
  13437. begin
  13438. Col := 1;
  13439. Result := Result + Copy(Line, LinePos, BreakPos - LinePos + 1);
  13440. if not (CurChar in QuoteChars) then
  13441. begin
  13442. while Pos <= LineLen do
  13443. begin
  13444. if Line[Pos] in BreakChars then
  13445. begin
  13446. Inc(Pos);
  13447. ExistingBreak := False;
  13448. end
  13449. else
  13450. begin
  13451. if StrLComp(Pointer(@Line[Pos]), sLineBreak, Length(sLineBreak)) = 0 then
  13452. begin
  13453. Inc(Pos, Length(sLineBreak));
  13454. ExistingBreak := True;
  13455. end
  13456. else
  13457. Break;
  13458. end;
  13459. end;
  13460. end;
  13461. if (Pos <= LineLen) and not ExistingBreak then
  13462. Result := Result + BreakStr;
  13463. Inc(BreakPos);
  13464. LinePos := BreakPos;
  13465. Pos := LinePos;
  13466. ExistingBreak := False;
  13467. end;
  13468. end;
  13469. Result := Result + Copy(Line, LinePos, MaxInt);
  13470. end;
  13471. function WrapText(const Line: string; MaxCol: Integer): string;
  13472. begin
  13473. Result := WrapText(Line, sLineBreak, [' ', '-', #9], MaxCol); { do not localize }
  13474. end;
  13475. function FindCmdLineSwitch(const Switch: string; const Chars: TSysCharSet;
  13476. IgnoreCase: Boolean): Boolean;
  13477. var
  13478. I: Integer;
  13479. S: string;
  13480. begin
  13481. for I := 1 to ParamCount do
  13482. begin
  13483. S := ParamStr(I);
  13484. if (Chars = []) or (S[1] in Chars) then
  13485. if IgnoreCase then
  13486. begin
  13487. if (AnsiCompareText(Copy(S, 2, Maxint), Switch) = 0) then
  13488. begin
  13489. Result := True;
  13490. Exit;
  13491. end;
  13492. end
  13493. else begin
  13494. if (AnsiCompareStr(Copy(S, 2, Maxint), Switch) = 0) then
  13495. begin
  13496. Result := True;
  13497. Exit;
  13498. end;
  13499. end;
  13500. end;
  13501. Result := False;
  13502. end;
  13503. function FindCmdLineSwitch(const Switch: string): Boolean;
  13504. begin
  13505. Result := FindCmdLineSwitch(Switch, SwitchChars, True);
  13506. end;
  13507. function FindCmdLineSwitch(const Switch: string; IgnoreCase: Boolean): Boolean;
  13508. begin
  13509. Result := FindCmdLineSwitch(Switch, SwitchChars, IgnoreCase);
  13510. end;
  13511. { Package info structures }
  13512. type
  13513. PPkgName = ^TPkgName;
  13514. TPkgName = packed record
  13515. HashCode: Byte;
  13516. Name: array[0..255] of Char;
  13517. end;
  13518. { PackageUnitFlags:
  13519. bit meaning
  13520. -----------------------------------------------------------------------------------------
  13521. 0 | main unit
  13522. 1 | package unit (dpk source)
  13523. 2 | $WEAKPACKAGEUNIT unit
  13524. 3 | original containment of $WEAKPACKAGEUNIT (package into which it was compiled)
  13525. 4 | implicitly imported
  13526. 5..7 | reserved
  13527. }
  13528. PUnitName = ^TUnitName;
  13529. TUnitName = packed record
  13530. Flags : Byte;
  13531. HashCode: Byte;
  13532. Name: array[0..255] of Char;
  13533. end;
  13534. { Package flags:
  13535. bit meaning
  13536. -----------------------------------------------------------------------------------------
  13537. 0 | 1: never-build 0: always build
  13538. 1 | 1: design-time only 0: not design-time only on => bit 2 = off
  13539. 2 | 1: run-time only 0: not run-time only on => bit 1 = off
  13540. 3 | 1: do not check for dup units 0: perform normal dup unit check
  13541. 4..25 | reserved
  13542. 26..27| (producer) 0: pre-V4, 1: undefined, 2: c++, 3: Pascal
  13543. 28..29| reserved
  13544. 30..31| 0: EXE, 1: Package DLL, 2: Library DLL, 3: undefined
  13545. }
  13546. PPackageInfoHeader = ^TPackageInfoHeader;
  13547. TPackageInfoHeader = packed record
  13548. Flags: Cardinal;
  13549. RequiresCount: Integer;
  13550. {Requires: array[0..9999] of TPkgName;
  13551. ContainsCount: Integer;
  13552. Contains: array[0..9999] of TUnitName;}
  13553. end;
  13554. function PackageInfoTable(Module: HMODULE): PPackageInfoHeader;
  13555. var
  13556. ResInfo: HRSRC;
  13557. Data: THandle;
  13558. begin
  13559. Result := nil;
  13560. ResInfo := FindResource(Module, 'PACKAGEINFO', RT_RCDATA);
  13561. if ResInfo <> 0 then
  13562. begin
  13563. Data := LoadResource(Module, ResInfo);
  13564. if Data <> 0 then
  13565. try
  13566. Result := LockResource(Data);
  13567. UnlockResource(Data);
  13568. finally
  13569. FreeResource(Data);
  13570. end;
  13571. end;
  13572. end;
  13573. function GetModuleName(Module: HMODULE): string;
  13574. var
  13575. ModName: array[0..MAX_PATH] of Char;
  13576. begin
  13577. SetString(Result, ModName, GetModuleFileName(Module, ModName, SizeOf(ModName)));
  13578. end;
  13579. var
  13580. Reserved: Integer;
  13581. procedure CheckForDuplicateUnits(Module: HMODULE);
  13582. var
  13583. ModuleFlags: Cardinal;
  13584. function IsUnitPresent(HC: Byte; UnitName: PChar; Module: HMODULE;
  13585. const ModuleName: string; var UnitPackage: string): Boolean;
  13586. var
  13587. I: Integer;
  13588. InfoTable: PPackageInfoHeader;
  13589. LibModule: PLibModule;
  13590. PkgName: PPkgName;
  13591. UName : PUnitName;
  13592. Count: Integer;
  13593. begin
  13594. Result := True;
  13595. if (StrIComp(UnitName, 'SysInit') <> 0) and
  13596. (StrIComp(UnitName, PChar(ModuleName)) <> 0) then
  13597. begin
  13598. LibModule := LibModuleList;
  13599. while LibModule <> nil do
  13600. begin
  13601. if LibModule.Instance <> Cardinal(Module) then
  13602. begin
  13603. InfoTable := PackageInfoTable(HMODULE(LibModule.Instance));
  13604. if (InfoTable <> nil) and (InfoTable.Flags and pfModuleTypeMask = pfPackageModule) and
  13605. ((InfoTable.Flags and pfIgnoreDupUnits) = (ModuleFlags and pfIgnoreDupUnits)) then
  13606. begin
  13607. PkgName := PPkgName(Integer(InfoTable) + SizeOf(InfoTable^));
  13608. Count := InfoTable.RequiresCount;
  13609. { Skip the Requires list }
  13610. for I := 0 to Count - 1 do Inc(Integer(PkgName), StrLen(PkgName.Name) + 2);
  13611. Count := Integer(Pointer(PkgName)^);
  13612. UName := PUnitName(Integer(PkgName) + 4);
  13613. for I := 0 to Count - 1 do
  13614. begin
  13615. with UName^ do
  13616. // Test Flags to ignore weak package units
  13617. if ((HashCode = HC) or (HashCode = 0) or (HC = 0)) and
  13618. ((Flags and $06) = 0) and (StrIComp(UnitName, Name) = 0) then
  13619. begin
  13620. UnitPackage := ChangeFileExt(ExtractFileName(
  13621. GetModuleName(HMODULE(LibModule.Instance))), '');
  13622. Exit;
  13623. end;
  13624. Inc(Integer(UName), StrLen(UName.Name) + 3);
  13625. end;
  13626. end;
  13627. end;
  13628. LibModule := LibModule.Next;
  13629. end;
  13630. end;
  13631. Result := False;
  13632. end;
  13633. function FindLibModule(Module: HModule): PLibModule;
  13634. begin
  13635. Result := LibModuleList;
  13636. while Result <> nil do
  13637. begin
  13638. if Result.Instance = Cardinal(Module) then Exit;
  13639. Result := Result.Next;
  13640. end;
  13641. end;
  13642. procedure InternalUnitCheck(Module: HModule);
  13643. var
  13644. I: Integer;
  13645. InfoTable: PPackageInfoHeader;
  13646. UnitPackage: string;
  13647. ModuleName: string;
  13648. PkgName: PPkgName;
  13649. UName: PUnitName;
  13650. Count: Integer;
  13651. LibModule: PLibModule;
  13652. begin
  13653. InfoTable := PackageInfoTable(Module);
  13654. if (InfoTable <> nil) and (InfoTable.Flags and pfModuleTypeMask = pfPackageModule) then
  13655. begin
  13656. if ModuleFlags = 0 then ModuleFlags := InfoTable.Flags;
  13657. ModuleName := ChangeFileExt(ExtractFileName(GetModuleName(Module)), '');
  13658. PkgName := PPkgName(Integer(InfoTable) + SizeOf(InfoTable^));
  13659. Count := InfoTable.RequiresCount;
  13660. for I := 0 to Count - 1 do
  13661. begin
  13662. with PkgName^ do
  13663. {$IFDEF MSWINDOWS}
  13664. InternalUnitCheck(GetModuleHandle(PChar(ChangeFileExt(Name, '.bpl'))));
  13665. {$ENDIF}
  13666. {$IFDEF LINUX}
  13667. InternalUnitCheck(GetModuleHandle(Name));
  13668. {$ENDIF}
  13669. Inc(Integer(PkgName), StrLen(PkgName.Name) + 2);
  13670. end;
  13671. LibModule := FindLibModule(Module);
  13672. if (LibModule = nil) or ((LibModule <> nil) and (LibModule.Reserved <> Reserved)) then
  13673. begin
  13674. if LibModule <> nil then LibModule.Reserved := Reserved;
  13675. Count := Integer(Pointer(PkgName)^);
  13676. UName := PUnitName(Integer(PkgName) + 4);
  13677. for I := 0 to Count - 1 do
  13678. begin
  13679. with UName^ do
  13680. // Test Flags to ignore weak package units
  13681. if ((Flags and ufWeakPackageUnit) = 0 ) and
  13682. IsUnitPresent(HashCode, Name, Module, ModuleName, UnitPackage) then
  13683. raise EPackageError.CreateResFmt(SDuplicatePackageUnit,
  13684. [ModuleName, Name, UnitPackage]);
  13685. Inc(Integer(UName), StrLen(UName.Name) + 3);
  13686. end;
  13687. end;
  13688. end;
  13689. end;
  13690. begin
  13691. Inc(Reserved);
  13692. ModuleFlags := 0;
  13693. InternalUnitCheck(Module);
  13694. end;
  13695. {$IFDEF LINUX}
  13696. function LoadLibrary(ModuleName: PChar): HMODULE;
  13697. begin
  13698. Result := HMODULE(dlopen(ModuleName, RTLD_LAZY));
  13699. end;
  13700. function FreeLibrary(Module: HMODULE): LongBool;
  13701. begin
  13702. Result := LongBool(dlclose(Pointer(Module)));
  13703. end;
  13704. function GetProcAddress(Module: HMODULE; Proc: PChar): Pointer;
  13705. var
  13706. Info: TDLInfo;
  13707. Error: PChar;
  13708. ModHandle: HMODULE;
  13709. begin
  13710. // dlsym doesn't clear the error state when the function succeeds
  13711. dlerror;
  13712. Result := dlsym(Pointer(Module), Proc);
  13713. Error := dlerror;
  13714. if Error <> nil then
  13715. Result := nil
  13716. else if dladdr(Result, Info) <> 0 then
  13717. begin
  13718. { In glibc 2.1.3 and earlier, dladdr returns a nil dli_fname
  13719. for addresses in the main program file. In glibc 2.1.91 and
  13720. later, dladdr fills in the dli_fname for addresses in the
  13721. main program file, but dlopen will segfault when given
  13722. the main program file name.
  13723. Workaround: Check the symbol base address against the main
  13724. program file's base address, and only call dlopen with a nil
  13725. filename to get the module name of the main program. }
  13726. if Info.dli_fbase = ExeBaseAddress then
  13727. Info.dli_fname := nil;
  13728. ModHandle := HMODULE(dlopen(Info.dli_fname, RTLD_LAZY));
  13729. if ModHandle <> 0 then
  13730. begin
  13731. dlclose(Pointer(ModHandle));
  13732. if ModHandle <> Module then
  13733. Result := nil;
  13734. end;
  13735. end else Result := nil;
  13736. end;
  13737. type
  13738. plink_map = ^link_map;
  13739. link_map = record
  13740. l_addr: Pointer;
  13741. l_name: PChar;
  13742. l_ld: Pointer;
  13743. l_next, l_prev: plink_map;
  13744. end;
  13745. pr_debug = ^r_debug;
  13746. r_debug = record
  13747. r_version: Integer;
  13748. r_map: plink_map;
  13749. r_brk: Pointer;
  13750. r_state: Integer;
  13751. r_ldbase: Pointer;
  13752. end;
  13753. var
  13754. _r_debug: pr_debug = nil;
  13755. function ScanLinkMap(Func: Pointer): plink_map;
  13756. var
  13757. linkmap: plink_map;
  13758. function Eval(linkmap: plink_map; Func: Pointer): Boolean;
  13759. asm
  13760. // MOV ECX,[EBP]
  13761. PUSH EBP
  13762. CALL EDX
  13763. POP ECX
  13764. end;
  13765. begin
  13766. if _r_debug = nil then
  13767. _r_debug := dlsym(RTLD_DEFAULT, '_r_debug');
  13768. if _r_debug = nil then
  13769. begin
  13770. Assert(False, 'Unable to locate ''_r_debug'' symbol'); // do not localize
  13771. Result := nil;
  13772. Exit;
  13773. end;
  13774. linkmap := _r_debug.r_map;
  13775. while linkmap <> nil do
  13776. begin
  13777. if not Eval(linkmap, Func) then Break;
  13778. linkmap := linkmap.l_next;
  13779. end;
  13780. Result := linkmap;
  13781. end;
  13782. function InitModule(linkmap: plink_map): HMODULE;
  13783. begin
  13784. if linkmap <> nil then
  13785. begin
  13786. Result := HMODULE(dlopen(linkmap.l_name, RTLD_LAZY));
  13787. if Result <> 0 then
  13788. dlclose(Pointer(Result));
  13789. end else Result := 0;
  13790. end;
  13791. function GetModuleHandle(ModuleName: PChar): HMODULE;
  13792. function CheckModuleName(linkmap: plink_map): Boolean;
  13793. var
  13794. BaseName: PChar;
  13795. begin
  13796. Result := True;
  13797. if ((ModuleName = nil) and ((linkmap.l_name = nil) or (linkmap.l_name[0] = #0))) or
  13798. ((ModuleName[0] = PathDelim) and (StrComp(ModuleName, linkmap.l_name) = 0)) then
  13799. begin
  13800. Result := False;
  13801. Exit;
  13802. end else
  13803. begin
  13804. // Locate the start of the actual filename
  13805. BaseName := StrRScan(linkmap.l_name, PathDelim);
  13806. if BaseName = nil then
  13807. BaseName := linkmap.l_name
  13808. else Inc(BaseName); // The filename is actually located at BaseName+1
  13809. if StrComp(ModuleName, BaseName) = 0 then
  13810. begin
  13811. Result := False;
  13812. Exit;
  13813. end;
  13814. end;
  13815. end;
  13816. begin
  13817. Result := InitModule(ScanLinkMap(@CheckModuleName));
  13818. end;
  13819. function GetPackageModuleHandle(PackageName: PChar): HMODULE;
  13820. var
  13821. PkgName: array[0..MAX_PATH] of Char;
  13822. function CheckPackageName(linkmap: plink_map): Boolean;
  13823. var
  13824. BaseName: PChar;
  13825. begin
  13826. Result := True;
  13827. if linkmap.l_name <> nil then
  13828. begin
  13829. // Locate the start of the actual filename
  13830. BaseName := StrRScan(linkmap.l_name, PathDelim);
  13831. if BaseName = nil then
  13832. BaseName := linkmap.l_name // If there is no path info, just use the whole name
  13833. else Inc(BaseName); // The filename is actually located at BaseName+1
  13834. Result := StrPos(BaseName, PkgName) = nil;
  13835. end;
  13836. end;
  13837. procedure MakePkgName(Prefix, Name: PChar);
  13838. begin
  13839. StrCopy(PkgName, Prefix);
  13840. StrLCat(PkgName, Name, sizeof(PkgName)-1);
  13841. PkgName[High(PkgName)] := #0;
  13842. end;
  13843. begin
  13844. if (PackageName = nil) or (StrScan(PackageName, PathDelim) <> nil) then
  13845. Result := 0
  13846. else
  13847. begin
  13848. MakePkgName('bpl', PackageName); // First check the default prefix
  13849. Result := InitModule(ScanLinkMap(@CheckPackageName));
  13850. if Result = 0 then
  13851. begin
  13852. MakePkgName('dcl', PackageName); // Next check the design-time prefix
  13853. Result := InitModule(ScanLinkMap(@CheckPackageName));
  13854. if Result = 0 then
  13855. begin
  13856. MakePkgName('', PackageName); // finally check without a prefix
  13857. Result := InitModule(ScanLinkMap(@CheckPackageName));
  13858. end;
  13859. end;
  13860. end;
  13861. end;
  13862. {$ENDIF}
  13863. {$IFDEF MSWINDOWS}
  13864. procedure Sleep; external kernel32 name 'Sleep'; stdcall;
  13865. {$ENDIF}
  13866. {$IFDEF LINUX}
  13867. procedure Sleep(milliseconds: Cardinal);
  13868. begin
  13869. usleep(milliseconds * 1000); // usleep is in microseconds
  13870. end;
  13871. {$ENDIF}
  13872. { InitializePackage }
  13873. procedure InitializePackage(Module: HMODULE);
  13874. type
  13875. TPackageLoad = procedure;
  13876. var
  13877. PackageLoad: TPackageLoad;
  13878. begin
  13879. CheckForDuplicateUnits(Module);
  13880. @PackageLoad := GetProcAddress(Module, 'Initialize'); //Do not localize
  13881. if Assigned(PackageLoad) then
  13882. PackageLoad
  13883. else
  13884. raise EPackageError.CreateFmt(sInvalidPackageFile, [GetModuleName(Module)]);
  13885. end;
  13886. { FinalizePackage }
  13887. procedure FinalizePackage(Module: HMODULE);
  13888. type
  13889. TPackageUnload = procedure;
  13890. var
  13891. PackageUnload: TPackageUnload;
  13892. begin
  13893. @PackageUnload := GetProcAddress(Module, 'Finalize'); //Do not localize
  13894. if Assigned(PackageUnload) then
  13895. PackageUnload
  13896. else
  13897. raise EPackageError.CreateRes(sInvalidPackageHandle);
  13898. end;
  13899. { LoadPackage }
  13900. function LoadPackage(const Name: string): HMODULE;
  13901. {$IFDEF LINUX}
  13902. var
  13903. DLErrorMsg: string;
  13904. {$ENDIF}
  13905. begin
  13906. {$IFDEF MSWINDOWS}
  13907. Result := SafeLoadLibrary(Name);
  13908. {$ENDIF}
  13909. {$IFDEF LINUX}
  13910. Result := HMODULE(dlOpen(PChar(Name), PkgLoadingMode));
  13911. {$ENDIF}
  13912. if Result = 0 then
  13913. begin
  13914. {$IFDEF LINUX}
  13915. DLErrorMsg := dlerror;
  13916. {$ENDIF}
  13917. raise EPackageError.CreateResFmt(sErrorLoadingPackage,
  13918. [Name,
  13919. {$IFDEF MSWINDOWS}SysErrorMessage(GetLastError){$ENDIF}
  13920. {$IFDEF LINUX}DLErrorMsg{$ENDIF}]);
  13921. end;
  13922. try
  13923. InitializePackage(Result);
  13924. except
  13925. {$IFDEF MSWINDOWS}
  13926. FreeLibrary(Result);
  13927. {$ENDIF}
  13928. {$IFDEF LINUX}
  13929. dlclose(Pointer(Result));
  13930. {$ENDIF}
  13931. raise;
  13932. end;
  13933. end;
  13934. { UnloadPackage }
  13935. procedure UnloadPackage(Module: HMODULE);
  13936. begin
  13937. FinalizePackage(Module);
  13938. {$IFDEF MSWINDOWS}
  13939. FreeLibrary(Module);
  13940. {$ENDIF}
  13941. {$IFDEF LINUX}
  13942. dlclose(Pointer(Module));
  13943. InvalidateModuleCache;
  13944. {$ENDIF}
  13945. end;
  13946. { GetPackageInfo }
  13947. procedure GetPackageInfo(Module: HMODULE; Param: Pointer; var Flags: Integer;
  13948. InfoProc: TPackageInfoProc);
  13949. var
  13950. InfoTable: PPackageInfoHeader;
  13951. I: Integer;
  13952. PkgName: PPkgName;
  13953. UName: PUnitName;
  13954. Count: Integer;
  13955. begin
  13956. InfoTable := PackageInfoTable(Module);
  13957. if not Assigned(InfoTable) then
  13958. raise EPackageError.CreateFmt(SCannotReadPackageInfo,
  13959. [ExtractFileName(GetModuleName(Module))]);
  13960. Flags := InfoTable.Flags;
  13961. with InfoTable^ do
  13962. begin
  13963. PkgName := PPkgName(Integer(InfoTable) + SizeOf(InfoTable^));
  13964. Count := RequiresCount;
  13965. for I := 0 to Count - 1 do
  13966. begin
  13967. InfoProc(PkgName.Name, ntRequiresPackage, 0, Param);
  13968. Inc(Integer(PkgName), StrLen(PkgName.Name) + 2);
  13969. end;
  13970. Count := Integer(Pointer(PkgName)^);
  13971. UName := PUnitName(Integer(PkgName) + 4);
  13972. for I := 0 to Count - 1 do
  13973. begin
  13974. InfoProc(UName.Name, ntContainsUnit, UName.Flags, Param);
  13975. Inc(Integer(UName), StrLen(UName.Name) + 3);
  13976. end;
  13977. if Flags and pfPackageModule <> 0 then
  13978. begin
  13979. PkgName := PPkgName(UName);
  13980. InfoProc(PkgName.Name, ntDcpBpiName, 0, Param);
  13981. end;
  13982. end;
  13983. end;
  13984. function GetPackageDescription(ModuleName: PChar): string;
  13985. var
  13986. ResModule: HMODULE;
  13987. ResInfo: HRSRC;
  13988. ResData: HGLOBAL;
  13989. {$IFDEF LINUX}
  13990. DLErrorMsg: string;
  13991. {$ENDIF}
  13992. begin
  13993. Result := '';
  13994. ResModule := LoadResourceModule(ModuleName);
  13995. if ResModule = 0 then
  13996. begin
  13997. {$IFDEF MSWINDOWS}
  13998. ResModule := LoadLibraryEx(ModuleName, 0, LOAD_LIBRARY_AS_DATAFILE);
  13999. {$ENDIF}
  14000. {$IFDEF LINUX}
  14001. ResModule := HMODULE(dlopen(ModuleName, RTLD_LAZY));
  14002. {$ENDIF}
  14003. if ResModule = 0 then
  14004. begin
  14005. {$IFDEF LINUX}
  14006. DLErrorMsg := dlerror;
  14007. {$ENDIF}
  14008. raise EPackageError.CreateResFmt(sErrorLoadingPackage,
  14009. [ModuleName,
  14010. {$IFDEF MSWINDOWS}SysErrorMessage(GetLastError){$ENDIF}
  14011. {$IFDEF LINUX}DLErrorMsg{$ENDIF}]);
  14012. end;
  14013. end;
  14014. try
  14015. ResInfo := FindResource(ResModule, 'DESCRIPTION', RT_RCDATA);
  14016. if ResInfo <> 0 then
  14017. begin
  14018. ResData := LoadResource(ResModule, ResInfo);
  14019. if ResData <> 0 then
  14020. try
  14021. Result := PWideChar(LockResource(ResData));
  14022. UnlockResource(ResData);
  14023. finally
  14024. FreeResource(ResData);
  14025. end;
  14026. end;
  14027. finally
  14028. {$IFDEF MSWINDOWS}
  14029. FreeLibrary(ResModule);
  14030. {$ENDIF}
  14031. {$IFDEF LINUX}
  14032. dlclose(Pointer(ResModule));
  14033. {$ENDIF}
  14034. end;
  14035. end;
  14036. procedure RaiseLastOSError;
  14037. begin
  14038. RaiseLastOSError(GetLastError);
  14039. end;
  14040. procedure RaiseLastOSError(LastError: Integer);
  14041. var
  14042. Error: EOSError;
  14043. begin
  14044. if LastError <> 0 then
  14045. Error := EOSError.CreateResFmt(SOSError, [LastError,
  14046. SysErrorMessage(LastError)])
  14047. else
  14048. Error := EOSError.CreateRes(SUnkOSError);
  14049. Error.ErrorCode := LastError;
  14050. raise Error;
  14051. end;
  14052. {$IFDEF MSWINDOWS}
  14053. { RaiseLastWin32Error }
  14054. procedure RaiseLastWin32Error;
  14055. begin
  14056. RaiseLastOSError;
  14057. end;
  14058. { Win32Check }
  14059. function Win32Check(RetVal: BOOL): BOOL;
  14060. begin
  14061. if not RetVal then RaiseLastOSError;
  14062. Result := RetVal;
  14063. end;
  14064. {$ENDIF}
  14065. type
  14066. PTerminateProcInfo = ^TTerminateProcInfo;
  14067. TTerminateProcInfo = record
  14068. Next: PTerminateProcInfo;
  14069. Proc: TTerminateProc;
  14070. end;
  14071. var
  14072. TerminateProcList: PTerminateProcInfo = nil;
  14073. procedure AddTerminateProc(TermProc: TTerminateProc);
  14074. var
  14075. P: PTerminateProcInfo;
  14076. begin
  14077. New(P);
  14078. P^.Next := TerminateProcList;
  14079. P^.Proc := TermProc;
  14080. TerminateProcList := P;
  14081. end;
  14082. function CallTerminateProcs: Boolean;
  14083. var
  14084. PI: PTerminateProcInfo;
  14085. begin
  14086. Result := True;
  14087. PI := TerminateProcList;
  14088. while Result and (PI <> nil) do
  14089. begin
  14090. Result := PI^.Proc;
  14091. PI := PI^.Next;
  14092. end;
  14093. end;
  14094. procedure FreeTerminateProcs;
  14095. var
  14096. PI: PTerminateProcInfo;
  14097. begin
  14098. while TerminateProcList <> nil do
  14099. begin
  14100. PI := TerminateProcList;
  14101. TerminateProcList := PI^.Next;
  14102. Dispose(PI);
  14103. end;
  14104. end;
  14105. { --- }
  14106. function AL1(const P): LongWord;
  14107. asm
  14108. MOV EDX,DWORD PTR [P]
  14109. XOR EDX,DWORD PTR [P+4]
  14110. XOR EDX,DWORD PTR [P+8]
  14111. XOR EDX,DWORD PTR [P+12]
  14112. MOV EAX,EDX
  14113. end;
  14114. function AL2(const P): LongWord;
  14115. asm
  14116. MOV EDX,DWORD PTR [P]
  14117. ROR EDX,5
  14118. XOR EDX,DWORD PTR [P+4]
  14119. ROR EDX,5
  14120. XOR EDX,DWORD PTR [P+8]
  14121. ROR EDX,5
  14122. XOR EDX,DWORD PTR [P+12]
  14123. MOV EAX,EDX
  14124. end;
  14125. const
  14126. AL1s: array[0..3] of LongWord = ($FFFFFFF0, $FFFFEBF0, 0, $FFFFFFFF);
  14127. AL2s: array[0..3] of LongWord = ($42C3ECEF, $20F7AEB6, $D1C2F74E, $3F6574DE);
  14128. procedure ALV;
  14129. begin
  14130. raise Exception.CreateRes(SNL);
  14131. end;
  14132. function ALR: Pointer;
  14133. var
  14134. LibModule: PLibModule;
  14135. begin
  14136. if MainInstance <> 0 then
  14137. Result := Pointer(LoadResource(MainInstance, FindResource(MainInstance, 'DVCLAL',
  14138. RT_RCDATA)))
  14139. else
  14140. begin
  14141. Result := nil;
  14142. LibModule := LibModuleList;
  14143. while LibModule <> nil do
  14144. begin
  14145. with LibModule^ do
  14146. begin
  14147. Result := Pointer(LoadResource(Instance, FindResource(Instance, 'DVCLAL',
  14148. RT_RCDATA)));
  14149. if Result <> nil then Break;
  14150. end;
  14151. LibModule := LibModule.Next;
  14152. end;
  14153. end;
  14154. end;
  14155. function GDAL: LongWord;
  14156. type
  14157. TDVCLAL = array[0..3] of LongWord;
  14158. PDVCLAL = ^TDVCLAL;
  14159. var
  14160. P: Pointer;
  14161. A1, A2: LongWord;
  14162. PAL1s, PAL2s: PDVCLAL;
  14163. ALOK: Boolean;
  14164. begin
  14165. P := ALR;
  14166. if P <> nil then
  14167. begin
  14168. A1 := AL1(P^);
  14169. A2 := AL2(P^);
  14170. Result := A1;
  14171. PAL1s := @AL1s;
  14172. PAL2s := @AL2s;
  14173. ALOK := ((A1 = PAL1s[0]) and (A2 = PAL2s[0])) or
  14174. ((A1 = PAL1s[1]) and (A2 = PAL2s[1])) or
  14175. ((A1 = PAL1s[2]) and (A2 = PAL2s[2]));
  14176. FreeResource(Integer(P));
  14177. if not ALOK then ALV;
  14178. end else Result := AL1s[3];
  14179. end;
  14180. procedure RCS;
  14181. var
  14182. P: Pointer;
  14183. ALOK: Boolean;
  14184. begin
  14185. P := ALR;
  14186. if P <> nil then
  14187. begin
  14188. ALOK := (AL1(P^) = AL1s[2]) and (AL2(P^) = AL2s[2]);
  14189. FreeResource(Integer(P));
  14190. end else ALOK := False;
  14191. if not ALOK then ALV;
  14192. end;
  14193. procedure RPR;
  14194. var
  14195. AL: LongWord;
  14196. begin
  14197. AL := GDAL;
  14198. if (AL <> AL1s[1]) and (AL <> AL1s[2]) then ALV;
  14199. end;
  14200. {$IFDEF MSWINDOWS}
  14201. procedure InitDriveSpacePtr;
  14202. var
  14203. Kernel: THandle;
  14204. begin
  14205. Kernel := GetModuleHandle(Windows.Kernel32);
  14206. if Kernel <> 0 then
  14207. @GetDiskFreeSpaceEx := GetProcAddress(Kernel, 'GetDiskFreeSpaceExA');
  14208. if not Assigned(GetDiskFreeSpaceEx) then
  14209. GetDiskFreeSpaceEx := @BackfillGetDiskFreeSpaceEx;
  14210. end;
  14211. {$ENDIF}
  14212. // Win95 does not return the actual value of the result.
  14213. // These implementations are consistent on all platforms.
  14214. function InterlockedIncrement(var I: Integer): Integer;
  14215. asm
  14216. MOV EDX,1
  14217. XCHG EAX,EDX
  14218. LOCK XADD [EDX],EAX
  14219. INC EAX
  14220. end;
  14221. function InterlockedDecrement(var I: Integer): Integer;
  14222. asm
  14223. MOV EDX,-1
  14224. XCHG EAX,EDX
  14225. LOCK XADD [EDX],EAX
  14226. DEC EAX
  14227. end;
  14228. function InterlockedExchange(var A: Integer; B: Integer): Integer;
  14229. asm
  14230. XCHG [EAX],EDX
  14231. MOV EAX,EDX
  14232. end;
  14233. // The InterlockedExchangeAdd Win32 API is not available on Win95.
  14234. function InterlockedExchangeAdd(var A: Integer; B: Integer): Integer;
  14235. asm
  14236. XCHG EAX,EDX
  14237. LOCK XADD [EDX],EAX
  14238. end;
  14239. { TSimpleRWSync }
  14240. constructor TSimpleRWSync.Create;
  14241. begin
  14242. inherited Create;
  14243. InitializeCriticalSection(FLock);
  14244. end;
  14245. destructor TSimpleRWSync.Destroy;
  14246. begin
  14247. inherited Destroy;
  14248. DeleteCriticalSection(FLock);
  14249. end;
  14250. function TSimpleRWSync.BeginWrite: Boolean;
  14251. begin
  14252. EnterCriticalSection(FLock);
  14253. Result := True;
  14254. end;
  14255. procedure TSimpleRWSync.EndWrite;
  14256. begin
  14257. LeaveCriticalSection(FLock);
  14258. end;
  14259. procedure TSimpleRWSync.BeginRead;
  14260. begin
  14261. EnterCriticalSection(FLock);
  14262. end;
  14263. procedure TSimpleRWSync.EndRead;
  14264. begin
  14265. LeaveCriticalSection(FLock);
  14266. end;
  14267. { TThreadLocalCounter }
  14268. const
  14269. Alive = High(Integer);
  14270. destructor TThreadLocalCounter.Destroy;
  14271. var
  14272. P, Q: PThreadInfo;
  14273. I: Integer;
  14274. begin
  14275. for I := 0 to High(FHashTable) do
  14276. begin
  14277. P := FHashTable[I];
  14278. FHashTable[I] := nil;
  14279. while P <> nil do
  14280. begin
  14281. Q := P;
  14282. P := P^.Next;
  14283. FreeMem(Q);
  14284. end;
  14285. end;
  14286. inherited Destroy;
  14287. end;
  14288. function TThreadLocalCounter.HashIndex: Byte;
  14289. var
  14290. H: Word;
  14291. begin
  14292. H := Word(GetCurrentThreadID);
  14293. Result := (WordRec(H).Lo xor WordRec(H).Hi) and 15;
  14294. end;
  14295. procedure TThreadLocalCounter.Open(var Thread: PThreadInfo);
  14296. var
  14297. P: PThreadInfo;
  14298. CurThread: Cardinal;
  14299. H: Byte;
  14300. begin
  14301. H := HashIndex;
  14302. CurThread := GetCurrentThreadID;
  14303. P := FHashTable[H];
  14304. while (P <> nil) and (P.ThreadID <> CurThread) do
  14305. P := P.Next;
  14306. if P = nil then
  14307. begin
  14308. P := Recycle;
  14309. if P = nil then
  14310. begin
  14311. P := PThreadInfo(AllocMem(sizeof(TThreadInfo)));
  14312. P.ThreadID := CurThread;
  14313. P.Active := Alive;
  14314. // Another thread could start traversing the list between when we set the
  14315. // head to P and when we assign to P.Next. Initializing P.Next to point
  14316. // to itself will make others spin until we assign the tail to P.Next.
  14317. P.Next := P;
  14318. P.Next := PThreadInfo(InterlockedExchange(Integer(FHashTable[H]), Integer(P)));
  14319. end;
  14320. end;
  14321. Thread := P;
  14322. end;
  14323. procedure TThreadLocalCounter.Close(var Thread: PThreadInfo);
  14324. begin
  14325. Thread := nil;
  14326. end;
  14327. procedure TThreadLocalCounter.Delete(var Thread: PThreadInfo);
  14328. begin
  14329. Thread.ThreadID := 0;
  14330. Thread.Active := 0;
  14331. end;
  14332. function TThreadLocalCounter.Recycle: PThreadInfo;
  14333. var
  14334. Gen: Integer;
  14335. begin
  14336. Result := FHashTable[HashIndex];
  14337. while (Result <> nil) do
  14338. begin
  14339. Gen := InterlockedExchange(Result.Active, Alive);
  14340. if Gen <> Alive then
  14341. begin
  14342. Result.ThreadID := GetCurrentThreadID;
  14343. Exit;
  14344. end
  14345. else
  14346. Result := Result.Next;
  14347. end;
  14348. end;
  14349. {$IFDEF MSWINDOWS}
  14350. { TMultiReadExclusiveWriteSynchronizer }
  14351. const
  14352. mrWriteRequest = $FFFF; // 65535 concurrent read requests (threads)
  14353. // 32768 concurrent write requests (threads)
  14354. // only one write lock at a time
  14355. // 2^32 lock recursions per thread (read and write combined)
  14356. constructor TMultiReadExclusiveWriteSynchronizer.Create;
  14357. begin
  14358. inherited Create;
  14359. FSentinel := mrWriteRequest;
  14360. FReadSignal := CreateEvent(nil, True, True, nil); // manual reset, start signaled
  14361. FWriteSignal := CreateEvent(nil, False, False, nil); // auto reset, start blocked
  14362. FWaitRecycle := INFINITE;
  14363. tls := TThreadLocalCounter.Create;
  14364. end;
  14365. destructor TMultiReadExclusiveWriteSynchronizer.Destroy;
  14366. begin
  14367. BeginWrite;
  14368. inherited Destroy;
  14369. CloseHandle(FReadSignal);
  14370. CloseHandle(FWriteSignal);
  14371. tls.Free;
  14372. end;
  14373. procedure TMultiReadExclusiveWriteSynchronizer.BlockReaders;
  14374. begin
  14375. ResetEvent(FReadSignal);
  14376. end;
  14377. procedure TMultiReadExclusiveWriteSynchronizer.UnblockReaders;
  14378. begin
  14379. SetEvent(FReadSignal);
  14380. end;
  14381. procedure TMultiReadExclusiveWriteSynchronizer.UnblockOneWriter;
  14382. begin
  14383. SetEvent(FWriteSignal);
  14384. end;
  14385. procedure TMultiReadExclusiveWriteSynchronizer.WaitForReadSignal;
  14386. begin
  14387. WaitForSingleObject(FReadSignal, FWaitRecycle);
  14388. end;
  14389. procedure TMultiReadExclusiveWriteSynchronizer.WaitForWriteSignal;
  14390. begin
  14391. WaitForSingleObject(FWriteSignal, FWaitRecycle);
  14392. end;
  14393. {$IFDEF DEBUG_MREWS}
  14394. var
  14395. x: Integer;
  14396. procedure TMultiReadExclusiveWriteSynchronizer.Debug(const Msg: string);
  14397. begin
  14398. OutputDebugString(PChar(Format('%d %s Thread=%x Sentinel=%d, FWriterID=%x',
  14399. [InterlockedIncrement(x), Msg, GetCurrentThreadID, FSentinel, FWriterID])));
  14400. end;
  14401. {$ENDIF}
  14402. function TMultiReadExclusiveWriteSynchronizer.BeginWrite: Boolean;
  14403. var
  14404. Thread: PThreadInfo;
  14405. HasReadLock: Boolean;
  14406. ThreadID: Cardinal;
  14407. Test: Integer;
  14408. OldRevisionLevel: Cardinal;
  14409. begin
  14410. {
  14411. States of FSentinel (roughly - during inc/dec's, the states may not be exactly what is said here):
  14412. mrWriteRequest: A reader or a writer can get the lock
  14413. 1 - (mrWriteRequest-1): A reader (possibly more than one) has the lock
  14414. 0: A writer (possibly) just got the lock, if returned from the main write While loop
  14415. < 0, but not a multiple of mrWriteRequest: Writer(s) want the lock, but reader(s) have it.
  14416. New readers should be blocked, but current readers should be able to call BeginRead
  14417. < 0, but a multiple of mrWriteRequest: Writer(s) waiting for a writer to finish
  14418. }
  14419. {$IFDEF DEBUG_MREWS}
  14420. Debug('Write enter------------------------------------');
  14421. {$ENDIF}
  14422. Result := True;
  14423. ThreadID := GetCurrentThreadID;
  14424. if FWriterID <> ThreadID then // somebody or nobody has a write lock
  14425. begin
  14426. // Prevent new readers from entering while we wait for the existing readers
  14427. // to exit.
  14428. BlockReaders;
  14429. OldRevisionLevel := FRevisionLevel;
  14430. tls.Open(Thread);
  14431. // We have another lock already. It must be a read lock, because if it
  14432. // were a write lock, FWriterID would be our threadid.
  14433. HasReadLock := Thread.RecursionCount > 0;
  14434. if HasReadLock then // acquiring a write lock requires releasing read locks
  14435. InterlockedIncrement(FSentinel);
  14436. {$IFDEF DEBUG_MREWS}
  14437. Debug('Write before loop');
  14438. {$ENDIF}
  14439. // InterlockedExchangeAdd returns prev value
  14440. while InterlockedExchangeAdd(FSentinel, -mrWriteRequest) <> mrWriteRequest do
  14441. begin
  14442. {$IFDEF DEBUG_MREWS}
  14443. Debug('Write loop');
  14444. Sleep(1000); // sleep to force / debug race condition
  14445. Debug('Write loop2a');
  14446. {$ENDIF}
  14447. // Undo what we did, since we didn't get the lock
  14448. Test := InterlockedExchangeAdd(FSentinel, mrWriteRequest);
  14449. // If the old value (in Test) was 0, then we may be able to
  14450. // get the lock (because it will now be mrWriteRequest). So,
  14451. // we continue the loop to find out. Otherwise, we go to sleep,
  14452. // waiting for a reader or writer to signal us.
  14453. if Test <> 0 then
  14454. begin
  14455. {$IFDEF DEBUG_MREWS}
  14456. Debug('Write starting to wait');
  14457. {$ENDIF}
  14458. WaitForWriteSignal;
  14459. end
  14460. {$IFDEF DEBUG_MREWS}
  14461. else
  14462. Debug('Write continue')
  14463. {$ENDIF}
  14464. end;
  14465. // At the EndWrite, first Writers are awoken, and then Readers are awoken.
  14466. // If a Writer got the lock, we don't want the readers to do busy
  14467. // waiting. This Block resets the event in case the situation happened.
  14468. BlockReaders;
  14469. // Put our read lock marker back before we lose track of it
  14470. if HasReadLock then
  14471. InterlockedDecrement(FSentinel);
  14472. FWriterID := ThreadID;
  14473. Result := Integer(OldRevisionLevel) = (InterlockedIncrement(Integer(FRevisionLevel)) - 1);
  14474. end;
  14475. Inc(FWriteRecursionCount);
  14476. {$IFDEF DEBUG_MREWS}
  14477. Debug('Write lock-----------------------------------');
  14478. {$ENDIF}
  14479. end;
  14480. procedure TMultiReadExclusiveWriteSynchronizer.EndWrite;
  14481. var
  14482. Thread: PThreadInfo;
  14483. begin
  14484. {$IFDEF DEBUG_MREWS}
  14485. Debug('Write end');
  14486. {$ENDIF}
  14487. assert(FWriterID = GetCurrentThreadID);
  14488. tls.Open(Thread);
  14489. Dec(FWriteRecursionCount);
  14490. if FWriteRecursionCount = 0 then
  14491. begin
  14492. FWriterID := 0;
  14493. InterlockedExchangeAdd(FSentinel, mrWriteRequest);
  14494. {$IFDEF DEBUG_MREWS}
  14495. Debug('Write about to UnblockOneWriter');
  14496. {$ENDIF}
  14497. UnblockOneWriter;
  14498. {$IFDEF DEBUG_MREWS}
  14499. Debug('Write about to UnblockReaders');
  14500. {$ENDIF}
  14501. UnblockReaders;
  14502. end;
  14503. if Thread.RecursionCount = 0 then
  14504. tls.Delete(Thread);
  14505. {$IFDEF DEBUG_MREWS}
  14506. Debug('Write unlock');
  14507. {$ENDIF}
  14508. end;
  14509. procedure TMultiReadExclusiveWriteSynchronizer.BeginRead;
  14510. var
  14511. Thread: PThreadInfo;
  14512. WasRecursive: Boolean;
  14513. SentValue: Integer;
  14514. begin
  14515. {$IFDEF DEBUG_MREWS}
  14516. Debug('Read enter');
  14517. {$ENDIF}
  14518. tls.Open(Thread);
  14519. Inc(Thread.RecursionCount);
  14520. WasRecursive := Thread.RecursionCount > 1;
  14521. if FWriterID <> GetCurrentThreadID then
  14522. begin
  14523. {$IFDEF DEBUG_MREWS}
  14524. Debug('Trying to get the ReadLock (we did not have a write lock)');
  14525. {$ENDIF}
  14526. // In order to prevent recursive Reads from causing deadlock,
  14527. // we need to always WaitForReadSignal if not recursive.
  14528. // This prevents unnecessarily decrementing the FSentinel, and
  14529. // then immediately incrementing it again.
  14530. if not WasRecursive then
  14531. begin
  14532. // Make sure we don't starve writers. A writer will
  14533. // always set the read signal when it is done, and it is initially on.
  14534. WaitForReadSignal;
  14535. while (InterlockedDecrement(FSentinel) <= 0) do
  14536. begin
  14537. {$IFDEF DEBUG_MREWS}
  14538. Debug('Read loop');
  14539. {$ENDIF}
  14540. // Because the InterlockedDecrement happened, it is possible that
  14541. // other threads "think" we have the read lock,
  14542. // even though we really don't. If we are the last reader to do this,
  14543. // then SentValue will become mrWriteRequest
  14544. SentValue := InterlockedIncrement(FSentinel);
  14545. // So, if we did inc it to mrWriteRequest at this point,
  14546. // we need to signal the writer.
  14547. if SentValue = mrWriteRequest then
  14548. UnblockOneWriter;
  14549. // This sleep below prevents starvation of writers
  14550. Sleep(0);
  14551. {$IFDEF DEBUG_MREWS}
  14552. Debug('Read loop2 - waiting to be signaled');
  14553. {$ENDIF}
  14554. WaitForReadSignal;
  14555. {$IFDEF DEBUG_MREWS}
  14556. Debug('Read signaled');
  14557. {$ENDIF}
  14558. end;
  14559. end;
  14560. end;
  14561. {$IFDEF DEBUG_MREWS}
  14562. Debug('Read lock');
  14563. {$ENDIF}
  14564. end;
  14565. procedure TMultiReadExclusiveWriteSynchronizer.EndRead;
  14566. var
  14567. Thread: PThreadInfo;
  14568. Test: Integer;
  14569. begin
  14570. {$IFDEF DEBUG_MREWS}
  14571. Debug('Read end');
  14572. {$ENDIF}
  14573. tls.Open(Thread);
  14574. Dec(Thread.RecursionCount);
  14575. if (Thread.RecursionCount = 0) then
  14576. begin
  14577. tls.Delete(Thread);
  14578. // original code below commented out
  14579. if (FWriterID <> GetCurrentThreadID) then
  14580. begin
  14581. Test := InterlockedIncrement(FSentinel);
  14582. // It is possible for Test to be mrWriteRequest
  14583. // or, it can be = 0, if the write loops:
  14584. // Test := InterlockedExchangeAdd(FSentinel, mrWriteRequest) + mrWriteRequest;
  14585. // Did not get executed before this has called (the sleep debug makes it happen faster)
  14586. {$IFDEF DEBUG_MREWS}
  14587. Debug(Format('Read UnblockOneWriter may be called. Test=%d', [Test]));
  14588. {$ENDIF}
  14589. if Test = mrWriteRequest then
  14590. UnblockOneWriter
  14591. else if Test <= 0 then // We may have some writers waiting
  14592. begin
  14593. if (Test mod mrWriteRequest) = 0 then
  14594. UnblockOneWriter; // No more readers left (only writers) so signal one of them
  14595. end;
  14596. end;
  14597. end;
  14598. {$IFDEF DEBUG_MREWS}
  14599. Debug('Read unlock');
  14600. {$ENDIF}
  14601. end;
  14602. {$ENDIF} //MSWINDOWS for TMultiReadExclusiveWriteSynchronizer
  14603. procedure FreeAndNil(var Obj);
  14604. var
  14605. Temp: TObject;
  14606. begin
  14607. Temp := TObject(Obj);
  14608. Pointer(Obj) := nil;
  14609. Temp.Free;
  14610. end;
  14611. { Interface support routines }
  14612. function Supports(const Instance: IInterface; const IID: TGUID; out Intf): Boolean;
  14613. begin
  14614. Result := (Instance <> nil) and (Instance.QueryInterface(IID, Intf) = 0);
  14615. end;
  14616. function Supports(const Instance: TObject; const IID: TGUID; out Intf): Boolean;
  14617. var
  14618. LUnknown: IUnknown;
  14619. begin
  14620. Result := (Instance <> nil) and
  14621. ((Instance.GetInterface(IUnknown, LUnknown) and Supports(LUnknown, IID, Intf)) or
  14622. Instance.GetInterface(IID, Intf));
  14623. end;
  14624. function Supports(const Instance: IInterface; const IID: TGUID): Boolean;
  14625. var
  14626. Temp: IInterface;
  14627. begin
  14628. Result := Supports(Instance, IID, Temp);
  14629. end;
  14630. function Supports(const Instance: TObject; const IID: TGUID): Boolean;
  14631. var
  14632. Temp: IInterface;
  14633. begin
  14634. Result := Supports(Instance, IID, Temp);
  14635. end;
  14636. function Supports(const AClass: TClass; const IID: TGUID): Boolean;
  14637. begin
  14638. Result := AClass.GetInterfaceEntry(IID) <> nil;
  14639. end;
  14640. {$IFDEF MSWINDOWS}
  14641. { TLanguages }
  14642. var
  14643. FTempLanguages: TLanguages;
  14644. function EnumLocalesCallback(LocaleID: PChar): Integer; stdcall;
  14645. begin
  14646. Result := FTempLanguages.LocalesCallback(LocaleID);
  14647. end;
  14648. { Query the OS for information for a specified locale. Unicode version. Works correctly on Asian WinNT. }
  14649. function GetLocaleDataW(ID: LCID; Flag: DWORD): string;
  14650. var
  14651. Buffer: array[0..1023] of WideChar;
  14652. begin
  14653. Buffer[0] := #0;
  14654. GetLocaleInfoW(ID, Flag, Buffer, SizeOf(Buffer) div 2);
  14655. Result := Buffer;
  14656. end;
  14657. { Query the OS for information for a specified locale. ANSI Version. Works correctly on Asian Win95. }
  14658. function GetLocaleDataA(ID: LCID; Flag: DWORD): string;
  14659. var
  14660. Buffer: array[0..1023] of Char;
  14661. begin
  14662. Buffer[0] := #0;
  14663. SetString(Result, Buffer, GetLocaleInfoA(ID, Flag, Buffer, SizeOf(Buffer)) - 1);
  14664. end;
  14665. { Called for each supported locale. }
  14666. function TLanguages.LocalesCallback(LocaleID: PChar): Integer; stdcall;
  14667. var
  14668. AID: LCID;
  14669. ShortLangName: string;
  14670. GetLocaleDataProc: function (ID: LCID; Flag: DWORD): string;
  14671. begin
  14672. if Win32Platform = VER_PLATFORM_WIN32_NT then
  14673. GetLocaleDataProc := @GetLocaleDataW
  14674. else
  14675. GetLocaleDataProc := @GetLocaleDataA;
  14676. AID := StrToInt('$' + Copy(LocaleID, 5, 4));
  14677. ShortLangName := GetLocaleDataProc(AID, LOCALE_SABBREVLANGNAME);
  14678. if ShortLangName <> '' then
  14679. begin
  14680. SetLength(FSysLangs, Length(FSysLangs) + 1);
  14681. with FSysLangs[High(FSysLangs)] do
  14682. begin
  14683. FName := GetLocaleDataProc(AID, LOCALE_SLANGUAGE);
  14684. FLCID := AID;
  14685. FExt := ShortLangName;
  14686. end;
  14687. end;
  14688. Result := 1;
  14689. end;
  14690. constructor TLanguages.Create;
  14691. begin
  14692. inherited Create;
  14693. FTempLanguages := Self;
  14694. EnumSystemLocales(@EnumLocalesCallback, LCID_SUPPORTED);
  14695. end;
  14696. function TLanguages.GetCount: Integer;
  14697. begin
  14698. Result := High(FSysLangs) + 1;
  14699. end;
  14700. function TLanguages.GetExt(Index: Integer): string;
  14701. begin
  14702. Result := FSysLangs[Index].FExt;
  14703. end;
  14704. function TLanguages.GetID(Index: Integer): string;
  14705. begin
  14706. Result := HexDisplayPrefix + IntToHex(FSysLangs[Index].FLCID, 8);
  14707. end;
  14708. function TLanguages.GetLCID(Index: Integer): LCID;
  14709. begin
  14710. Result := FSysLangs[Index].FLCID;
  14711. end;
  14712. function TLanguages.GetName(Index: Integer): string;
  14713. begin
  14714. Result := FSysLangs[Index].FName;
  14715. end;
  14716. function TLanguages.GetNameFromLocaleID(ID: LCID): string;
  14717. var
  14718. Index: Integer;
  14719. begin
  14720. Result := sUnknown;
  14721. Index := IndexOf(ID);
  14722. if Index <> - 1 then Result := Name[Index];
  14723. if Result = '' then Result := sUnknown;
  14724. end;
  14725. function TLanguages.GetNameFromLCID(const ID: string): string;
  14726. begin
  14727. Result := NameFromLocaleID[StrToIntDef(ID, 0)];
  14728. end;
  14729. function TLanguages.IndexOf(ID: LCID): Integer;
  14730. begin
  14731. for Result := Low(FSysLangs) to High(FSysLangs) do
  14732. if FSysLangs[Result].FLCID = ID then Exit;
  14733. Result := -1;
  14734. end;
  14735. var
  14736. FLanguages: TLanguages;
  14737. function Languages: TLanguages;
  14738. begin
  14739. if FLanguages = nil then
  14740. FLanguages := TLanguages.Create;
  14741. Result := FLanguages;
  14742. end;
  14743. function SafeLoadLibrary(const Filename: string; ErrorMode: UINT): HMODULE;
  14744. var
  14745. OldMode: UINT;
  14746. FPUControlWord: Word;
  14747. begin
  14748. OldMode := SetErrorMode(ErrorMode);
  14749. try
  14750. asm
  14751. FNSTCW FPUControlWord
  14752. end;
  14753. try
  14754. Result := LoadLibrary(PChar(Filename));
  14755. finally
  14756. asm
  14757. FNCLEX
  14758. FLDCW FPUControlWord
  14759. end;
  14760. end;
  14761. finally
  14762. SetErrorMode(OldMode);
  14763. end;
  14764. end;
  14765. {$ENDIF}
  14766. {$IFDEF LINUX}
  14767. function SafeLoadLibrary(const FileName: string; Dummy: LongWord): HMODULE;
  14768. var
  14769. FPUControlWord: Word;
  14770. begin
  14771. asm
  14772. FNSTCW FPUControlWord
  14773. end;
  14774. try
  14775. Result := LoadLibrary(PChar(Filename));
  14776. finally
  14777. asm
  14778. FNCLEX
  14779. FLDCW FPUControlWord
  14780. end;
  14781. end;
  14782. end;
  14783. {$ENDIF}
  14784. {$IFDEF MSWINDOWS}
  14785. function GetEnvironmentVariable(const Name: string): string;
  14786. const
  14787. BufSize = 1024;
  14788. var
  14789. Len: Integer;
  14790. Buffer: array[0..BufSize - 1] of Char;
  14791. begin
  14792. Result := '';
  14793. Len := Windows.GetEnvironmentVariable(PChar(Name), @Buffer, BufSize);
  14794. if Len < BufSize then
  14795. SetString(Result, PChar(@Buffer), Len)
  14796. else
  14797. begin
  14798. SetLength(Result, Len - 1);
  14799. Windows.GetEnvironmentVariable(PChar(Name), PChar(Result), Len);
  14800. end;
  14801. end;
  14802. {$ENDIF}
  14803. {$IFDEF LINUX}
  14804. function GetEnvironmentVariable(const Name: string): string;
  14805. begin
  14806. Result := getenv(PChar(Name));
  14807. end;
  14808. {$ENDIF}
  14809. {$IFDEF LINUX}
  14810. procedure CheckLocale;
  14811. var
  14812. P,Q: PChar;
  14813. begin
  14814. P := gnu_get_libc_version();
  14815. Q := getenv('LC_ALL');
  14816. if (Q = nil) or (Q[0] = #0) then
  14817. Q := getenv('LANG');
  14818. // 2.1.3 <= current version < 2.1.91
  14819. if (strverscmp('2.1.3', P) <= 0) and
  14820. (strverscmp(P, '2.1.91') < 0) and
  14821. ((Q = nil) or (Q[0] = #0)) then
  14822. begin
  14823. // GNU libc 2.1.3 will segfault in towupper() if environment variables don't
  14824. // specify a locale. This can happen when Apache launches CGI subprocesses.
  14825. // Solution: set a locale if the environment variable is missing.
  14826. // Works in 2.1.2, fixed in glibc 2.1.91 and later
  14827. setlocale(LC_ALL, 'POSIX');
  14828. end
  14829. else
  14830. // Configure the process locale settings according to
  14831. // the system environment variables (LC_CTYPE, LC_COLLATE, etc)
  14832. setlocale(LC_ALL, '');
  14833. // Note:
  14834. // POSIX/C is the default locale on many Unix systems, but its 7-bit charset
  14835. // causes char to widechar conversions to fail on any high-ascii
  14836. // character. To support high-ascii charset conversions, set the
  14837. // LC_CTYPE environment variable to something else or call setlocale to set
  14838. // the LC_CTYPE information for this process. It doesn't matter what
  14839. // you set it to, as long as it's not POSIX.
  14840. if StrComp(nl_langinfo(_NL_CTYPE_CODESET_NAME), 'ANSI_X3.4-1968') = 0 then
  14841. setlocale(LC_CTYPE, 'en_US'); // selects codepage ISO-8859-1
  14842. end;
  14843. procedure PropagateSignals;
  14844. var
  14845. Exc: TObject;
  14846. begin
  14847. {
  14848. If there is a current exception pending, then we're shutting down because
  14849. it went unhandled. If that exception is the result of a signal, then we
  14850. need to propagate that back out to the world as a real signal death. See
  14851. the discussion at http://www2.cons.org/cracauer/sigint.html for more info.
  14852. }
  14853. Exc := ExceptObject;
  14854. if (Exc <> nil) and (Exc is EExternal) then
  14855. kill(getpid, EExternal(Exc).SignalNumber);
  14856. end;
  14857. {
  14858. Under Win32, SafeCallError is implemented in ComObj. Under Linux, we
  14859. don't have ComObj, so we've substituted a similar mechanism here.
  14860. }
  14861. procedure SafeCallError(ErrorCode: Integer; ErrorAddr: Pointer);
  14862. var
  14863. ExcMsg: String;
  14864. begin
  14865. ExcMsg := GetSafeCallExceptionMsg;
  14866. SetSafeCallExceptionMsg('');
  14867. if ExcMsg <> '' then
  14868. begin
  14869. raise ESafeCallException.Create(ExcMsg) at GetSafeCallExceptionAddr;
  14870. end
  14871. else
  14872. raise ESafeCallException.CreateRes(@SSafecallException);
  14873. end;
  14874. {$ENDIF}
  14875. initialization
  14876. if ModuleIsCpp then HexDisplayPrefix := '0x';
  14877. InitExceptions;
  14878. {$IFDEF LINUX}
  14879. SafeCallErrorProc := @SafeCallError;
  14880. ExitProcessProc := PropagateSignals;
  14881. CheckLocale;
  14882. {$ENDIF}
  14883. {$IFDEF MSWINDOWS}
  14884. InitPlatformId;
  14885. InitDriveSpacePtr;
  14886. {$ENDIF}
  14887. GetFormatSettings; { Win implementation uses platform id }
  14888. finalization
  14889. {$IFDEF MSWINDOWS}
  14890. FreeAndNil(FLanguages);
  14891. {$ENDIF}
  14892. {$IFDEF LINUX}
  14893. if libuuidHandle <> nil then
  14894. dlclose(libuuidHandle);
  14895. {$ENDIF}
  14896. FreeTerminateProcs;
  14897. DoneExceptions;
  14898. end.