home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / sp15demo.zip / libsrc.zip / LIBSRC / SYSUTILS.PAS < prev    next >
Pascal/Delphi Source File  |  1996-02-24  |  90KB  |  3,455 lines

  1. {*******************************************************}
  2. {                                                       }
  3. { Speed-Pascal Runtime Library                          }
  4. { System Utilities Unit (Delphi compatible)             }
  5. {                                                       }
  6. { Ported 1995 by Joerg Pleumann                         }
  7. {                                                       }
  8. { Mail all bugs and suggestions to:                     }
  9. {                                                       }
  10. { Internet: sa021pl@unidui.uni-duisburg.de              }
  11. { FidoNet:  Joerg Pleumann@2:2448/136.6                 }
  12. {                                                       }
  13. {                                                       }
  14. {*******************************************************}
  15.  
  16. {$define PM} { Without this switch the compiler
  17.                uses VIO calls instead of PM ones. }
  18.  
  19. unit SysUtils;
  20.  
  21. interface
  22.  
  23. uses
  24.   BseDos, BseErr, OS2Def;
  25.  
  26. type
  27.  
  28.   { System-dependent integer types - belongs into SYSTEM }
  29.   Cardinal = LongWord;
  30.  
  31. const
  32.  
  33.   vtInteger  = 0;
  34.   vtBoolean  = 1;
  35.   vtChar     = 2;
  36.   vtExtended = 3;
  37.   vtString   = 4;
  38.   vtPointer  = 5;
  39.   vtPChar    = 6;
  40.   vtObject   = 7;
  41.   vtClass    = 8;
  42.  
  43. type
  44.   PExtended = ^Extended;
  45.  
  46. type
  47.   TVarRec = record
  48.     case Integer of
  49.       vtInteger:  (VInteger: LongInt; VType: LongInt);
  50.       vtBoolean:  (VBoolean: Boolean);
  51.       vtChar:     (VChar: Char);
  52.       vtExtended: (VExtended: PExtended);
  53.       vtString:   (VString: PString);
  54.       vtPointer:  (VPointer: Pointer);
  55.       vtPChar:    (VPChar: PChar);
  56.       vtObject:   (VObject: TObject);
  57.       vtClass:    (VClass: TClass);
  58.   end;
  59.  
  60. const
  61.  
  62.  { File open modes }
  63.  
  64.   {$IFDEF OS2}
  65.   fmOpenRead       = $0000;
  66.   fmOpenWrite      = $0001;
  67.   fmOpenReadWrite  = $0002;
  68.   fmShareCompat    = $0000;
  69.   fmShareExclusive = $0010;
  70.   fmShareDenyWrite = $0020;
  71.   fmShareDenyRead  = $0030;
  72.   fmShareDenyNone  = $0040;
  73.   {$ENDIF}
  74.   {$IFDEF Win95}
  75.   fmOpenRead       = $80000000;
  76.   fmOpenWrite      = $40000000;
  77.   fmOpenReadWrite  = $C0000000;
  78.   fmShareCompat    = $00000003;
  79.   fmShareExclusive = $00000000;
  80.   fmShareDenyWrite = $00000001;
  81.   fmShareDenyRead  = $00000002;
  82.   fmShareDenyNone  = $00000003;
  83.   {$ENDIF}
  84.  
  85.   { File attribute constants - no faVolumeID since there is no such constant in OS/2! }
  86.  
  87.   faReadOnly       = $0001;
  88.   faHidden         = $0002;
  89.   faSysFile        = $0004;
  90.   faDirectory      = $0010;
  91.   faArchive        = $0020;
  92.  
  93.   faAnyFile        = faReadOnly or faHidden or faSysFile or faDirectory or faArchive;
  94.  
  95.   { OS/2-specific file attribute constants for searching files }
  96.  
  97.   faMustReadOnly   = $0100;
  98.   faMustHidden     = $0200;
  99.   faMustSysFile    = $0400;
  100.   faMustDirectory  = $1000;
  101.   faMustArchive    = $2000;
  102.  
  103.   SecsPerDay = 24 * 60 * 60;
  104.   MSecsPerDay = SecsPerDay * 1000;
  105.  
  106. type
  107.  
  108.   { Date/Time data structure (definition missing in Delphi) }
  109.  
  110.   TDateTime = Extended;
  111.  
  112.   { Type conversion records }
  113.  
  114.   WordRec = record
  115.     Lo, Hi: Byte;
  116.   end;
  117.  
  118.   LongRec = record
  119.     Lo, Hi: Word;
  120.   end;
  121.  
  122.   TMethod = record
  123.     Code, Data: Pointer;
  124.   end;
  125.  
  126.   {Useful arrays }
  127.  
  128.   PByteArray = ^TByteArray;
  129.   TByteArray = array[0..MaxLongInt] of Byte;
  130.  
  131.   PWordArray = ^TWordArray;
  132.   TWordArray = array[0..MaxLongInt div 2] of Word;
  133.  
  134.   { Generic procedure pointer }
  135.  
  136.   TProcedure = procedure;
  137.  
  138.   { Generic filename type }
  139.  
  140.   TFileName = string;
  141.  
  142.   { Search record used by FindFirst, FindNext, and FindClose }
  143.  
  144.   TSearchRec = record
  145.     HDir: LongInt;
  146.     Attr: Byte;
  147.     Time: Longint;
  148.     Size: Longint;
  149.     Name: string;
  150.   end;
  151.  
  152.   { Typed-file and untyped-file record - removed. Use 'FileRec' from SYSTEM }
  153.  
  154.   { FloatToText format codes }
  155.  
  156.   TFloatFormat = (ffGeneral, ffExponent, ffFixed, ffNumber, ffCurrency);
  157.  
  158.   { FloatToDecimal result record }
  159.  
  160.   TFloatRec = record
  161.     Exponent: Integer;
  162.     Negative: Boolean;
  163.     Digits: array[0..18] of Char;
  164.   end;
  165.  
  166.   { Exception support missing here, already present in SYSTEM }
  167.  
  168. const
  169.   EmptyStr: string[1] = '';
  170.   NullStr: PString = @EmptyStr;
  171.  
  172. { Currency and date/time formatting options }
  173.  
  174. var
  175.   CurrencyString: string[7];
  176.   CurrencyFormat: Byte;
  177.   NegCurrFormat: Byte; // not available under OS/2, synthesized :-)
  178.   ThousandSeparator: Char;
  179.   DecimalSeparator: Char;
  180.   CurrencyDecimals: Byte;
  181.   DateSeparator: Char;
  182.   ShortDateFormat: string[15];
  183.   LongDateFormat: string[31];
  184.   TimeSeparator: Char;
  185.  
  186.   ListSeparator: Char;
  187.  
  188.   DateOrder: Byte;
  189.   TwelveHours: Boolean;
  190.  
  191. const
  192.  
  193.   TimeAMString: string[7] = 'am';
  194.   TimePMString: string[7] = 'pm';
  195.  
  196. var
  197.  
  198.   ShortTimeFormat: string[15];
  199.   LongTimeFormat: string[31];
  200.  
  201. const
  202.  
  203.   ShortMonthNames: array[1..12] of string[7] =
  204.     ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
  205.      'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
  206.   LongMonthNames: array[1..12] of string[15] =
  207.     ('January', 'February', 'March',     'April',   'May',      'June',
  208.      'July',    'August',   'September', 'October', 'November', 'December');
  209.   ShortDayNames: array[1..7] of string[7] =
  210.     ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
  211.   LongDayNames: array[1..7] of string[15] =
  212.     ('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday');
  213.  
  214. { Memory management routines }
  215.  
  216. function AllocMem(Size: Cardinal): Pointer;
  217. function ReAllocMem(P: Pointer; CurSize, NewSize: Cardinal): Pointer;
  218.  
  219. { Exit procedure handling }
  220. procedure AddExitProc(Proc: TProcedure);
  221. procedure CallExitProcs;
  222.  
  223. { String handling routines }
  224.  
  225. function NewStr(const S: String): PString;
  226. procedure DisposeStr(P: PString);
  227. procedure AssignStr(var P: PString; const S: string);
  228. procedure AppendStr(var Dest: string; const S: string);
  229. function UpperCase(const S: string): string;
  230. function LowerCase(const S: string): string;
  231. function CompareStr(const S1, S2: string): Integer;
  232. function CompareText(const S1, S2: string): Integer;
  233. function AnsiUpperCase(const S: string): string;
  234. {function AnsiLowerCase(const S: string): string;} // Not supported by OS/2
  235. {function AnsiCompareStr(const S1, S2: string): Integer;} // Not supported by OS/2
  236. function AnsiCompareText(const S1, S2: string): Integer;
  237. function IsValidIdent(const Ident: string): Boolean;
  238. function IntToStr(Value: LongInt): string;
  239. function IntToHex(Value: LongInt; Digits: Integer): string;
  240. function StrToInt(const S: string): LongInt;
  241. function StrToIntDef(const S: string; Default: LongInt): LongInt;
  242. function LoadStr(Ident: Word): string;
  243. function FmtLoadStr(Ident: Word; const Args: array of const): string;
  244.  
  245. { NEW NEW NEW SetLength changes the length of a string }
  246. procedure SetLength(var S: string; NewLength: Byte);
  247.  
  248. { File management routines }
  249.  
  250. function FileOpen(const FileName: string; Mode: Word): LongInt;
  251. function FileCreate(const FileName: string): LongInt;
  252. function FileOpenOrCreate(const FileName: string; Mode: Word): LongInt;
  253. function FileCreateIfNew(const FileName: string; Mode: Word): LongInt;
  254. function FileRead(Handle: LongInt; var Buffer; Count: Longint): Longint;
  255. function FileWrite(Handle: LongInt; var {const} Buffer; Count: LongInt): LongInt;
  256. function FileSeek(Handle: LongInt; Offset: LongInt; Origin: Integer): LongInt;
  257. procedure FileClose(Handle: LongInt);
  258. function FileAge(const FileName: string): Longint;
  259. function FileExists(const FileName: string): Boolean;
  260. function FindFirst(const Path: string; Attr: Integer; var SearchRec: TSearchRec): LongInt;
  261. function FindNext(var SearchRec: TSearchRec): LongInt;
  262. procedure FindClose(var SearchRec: TSearchRec);
  263. function FileGetDate(Handle: LongInt): Longint;
  264. procedure FileSetDate(Handle: Integer; Age: Longint);
  265. function FileGetAttr(const FileName: string): LongInt;
  266. function FileSetAttr(const FileName: string; Attr: Integer): Integer;
  267. function CopyFile(const SourceName, DestName: string): Boolean;
  268. function DeleteFile(const FileName: string): Boolean;
  269. function RenameFile(const OldName, NewName: string): Boolean;
  270. function ChangeFileExt(const FileName, Extension: string): string;
  271. function ExtractFilePath(const FileName: string): string;
  272. function ExtractFileName(const FileName: string): string;
  273. function ExtractFileExt(const FileName: string): string;
  274. function ConcatFileName(const PathName, FileName: string): string;
  275. function ExpandFileName(FileName: string): string;
  276. function EditFileName(const Name, Edit: string): string;
  277. function FileSearch(const Name, DirList: string): string;
  278. function DiskFree(Drive: Byte): Longint;
  279. function DiskSize(Drive: Byte): Longint;
  280. function FileDateToDateTime(FileDate: Longint): TDateTime;
  281. function DateTimeToFileDate(DateTime: TDateTime): Longint;
  282.  
  283. { PChar routines }
  284.  
  285. function StrLen(Str: PChar): Cardinal;
  286. function StrEnd(Str: PChar): PChar;
  287. function StrMove(Dest, Source: PChar; Count: Cardinal): PChar;
  288. function StrCopy(Dest, Source: PChar): PChar;
  289. function StrECopy(Dest, Source: PChar): PChar;
  290. function StrLCopy(Dest, Source: PChar; MaxLen: Cardinal): PChar;
  291. function StrPCopy(Dest: PChar; const Source: String): PChar;
  292. function StrPLCopy(Dest: PChar; const Source: string; MaxLen: Cardinal): PChar;
  293. function StrCat(Dest, Source: PChar): PChar;
  294. function StrLCat(Dest, Source: PChar; MaxLen: Cardinal): PChar;
  295. function StrComp(Str1, Str2: PChar): Integer;
  296. function StrIComp(Str1, Str2: PChar): Integer;
  297. function StrLComp(Str1, Str2: PChar; MaxLen: Cardinal): Integer;
  298. function StrLIComp(Str1, Str2: PChar; MaxLen: Cardinal): Integer;
  299. function StrScan(Str: PChar; Chr: Char): PChar;
  300. function StrRScan(Str: PChar; Chr: Char): PChar;
  301. function StrPos(Str, SubStr: PChar): PChar;
  302. function StrUpper(Str: PChar): PChar;
  303. function StrLower(Str: PChar): PChar;
  304. function StrPas(Str: PChar): String;
  305. function StrAlloc(Size: Cardinal): PChar;
  306. function StrBufSize(Str: PChar): Cardinal;
  307. function StrNew(Str: PChar): PChar;
  308. procedure StrDispose(Str: PChar);
  309.  
  310. { String formatting routines }
  311.  
  312. { Format strings contain two types of characters
  313.  
  314.   * Plain characters are copied verbatim to the resulting string.
  315.  
  316.   * Format characters apply formatting to them.
  317.  
  318.     Format specifiers have the following form:
  319.  
  320.       "%" [index ":"] ["-"] [width] ["." prec] type
  321. }
  322.  
  323. function Format(const Format: string; const Args: array of const): string;
  324. procedure FmtStr(var Result: string; const Format: string;
  325.   const Args: array of const);
  326. function StrFmt(Buffer, Format: PChar; const Args: array of const): PChar;
  327. function StrLFmt(Buffer: PChar; MaxLen: Cardinal; Format: PChar;
  328.   const Args: array of const): PChar;
  329. function FormatBuf(var Buffer; BufLen: Cardinal; const Format;
  330.   FmtLen: Cardinal; const Args: array of const): Cardinal;
  331.  
  332. { Floating point conversion routines }
  333.  
  334. function FloatToStr(Value: Extended): string;
  335. function FloatToStrF(Value: Extended; Format: TFloatFormat;
  336.   Precision, Digits: Integer): string;
  337. function FloatToText(Buffer: PChar; Value: Extended; Format: TFloatFormat;
  338.   Precision, Digits: Integer): Integer;
  339. function FormatFloat(const Format: string; Value: Extended): string;
  340. function FloatToTextFmt(Buffer: PChar; Value: Extended;
  341.   Format: PChar): Integer;
  342. function StrToFloat(const S: string): Extended;
  343. function TextToFloat(Buffer: PChar; var Value: Extended): Boolean;
  344. procedure FloatToDecimal(var Result: TFloatRec; Value: Extended;
  345.   Precision, Decimals: Integer);
  346.  
  347. { Date/time support routines }
  348.  
  349. function EncodeDate(Year, Month, Day: Word): TDateTime;
  350. function EncodeTime(Hour, Min, Sec, MSec: Word): TDateTime;
  351. procedure DecodeDate(Date: TDateTime; var Year, Month, Day: Word);
  352. procedure DecodeTime(Time: TDateTime; var Hour, Min, Sec, MSec: Word);
  353. function DayOfWeek(Date: TDateTime): Integer;
  354. function Date: TDateTime;
  355. function Time: TDateTime;
  356. function Now: TDateTime;
  357. function DateToStr(Date: TDateTime): string;
  358. function TimeToStr(Time: TDateTime): string;
  359. function DateTimeToStr(DateTime: TDateTime): string;
  360. function StrToDate(const S: string): TDateTime;
  361. function StrToTime(const S: string): TDateTime;
  362. function StrToDateTime(const S: string): TDateTime;
  363. function FormatDateTime(const Format: string; DateTime: TDateTime): string;
  364. procedure DateTimeToString(var Result: string; const Format: string;
  365.   DateTime: TDateTime);
  366.  
  367. { Initialization file support }
  368.  
  369. {function GetProfileStr(Section, Entry: PChar; const Default: string): string;
  370. function GetProfileChar(Section, Entry: PChar; Default: Char): Char;}
  371.  
  372. {  The OS2 user profile can only be accessed from inside PM programs. }
  373.  
  374. {$ifdef PM}
  375.  
  376. {
  377. function OpenProfile: Boolean;
  378. procedure CloseProfile;
  379. }
  380. function GetProfileStr(const Section, Entry: cstring; const Default: string): string;
  381. function GetProfileChar(const Section, Entry: cstring; Default: Char): Char;
  382. function GetProfileInt(const Section, Entry: cstring; Default: Integer): Integer;
  383.  
  384. {$endif}
  385.  
  386. procedure GetFormatSettings;
  387.  
  388. { Exception handling routines }
  389.  
  390. procedure ConvertError(const Msg: String);
  391.  
  392. implementation
  393.  
  394. {$ifdef PM}
  395.  
  396. {$IFDEF OS2}
  397. uses
  398.   PMSHL, PMWIN;
  399. {$ENDIF}
  400. {$IFDEF Win95}
  401. uses
  402.   WinBase,WinUser;
  403. {$ENDIF}
  404. {$endif}
  405.  
  406. const
  407.  
  408.   DaysPassed: array[False..True, 1..13] of Integer =
  409.     ((0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365),
  410.      (0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366));
  411.  
  412. var
  413.  
  414.   CollatingSequence: array[#0..#255] of Byte;
  415.  
  416. const
  417.  
  418.   Hexadecimals: array[0..15] of Char = '0123456789ABCDEF';
  419.  
  420. procedure ConvertError(const Msg: String);
  421. begin
  422.   raise EConvertError.Create(Msg);
  423. end;
  424.  
  425. { String / PChar utility functions }
  426.  
  427. assembler
  428.  
  429.   { This function returns the length of a string and a pointer to the
  430.     zero terminator.
  431.  
  432.     Input:   EDI holds pointer string
  433.     Output:  EDI holds pointer to zero terminator, EAX holds string length
  434.     Changes: EAX, EBX, ECX, EDI }
  435.  
  436.   !StringLength PROC NEAR32
  437.  
  438.     MOV       EBX, EDI
  439.     XOR       EAX, EAX
  440.     CMP       EDI, 0
  441.     JE        !Out!StringLength
  442.     MOV       ECX, $ffffffff
  443.     CLD
  444.     REPNE
  445.     SCASB
  446.     NOT       ECX
  447.     MOV       EAX, ECX
  448.     DEC       EAX
  449.     DEC       EDI
  450.  
  451.   !Out!StringLength:
  452.     RETN32
  453.  
  454.   !StringLength ENDP
  455.  
  456.   { This functions copies a maximum number of characters from one string
  457.     to another.
  458.  
  459.     Input:   ESI holds source, EDI holds destination, ECX hold maximum
  460.              number of characters
  461.     Output:  EDI holds end of destination string
  462.     Changes: EAX, EBX, ECX, EDX, ESI, EDI }
  463.  
  464.   !StringCopy PROC NEAR32
  465.  
  466.     MOV       EBX, ECX
  467.     MOV       EDX, EDI
  468.     XOR       EAX, EAX
  469.     CMP       EDI, 0
  470.     JE        !Out!StringCopy
  471.     CMP       ESI, 0
  472.     JE        !Out!StringCopy
  473.     MOV       EDI, ESI
  474.     CLD
  475.     REPNE
  476.     SCASB
  477.     SUB       EBX, ECX
  478.     MOV       ECX, EBX
  479.     SHR       ECX, 2
  480.     MOV       EDI, EDX
  481.     REP       MOVSD
  482.     MOV       ECX, EBX
  483.     AND       ECX, 3
  484.     REP
  485.     MOVSB
  486.     STOSB
  487.     DEC       EDI
  488.     DEC       EDI
  489.  
  490.   !Out!StringCopy:
  491.     RETN32
  492.  
  493.   !StringCopy ENDP
  494.  
  495.   // This function compares a maximum number of characters
  496.  
  497.   !StringCompare PROC NEAR32
  498.  
  499.     REPE
  500.     CMPSB
  501.     XOR        EAX, EAX
  502.     MOV        AL, [ESI - 1]
  503.     MOV        BL, [EDI - 1]
  504.     SUB        EAX, EBX
  505.     RETN32
  506.  
  507.   !StringCompare ENDP
  508.  
  509.   //
  510.  
  511.   !StringICompare PROC NEAR32
  512.  
  513.     XOR        EAX, EAX;
  514.  
  515.   !Loop!StringICompare:
  516.  
  517.     REPE
  518.     CMPSB
  519.     JE         !Out!StringICompare
  520.  
  521.     XOR        EBX, EBX
  522.     MOV        BL, [ESI - 1]
  523.     CMP        BL, 'A'
  524.     JL         !UpcaseSecondChar!StringICompare
  525.     CMP        BL, 'Z'
  526.     JG         !UpcaseSecondChar!StringICompare
  527.     OR         BL, 32
  528.  
  529.   !UpcaseSecondChar!StringICompare:
  530.  
  531.     XOR        EDX, EDX
  532.     MOV        DL, [EDI - 1]
  533.     CMP        DL, 'A'
  534.     JL         !CompareSingleChar!StringICompare
  535.     CMP        DL, 'Z'
  536.     JG         !CompareSingleChar!StringICompare
  537.     OR         DL, 32
  538.  
  539.   !CompareSingleChar!StringICompare:
  540.  
  541.     SUB        EBX, EDX
  542.     JE         !Loop!StringICompare
  543.     MOV        EAX, EBX
  544.  
  545.   !Out!StringICompare:
  546.  
  547.     RETN32
  548.  
  549.   !StringICompare ENDP
  550.  
  551. end;
  552.  
  553. { Memory management routines }
  554.  
  555. function AllocMem(Size: Cardinal): Pointer;
  556. var
  557.   P: Pointer;
  558. begin
  559.   GetMem(P, Size);
  560.   FillChar(P^, Size, 0);
  561.   AllocMem := P;
  562. end;
  563.  
  564. function ReAllocMem(P: Pointer; CurSize, NewSize: Cardinal): Pointer;
  565. var
  566.   Q: PByteArray;
  567. begin
  568.   if NewSize <> 0 then GetMem(Q, NewSize)
  569.   else Q := nil;
  570.  
  571.   if NewSize > 0 then
  572.     begin
  573.       if NewSize > CurSize then
  574.         begin
  575.           FillChar(Q^[CurSize], NewSize - CurSize, 0);
  576.           NewSize := CurSize;
  577.         end;
  578.       if NewSize <> 0 then Move(P^, Q^, NewSize);
  579.     end;
  580.   if CurSize <> 0 then FreeMem(P, CurSize);
  581.   ReAllocMem := Q;
  582. end;
  583.  
  584. { Exit procedure handling }
  585.  
  586. type
  587.   PExitNode = ^TExitNode;
  588.   TExitNode = record
  589.     Next: PExitNode;
  590.     Proc: TProcedure;
  591.   end;
  592.  
  593. const
  594.   ExitChain: PExitNode = nil;
  595.  
  596. var
  597.   SaveExitProc: Pointer;
  598.  
  599. procedure CallExitProcs;
  600. var
  601.   First: PExitNode;
  602.   Proc: TProcedure;
  603. begin
  604.   while ExitChain <> nil do
  605.     begin
  606.       First := ExitChain;
  607.       Proc := First^.Proc;
  608.       ExitChain := First^.Next;
  609.       ExitProc := nil; { Avoids recursion! }
  610.       Dispose(First);
  611.       Proc;
  612.     end;
  613.   ExitProc := SaveExitProc;
  614. end;
  615.  
  616. procedure AddExitProc(Proc: TProcedure);
  617. var
  618.   NewNode: PExitNode;
  619. begin
  620.   if ExitChain = nil then
  621.     SaveExitProc := ExitProc;
  622.   New(NewNode);
  623.   NewNode^.Next := ExitChain;
  624.   NewNode^.Proc := Proc;
  625.   ExitChain := NewNode;
  626.   ExitProc := @CallExitProcs;
  627. end;
  628.  
  629. { String handling routines }
  630.  
  631. function NewStr(const S: String): PString;
  632. begin
  633.   if Length(S) = 0 then
  634.     Result := NullStr
  635.   else
  636.     begin
  637.       GetMem(Result, Length(S) + 1);
  638.       Result^ := S;
  639.     end;
  640. end;
  641.  
  642. procedure DisposeStr(P: PString);
  643. begin
  644.   if (P <> NullStr) and (P <> nil) then
  645.     FreeMem(P, Length(P^) + 1);
  646. end;
  647.  
  648. procedure AssignStr(var P: PString; const S: string);
  649. begin
  650.   DisposeStr(P);
  651.   P := NewStr(S);
  652. end;
  653.  
  654. procedure AppendStr(var Dest: string; const S: string);
  655. begin
  656.   Insert(S, Dest, Length(Dest) + 1);
  657. end;
  658.  
  659. function UpperCase(const S: string): string;
  660. var
  661.   T: string;
  662.   N, C: Integer;
  663. begin
  664.   T := S;
  665.   for N := 1 to Length(T) do
  666.     begin
  667.       C := Ord(T[N]);
  668.       if (C >= Ord('a')) and (C <= Ord('z')) then
  669.         T[N] := Chr(C and not 32);
  670.     end;
  671.   UpperCase := T;
  672. end;
  673.  
  674. function LowerCase(const S: string): string;
  675. var
  676.   T: string;
  677.   N, C: Integer;
  678. begin
  679.   T := S;
  680.   for N := 1 to Length(T) do
  681.   begin
  682.     C := Ord(T[N]);
  683.     if (C >= Ord('A')) and (C <= Ord('Z')) then T[N] := Chr(C or 32);
  684.   end;
  685.   LowerCase := T;
  686. end;
  687.  
  688. function CompareStr(const S1, S2: string): Integer;
  689. begin
  690.   if S1 <= S2 then
  691.     begin
  692.       if S1 = S2 then
  693.         CompareStr := 0
  694.       else
  695.         CompareStr := -1;
  696.     end
  697.   else
  698.     CompareStr := +1
  699. end;
  700.  
  701. function CompareText(const S1, S2: string): Integer;
  702. var
  703.   L1, L2, L: Integer;
  704. begin
  705.   L1 := Length(S1);
  706.   L2 := Length(S2);
  707.   if L1 <= L2 then L := L1
  708.   else L := L2;
  709.   Result := StrLIComp(@S1[1], @S2[1], L);
  710.   if Result = 0 then
  711.   begin
  712.     if L1 < L2 then Result := -1
  713.     else if L1 > L2 then Result := 1;
  714.   end;
  715.   {CompareText := CompareStr(UpperCase(S1), UpperCase(S2));}
  716. end;
  717.  
  718. {$ifdef PM}
  719.  
  720. {$IFDEF OS2}
  721. function AnsiUpperCase(const S: string): string;
  722. var
  723.   Temp: cstring[256];
  724. begin
  725.   Temp := S;
  726.   WinUpper(AppHandle, 0, 0, Temp);
  727.   Result := Temp;
  728. end;
  729. {$ENDIF}
  730. {$IFDEF Win95}
  731. function AnsiUpperCase(const S: string): string;
  732. VAR S1:STRING;
  733. begin
  734.   S1:=S;
  735.   AnsiUpperBuff(@S1[1], Length(S1));
  736.   AnsiUpperCase:=S1;
  737. end;
  738. {$ENDIF}
  739.  
  740. {$else}
  741.  
  742. {$IFDEF OS2}
  743. function AnsiUpperCase(const S: string): string;
  744. var
  745.   CC: COUNTRYCODE;
  746. begin
  747.   Result := S;
  748.   CC.Country := 0;
  749.   CC.CodePage := 0;
  750.   DosMapCase(Length(Result), CC, Result[1]);
  751. end;
  752. {$ENDIF}
  753. {$IFDEF Win95}
  754. function AnsiUpperCase(const S: string): string;
  755. VAR S1:STRING;
  756. begin
  757.   S1:=S;
  758.   AnsiUpperBuff(@S1[1], Length(S1));
  759.   AnsiUpperCase:=S1;
  760. end;
  761. {$ENDIF}
  762.  
  763. {$endif}
  764.  
  765. {$ifdef PM}
  766.  
  767. {$IFDEF OS2}
  768. function AnsiCompareText(const S1, S2: string): Integer;
  769. var
  770.   Temp1, Temp2: cstring[256];
  771. begin
  772.   Temp1 := S1;
  773.   Temp2 := S2;
  774.   case WinCompareStrings(AppHandle, 0, 0, Temp1, Temp2, 0) of
  775.     WCS_LT: Result := -1;
  776.     WCS_EQ: Result :=  0;
  777.     WCS_GT: Result :=  1;
  778.   end;
  779. end;
  780. {$ENDIF}
  781.  
  782. {$IFDEF Win95}
  783. function AnsiCompareText(const S1, S2: string): Integer;
  784. var
  785.   Temp1,Temp2:array[0..255] of Char;
  786. begin
  787.   AnsiCompareText:=lstrcmpi(StrPCopy(Temp1,S1),
  788.                             StrPCopy(Temp2,S2));
  789. end;
  790. {$ENDIF}
  791.  
  792. {$else}
  793.  
  794. function AnsiCompareText(const S1, S2: string): Integer;
  795. var
  796.   N, L1, L2: Integer;
  797. begin
  798.   N := 1;
  799.   L1 := Length(S1);
  800.   L2 := Length(S2);
  801.   while (N <= L1) and (N <= L2) and
  802.         (CollatingSequence[S1[N]] = CollatingSequence[S2[N]]) do Inc(N);
  803.  
  804.   if (N <= L1) and (N <= L2) then
  805.   begin
  806.     if CollatingSequence[S1[N]] < CollatingSequence[S2[N]] then Result := -1
  807.     else if CollatingSequence[S1[N]] > CollatingSequence[S2[N]] then Result := 1
  808.     else Result := 0;
  809.   end
  810.   else
  811.   begin
  812.     if L1 < L2 then Result := -1
  813.     else if L1 > L2 then Result := 1
  814.     else Result := 0;
  815.   end;
  816. end;
  817.  
  818. {$endif}
  819.  
  820. function IsValidIdent(const Ident: string): Boolean;
  821. var
  822.   L, N: Integer;
  823. begin
  824.   L := Length(Ident);
  825.   if L = 0 then
  826.     IsValidIdent := False
  827.   else
  828.     begin
  829.       if Ident[1] in ['a'..'z', 'A'..'Z', '_'] then
  830.         begin
  831.           N := 2;
  832.           while (N <= L) and (Ident[N] in ['a'..'z', 'A'..'Z', '_', '0'..'9']) do
  833.             Inc(N);
  834.           if N > L then
  835.             IsValidIdent := True
  836.           else
  837.             IsValidIdent := False;
  838.         end
  839.       else
  840.         IsValidIdent := False;
  841.     end;
  842. end;
  843.  
  844. function IntToStr(Value: Longint): string;
  845. begin
  846.   Str(Value, Result);
  847. end;
  848.  
  849. function IntToHex(Value: Longint; Digits: Integer): string;
  850. begin
  851.   Result := '';
  852.   repeat
  853.     Dec(Digits);
  854.     Result := Hexadecimals[Value and $0F] + Result;
  855.     Value := Value shr 4;
  856.   until Value = 0;
  857.   if Digits > 0 then
  858.   begin
  859.     Move(Result[1], Result[1 + Digits], Byte(Result[0]));
  860.     FillChar(Result[1], Digits, '0');
  861.     Inc(Byte(Result[0]), Digits);
  862.   end;
  863. end;
  864.  
  865. function StrToInt(const S: string): Longint;
  866. var
  867.   L: LongInt;
  868.   Err: Integer;
  869. begin
  870.   Val(S, L, Err);
  871.   if Err <> 0 then
  872.     raise EConvertError.Create('StrToInt(' + S + ')')
  873.   else
  874.     StrToInt := L;
  875. end;
  876.  
  877. function StrToIntDef(const S: string; Default: Longint): Longint;
  878. var
  879.   L: LongInt;
  880.   Err: Integer;
  881. begin
  882.   Val(S, L, Err);
  883.   if Err <> 0 then
  884.     StrToIntDef := Default
  885.   else
  886.     StrToIntDef := L;
  887. end;
  888.  
  889. {$IFDEF OS2}
  890. function LoadStr(Ident: Word): string;
  891. var
  892.   L: LongInt;
  893. begin
  894.   if DosGetMessage(nil, 0, Result[1], 255, Ident, nil, L) <> NO_ERROR then L := 0;
  895.   SetLength(Result, L);
  896. end;
  897. {$ENDIF}
  898.  
  899. {$IFDEF Win95}
  900. function LoadStr(Ident: Word): string;
  901. var S:STRING;
  902. begin
  903.   S[0] := Char(LoadString(AppHandle,Ident,@S[1],254));
  904.   LoadStr:=S;
  905. end;
  906. {$ENDIF}
  907.  
  908. function FmtLoadStr(Ident: Word; const Args: array of const): string;
  909. begin
  910.   FmtStr(Result, LoadStr(Ident), Args);
  911. end;
  912.  
  913. procedure SetLength(var S: string; NewLength: Byte);
  914. begin
  915.   Byte(S[0]) := NewLength;
  916. end;
  917.  
  918. { File management routines }
  919.  
  920. {$IFDEF OS2}
  921. function FileOpen(const FileName: string; Mode: Word): LongInt;
  922. const
  923.   Action = OPEN_ACTION_OPEN_IF_EXISTS or OPEN_ACTION_FAIL_IF_NEW;
  924. var
  925.   rc, ActionTaken, Handle: LongInt;
  926.   FileNameZ: CString[256];
  927. begin
  928.   FileNameZ := FileName;
  929.   if DosOpen(FileNameZ, Handle, ActionTaken, 0, 0, Action, Mode, nil) = NO_ERROR then
  930.     FileOpen := Handle
  931.   else
  932.     FileOpen := -1;
  933. end;
  934. {$ENDIF}
  935.  
  936. {$IFDEF Win95}
  937. ??????????????
  938. {$ENDIF}
  939.  
  940. function FileOpenOrCreate(const FileName: string; Mode: Word): LongInt;
  941. const
  942.   Action = OPEN_ACTION_OPEN_IF_EXISTS or OPEN_ACTION_CREATE_IF_NEW;
  943. var
  944.   rc, ActionTaken, Handle: LongInt;
  945.   FileNameZ: CString[256];
  946. begin
  947.   FileNameZ := FileName;
  948.   if DosOpen(FileNameZ, Handle, ActionTaken, 0, 0, Action, Mode, nil) = NO_ERROR then
  949.     Result := Handle
  950.   else
  951.     Result := -1;
  952. end;
  953.  
  954. function FileCreateIfNew(const FileName: string; Mode: Word): LongInt;
  955. const
  956.   Action = OPEN_ACTION_FAIL_IF_EXISTS or OPEN_ACTION_CREATE_IF_NEW;
  957. var
  958.   rc, ActionTaken, Handle: LongInt;
  959.   FileNameZ: CString[256];
  960. begin
  961.   FileNameZ := FileName;
  962.   if DosOpen(FileNameZ, Handle, ActionTaken, 0, 0, Action, Mode, nil) = NO_ERROR then
  963.     Result := Handle
  964.   else
  965.     Result := -1;
  966. end;
  967.  
  968. function FileCreate(const FileName: string): LongInt;
  969. const
  970.   Action = OPEN_ACTION_REPLACE_IF_EXISTS or OPEN_ACTION_CREATE_IF_NEW;
  971.   Mode = fmOpenWrite + fmShareDenyNone;
  972. var
  973.   ActionTaken, Handle: LongInt;
  974.   FileNameZ: CString[256];
  975. begin
  976.   FileNameZ := FileName;
  977.   if DosOpen(FileNameZ, Handle, ActionTaken, 0, 0, Action, Mode, nil) = NO_ERROR then
  978.     FileCreate := Handle
  979.   else
  980.     FileCreate := -1;
  981. end;
  982.  
  983. function FileRead(Handle: LongInt; var Buffer; Count: Longint): Longint;
  984. var
  985.   Result: LongInt;
  986. begin
  987.   if DosRead(Handle, Buffer, Count, Result) = NO_ERROR then
  988.     FileRead := Result
  989.   else
  990.     FileRead := -1;
  991. end;
  992.  
  993. function FileWrite(Handle: LongInt; var {const} Buffer; Count: Longint): Longint;
  994. begin
  995.   if DosWrite(Handle, Buffer, Count, Result) <> NO_ERROR then
  996.     Result := -1;
  997. end;
  998.  
  999. function FileSeek(Handle: LongInt; Offset: Longint; Origin: Integer): Longint;
  1000. var
  1001.   NewPos: LongInt;
  1002. begin
  1003.   if DosSetFilePtr(Handle, Offset, Origin, NewPos) = NO_ERROR then
  1004.     FileSeek := NewPos
  1005.   else
  1006.     FileSeek := -1;
  1007. end;
  1008.  
  1009. procedure FileClose(Handle: LongInt);
  1010. begin
  1011.   DosClose(Handle);
  1012. end;
  1013.  
  1014. function FileAge(const FileName: string): Longint;
  1015. var
  1016.   FileNameZ: cstring[256];
  1017.   Buffer: FILESTATUS3;
  1018. begin
  1019.   FileNameZ := FileName;
  1020.   if DosQueryPathInfo(FileNameZ, FIL_STANDARD, Buffer, SizeOf(Buffer)) = NO_ERROR then
  1021.     FileAge := (Buffer.fdateLastWrite shl 16) or Buffer.ftimeLastWrite
  1022.   else FileAge := -1;
  1023. end;
  1024.  
  1025. function FileExists(const FileName: string): Boolean;
  1026. var
  1027.   SearchRec: TSearchRec;
  1028. begin
  1029.   if FindFirst(FileName, faAnyFile, SearchRec) = 0 then
  1030.     begin
  1031.       FileExists := True;
  1032.       FindClose(SearchRec);
  1033.     end
  1034.   else
  1035.     FileExists := False;
  1036. end;
  1037.  
  1038. function FindFirst(const Path: string; Attr: Integer; var SearchRec: TSearchRec): LongInt;
  1039. var
  1040.   OS2SearchRec: FILEFINDBUF3;
  1041.   Result, Count: LongInt;
  1042. const
  1043.   Size = SizeOf(OS2SearchRec);
  1044. var
  1045.   PathZ: CString[256];
  1046. begin
  1047.   PathZ := Path;
  1048.   SearchRec.HDir := HDIR_CREATE;
  1049.   Count := 1;
  1050.   Result := DosFindFirst(PathZ, SearchRec.HDir, Attr, OS2SearchRec, Size, Count, FIL_STANDARD);
  1051.   if Result = NO_ERROR then
  1052.   begin
  1053.     with OS2SearchRec do
  1054.     begin
  1055.       SearchRec.Name := achName;
  1056.       SearchRec.Size := cbFile;
  1057.       SearchRec.Attr := attrFile;
  1058.       SearchRec.Time := fdateLastWrite;
  1059.       SearchRec.Time := SearchRec.Time shl 16 + ftimeLastWrite;
  1060.     end;
  1061.     FindFirst := 0;
  1062.   end
  1063.   else FindFirst := -Result;
  1064. end;
  1065.  
  1066. function FindNext(var SearchRec: TSearchRec): LongInt;
  1067. var
  1068.   OS2SearchRec: FILEFINDBUF3;
  1069.   Result: Integer;
  1070.   Count: LongInt;
  1071. const
  1072.   Size = SizeOf(OS2SearchRec);
  1073. begin
  1074.   Count := 1;
  1075.   Result := DosFindNext (SearchRec.HDir, OS2SearchRec, Size, Count);
  1076.   if Result = NO_ERROR then
  1077.   begin
  1078.     with OS2SearchRec do
  1079.     begin
  1080.       SearchRec.Name := achName;
  1081.       SearchRec.Size := cbFile;
  1082.       SearchRec.Attr := attrFile;
  1083.       SearchRec.Time := fdateLastWrite;
  1084.       SearchRec.Time := SearchRec.Time shl 16 + ftimeLastWrite;
  1085.     end;
  1086.     FindNext := 0;
  1087.   end
  1088.   else FindNext := -Result;
  1089. end;
  1090.  
  1091. procedure FindClose(var SearchRec: TSearchRec);
  1092. begin
  1093.   DosFindClose(SearchRec.HDir);
  1094. end;
  1095.  
  1096. function FileGetDate(Handle: LongInt): Longint;
  1097. var
  1098.   Buffer: FILESTATUS3;
  1099. begin
  1100.   if DosQueryFileInfo(Handle, FIL_STANDARD, Buffer, SizeOf(Buffer)) = NO_ERROR then
  1101.     FileGetDate := (Buffer.fdateLastWrite shl 16) or Buffer.ftimeLastWrite
  1102.   else FileGetDate := -1;
  1103. end;
  1104.  
  1105. procedure FileSetDate(Handle: Integer; Age: Longint);
  1106. var
  1107.   Buffer: FILESTATUS3;
  1108. begin
  1109.   FillChar(Buffer, SizeOf(Buffer), 0);
  1110.   Buffer.ftimeLastWrite := Age and $FFFF;
  1111.   Buffer.fdateLastWrite := Age shr 16;
  1112.   DosSetFileInfo(Handle, FIL_STANDARD, Buffer, SizeOf(Buffer));
  1113. end;
  1114.  
  1115. function FileGetAttr(const FileName: string): LongInt;
  1116. var
  1117.   Buffer: FILESTATUS3;
  1118.   FileNameZ: cstring[256];
  1119. begin
  1120.   FileNameZ := FileName;
  1121.   Result := - DosQueryPathInfo(FileNameZ, FIL_STANDARD, Buffer, SizeOf(Buffer));
  1122.   if Result = 0 then Result := Buffer.attrFile;
  1123. end;
  1124.  
  1125. function FileSetAttr(const FileName: string; Attr: Integer): Integer;
  1126. var
  1127.   Buffer: FILESTATUS3;
  1128.   FileNameZ: cstring[256];
  1129. begin
  1130.   FileNameZ := FileName;
  1131.   FillChar(Buffer, SizeOf(Buffer), 0);
  1132.   Buffer.attrFile := Attr;
  1133.   Result := - DosSetPathInfo(FileNameZ, FIL_STANDARD, Buffer, SizeOf(Buffer), 0);
  1134. end;
  1135.  
  1136. function CopyFile(const SourceName, DestName: string): Boolean;
  1137. var
  1138.   SourceZ, DestZ: cstring[256];
  1139. begin
  1140.   SourceZ := SourceName;
  1141.   DestZ := DestName;
  1142.   Result := (DosCopy(SourceZ, DestZ, DCPY_EXISTING) = NO_ERROR);
  1143. end;
  1144.  
  1145. function DeleteFile(const FileName: string): Boolean;
  1146. var
  1147.   FileNameZ: cstring[256];
  1148. begin
  1149.   FileNameZ := FileName;
  1150.   Result := (DosDelete(FileNameZ) = NO_ERROR);
  1151. end;
  1152.  
  1153. function RenameFile(const OldName, NewName: string): Boolean;
  1154. var
  1155.   OldNameZ, NewNameZ: cstring[256];
  1156. begin
  1157.   OldNameZ := OldName;
  1158.   NewNameZ := NewName;
  1159.   Result := (DosMove(OldNameZ, NewNameZ) = NO_ERROR);
  1160. end;
  1161.  
  1162. function ChangeFileExt(const FileName, Extension: string): string;
  1163. var
  1164.   P: Integer;
  1165. begin
  1166.   P := Length(FileName);
  1167.   while (P > 0) and (FileName[P] <> '.') do Dec(P);
  1168.   if P = 0 then Result := FileName + Extension
  1169.   else Result := Copy(FileName, 1, P - 1) + Extension;
  1170. end;
  1171.  
  1172. function ExtractFilePath(const FileName: string): string;
  1173. var
  1174.   P: Integer;
  1175. begin
  1176.   P := Length(FileName);
  1177.   while (P > 0) and (FileName[P] <> ':') and (FileName[P] <> '\') do Dec(P);
  1178.   Result := Copy(FileName, 1, P);
  1179. end;
  1180.  
  1181. function ExtractFileName(const FileName: string): string;
  1182. var
  1183.   P: Integer;
  1184. begin
  1185.   P := Length(FileName);
  1186.   while (P > 0) and (FileName[P] <> ':') and (FileName[P] <> '\') do Dec(P);
  1187.   Result := Copy(FileName, P + 1, 255);
  1188. end;
  1189.  
  1190. function ExtractFileExt(const FileName: string): string;
  1191. var
  1192.   P: Integer;
  1193. begin
  1194.   P := Length(FileName);
  1195.   while (P > 0) and (FileName[P] <> '.') do Dec(P);
  1196.   if P = 0 then Result := ''
  1197.   else Result := Copy(FileName, P, 255);
  1198. end;
  1199.  
  1200. function ConcatFileName(const PathName, FileName: string): string;
  1201. begin
  1202.   if (PathName = '') or (FileName = '') or
  1203.     (PathName[Length(PathName)] in ['\', ':']) then
  1204.       Result := PathName + FileName
  1205.   else Result := PathName + '\' + FileName;
  1206. end;
  1207.  
  1208. function ExpandFileName(FileName: string): string;
  1209. const
  1210.   Level = FIL_QUERYFULLNAME;
  1211. var
  1212.   FileNameZ, Buffer: cstring[256];
  1213. begin
  1214.   FileNameZ := FileName;
  1215.   if DosQueryPathInfo(FileNameZ, Level, Buffer, SizeOf(Buffer)) = NO_ERROR then Result := Buffer
  1216.   else Result := '';
  1217. end;
  1218.  
  1219. function EditFileName(const Name, Edit: string): string;
  1220. var
  1221.   Buffer, NameZ, EditZ: cstring[256];
  1222. begin
  1223.   NameZ := Name;
  1224.   EditZ := Edit;
  1225.   if DosEditName(1, NameZ, EditZ, Buffer, 256) = 0 then Result := Buffer
  1226.   else Result := '';
  1227. end;
  1228.  
  1229. function FileSearch(const Name, DirList: string): string;
  1230. const
  1231.   Flags = SEARCH_IGNORENETERRS;
  1232. var
  1233.   NameZ, DirListZ, Buffer: cstring[256];
  1234. begin
  1235.   NameZ := Name;
  1236.   DirListZ := DirList;
  1237.   if DosSearchPath(Flags, DirListZ, NameZ, Buffer, SizeOf(Buffer)) = NO_ERROR then
  1238.     Result := Buffer
  1239.   else Result := '';
  1240. end;
  1241.  
  1242. function DiskFree(Drive: Byte): Longint;
  1243. var
  1244.   Buffer: FSALLOCATE;
  1245. begin
  1246.   if DosQueryFSInfo(Drive, FSIL_ALLOC, Buffer, SizeOf(Buffer)) = NO_ERROR then
  1247.     with Buffer do Result := cUnitAvail * cSectorUnit * cbSector
  1248.   else Result := -1;
  1249. end;
  1250.  
  1251. function DiskSize(Drive: Byte): Longint;
  1252. var
  1253.   Buffer: FSALLOCATE;
  1254. begin
  1255.   if DosQueryFSInfo(Drive, FSIL_ALLOC, Buffer, SizeOf(Buffer)) = NO_ERROR then
  1256.     with Buffer do Result := cUnit * cSectorUnit * cbSector
  1257.   else Result := -1;
  1258. end;
  1259.  
  1260. function FileDateToDateTime(FileDate: Longint): TDateTime;
  1261. var
  1262.   Day, Month, Year, Hour, Min, Sec: Word;
  1263. begin
  1264.   Sec      := (FileDate and 31) shl 1;
  1265.   FileDate := FileDate shr 5;
  1266.   Min      := FileDate and 63;
  1267.   FileDate := FileDate shr 6;
  1268.   Hour     := FileDate and 31;
  1269.   FileDate := FileDate shr 5;
  1270.  
  1271.   Day      := FileDate and 31;
  1272.   FileDate := FileDate shr 5;
  1273.   Month    := FileDate and 15;
  1274.   FileDate := FileDate shr 4;
  1275.   Year     := 1980 + (FileDate and 127);
  1276.  
  1277.   Result := EncodeDate(Year, Month, Day) + EncodeTime(Hour, Min, Sec, 0);
  1278. end;
  1279.  
  1280. function DateTimeToFileDate(DateTime: TDateTime): Longint;
  1281. var
  1282.   Day, Month, Year, Hour, Min, Sec, MSec: Word;
  1283.   FileDate: LongInt;
  1284. begin
  1285.   DecodeDate(DateTime, Year, Month, Day);
  1286.   DecodeTime(DateTime, Hour, Min, Sec, MSec);
  1287.  
  1288.   FileDate := Year - 1980;
  1289.   FileDate := (FileDate shl 4) or Month;
  1290.   FileDate := (FileDate shl 5) or Day;
  1291.   FileDate := Hour;
  1292.   FileDate := (FileDate shl 6) or Min;
  1293.   FileDate := (FileDate shl 5) or (Sec div 2);
  1294.  
  1295.   Result := FileDate;
  1296. end;
  1297.  
  1298. { PChar routines }
  1299.  
  1300. function StrLen(Str:PChar): LongWord;
  1301. begin
  1302.   asm
  1303.     MOV       EDI, $Str
  1304.     CALLN32   !StringLength
  1305.     MOV       $!FuncResult, EAX
  1306.   end;
  1307. end;
  1308.  
  1309. function StrEnd(Str:PChar):PChar;
  1310. begin
  1311.   asm
  1312.     MOV       EDI, $Str
  1313.     CALLN32   !StringLength
  1314.     MOV       $!FuncResult, EDI
  1315.   end;
  1316. end;
  1317.  
  1318. function StrMove(Dest, Source: PChar; Count: Cardinal): PChar;
  1319. begin
  1320.   if (Source = nil) or (Dest = nil) or (Count = 0) then Result := nil
  1321.   else
  1322.   begin
  1323.     Move(Source^, Dest^, Count);
  1324.     Result := Dest;
  1325.   end;
  1326. end;
  1327.  
  1328. function StrCopy(Dest, Source:PChar):PChar;
  1329. begin
  1330.   asm
  1331.     MOV       ESI, $Source
  1332.     MOV       EDI, $Dest
  1333.     MOV       ECX, $FFFFFFFF
  1334.     CALLN32   !StringCopy
  1335.     MOV       EAX, $Dest
  1336.     MOV       $!FuncResult, EAX
  1337.   end;
  1338. end;
  1339.  
  1340. function StrECopy(Dest, Source:PChar):PChar;
  1341. begin
  1342.   asm
  1343.     MOV       ESI, $Source
  1344.     MOV       EDI, $Dest
  1345.     MOV       ECX, $FFFFFFFF
  1346.     CALLN32   !StringCopy
  1347.     MOV       $!FuncResult, EDI
  1348.   end;
  1349. end;
  1350.  
  1351. function StrLCopy(Dest, Source:PChar; MaxLen: Cardinal):PChar;
  1352. begin
  1353.   asm
  1354.     MOV       ESI, $Source
  1355.     MOV       EDI, $Dest
  1356.     MOV       ECX, $MaxLen
  1357.     CALLN32   !StringCopy
  1358.     MOV       EAX, $Dest
  1359.     MOV       $!FuncResult, EAX
  1360.   end;
  1361. end;
  1362.  
  1363. function StrPCopy(Dest: PChar; const Source: string): PChar;
  1364. begin
  1365.   asm
  1366.     MOV       EDI, $Dest
  1367.     MOV       ESI, $Source
  1368.     XOR       ECX, ECX
  1369.     MOV       CL, [ESI]
  1370.     INC       ESI
  1371.     CALLN32   !StringCopy
  1372.     MOV       EAX, $Dest
  1373.     MOV       $!FuncResult, EAX
  1374.   end;
  1375. end;
  1376.  
  1377. function StrPLCopy(Dest: PChar; const Source: string; MaxLen: Cardinal): PChar;
  1378. begin
  1379.   asm
  1380.     MOV       EDI, $Dest
  1381.     MOV       ESI, $Source
  1382.     XOR       ECX, ECX
  1383.     MOV       CL, [ESI]
  1384.     INC       ESI
  1385.     CMP       ECX, $MaxLen
  1386.     JLE       StrPLCopy_1
  1387.     MOV       ECX, $MaxLen
  1388.  
  1389.     StrPLCopy_1:
  1390.  
  1391.     CALLN32   !StringCopy
  1392.     MOV       EAX, $Dest
  1393.     MOV       $!FuncResult, EAX
  1394.   end;
  1395. end;
  1396.  
  1397. function StrCat(Dest, Source: PChar): PChar;
  1398. begin
  1399.   asm
  1400.     MOV       EDI, $Dest
  1401.     MOV       ESI, $Source
  1402.     CALLN32   !StringLength
  1403.     MOV       ECX, $FFFFFFFF
  1404.     CALLN32   !StringCopy
  1405.     MOV       EAX, $Dest
  1406.     MOV       $!FuncResult, EAX
  1407.   end;
  1408. end;
  1409.  
  1410. function StrLCat(Dest, Source: PChar; MaxLen: Cardinal): PChar;
  1411. begin
  1412.   asm
  1413.     MOV       EDI, $Dest
  1414.     MOV       ESI, $Source
  1415.     CALLN32   !StringLength
  1416.     MOV       ECX, $MaxLen
  1417.     SUB       ECX, EAX
  1418.     JLE       StrLCat_1
  1419.     CALLN32   !StringCopy
  1420.  
  1421.     StrLCat_1:
  1422.  
  1423.     MOV       EAX, $Dest
  1424.     MOV       $!FuncResult, EAX
  1425.   end;
  1426. end;
  1427.  
  1428. function StrComp(Str1, Str2: PChar): Integer;
  1429. begin
  1430.   asm
  1431.     MOV        EDI, $Str1
  1432.     CALLN32    !StringLength
  1433.     MOV        ECX, EAX
  1434.     MOV        ESI, $Str1
  1435.     MOV        EDI, $Str2
  1436.     CALLN32    !StringCompare
  1437.     MOV        $!FuncResult, EAX
  1438.   end;
  1439. end;
  1440.  
  1441. function StrLComp(Str1, Str2: PChar; MaxLen: Cardinal): Integer;
  1442. begin
  1443.   asm
  1444.     MOV        EDI, $Str1
  1445.     MOV        ECX, $MaxLen
  1446.     MOV        EBX, ECX
  1447.     XOR        EAX, EAX
  1448.     REPNZ      SCASB
  1449.     SUB        EBX, ECX
  1450.     MOV        ECX, EBX
  1451.     MOV        ESI, $Str1
  1452.     MOV        EDI, $Str2
  1453.     CALLN32    !StringCompare
  1454.     MOV        $!FuncResult, EAX
  1455.   end;
  1456. end;
  1457.  
  1458. function StrIComp(Str1, Str2: PChar): Integer;
  1459. begin
  1460.   asm
  1461.     MOV        EDI, $Str1
  1462.     CALLN32    !StringLength
  1463.     MOV        ECX, EAX
  1464.     MOV        ESI, $Str1
  1465.     MOV        EDI, $Str2
  1466.     CALLN32    !StringICompare
  1467.     MOV        $!FuncResult, EAX
  1468.   end;
  1469. end;
  1470.  
  1471. function StrLIComp(Str1, Str2: PChar; MaxLen: Cardinal): Integer;
  1472. begin
  1473.   asm
  1474.     MOV        EDI, $Str1
  1475.     MOV        ECX, $MaxLen
  1476.     MOV        EBX, ECX
  1477.     XOR        EAX, EAX
  1478.     REPNZ      SCASB
  1479.     SUB        EBX, ECX
  1480.     MOV        ECX, EBX
  1481.     MOV        ESI, $Str1
  1482.     MOV        EDI, $Str2
  1483.     CALLN32    !StringICompare
  1484.     MOV        $!FuncResult, EAX
  1485.   end;
  1486. end;
  1487.  
  1488. function StrScan(Str: PChar; Chr: Char): PChar;
  1489. begin
  1490.   asm
  1491.     MOV        EDI, $Str
  1492.     CALLN32    !StringLength
  1493.     INC        EAX
  1494.     MOV        ECX, EAX
  1495.     XOR        EBX, EBX
  1496.     MOV        AL, $Chr
  1497.     MOV        EDI, $Str
  1498.     REPNZ      SCASB
  1499.     DEC        EDI
  1500.     CMP        AL, [EDI]
  1501.     JNE        !StrScan_1
  1502.     MOV        EBX, EDI
  1503.  
  1504.     !StrScan_1:
  1505.  
  1506.     MOV        $!FuncResult, EBX
  1507.   end;
  1508. end;
  1509.  
  1510. function StrRScan(Str: PChar; Chr: Char): PChar;
  1511. begin
  1512.   asm
  1513.     MOV        EDI, $Str
  1514.     CALLN32    !StringLength
  1515.     INC        EAX
  1516.     MOV        ECX, EAX
  1517.     XOR        EBX, EBX
  1518.     MOV        AL, $Chr
  1519.     STD
  1520.     REPNZ      SCASB
  1521.     INC        EDI
  1522.     CMP        AL, [EDI]
  1523.     JNE        !StrRScan_1
  1524.     MOV        EBX, EDI
  1525.  
  1526.     !StrRScan_1:
  1527.  
  1528.     CLD
  1529.     MOV        $!FuncResult, EBX
  1530.   end;
  1531. end;
  1532.  
  1533. function StrPos(Str, SubStr: PChar): PChar;
  1534. begin
  1535.   asm
  1536.     MOV       EDI, $SubStr
  1537.     CALLN32   !StringLength
  1538.     CMP       EAX, 0
  1539.     JE        !ErrOutStrPos
  1540.  
  1541.     MOV       EDX, EAX
  1542.     MOV       EDI, $Str
  1543.     CALLN32   !StringLength
  1544.     CMP       EAX, 0
  1545.     JE        !ErrOutStrPos
  1546.     SUB       EAX, EDX
  1547.     JB        !ErrOutStrPos
  1548.     MOV       EDI, $Str
  1549.  
  1550.     !1:
  1551.  
  1552.     MOV       ESI, $SubStr
  1553.     LODSB
  1554.     REPNE     SCASB
  1555.     JNE       !ErrOutStrPos;
  1556.     MOV       EAX, ECX
  1557.     PUSH      EDI
  1558.     MOV       ECX, EDX
  1559.     DEC       ECX
  1560.     REPE      CMPSB
  1561.     MOV       ECX, EAX
  1562.     POP       EDI
  1563.     JNE       !1
  1564.     MOV       EAX, EDI
  1565.     DEC       EAX
  1566.     JMP       !Out
  1567.  
  1568.     !ErrOutStrPos:
  1569.  
  1570.     XOR EAX,EAX
  1571.  
  1572.     !Out:
  1573.  
  1574.     MOV $!FuncResult, EAX
  1575.   end;
  1576. end;
  1577.  
  1578. function StrLower(Str: PChar): PChar;
  1579. begin
  1580.   asm
  1581.     CLD
  1582.     MOV       ESI, $Str
  1583.  
  1584.     !StringLower1:
  1585.  
  1586.     LODSB
  1587.     OR        AL, AL
  1588.     JE        !OutStrLower
  1589.  
  1590.     CMP       AL, 'A'
  1591.     JB        !StringLower1
  1592.     CMP       AL, 'Z'
  1593.     JA        !StringLower1
  1594.     OR        AL, 32
  1595.     MOV       [ESI-1], AL
  1596.     JMP       !StringLower1
  1597.  
  1598.     !OutStrLower:
  1599.  
  1600.     MOV        EAX, $Str
  1601.     MOV        $!FuncResult, EAX
  1602.   END;
  1603. END;
  1604.  
  1605. function StrUpper(Str: PChar): PChar;
  1606. begin
  1607.   asm
  1608.     CLD
  1609.     MOV       ESI, $Str
  1610.  
  1611.     !StringUpper_Loop:
  1612.  
  1613.     LODSB
  1614.     OR        AL, AL
  1615.     JE        !OutStrUpper
  1616.  
  1617.     CMP       AL, 'a'
  1618.     JB        !StringUpper_Loop
  1619.     CMP       AL, 'z'
  1620.     JA        !StringUpper_Loop
  1621.     AND       AL, $DF
  1622.     MOV       [ESI-1], AL
  1623.     JMP       !StringUpper_Loop
  1624.  
  1625.     !OutStrUpper:
  1626.  
  1627.     MOV        EAX, $Str
  1628.     MOV        $!FuncResult, EAX
  1629.   end;
  1630. end;
  1631.  
  1632. function StrPas(Str: PChar): string;
  1633. begin
  1634.   Result := Str^;
  1635. end;
  1636.  
  1637. function StrAlloc(Size: Cardinal): PChar;
  1638. type
  1639.   PLong = ^LongInt;
  1640. var
  1641.   P: PChar;
  1642. begin
  1643.   GetMem(P, Size + 4);
  1644.   PLong(P)^ := Size + 4;
  1645.   Inc(P, 4);
  1646.   StrAlloc := P;
  1647. end;
  1648.  
  1649. function StrBufSize(Str: PChar): Cardinal;
  1650. type
  1651.   PLong = ^LongInt;
  1652. begin
  1653.   Dec(Str, 4);
  1654.   StrBufSize := PLong(Str)^ - 4;
  1655. end;
  1656.  
  1657. function StrNew(Str: PChar): PChar;
  1658. var
  1659.   Size: LongInt;
  1660. begin
  1661.   if Str = nil then
  1662.     StrNew := nil
  1663.   else
  1664.     begin
  1665.       Size := StrLen(Str) + 1;
  1666.       StrNew := StrMove(StrAlloc(Size), Str, Size);
  1667.     end;
  1668. end;
  1669.  
  1670. procedure StrDispose(Str: PChar);
  1671. type
  1672.   PLong = ^LongInt;
  1673. begin
  1674.   if Str <> nil then
  1675.     begin
  1676.       Dec(Str, 4);
  1677.       FreeMem(Str, PLong(Str)^);
  1678.     end;
  1679. end;
  1680.  
  1681. { String formatting routines }
  1682.  
  1683. function FormatBuf(var Buffer; BufLen: Cardinal; const Format; FmtLen: Cardinal; const Args: array of const): Cardinal;
  1684. var
  1685.   { Format and result buffers }
  1686.  
  1687.   FmtPos, OldFmtPos, BufPos, ArgPos: LongInt;
  1688.   Buf: CString absolute Buffer;
  1689.   Fmt: CString absolute Format;
  1690.  
  1691.   { Argument buffer }
  1692.  
  1693.   VArgs: array[0..1023] of TVarRec absolute Args;
  1694.  
  1695.   { Workaround for High() problem }
  1696.  
  1697.   High_Args: LongInt;
  1698.  
  1699.   { Format details }
  1700.  
  1701.   Index, Width, Precision: LongInt;
  1702.   LeftAlign: Boolean;
  1703.   ArgType: Char;
  1704.  
  1705.   { Temporary variables }
  1706.  
  1707.   C: Char;
  1708.   P: Pointer;
  1709.   E: Extended;
  1710.   Pnt, L, M: LongInt;
  1711.   S: String[80];
  1712.  
  1713.   { Raise exception: Format and argument don't match }
  1714.  
  1715.   procedure IllegalArg;
  1716.   begin
  1717.     ConvertError('Format ''%' + ArgType + ''' invalid or not compatible with argument');
  1718.   end;
  1719.  
  1720.   { Raise exception: Out of arguments }
  1721.  
  1722.   procedure OutOfArgs;
  1723.   begin
  1724.     ConvertError('No argument for format ''%' + ArgType + '''');
  1725.   end;
  1726.  
  1727.   { Get an argument from the open array. If the
  1728.     type is unexpected, raise an exception. }
  1729.  
  1730.   function GetIntegerArg: LongInt;
  1731.   begin
  1732.     if ArgPos > High_Args then OutOfArgs;
  1733.     if VArgs[ArgPos].VType <> vtInteger then IllegalArg;
  1734.     Result := VArgs[ArgPos].VInteger;
  1735.     Inc(ArgPos);
  1736.   end;
  1737.  
  1738.   function GetExtendedArg: Extended;
  1739.   begin
  1740.     if ArgPos > High_Args then OutOfArgs;
  1741.     if VArgs[ArgPos].VType <> vtExtended then IllegalArg;
  1742.     Result := VArgs[ArgPos].VExtended^;
  1743.     Inc(ArgPos);
  1744.   end;
  1745.  
  1746.   function GetPointerArg: Pointer;
  1747.   begin
  1748.     if ArgPos > High_Args then OutOfArgs;
  1749.     if VArgs[ArgPos].VType <> vtPointer then IllegalArg;
  1750.     Result := VArgs[ArgPos].VPointer;
  1751.     Inc(ArgPos);
  1752.   end;
  1753.  
  1754.   procedure GetStringArg(var FirstChar: Pointer; var Len: LongInt);
  1755.   begin
  1756.     if ArgPos > High_Args then OutOfArgs;
  1757.     case VArgs[ArgPos].VType of
  1758.       vtChar:    begin
  1759.                    FirstChar := @VArgs[ArgPos].VChar;
  1760.                    Len := 1;
  1761.                  end;
  1762.       vtString:  begin
  1763.                    FirstChar := VArgs[ArgPos].VString;
  1764.                    Len := Byte(FirstChar^);
  1765.                 {   WriteLn('Len=', Len); }
  1766.                    Inc(FirstChar);
  1767.                  end;
  1768.       vtPointer: begin { Should be vtPChar }
  1769.                    FirstChar := VArgs[ArgPos].VPChar;
  1770.                    Len := StrLen(FirstChar);
  1771.                  end;
  1772.       else       IllegalArg;
  1773.     end;
  1774.     Inc(ArgPos);
  1775.   end;
  1776.  
  1777.   { Parse a number from the format string. A '*' means:
  1778.     get the next integer argument from the open array. }
  1779.  
  1780.   function ParseNum: LongInt;
  1781.   begin
  1782.     if Fmt[FmtPos] = '*' then Result := GetIntegerArg
  1783.     else
  1784.     begin
  1785.       Result := 0;
  1786.       while (Fmt[FmtPos] in ['0'..'9']) and (FmtPos < FmtLen) do
  1787.       begin
  1788.         Result := Result * 10 + Ord(Fmt[FmtPos]) - 48;
  1789.         Inc(FmtPos);
  1790.       end;
  1791.     end;
  1792.   end;
  1793.  
  1794.   { Parse a whole format specifier. }
  1795.  
  1796.   function ParseFmtSpec: Char;
  1797.   label
  1798.     LIndex, LColon, LMinus, LWidth, LPoint, LPRec, LType;
  1799.   begin
  1800.     Width := -1;
  1801.     Index := -1;
  1802.     Precision := -1;
  1803.     LeftAlign := False;
  1804.     ArgType := #0;
  1805.     C := Fmt[FmtPos];
  1806.  
  1807.     LIndex:
  1808.  
  1809.       if not (C in ['0'..'9']) then goto LMinus;
  1810.       Width := ParseNum;
  1811.       if FmtPos >= FmtLen then exit;
  1812.       C := Fmt[FmtPos];
  1813.  
  1814.     LColon:
  1815.  
  1816.       if C <> ':' then goto LPoint;
  1817.       Index := Width;
  1818.       Width := -1;
  1819.       Inc(FmtPos);
  1820.       if FmtPos >= FmtLen then exit;
  1821.       C := Fmt[FmtPos];
  1822.  
  1823.     LMinus:
  1824.  
  1825.       if C <> '-' then goto LWidth;
  1826.       LeftAlign := True;
  1827.       Inc(FmtPos);
  1828.       if FmtPos >= FmtLen then exit;
  1829.       C := Fmt[FmtPos];
  1830.  
  1831.     LWidth:
  1832.  
  1833.       if not (C in ['0'..'9']) then goto LPoint;
  1834.       Width := ParseNum;
  1835.       if FmtPos >= FmtLen then exit;
  1836.       C := Fmt[FmtPos];
  1837.  
  1838.     LPoint:
  1839.  
  1840.       if C <> '.' then goto LType;
  1841.       Inc(FmtPos);
  1842.       Precision := ParseNum;
  1843.       if FmtPos >= FmtLen then exit;
  1844.       C := Fmt[FmtPos];
  1845.  
  1846.     LType:
  1847.  
  1848.       Result := UpCase(C);
  1849.       ArgType := Result;
  1850.       {WriteLn;
  1851.       WriteLn('Index:', Index, ' Align:', LeftAlign, ' Width:', Width, ' Prec: ', Precision, ' Type:', Result);
  1852.       WriteLn;}
  1853.       Inc(FmtPos);
  1854.   end;
  1855.  
  1856.   { Append something to the result buffer }
  1857.  
  1858.   procedure AppendStr(P: Pointer; Count: LongInt);
  1859.   begin
  1860.     if BufLen - BufPos < Count then Count := BufLen - BufPos;
  1861.     Move(P^, Buf[BufPos], Count);
  1862.     Inc(BufPos, Count);
  1863.   end;
  1864.  
  1865.   procedure AppendChar(C: Char; Count: LongInt);
  1866.   begin
  1867.     if BufLen - BufPos < Count then Count := BufLen - BufPos;
  1868.     FillChar(Buf[BufPos], Count, C);
  1869.     Inc(BufPos, Count);
  1870.   end;
  1871.  
  1872. begin
  1873.   FmtPos := 0;
  1874.   OldFmtPos := 0;
  1875.   BufPos := 0;
  1876.   ArgPos := 0;
  1877.  
  1878.   High_Args := High(Args);
  1879.  
  1880.   while (FmtPos < FmtLen) and (BufPos < BufLen) do
  1881.   begin
  1882.     C := Fmt[FmtPos];
  1883.     Inc(FmtPos);
  1884.     if C = '%' then
  1885.     begin
  1886.       C := ParseFmtSpec;
  1887.       if C = 'S' then
  1888.       begin
  1889.         GetStringArg(P, L);
  1890.         if (Precision > -1) and (Precision < L) then L := Precision;
  1891.       end
  1892.       else
  1893.       begin
  1894.         case C of
  1895.           'D': begin
  1896.                  Str(GetIntegerArg, S);
  1897.                  L := Length(S);
  1898.                  if (Precision <> -1) and (L < Precision) then
  1899.                  begin
  1900.                    SetLength(S, Precision);
  1901.                    Move(S[1], S[1 + Precision - L], L);
  1902.                    FillChar(S[1], Precision - L, '0');
  1903.                  end;
  1904.                end;
  1905.           'E': S := FloatToStrF(GetExtendedArg, ffExponent, Precision, 3);
  1906.           'F': S := FloatToStrF(GetExtendedArg, ffFixed, 9999, Precision);
  1907.           'G': S := FloatToStrF(GetExtendedArg, ffGeneral, Precision, 3);
  1908.           'N': S := FloatToStrF(GetExtendedArg, ffFixed, 9999, Precision);
  1909.           'M': S := FloatToStrF(GetExtendedArg, ffCurrency, 9999, Precision);
  1910.           'P': begin
  1911.                  L := LongInt(GetPointerArg);
  1912.                  S := IntToHex(L shr 16, 4) + ':' + IntToHex(L and $FFFF, 4);
  1913.                end;
  1914.           'X': begin
  1915.                  if Precision <> -1 then S := IntToHex(GetIntegerArg, Precision)
  1916.                  else S := IntToHex(GetIntegerArg, 0);
  1917.                end;
  1918.           else raise Exception.Create('Illegal format type');
  1919.         end;
  1920.         P := @S[1];
  1921.         L := Length(S);
  1922.       end;
  1923.  
  1924.       { Now P points to the first char to append to our result, L holds the
  1925.         length of the text to insert. If Width > L then we have to pad our
  1926.         text with spaces. }
  1927.  
  1928.       if LeftAlign then
  1929.       begin
  1930.         AppendStr(@S[1], L);
  1931.         if (Width > -1) and (L < Width) then AppendChar(' ', Width - L );
  1932.       end
  1933.       else
  1934.       begin
  1935.         if (Width > -1) and (L < Width) then AppendChar(' ', Width - L );
  1936.         AppendStr(@S[1], L);
  1937.       end;
  1938.     end
  1939.     else
  1940.     begin
  1941.       { Ordinary character }
  1942.       Buf[BufPos] := C;
  1943.       Inc(BufPos);
  1944.     end;
  1945.     OldFmtPos := FmtPos;
  1946.   end;
  1947.   Result := BufPos;
  1948. end;
  1949.  
  1950. function Format(const Format: string; const Args: array of const): string;
  1951. begin
  1952.   SetLength(Result, FormatBuf(Result[1], 255, Format[1], Length(Format), Args));
  1953. end;
  1954.  
  1955. procedure FmtStr(var Result: string; const Format: string; const Args: array of const);
  1956. begin
  1957.   SetLength(Result, FormatBuf(Result[1], 255, Format[1], Length(Format), Args));
  1958. end;
  1959.  
  1960. function StrFmt(Buffer, Format: PChar; const Args: array of const): PChar;
  1961. begin
  1962.   FormatBuf(Buffer, MaxLongInt, Format, StrLen(Format), Args);
  1963.   Result := Buffer;
  1964. end;
  1965.  
  1966. function StrLFmt(Buffer: PChar; MaxLen: Cardinal; Format: PChar; const Args: array of const): PChar;
  1967. begin
  1968.   FormatBuf(Buffer, MaxLen, Format, StrLen(Format), Args);
  1969.   Result := Buffer;
  1970. end;
  1971.  
  1972. { Floating point conversion routines }
  1973.  
  1974. function FloatToStr(Value: Extended): string;
  1975. begin
  1976.   Result := FloatToStrF(Value, ffGeneral, 15, 0);
  1977. end;
  1978.  
  1979. function FloatToStrF(Value: Extended; Format: TFloatFormat; Precision, Digits: Integer): string;
  1980. var
  1981.   P: Integer;
  1982.   Negative, TooSmall, TooLarge: Boolean;
  1983. begin
  1984.   case Format of
  1985.  
  1986.     ffGeneral:
  1987.  
  1988.       begin
  1989.         if (Precision = -1) or (Precision > 15) then Precision := 15;
  1990.         TooSmall := Value < 0.00001;
  1991.         if not TooSmall then
  1992.         begin
  1993.           Str(Value:0:999, Result);
  1994.           P := Pos('.', Result);
  1995.           Result[P] := DecimalSeparator;
  1996.           TooLarge := P > Precision + 1;
  1997.         end;
  1998.         if TooSmall or TooLarge then
  1999.           Result := FloatToStrF(Value, ffExponent, Precision, Digits)
  2000.         else
  2001.         begin
  2002.           P := Length(Result);
  2003.           while Result[P] = '0' do Dec(P);
  2004.           if Result[P] = DecimalSeparator then Dec(P);
  2005.           SetLength(Result, P);
  2006.         end;
  2007.       end;
  2008.  
  2009.     ffExponent:
  2010.  
  2011.       begin
  2012.         if (Precision = -1) or (Precision > 15) then Precision := 15;
  2013.         Str(Value:Precision + 8, Result);
  2014.         Result[3] := DecimalSeparator;
  2015.         if (Digits < 4) and (Result[Precision + 5] = '0') then
  2016.         begin
  2017.           Delete(Result, Precision + 5, 1);
  2018.           if (Digits < 3) and (Result[Precision + 5] = '0') then
  2019.           begin
  2020.             Delete(Result, Precision + 5, 1);
  2021.             if (Digits < 2) and (Result[Precision + 5] = '0') then
  2022.             begin
  2023.               Delete(Result, Precision + 5, 1);
  2024.               if (Digits < 1) and (Result[Precision + 5] = '0') then Delete(Result, Precision + 3, 3);
  2025.             end;
  2026.           end;
  2027.         end;
  2028.         if Result[1] = ' ' then Delete(Result, 1, 1);
  2029.       end;
  2030.  
  2031.     ffFixed:
  2032.  
  2033.       begin
  2034.         if Digits = -1 then Digits := 2
  2035.         else if Digits > 15 then Digits := 15;
  2036.         Str(Value:0:Digits, Result);
  2037.         if Result[1] = ' ' then Delete(Result, 1, 1);
  2038.         P := Pos('.', Result);
  2039.         if P <> 0 then Result[P] := DecimalSeparator;
  2040.       end;
  2041.  
  2042.     ffNumber:
  2043.  
  2044.       begin
  2045.         if Digits = -1 then Digits := 2
  2046.         else if Digits > 15 then Digits := 15;
  2047.         Str(Value:0:Digits, Result);
  2048.         if Result[1] = ' ' then Delete(Result, 1, 1);
  2049.         P := Pos('.', Result);
  2050.         if P <> 0 then Result[P] := DecimalSeparator;
  2051.         Dec(P, 3);
  2052.         while (P > 1) do
  2053.         begin
  2054.           if Result[P - 1] <> '-' then Insert(ThousandSeparator, Result, P);
  2055.           Dec(P, 3);
  2056.         end;
  2057.       end;
  2058.  
  2059.     ffCurrency:
  2060.  
  2061.       begin
  2062.         if Value < 0 then
  2063.         begin
  2064.           Negative := True;
  2065.           Value := -Value;
  2066.         end
  2067.         else Negative := False;
  2068.  
  2069.         if Digits = -1 then Digits := CurrencyDecimals
  2070.         else if Digits > 15 then Digits := 15;
  2071.         Str(Value:0:Digits, Result);
  2072.         if Result[1] = ' ' then Delete(Result, 1, 1);
  2073.         P := Pos('.', Result);
  2074.         if P <> 0 then Result[P] := DecimalSeparator;
  2075.         Dec(P, 3);
  2076.         while (P > 1) do
  2077.         begin
  2078.           Insert(ThousandSeparator, Result, P);
  2079.           Dec(P, 3);
  2080.         end;
  2081.  
  2082.         if not Negative then
  2083.         begin
  2084.           case CurrencyFormat of
  2085.             0: Result := CurrencyString + Result;
  2086.             1: Result := Result + CurrencyString;
  2087.             2: Result := CurrencyString + ' ' + Result;
  2088.             3: Result := Result + ' ' + CurrencyString;
  2089.           end
  2090.         end
  2091.         else
  2092.         begin
  2093.           case NegCurrFormat of
  2094.             0: Result := '(' + CurrencyString + Result + ')';
  2095.             1: Result := '-' + CurrencyString + Result;
  2096.             2: Result := CurrencyString + '-' + Result;
  2097.             3: Result := CurrencyString + Result + '-';
  2098.             4: Result := '(' + Result + CurrencyString + ')';
  2099.             5: Result := '-' + Result + CurrencyString;
  2100.             6: Result := Result + '-' + CurrencyString;
  2101.             7: Result := Result + CurrencyString + '-';
  2102.             8: Result := '-' + Result + ' ' + CurrencyString;
  2103.             9: Result := '-' + CurrencyString + ' ' + Result;
  2104.             10: Result := CurrencyString + ' ' + Result + '-';
  2105.           end;
  2106.         end;
  2107.       end;
  2108.   end;
  2109. end;
  2110.  
  2111. function FloatToText(Buffer: PChar; Value: Extended; Format: TFloatFormat; Precision, Digits: Integer): Integer;
  2112. var
  2113.   Tmp: string[40];
  2114. begin
  2115.   Tmp := FloatToStrF(Value, Format, Precision, Digits);
  2116.   Result := Length(Tmp);
  2117.   Move(Tmp[1], Buffer[0], Result);
  2118. end;
  2119.  
  2120. function StrToFloat(const S: string): Extended;
  2121. var
  2122.   Error: Integer;
  2123.   Tmp: string;
  2124.   P: Integer;
  2125. begin
  2126.   Tmp := S;
  2127.   P := Pos(DecimalSeparator, Tmp);
  2128.   if P <> 0 then Tmp[P] := '.';
  2129.   Val(Tmp, Result, Error);
  2130.   if Error <> 0 then ConvertError('Not a number!');
  2131. end;
  2132.  
  2133. function TextToFloat(Buffer: PChar; var Value: Extended): Boolean;
  2134. var
  2135.   Error: Integer;
  2136.   Tmp: string;
  2137.   P: Integer;
  2138. begin
  2139.   Tmp := StrPas(Buffer);
  2140.   P := Pos(DecimalSeparator, Tmp);
  2141.   if P <> 0 then Tmp[P] := '.';
  2142.   Val(Tmp, Value, Error);
  2143.   Result := (Error = 0);
  2144. end;
  2145.  
  2146. function FloatToTextFmt(Buffer: PChar; Value: Extended; Format: PChar): Integer;
  2147. var
  2148.   Digits: string[40];                         { Ziffern-String                   }
  2149.   Exponent: string[8];                        { Exponenten-String                }
  2150.   FmtStart, FmtStop: PChar;                   { Anfang und Ende des relevanten   }
  2151.                                               { Teils des Formatstrings          }
  2152.   ExpFmt, ExpSize: Integer;                   { Formatangaben für                }
  2153.                                               { Exponentialdarstellung           }
  2154.   Placehold: array[1..4] of Integer;          { Platzhalter in den 4 Bereichen   }
  2155.   Thousand: Boolean;                          { Tausender-Separatoren?           }
  2156.   UnexpectedDigits: Integer;                  { Anzahl der Zeichen, um die die   }
  2157.                                               { formatierte Zahl die Platzhalter }
  2158.                                               { überschreitet. Diese Zeichen     }
  2159.                                               { werden vor der ersten Ziffer     }
  2160.                                               { ausgegeben                       }
  2161.   DigitExponent: Integer;                     { Exponent der ersten Ziffer       }
  2162.                                               { von Digits                       }
  2163.  
  2164.   { Ermittle das Ende der Format-Sektion, die mit P beginnt. False, wenn leer. }
  2165.  
  2166.   function GetSectionEnd(var P: PChar): Boolean;
  2167.   var
  2168.     C: Char;
  2169.     SQ, DQ: Boolean;
  2170.   begin
  2171.     Result := False;
  2172.     SQ := False;
  2173.     DQ := False;
  2174.     C := P[0];
  2175.     while (C <> #0) and ((C <> ';') or SQ or DQ) do
  2176.     begin
  2177.       Result := True;
  2178.       case C of
  2179.         #34: if not SQ then DQ := not DQ;
  2180.         #39: if not DQ then SQ := not SQ;
  2181.       end;
  2182.       Inc(P);
  2183.       C := P[0];
  2184.     end;
  2185.   end;
  2186.  
  2187.   { Ermittle Anfang und Ende der benötigten Format-Sektion. Ist die Sektion nicht
  2188.     vorhanden, weiche auf Sektion 1 aus. Falls Sektion 2 gewählt wird, geht das
  2189.     Vorzeichen von Value verloren. }
  2190.  
  2191.   procedure GetSectionRange(Section: Integer);
  2192.   var
  2193.     Sec: array[1..3] of PChar;
  2194.     SecOk: array[1..3] of Boolean;
  2195.   begin
  2196.     Sec[1] := Format;
  2197.     SecOk[1] := GetSectionEnd(Sec[1]);
  2198.     if Section > 1 then
  2199.     begin
  2200.       Sec[2] := Sec[1];
  2201.       if Sec[2][0] <> #0 then Inc(Sec[2]);
  2202.       SecOk[2] := GetSectionEnd(Sec[2]);
  2203.       if Section > 2 then
  2204.       begin
  2205.         Sec[3] := Sec[2];
  2206.         if Sec[3][0] <> #0 then Inc(Sec[3]);
  2207.         SecOk[3] := GetSectionEnd(Sec[3]);
  2208.       end;
  2209.     end;
  2210.     if not SecOk[1] then FmtStart := nil
  2211.     else
  2212.     begin
  2213.       if not SecOk[Section] then Section := 1
  2214.       else if Section = 2 then Value := -Value;   { Vorzeichen beseitigen }
  2215.       if Section = 1 then FmtStart := Format else
  2216.       begin
  2217.         FmtStart := Sec[Section - 1];
  2218.         Inc(FmtStart);
  2219.       end;
  2220.       FmtStop := Sec[Section];
  2221.     end;
  2222.   end;
  2223.  
  2224.   { Ermittle die Format-Optionen der Sektion, die von FmtStart bis FmtStop geht. }
  2225.  
  2226.   procedure GetFormatOptions;
  2227.   var
  2228.     Fmt: PChar;
  2229.     SQ, DQ: Boolean;
  2230.     Area: Integer;
  2231.   begin
  2232.     SQ := False;
  2233.     DQ := False;
  2234.     Fmt := FmtStart;
  2235.     ExpFmt := 0;
  2236.     Area := 1;
  2237.     Thousand := False;
  2238.     PlaceHold[1] := 0;
  2239.     PlaceHold[2] := 0;
  2240.     PlaceHold[3] := 0;
  2241.     PlaceHold[4] := 0;
  2242.  
  2243.     while Fmt <> FmtStop do
  2244.     begin
  2245.       case Fmt[0] of
  2246.         #34:
  2247.         begin
  2248.           if not SQ then DQ := not DQ;
  2249.           Inc(Fmt);
  2250.         end;
  2251.  
  2252.         #39:
  2253.         begin
  2254.           if not DQ then SQ := not SQ;
  2255.           Inc(Fmt);
  2256.         end;
  2257.  
  2258.         else if not SQ or DQ then
  2259.         case Fmt[0] of
  2260.           '0':
  2261.           begin
  2262.             case Area of
  2263.               1:
  2264.                 Area := 2;
  2265.               4:
  2266.                 begin
  2267.                   Area := 3;
  2268.                   Inc(Placehold[3], PlaceHold[4]);
  2269.                   PlaceHold[4] := 0;
  2270.                 end;
  2271.             end;
  2272.             Inc(PlaceHold[Area]);
  2273.             Inc(Fmt);
  2274.           end;
  2275.  
  2276.           '#':
  2277.           begin
  2278.             if Area = 3 then Area := 4;
  2279.             Inc(PlaceHold[Area]);
  2280.             Inc(Fmt);
  2281.           end;
  2282.  
  2283.           '.':
  2284.           begin
  2285.             if Area < 3 then Area := 3;
  2286.             Inc(Fmt);
  2287.           end;
  2288.  
  2289.           ',':
  2290.           begin
  2291.             Thousand := True;
  2292.             Inc(Fmt);
  2293.           end;
  2294.  
  2295.           'e', 'E':
  2296.           if ExpFmt = 0 then
  2297.           begin
  2298.             if Fmt[0] = 'E' then ExpFmt := 1 else ExpFmt := 3;
  2299.             Inc(Fmt);
  2300.             if Fmt <> FmtStop then
  2301.             begin
  2302.               case Fmt[0] of
  2303.                 '+':
  2304.                 begin
  2305.                 end;
  2306.  
  2307.                 '-':
  2308.                 Inc(ExpFmt);
  2309.  
  2310.                 else ExpFmt := 0;
  2311.               end;
  2312.               if ExpFmt <> 0 then
  2313.               begin
  2314.                 Inc(Fmt);
  2315.                 ExpSize := 0;
  2316.                 while (Fmt <> FmtStop) and (ExpSize < 4) and (Fmt[0] in ['0'..'9']) do
  2317.                 begin
  2318.                   Inc(ExpSize);
  2319.                   Inc(Fmt);
  2320.                 end;
  2321.               end;
  2322.             end;
  2323.           end
  2324.           else Inc(Fmt);
  2325.  
  2326.           else Inc(Fmt);
  2327.  
  2328.         end; { CASE }
  2329.       end; { CASE }
  2330.     end; { WHILE .. BEGIN }
  2331.   end;
  2332.  
  2333.   function _Abs(E: Extended): Extended;
  2334.   begin
  2335.     if E < 0 then E := -E;
  2336.     Result := E;
  2337.   end;
  2338.  
  2339.   procedure FloatToStr;
  2340.   var
  2341.     I, J, Exp, Width, Decimals, DecimalPoint, Len: Integer;
  2342.   begin
  2343.     if ExpFmt = 0 then
  2344.     begin
  2345.       { Fixpoint }
  2346.       Decimals := PlaceHold[3] + PlaceHold[4];
  2347.       Width := PlaceHold[1] + PlaceHold[2] + Decimals;
  2348.  
  2349.       if Decimals = 0 then Str(Value: Width: 0, Digits)
  2350.       else Str(Value: Width + 1: Decimals, Digits);
  2351.  
  2352.       Len := Length(Digits);
  2353.  
  2354.       { Position des Punktes ermitteln }
  2355.       if Decimals = 0 then DecimalPoint := Len  + 1 else DecimalPoint := Len - Decimals;
  2356.  
  2357.       { Falls Zahl < 1 und keine Vorkommastellen gewünscht, 0 beseitigen }
  2358.       if (_Abs(Value) < 1) {(Value < 1) and (Value > -1)} and (PlaceHold[2] = 0) then
  2359.          { ^--- Wegen Bug! }
  2360.       begin
  2361.         if PlaceHold[1] = 0 then Delete(Digits, DecimalPoint - 1, 1)
  2362.         else Digits[DecimalPoint - 1] := ' ';
  2363.       end;
  2364.  
  2365.       { Optionale Nullen rechts in Leerzeichen umwandeln }
  2366.       I := Len;
  2367.       J := DecimalPoint + PlaceHold[3];
  2368.       while (I > J) and (Digits[I] = '0') do
  2369.       begin
  2370.         Digits[I] := ' ';
  2371.         Dec(I);
  2372.       end;
  2373.  
  2374.       { Falls ganze Zahl und keine Pflicht-Nachkommastellen, Komma entfernen }
  2375.  
  2376.       if (DecimalPoint < Len) and (Digits[DecimalPoint + 1] = ' ') then
  2377.           Digits[DecimalPoint] := ' ';
  2378.  
  2379.       { Leerzeichen an 'Pflicht'-Stellen links des Kommas in '0' ändern }
  2380.  
  2381.       I := DecimalPoint - PlaceHold[2];
  2382.       while (I < DecimalPoint) and (Digits[I] = ' ') do
  2383.       begin
  2384.         Digits[I] := '0';
  2385.         Inc(I);
  2386.       end;
  2387.  
  2388.       Exp := 0;
  2389.     end
  2390.     else
  2391.     begin
  2392.       { Scientific: Exactly <Width> digits with <Precision> decimals
  2393.         and adjusted exponent. }
  2394.       if PlaceHold[1] + PlaceHold[2] = 0 then PlaceHold[1] := 1;
  2395.  
  2396.       Decimals := PlaceHold[3] + PlaceHold[4];
  2397.       Width := PlaceHold[1] + PlaceHold[2] + Decimals;
  2398.  
  2399.       Str(Value: Width + 8, Digits);
  2400.  
  2401.       { Exponenten ermitteln und herausschneiden }
  2402.       I := Length(Digits) - 5;
  2403.       Val(Copy(Digits, I  + 1, 5), Exp, J);
  2404.       Exp := Exp + 1 - (PlaceHold[1] + PlaceHold[2]);
  2405.       Delete(Digits, I, 6);
  2406.  
  2407.       { Weil Str() bei Exponentialdarstellung mindestens eine Nachkommastelle
  2408.         liefert, müssen wir die eventuell beseitigen, wenn wir keine wollen. }
  2409.       if (Decimals = 0) and (PlaceHold[1] + PlaceHold[2] <= 1) then
  2410.       begin
  2411.         if Digits[4] >= '5' then
  2412.         begin
  2413.           Inc(Digits[2]);
  2414.           if Digits[2] > '9' then
  2415.           begin
  2416.             Digits[2] := '1';
  2417.             Inc(Exp);
  2418.           end;
  2419.         end;
  2420.         Delete(Digits, 3, 2);
  2421.         DecimalPoint := Length(Digits) + 1;
  2422.       end
  2423.       else
  2424.       begin
  2425. //      WriteLn(Digits);
  2426.  
  2427.         { Komma hinter die gewünschte Vorkommstellen versetzen }
  2428.         Delete(Digits, 3, 1);
  2429.         DecimalPoint := 2 + PlaceHold[1] + PlaceHold[2];
  2430.         if Decimals <> 0 then Insert('.', Digits, DecimalPoint);
  2431.       end;
  2432.  
  2433. //      WriteLn(Digits);
  2434.  
  2435.       { Optionale Nullen rechts in Leerzeichen umwandeln }
  2436.       I := Length(Digits);
  2437.       J := DecimalPoint + PlaceHold[3];
  2438.       while (I > J) and (Digits[I] = '0') do
  2439.       begin
  2440.         Digits[I] := ' ';
  2441.         Dec(I);
  2442.       end;
  2443.  
  2444.       { Falls ganze Zahl und keine Pflicht-Nachkommastellen, Komma entfernen }
  2445.  
  2446.       if (DecimalPoint < Length(Digits)) and (Digits[DecimalPoint + 1] = ' ') then
  2447.           Digits[DecimalPoint] := ' ';
  2448.  
  2449.       if Digits[1] = ' ' then
  2450.       begin
  2451.         Delete(Digits, 1, 1);
  2452.         Dec(DecimalPoint);
  2453.       end;
  2454.  
  2455.       { Exponent-String berechnen }
  2456.       Str(Abs(Exp), Exponent);
  2457.       while Length(Exponent) < ExpSize do Insert('0', Exponent, 1);
  2458.       if Exp >= 0 then
  2459.       begin
  2460.         if ExpFmt in [1, 3] then Insert('+', Exponent, 1);
  2461.       end
  2462.       else Insert('-', Exponent, 1);
  2463.       if ExpFmt < 3 then Insert('E', Exponent, 1) else Insert('e', Exponent, 1);
  2464.     end;
  2465.  
  2466.     DigitExponent := DecimalPoint - 2;
  2467.     if Digits[1] = '-' then Dec(DigitExponent);
  2468.  
  2469.     UnexpectedDigits := DecimalPoint - 1 - (PlaceHold[1] + PlaceHold[2]);
  2470.   end;
  2471.  
  2472.   function PutResult: LongInt;
  2473.   var
  2474.     SQ, DQ: Boolean;
  2475.     Fmt, Buf: PChar;
  2476.     Dig, N, I: Integer;
  2477.   begin
  2478.     SQ := False;
  2479.     DQ := False;
  2480.     Fmt := FmtStart;
  2481.     Buf := Buffer;
  2482.     Dig := 1;
  2483.  
  2484.     while Fmt <> FmtStop do
  2485.     begin
  2486.       case Fmt[0] of
  2487.         #34:
  2488.         begin
  2489.           if not SQ then DQ := not DQ;
  2490.           Inc(Fmt);
  2491.         end;
  2492.  
  2493.         #39:
  2494.         begin
  2495.           if not DQ then SQ := not SQ;
  2496.           Inc(Fmt);
  2497.         end;
  2498.  
  2499.         else
  2500.         if not SQ or DQ then
  2501.         begin
  2502.           case Fmt[0] of
  2503.             '0', '#', '.':
  2504.             begin
  2505.               if (Dig = 1) and (UnexpectedDigits > 0) then
  2506.               begin
  2507.                 { Vor der ersten Ziffer alles ausgeben, was zuviel ist }
  2508.                 for N := 1 to UnexpectedDigits do
  2509.                 begin
  2510.                   Buf[0] := Digits[N];
  2511.                   Inc(Buf);
  2512.                   if Thousand and (Digits[N] <> '-') then
  2513.                   begin
  2514.                     if (DigitExponent mod 3 = 0) and (DigitExponent > 0) then
  2515.                     begin
  2516.                       Buf[0] := ThousandSeparator;
  2517.                       Inc(Buf);
  2518.                     end;
  2519.                     Dec(DigitExponent);
  2520.                   end;
  2521.                 end;
  2522.                 Inc(Dig, UnexpectedDigits);
  2523.               end;
  2524.  
  2525.               if Digits[Dig] <> ' ' then
  2526.               begin
  2527.                 if Digits[Dig] = '.' then Buf[0] := DecimalSeparator
  2528.                 else Buf[0] := Digits[Dig];
  2529.                 Inc(Buf);
  2530.                 if Thousand and (DigitExponent mod 3 = 0) and (DigitExponent > 0) then
  2531.                 begin
  2532.                   Buf[0] := ThousandSeparator;
  2533.                   Inc(Buf);
  2534.                 end;
  2535.               end;
  2536.               Inc(Dig);
  2537.               Dec(DigitExponent);
  2538.               Inc(Fmt);
  2539.             end;
  2540.  
  2541.             'e', 'E':
  2542.             if ExpFmt <> 0 then
  2543.             begin
  2544.               Inc(Fmt);
  2545.               if Fmt <> FmtStop then
  2546.               begin
  2547.                 if Fmt[0] in ['+', '-'] then
  2548.                 begin
  2549.                   Inc(Fmt, ExpSize);
  2550.                   for N := 1 to Length(Exponent) do Buf[N - 1] := Exponent[N];
  2551.                   Inc(Buf, Length(Exponent));
  2552.                   ExpFmt := 0;
  2553.                 end;
  2554.                 Inc(Fmt);
  2555.               end;
  2556.             end; { DIESES SEMIKOLON AUF KEINEM FALL ENTFERNEN!!! }
  2557.  
  2558.             else
  2559.             begin
  2560.               { Gewöhnliches Zeichen }
  2561.               if Fmt[0] <> ',' then
  2562.               begin
  2563.                 Buf[0] := Fmt[0];
  2564.                 Inc(Buf);
  2565.               end;
  2566.               Inc(Fmt);
  2567.             end;
  2568.  
  2569.           end; { CASE }
  2570.         end
  2571.         else
  2572.         begin
  2573.           { Zeichen innerhalb von Hochkommas }
  2574.           Buf[0] := Fmt[0];
  2575.           Inc(Buf);
  2576.           Inc(Fmt);
  2577.         end;
  2578.       end; { CASE }
  2579.     end; { WHILE .. BEGIN }
  2580.     Result := LongInt(Buf) - LongInt(Buffer);
  2581.   end;
  2582.  
  2583. begin
  2584.   if Value > 0 then GetSectionRange(1)
  2585.   else if Value < 0 then GetSectionRange(2)
  2586.   else GetSectionRange(3);
  2587.  
  2588.   if FmtStart = nil then
  2589.   begin
  2590.     { WriteLn('No format sections available.'); }
  2591.     Result := FloatToText(Buffer, Value, ffGeneral, 15, 4);
  2592.   end
  2593.   else
  2594.   begin
  2595.     GetFormatOptions;
  2596.     { WriteLn('Parsing complete'); }
  2597.     if (ExpFmt = 0) and (_Abs(Value) >= 1E18) then Result := FloatToText(Buffer, Value, ffGeneral, 15, 4)
  2598.     else
  2599.     begin
  2600.       FloatToStr;
  2601. {
  2602.       WriteLn('FloatToStr() complete: "', Digits, '" / ', Exponent);
  2603.       WriteLn('Unexpected digits: ', UnexpectedDigits);
  2604.       WriteLn('DigitExponent: ', DigitExponent);}
  2605.       Result := PutResult;
  2606. {      WriteLn('PutResult() complete'); }
  2607.     end;
  2608.   end;
  2609. end;
  2610.  
  2611. function FormatFloat(const Format: string; Value: Extended): string;
  2612. var
  2613.   Temp: cstring[128];
  2614. begin
  2615.   Temp := Format;
  2616.   SetLength(Result, FloatToTextFmt(@Result[1], Value, @Temp));
  2617. end;
  2618.  
  2619. procedure FloatToDecimal(var Result: TFloatRec; Value: Extended; Precision, Decimals: Integer);
  2620. var
  2621.   Buffer: string[24];
  2622.   Error, N: Integer;
  2623. begin
  2624. {  if Precision > 15 then Precision := 15;
  2625.    if Decimals > 15 then Decimals := 15; }
  2626.  
  2627.   Str(Value:23, Buffer);
  2628.   {WriteLn('Buffer is: ', Buffer);}
  2629.  
  2630.   Result.Negative := (Buffer[1] = '-');
  2631.   Val(Copy(Buffer, 19, 5), Result.Exponent, Error);
  2632.   Inc(Result. Exponent);
  2633.   {WriteLn('Exponent is: ', Result.Exponent);}
  2634.  
  2635.   Result.Digits[0] := Buffer[2];
  2636.   Move(Buffer[4], Result.Digits[1], 14);
  2637.  
  2638.   if Decimals + Result.Exponent < Precision then N := Decimals + Result.Exponent
  2639.   else N := Precision;
  2640.  
  2641.   {WriteLn('Cut point is ', N);}
  2642.  
  2643.   if N > 15 then N := 15;
  2644.  
  2645.   {WriteLn('That makes ', N, ' with our precision.');}
  2646.  
  2647.   {WriteLn;}
  2648.  
  2649.   if N = 0 then
  2650.   begin
  2651.     if Result.Digits[0] >= '5' then
  2652.     begin
  2653.       Result.Digits[0] := '1';
  2654.       Result.Digits[1] := #0;
  2655.       Inc(Result.Exponent);
  2656.     end
  2657.     else Result.Digits[0] := #0;
  2658.   end
  2659.   else if N > 0 then
  2660.   begin
  2661.     if Result.Digits[N] >= '5' then
  2662.     begin
  2663.       { Round up }
  2664.       repeat
  2665.         Result.Digits[N] := #0;
  2666.         Dec(N);
  2667.         Inc(Result.Digits[N]);
  2668.       until (N = 0) or (Result.Digits[N] < ':');
  2669.       if Result.Digits[0] = ':' then
  2670.       begin
  2671.         Result.Digits[0] := '1';
  2672.         Inc(Result.Exponent);
  2673.       end;
  2674.     end
  2675.     else
  2676.     begin
  2677.       { Cut zeros }
  2678.       Result.Digits[N] := '0';
  2679.       while (Result.Digits[N] = '0') and (N > -1) do
  2680.       begin
  2681.         Result.Digits[N] := #0;
  2682.         Dec(N);
  2683.       end;
  2684.     end;
  2685.   end
  2686.   else Result.Digits[0] := #0;
  2687.  
  2688.   if Result.Digits[0] = #0 then
  2689.   begin
  2690.     { Zero has neither exponent nor signum }
  2691.     Result.Exponent := 0;
  2692.     Result.Negative := False;
  2693.   end;
  2694. end;
  2695.  
  2696. { Time encoding and decoding }
  2697.  
  2698. function _EncodeDate(var Date: TDateTime; Year, Month, Day: LongWord): Boolean;
  2699. var
  2700.   LeapYear: Boolean;
  2701. begin
  2702.   if (Year <= 9999) and (Month in [1..12]) and (Day in [1..31]) then
  2703.   begin
  2704.     LeapYear := (Year mod 4 = 0) and not (Year mod 100 = 0) or (Year mod 400 = 0);
  2705.     Dec(Year);
  2706.     Date := Year * 365 + Year div 4 - Year div 100 + Year div 400
  2707.             + 1 + DaysPassed[LeapYear, Month] + Day - 1;
  2708.     Result := True;
  2709.   end
  2710.   else Result := False;
  2711. end;
  2712.  
  2713. function _EncodeTime(var Time: TDateTime; Hour, Min, Sec, MSec: LongWord): Boolean;
  2714. var
  2715.   Temp: LongWord;
  2716. begin
  2717.   if (Hour < 24) and (Min < 60) and (Sec < 60) and (MSec < 1000) then
  2718.   begin
  2719.     Time := (((Hour * 60 + Min) * 60 + Sec) * 1000 + MSec) / MSecsPerDay;
  2720.     Result := True
  2721.   end
  2722.   else Result := False;
  2723. end;
  2724.  
  2725. function EncodeDate(Year, Month, Day: Word): TDateTime;
  2726. begin
  2727.   if not _EncodeDate(Result, Year, Month, Day) then ConvertError('Bla');
  2728. end;
  2729.  
  2730. function EncodeTime(Hour, Min, Sec, MSec: Word): TDateTime;
  2731. begin
  2732.   if not _EncodeTime(Result, Hour, Min, Sec, MSec) then ConvertError('Bla');
  2733. end;
  2734.  
  2735. procedure DecodeDate(Date: TDateTime; var Year, Month, Day: Word);
  2736. const
  2737.   Days400 = 146097;
  2738.   Days100 = 36524;
  2739.   Days4   = 1461;
  2740. var
  2741.   Cnt, DayNum: LongInt;
  2742.   LeapYear: Boolean;
  2743. begin
  2744.   DayNum := Trunc(Date);
  2745.  
  2746.   Year := 1;
  2747.  
  2748.   while DayNum > Days400 do
  2749.     begin
  2750.       Inc(Year, 400);
  2751.       Dec(DayNum, Days400);
  2752.     end;
  2753.  
  2754.   Cnt := 0;
  2755.   while (DayNum > Days100) and (Cnt < 3) do
  2756.     begin
  2757.       Inc(Year, 100);
  2758.       Dec(DayNum, Days100);
  2759.       Inc(Cnt);
  2760.     end;
  2761.  
  2762.   while DayNum > Days4 do
  2763.     begin
  2764.       Inc(Year, 4);
  2765.       Dec(DayNum, Days4);
  2766.     end;
  2767.  
  2768.   Cnt := 0;
  2769.   while (DayNum > 365) and (Cnt < 3) do
  2770.     begin
  2771.       Inc(Year);
  2772.       Dec(DayNum, 365);
  2773.       Inc(Cnt);
  2774.     end;
  2775.  
  2776.   LeapYear := (Year mod 4 = 0) and not (Year mod 100 = 0) or (Year mod 400 = 0);
  2777.  
  2778.   Month := 0;
  2779.   while DaysPassed[LeapYear, Month + 1] < DayNum do
  2780.     Inc(Month);
  2781.  
  2782.   Day := DayNum - DaysPassed[LeapYear, Month];
  2783. end;
  2784.  
  2785. procedure DecodeTime(Time: TDateTime; var Hour, Min, Sec, MSec: Word);
  2786. begin
  2787.   Time := Frac(Time) * 24;
  2788.   Hour := Trunc(Time);
  2789.   Time := Frac(Time) * 60;
  2790.   Min  := Trunc(Time);
  2791.   Time := Frac(Time) * 60;
  2792.   Sec  := Trunc(Time);
  2793.   MSec := Trunc(Frac(Time) * 1000);
  2794. end;
  2795.  
  2796. function DayOfWeek(Date: TDateTime): Integer;
  2797. begin
  2798.   DayOfWeek := 1 + (1 + Trunc(Date)) mod 7;
  2799. end;
  2800.  
  2801. function Date: TDateTime;
  2802. var
  2803.   DT: DATETIME;
  2804. begin
  2805.   DosGetDateTime (DT);
  2806.   Date := EncodeDate(DT.Year, DT.Month, DT.Day);
  2807. end;
  2808.  
  2809. function Time: TDateTime;
  2810. var
  2811.   DT: DATETIME;
  2812. begin
  2813.   DosGetDateTime (DT);
  2814.   Time := EncodeTime(DT.Hour, DT.Min, DT.Sec, DT.Hundredths * 10);
  2815. end;
  2816.  
  2817. function Now: TDateTime;
  2818. var
  2819.   DT: DATETIME;
  2820. begin
  2821.   DosGetDateTime (DT);
  2822.   Now := EncodeDate(DT.Year, DT.Month, DT.Day) + EncodeTime(DT.Hour, DT.Min, DT.Sec, DT.Hundredths * 10);
  2823. end;
  2824.  
  2825. { Date/time to string conversions }
  2826.  
  2827. procedure DateTimeToString(var Result: string; const Format: string; DateTime: TDateTime);
  2828. var
  2829.   Year, Month, Day, Hour, Min, Sec, MSec, Hour12: Integer;
  2830.   BeforeNoon: Boolean;
  2831.  
  2832.   procedure _DateTimeToString(var Result: string; const Format: string; Recursive: Boolean);
  2833.     { Internal function to control recursion in format specifiers. Avoids
  2834.       stack overflow when format strings contain macros for other format
  2835.       strings. }
  2836.  
  2837.   var
  2838.     Start, Count, Pos, Len, LastHourPos, LastHourSize, Tmp: Integer;
  2839.     Token: Char;
  2840.     UseMinutes: Boolean;
  2841.  
  2842.     procedure AppendInt(I, Digits: Integer);
  2843.     var
  2844.       S: string[5];
  2845.       P: Integer;
  2846.     begin
  2847.       Str(I:Digits, S);
  2848.       P := 1;
  2849.       while S[P] = ' ' do
  2850.       begin
  2851.         S[P] := '0';
  2852.         Inc(P);
  2853.       end;
  2854.       AppendStr(Result, S);
  2855.     end;
  2856.  
  2857.     procedure AppendStr(const S: string);
  2858.     begin
  2859.       Insert(S, Result, Length(Result) + 1);
  2860.     end;
  2861.  
  2862.     function CountChar(C: Char; Max: Integer): Integer;
  2863.     var
  2864.       Result: Integer;
  2865.     begin
  2866.       Result := 1;
  2867.       while (Pos <= Len) and (UpCase(Format[Pos]) = C) and (Result < Max) do
  2868.       begin
  2869.         Inc(Pos);
  2870.         Inc(Result);
  2871.       end;
  2872.       CountChar := Result;
  2873.     end;
  2874.  
  2875.     function IsSubStr(const S: string): Boolean;
  2876.     begin
  2877.       IsSubStr := (UpperCase(Copy(Format, Pos, Length(S))) = S);
  2878.     end;
  2879.  
  2880.     procedure GetNextToken(BeforeNoon: Boolean);
  2881.     begin
  2882.       Start := Pos;
  2883.       Token := UpCase(Format[Pos]);
  2884.       Inc(Pos);
  2885.       case Token of
  2886.         #34,
  2887.         #39: begin
  2888.                Inc(Start);
  2889.                while (Pos <= Len) and (Format[Pos] <> Token) do Inc(Pos);
  2890.                Count := Pos - Start;
  2891.                if Pos < Len then Inc(Pos);
  2892.                Token := '$';
  2893.              end;
  2894.         'D': Count := CountChar('D', 6);
  2895.         'M': Count := CountChar('M', 4);
  2896.         'Y': Count := CountChar('Y', 4);
  2897.         'H',
  2898.         'N',
  2899.         'S',
  2900.         'T': Count := CountChar(Token, 2);
  2901.         'A': begin
  2902.                if IsSubStr('MPM') then
  2903.                begin
  2904.                  Inc(Pos, 3);
  2905.                  Count := 0;
  2906.                end
  2907.                else if IsSubStr('/P') then
  2908.                begin
  2909.                  Inc(Pos, 2);
  2910.                  if not BeforeNoon then Inc(Start, 2);
  2911.                  Count := 1;
  2912.                end
  2913.                else if IsSubStr('M/PM') then
  2914.                begin
  2915.                  Inc(Pos, 4);
  2916.                  if not BeforeNoon then Inc(Start, 3);
  2917.                  Count := 2;
  2918.                end
  2919.                else
  2920.                begin
  2921.                  Token := '$';
  2922.                  Count := 1;
  2923.                end;
  2924.              end;
  2925.         'C',
  2926.         '/',
  2927.         ':': begin
  2928.                { Nope }
  2929.              end;
  2930.         else begin
  2931.                Token := '$';
  2932.                Count := 1;
  2933.                while (Pos <= Len) and not (UpCase(Format[Pos]) in
  2934.                    [#34, #39, 'A', 'C', 'D', 'H', 'M', 'N', 'S', 'T', 'Y', ':', '/']) do
  2935.                begin
  2936.                  Inc(Pos);
  2937.                  Inc(Count);
  2938.                end;
  2939.              end;
  2940.       end;
  2941.  
  2942.       if (Token = 'M') and UseMinutes then Token := 'N';
  2943.  
  2944.       case Token of
  2945.         'H': UseMinutes := True;
  2946.         'A', 'C', 'D', 'M', 'N', 'S', 'T', 'Y': UseMinutes := False;
  2947.       end;
  2948.     end;
  2949.  
  2950.   begin
  2951.     Pos := 1;
  2952.     Len := Length(Format);
  2953.     LastHourPos := 0;
  2954.     UseMinutes := False;
  2955.  
  2956.     if Len = 0 then _DateTimeToString(Result, 'C', True)
  2957.     else while (Pos <= Len) do
  2958.     begin
  2959.       GetNextToken(BeforeNoon);
  2960.       // WriteLn('Token=', Token, ' Start=', Start, ' Count=', Count);
  2961.       case Token of
  2962.         'C': if Recursive then
  2963.              begin
  2964.                _DateTimeToString(Result, ShortDateFormat, False);
  2965.                if (Hour + Min + Sec) > 0 then
  2966.                begin
  2967.                  AppendStr(' ');
  2968.                  _DateTimeToString(Result, LongTimeFormat, False);
  2969.                end;
  2970.              end
  2971.              else AppendStr('C');
  2972.         'D': case Count of
  2973.                1: AppendInt(Day, 1);
  2974.                2: AppendInt(Day, 2);
  2975.                3: AppendStr(ShortDayNames[DayOfWeek(DateTime)]);
  2976.                4: AppendStr(LongDayNames[DayOfWeek(DateTime)]);
  2977.                5: if Recursive then _DateTimeToString(Result, ShortDateFormat, False)
  2978.                   else AppendStr('DDDDD');
  2979.                6: if Recursive then _DateTimeToString(Result, LongDateFormat, False)
  2980.                   else AppendStr('DDDDDD');
  2981.              end;
  2982.         'M': case Count of
  2983.                1: AppendInt(Month, 1);
  2984.                2: AppendInt(Month, 2);
  2985.                3: AppendStr(ShortMonthNames[Month]);
  2986.                4: AppendStr(LongMonthNames[Month]);
  2987.              end;
  2988.         'Y': case Count of
  2989.                1, 2: AppendInt(Year mod 100, 2);
  2990.                3, 4: AppendInt(Year, 4);
  2991.              end;
  2992.         'H': begin
  2993.                LastHourPos := Length(Result) + 1;
  2994.                LastHourSize := Count;
  2995.                AppendInt(Hour, Count);
  2996.              end;
  2997.         'N': AppendInt(Min, Count);
  2998.         'S': AppendInt(Sec, Count);
  2999.         'T': case Count of
  3000.                1: if Recursive then _DateTimeToString(Result, ShortTimeFormat, False)
  3001.                   else AppendStr('T');
  3002.                2: if Recursive then _DateTimeToString(Result, LongTimeFormat, False)
  3003.                   else AppendStr('TT');
  3004.              end;
  3005.         'A': begin
  3006.                if LastHourPos <> 0 then
  3007.                begin
  3008.                  if (LastHourSize = 1) and (Hour < 10) then Tmp := 1
  3009.                  else Tmp := 2;
  3010.                  Delete(Result, LastHourPos, Tmp);
  3011.                  if (LastHourSize = 2) and (Hour12 < 10) then
  3012.                      Insert('0' + IntToStr(Hour12), Result, LastHourPos)
  3013.                  else Insert(IntToStr(Hour12), Result, LastHourPos);
  3014.                  LastHourPos := 0;
  3015.                end;
  3016.                case Count of
  3017.                  0: if BeforeNoon then AppendStr(TimeAMString)
  3018.                     else AppendStr(TimePMString);
  3019.                  1: AppendStr(Format[Start]);
  3020.                  2: AppendStr(Format[Start] + Format[Start  + 1]);
  3021.                end
  3022.              end;
  3023.         '/': AppendStr(DateSeparator);
  3024.         ':': AppendStr(TimeSeparator);
  3025.         '$': AppendStr(Copy(Format, Start, Count));
  3026.       end;
  3027.     end;
  3028.   end;
  3029.  
  3030. begin
  3031.   DecodeDate(DateTime, Year, Month, Day);
  3032.   DecodeTime(DateTime, Hour, Min, Sec, MSec);
  3033.  
  3034.   if (Hour = 0) or (Hour > 12) then
  3035.   begin
  3036.     if Hour = 0 then Hour12 := 12
  3037.     else Hour12 := Hour - 12;
  3038.     BeforeNoon := False;
  3039.   end
  3040.   else
  3041.   begin
  3042.     BeforeNoon := True;
  3043.     Hour12 := Hour;
  3044.   end;
  3045.   Result := '';
  3046.  
  3047.   if Length(Format) <> 0 then _DateTimeToString(Result, Format, True)
  3048.   else _DateTimeToString(Result, 'C', True)
  3049. end;
  3050.  
  3051. function DateToStr(Date: TDateTime): string;
  3052. begin
  3053.   DateTimeToString(Result, ShortDateFormat, Date);
  3054. end;
  3055.  
  3056. function TimeToStr(Time: TDateTime): string;
  3057. begin
  3058.   DateTimeToString(Result, LongTimeFormat, Time);
  3059. end;
  3060.  
  3061. function DateTimeToStr(DateTime: TDateTime): string;
  3062. begin
  3063.   DateTimeToString(Result, ShortDateFormat + ' ' + LongTimeFormat, DateTime);
  3064. end;
  3065.  
  3066. function FormatDateTime(const Format: string; DateTime: TDateTime): string;
  3067. begin
  3068.   DateTimeToString(Result, Format, DateTime);
  3069. end;
  3070.  
  3071. { String to date/time conversions }
  3072.  
  3073. procedure IgnoreSpaces(const S: string; var Pos: Integer; Len: Integer);
  3074. begin
  3075.   while (Pos <= Len) and (S[Pos] = ' ') do Inc(Pos);
  3076. end;
  3077.  
  3078. function GetNumber(var Num: Integer; const S: string; var Pos: Integer; Len: Integer): Boolean;
  3079. begin
  3080.   Result := False;
  3081.   Num := 0;
  3082.   IgnoreSpaces(S, Pos, Len);
  3083.   while (Pos <= Len) and (S[Pos] in ['0'..'9']) do
  3084.   begin
  3085.     Result := True;
  3086.     Num := Num * 10 + Ord(S[Pos]) - 48;
  3087.     Inc(Pos);
  3088.   end;
  3089. end;
  3090.  
  3091. function CompareString(const SubStr, S: string; var Pos: Integer; Len: Integer): Boolean;
  3092. begin
  3093.   if CompareText(SubStr, Copy(S, 1, Length(SubStr))) = 0 then
  3094.   begin
  3095.     Result := True;
  3096.     Inc(Pos, Length(SubStr));
  3097.   end
  3098.   else Result := False;
  3099. end;
  3100.  
  3101. function CompareChar(C: Char; S: string; var Pos: Integer; Len: Integer): Boolean;
  3102. begin
  3103.   if (Pos <= Len) and (UpCase(C) = UpCase(S[Pos])) then
  3104.   begin
  3105.     Result := True;
  3106.     Inc(Pos);
  3107.   end
  3108.   else Result := False;
  3109. end;
  3110.  
  3111. function CutString(var S: string; Separator: Char): string;
  3112. var
  3113.   P: Integer;
  3114. begin
  3115.   P := Pos(Separator, S);
  3116.   if P = 0 then P := Length(S) + 1;
  3117.   Result := Copy(S, 1, P - 1);
  3118.   Delete(S, 1, P);
  3119. end;
  3120.  
  3121. function ParseDate(var Date: TDateTime; const S: String; var Pos: Integer; Len: Integer): Boolean;
  3122. var
  3123.   Head, Temp: string[15];
  3124.   N, Year, Month, Day: Integer;
  3125.   Number: array[1..3] of Integer;
  3126.   Order: string[3];
  3127.  
  3128.   function GetCurrentYear: Integer;
  3129.   var
  3130.     M, D: Integer;
  3131.   begin
  3132.     DecodeDate(Now, Result, M, D);
  3133.   end;
  3134.  
  3135. begin
  3136.   Order := 'XXX';
  3137.  
  3138.   Result := False;
  3139.  
  3140.   if not GetNumber(Number[1], S, Pos, Len) then Exit;
  3141.   if not CompareChar(DateSeparator, S, Pos, Len) then Exit;
  3142.   if not GetNumber(Number[2], S, Pos, Len) then Exit;
  3143.   if not CompareChar(DateSeparator, S, Pos, Len) then Exit;
  3144.   if not GetNumber(Number[3], S, Pos, Len) then Number[3] := -1;
  3145.  
  3146.   for N := 1 to 3 do WriteLn(Number[N]);
  3147.  
  3148.   Temp := ShortDateFormat;
  3149.  
  3150.   for N := 1 to 3 do
  3151.   begin
  3152.     Head := CutString(Temp, '/');
  3153.     if Length(Head) <> 0 then Order[N] := UpCase(Head[1]);
  3154.   end;
  3155.  
  3156.   if Order = 'MDY' then
  3157.   begin
  3158.     Month := Number[1];
  3159.     Day := Number[2];
  3160.     Year := Number[3];
  3161.   end
  3162.   else if Order = 'DMY' then
  3163.   begin
  3164.     WriteLn('DMY');
  3165.     Day := Number[1];
  3166.     Month := Number[2];
  3167.     Year := Number[3];
  3168.   end
  3169.   else if Order = 'YMD' then
  3170.   begin
  3171.     if Number[3] = -1 then
  3172.     begin
  3173.       Year := -1;
  3174.       Month := Number[1];
  3175.       Day := Number[2];
  3176.     end
  3177.     else
  3178.     begin
  3179.       Year := Number[1];
  3180.       Month := Number[2];
  3181.       Day := Number[3];
  3182.     end;
  3183.   end;
  3184.  
  3185.   if Year = -1 then Year := GetCurrentYear
  3186.   else if Year < 100 then Inc(Year, 1900);
  3187.  
  3188.   Result := True;
  3189.   Result := _EncodeDate(Date, Year, Month, Day);
  3190. end;
  3191.  
  3192. function ParseTime(var Time: TDateTime; const S: String; var Pos: Integer; Len: Integer): Boolean;
  3193. var
  3194.   Hour, Min, Sec: Word;
  3195. begin
  3196.   Result := False;
  3197.  
  3198.   if not GetNumber(Hour, S, Pos, Len) then Exit;
  3199.   if not CompareChar(TimeSeparator, S, Pos, Len) then Exit;
  3200.   if not GetNumber(Min, S, Pos, Len) then Exit;
  3201.   if CompareChar(TimeSeparator, S, Pos, Len) and not GetNumber(Sec, S, Pos, Len) then Exit;
  3202.  
  3203.   IgnoreSpaces(S, Pos, Len);
  3204.   if CompareChar('A', S, Pos, Len) then
  3205.   begin
  3206.     CompareChar('M', S, Pos, Len);
  3207.     if Hour = 12 then Hour := 0;
  3208.   end
  3209.   else if CompareChar('P', S, Pos, Len) then
  3210.   begin
  3211.     CompareChar('M', S, Pos, Len);
  3212.     if (Hour >= 1) and (Hour <= 11) then Inc(Hour, 12);
  3213.   end;
  3214.  
  3215.   Result := _EncodeTime(Time, Hour, Min, Sec, 0);
  3216. end;
  3217.  
  3218. function StrToDate(const S: string): TDateTime;
  3219. var
  3220.   Pos, Len: Integer;
  3221. begin
  3222.   Pos := 1;
  3223.   Len := Length(S);
  3224.   if not ParseDate(Result, S, Pos, Len) then ConvertError('No legal Date!');
  3225. end;
  3226.  
  3227. function StrToTime(const S: string): TDateTime;
  3228. var
  3229.   Pos, Len: Integer;
  3230. begin
  3231.   Pos := 1;
  3232.   Len := Length(S);
  3233.   if not ParseTime(Result, S, Pos, Len) then ConvertError('No legal Time!');
  3234. end;
  3235.  
  3236. function StrToDateTime(const S: string): TDateTime;
  3237. var
  3238.   Time: TDateTime;
  3239.   Pos, Len: Integer;
  3240. begin
  3241.   Pos := 1;
  3242.   Len := Length(S);
  3243.   if not ParseDate(Result, S, Pos, Len) then ConvertError('No legal date!');
  3244.   if ParseDate(Time, S, Pos, Len) then Result := Result + Time;
  3245. end;
  3246.  
  3247. { Initialization file support }
  3248.  
  3249. {$ifdef PM }
  3250.  
  3251. var
  3252.   UserProfile: HINI;
  3253.  
  3254. imports
  3255.  
  3256. FUNCTION PrfQueryProfileInt(ahini:HINI;CONST pszApp,pszKey:CSTRING;
  3257.              sDefault:LONG):LONG;
  3258.              APIENTRY; PMSHAPI index 114;
  3259.  
  3260. FUNCTION PrfQueryProfileString(ahini:HINI;CONST pszApp,pszKey,pszDefault: CSTRING;
  3261.              pBUFFER: PCHAR; cchBufferMax:ULONG):ULONG;
  3262.              APIENTRY; PMSHAPI index 115;
  3263.  
  3264. end;
  3265.  
  3266. {
  3267. function OpenProfile: Boolean;
  3268. var
  3269.   Info: PRFPROFILE;
  3270.   Name: cstring[256];
  3271. begin
  3272.   Info.cchUserName := 256;
  3273.   Info.pszUserName := @Name;
  3274.   Info.cchSysName := 0;
  3275.   Info.pszSysName := @Name;
  3276.   Result := PrfQueryProfile(AppHandle, Info);
  3277.   if not Result then Exit;
  3278.   UserProfile := PrfOpenProfile(AppHandle, Name);
  3279. end;
  3280.  
  3281. procedure CloseProfile;
  3282. begin
  3283.   PrfCloseProfile(UserProfile);
  3284. end;
  3285. }
  3286.  
  3287. function GetProfileStr(const Section, Entry: cstring; const Default: string): string;
  3288. var
  3289.   CDefault: cstring[256];
  3290. begin
  3291.   CDefault := Default;
  3292.   SetLength(Result, PrfQueryProfileString(HINI_UserProfile, Section,
  3293.       Entry, CDefault, @Result[1], 255));
  3294. end;
  3295.  
  3296. function GetProfileChar(const Section, Entry: cstring; Default: Char): Char;
  3297. var
  3298.   InBuf, OutBuf: cstring[2];
  3299. begin
  3300.   InBuf[0] := Default;
  3301.   InBuf[1] := #0;
  3302.   PrfQueryProfileString(HINI_UserProfile, Section, Entry, InBuf, @OutBuf, 2);
  3303.   Result := OutBuf[0];
  3304. end;
  3305.  
  3306. function GetProfileInt(const Section, Entry: cstring; Default: Integer): Integer;
  3307. begin
  3308.   Result := PrfQueryProfileInt(HINI_UserProfile, Section, Entry, Default);
  3309. end;
  3310.  
  3311. procedure GetFormatSettings;
  3312. const
  3313.   Key = 'PM_National';
  3314. var
  3315.   Temp: Integer;
  3316. begin
  3317.   // if not OpenProfile then Exit;
  3318.  
  3319.   TimeAmString := GetProfileStr(Key, 's1159', 'am');
  3320.   TimePmString := GetProfileStr(Key, 's2359', 'pm');
  3321.   CurrencyString := GetProfileStr(Key, 'sCurrency', '$');
  3322.   ThousandSeparator := GetProfileChar(Key, 'sThousand', ',');
  3323.   DecimalSeparator := GetProfileChar(Key, 'sDecimal', '.');
  3324.   DateSeparator := GetProfileChar(Key, 'sDate', '/');
  3325.   TimeSeparator := GetProfileChar(Key, 'sTime', ':');
  3326.   ListSeparator := GetProfileChar(Key, 'sList', 'X');
  3327.  
  3328.   DateOrder := GetProfileInt(Key, 'iDate', 0);
  3329.   case DateOrder of
  3330.     0: begin
  3331.          ShortDateFormat := 'm/d/yy';
  3332.          LongDateFormat := 'mm/dd/yyyy';
  3333.        end;
  3334.     1: begin
  3335.          ShortDateFormat := 'd/m/yy';
  3336.          LongDateFormat := 'dd/mm/yyyy';
  3337.        end;
  3338.     2: begin
  3339.          ShortDateFormat := 'y/m/dd';
  3340.          LongDateFormat := 'yyyy/mm/dd';
  3341.        end;
  3342.   end;
  3343.  
  3344.   CurrencyFormat := GetProfileInt(Key, 'iCurrency', 0);
  3345.  
  3346.   case CurrencyFormat of
  3347.     0: NegCurrFormat := 1;
  3348.     1: NegCurrFormat := 5;
  3349.     2: NegCurrFormat := 9;
  3350.     3: NegCurrFormat := 8;
  3351.   end;
  3352.  
  3353.   CurrencyDecimals := GetProfileInt(Key, 'iDigits', 2);
  3354.  
  3355.   case GetProfileInt(Key, 'iLzero', 0) of
  3356.     0: begin
  3357.          ShortTimeFormat := 'h:mm';
  3358.          LongTimeFormat := 'h:mm:ss';
  3359.        end;
  3360.     1: begin
  3361.          ShortTimeFormat := 'hh:mm';
  3362.          LongTimeFormat := 'hh:mm:ss';
  3363.        end;
  3364.   end;
  3365.  
  3366.   if GetProfileInt(Key, 'iTime', 0) = 0 then
  3367.   begin
  3368.     ShortTimeFormat := ShortTimeFormat + ' ampm';
  3369.     LongTimeFormat := LongTimeFormat + ' ampm';
  3370.     TwelveHours := True;
  3371.   end
  3372.   else TwelveHours := False;
  3373.  
  3374.   // CloseProfile;
  3375. end;
  3376.  
  3377. {$else}
  3378.  
  3379. procedure GetFormatSettings;
  3380. var
  3381.   CC: COUNTRYCODE;
  3382.   CI: COUNTRYINFO;
  3383.   L: LongInt;
  3384. begin
  3385.   CC.Country :=  0;
  3386.   CC.CodePage := 0;
  3387.   if DosQueryCtryInfo(SizeOf(CI), CC, CI, L) <> NO_ERROR then Halt(255);
  3388.  
  3389.   CurrencyString := CI.szCurrency;
  3390.   CurrencyFormat := CI.fsCurrencyFmt;
  3391.  
  3392.   ThousandSeparator := CI.szThousandsSeparator[0];
  3393.   DecimalSeparator := CI.szDecimal[0];
  3394.   DateSeparator := CI.szDateSeparator[0];
  3395.   TimeSeparator := CI.szTimeSeparator[0];
  3396.   ListSeparator := CI.szDataSeparator[0];
  3397.   CurrencyDecimals := CI.cDecimalPlace;
  3398.  
  3399.   case CurrencyFormat of
  3400.     0: NegCurrFormat := 1;
  3401.     1: NegCurrFormat := 5;
  3402.     2: NegCurrFormat := 9;
  3403.     3: NegCurrFormat := 8;
  3404.   end;
  3405.  
  3406.   DateOrder := CI.fsDateFmt;
  3407.   case DateOrder of
  3408.     0: begin
  3409.          ShortDateFormat := 'mm/dd/yy';
  3410.          LongDateFormat := 'mm/dd/yyyy';
  3411.        end;
  3412.     1: begin
  3413.          ShortDateFormat := 'dd/mm/yy';
  3414.          LongDateFormat := 'dd/mm/yyyy';
  3415.        end;
  3416.     2: begin
  3417.          ShortDateFormat := 'yy/mm/dd';
  3418.          LongDateFormat := 'yyyy/mm/dd';
  3419.        end;
  3420.   end;
  3421.  
  3422.   case CI.fsTimeFmt of
  3423.     0: begin
  3424.          ShortTimeFormat := 'hh:mm ampm';
  3425.          LongTimeFormat := 'hh:mm:ss ampm';
  3426.          TwelveHours := True;
  3427.        end;
  3428.     1: begin
  3429.          ShortTimeFormat := 'hh:mm';
  3430.          LongTimeFormat := 'hh:mm:ss';
  3431.          TwelveHours := False;
  3432.        end;
  3433.   end;
  3434.  
  3435.   DosQueryCollate(256, CC, CollatingSequence, L);
  3436. end;
  3437.  
  3438. {$endif}
  3439.  
  3440. begin
  3441.   GetFormatSettings;
  3442. end.
  3443.  
  3444. {
  3445.   Changes: 25-11-95 - CompareText optimized
  3446.            28-11-95 - OpenProfile / CloseProfile entfernt (weil
  3447.                       überflüssig)
  3448.            30-11-95 - Fehler in FindFirst/FindNext beseitigt
  3449.            07-12-95 - Neue Funktion EditFileName
  3450.            08.12.95 - Kleine Änderung an NewStr / DisposeStr
  3451.            18.12.95 - Neue Funktionen FileOpenOrCreate und
  3452.                       FileCreateIfNew für atomare Operationen bei
  3453.                       z.B. LogFiles
  3454. }
  3455.