stringl.inc 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 1999-2000 by the Free Pascal development team
  4. See the file COPYING.FPC, included in this distribution,
  5. for details about the copyright.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. **********************************************************************}
  10. {****************************************************************************}
  11. {* TStringsEnumerator *}
  12. {****************************************************************************}
  13. constructor TStringsEnumerator.Create(AStrings: TStrings);
  14. begin
  15. inherited Create;
  16. FStrings := AStrings;
  17. FPosition := -1;
  18. end;
  19. function TStringsEnumerator.GetCurrent: String;
  20. begin
  21. Result := FStrings[FPosition];
  22. end;
  23. function TStringsEnumerator.MoveNext: Boolean;
  24. begin
  25. Inc(FPosition);
  26. Result := FPosition < FStrings.Count;
  27. end;
  28. {****************************************************************************}
  29. {* TStrings *}
  30. {****************************************************************************}
  31. // Function to quote text. Should move maybe to sysutils !!
  32. // Also, it is not clear at this point what exactly should be done.
  33. { //!! is used to mark unsupported things. }
  34. Function QuoteString (Const S : String; Quote : String) : String;
  35. Var
  36. I,J : Integer;
  37. begin
  38. J:=0;
  39. Result:=S;
  40. for i:=1to length(s) do
  41. begin
  42. inc(j);
  43. if S[i]=Quote then
  44. begin
  45. System.Insert(Quote,Result,J);
  46. inc(j);
  47. end;
  48. end;
  49. Result:=Quote+Result+Quote;
  50. end;
  51. {
  52. For compatibility we can't add a Constructor to TSTrings to initialize
  53. the special characters. Therefore we add a routine which is called whenever
  54. the special chars are needed.
  55. }
  56. Procedure Tstrings.CheckSpecialChars;
  57. begin
  58. If Not FSpecialCharsInited then
  59. begin
  60. FQuoteChar:='"';
  61. FDelimiter:=',';
  62. FNameValueSeparator:='=';
  63. FSpecialCharsInited:=true;
  64. FLBS:=DefaultTextLineBreakStyle;
  65. end;
  66. end;
  67. Function TStrings.GetLBS : TTextLineBreakStyle;
  68. begin
  69. CheckSpecialChars;
  70. Result:=FLBS;
  71. end;
  72. Procedure TStrings.SetLBS (AValue : TTextLineBreakStyle);
  73. begin
  74. CheckSpecialChars;
  75. FLBS:=AValue;
  76. end;
  77. procedure TStrings.SetDelimiter(c:Char);
  78. begin
  79. CheckSpecialChars;
  80. FDelimiter:=c;
  81. end;
  82. procedure TStrings.SetQuoteChar(c:Char);
  83. begin
  84. CheckSpecialChars;
  85. FQuoteChar:=c;
  86. end;
  87. procedure TStrings.SetNameValueSeparator(c:Char);
  88. begin
  89. CheckSpecialChars;
  90. FNameValueSeparator:=c;
  91. end;
  92. function TStrings.GetCommaText: string;
  93. Var
  94. C1,C2 : Char;
  95. FSD : Boolean;
  96. begin
  97. CheckSpecialChars;
  98. FSD:=StrictDelimiter;
  99. C1:=Delimiter;
  100. C2:=QuoteChar;
  101. Delimiter:=',';
  102. QuoteChar:='"';
  103. StrictDelimiter:=False;
  104. Try
  105. Result:=GetDelimitedText;
  106. Finally
  107. Delimiter:=C1;
  108. QuoteChar:=C2;
  109. StrictDelimiter:=FSD;
  110. end;
  111. end;
  112. Function TStrings.GetDelimitedText: string;
  113. Var
  114. I : integer;
  115. p : pchar;
  116. c : set of char;
  117. S : String;
  118. begin
  119. CheckSpecialChars;
  120. result:='';
  121. if StrictDelimiter then
  122. c:=[#0,Delimiter]
  123. else
  124. c:=[#0..' ',QuoteChar,Delimiter];
  125. For i:=0 to count-1 do
  126. begin
  127. S:=Strings[i];
  128. p:=pchar(S);
  129. while not(p^ in c) do
  130. inc(p);
  131. // strings in list may contain #0
  132. if (p<>pchar(S)+length(S)) and not StrictDelimiter then
  133. Result:=Result+QuoteString(S,QuoteChar)
  134. else
  135. Result:=Result+S;
  136. if I<Count-1 then
  137. Result:=Result+Delimiter;
  138. end;
  139. If (Length(Result)=0) and (Count=1) then
  140. Result:=QuoteChar+QuoteChar;
  141. end;
  142. procedure TStrings.GetNameValue(Index : Integer; Out AName,AValue : String);
  143. Var L : longint;
  144. begin
  145. CheckSpecialChars;
  146. AValue:=Strings[Index];
  147. L:=Pos(FNameValueSeparator,AValue);
  148. If L<>0 then
  149. begin
  150. AName:=Copy(AValue,1,L-1);
  151. System.Delete(AValue,1,L);
  152. end
  153. else
  154. AName:='';
  155. end;
  156. function TStrings.ExtractName(const s:String):String;
  157. var
  158. L: Longint;
  159. begin
  160. CheckSpecialChars;
  161. L:=Pos(FNameValueSeparator,S);
  162. If L<>0 then
  163. Result:=Copy(S,1,L-1)
  164. else
  165. Result:='';
  166. end;
  167. function TStrings.GetName(Index: Integer): string;
  168. Var
  169. V : String;
  170. begin
  171. GetNameValue(Index,Result,V);
  172. end;
  173. Function TStrings.GetValue(const Name: string): string;
  174. Var
  175. L : longint;
  176. N : String;
  177. begin
  178. Result:='';
  179. L:=IndexOfName(Name);
  180. If L<>-1 then
  181. GetNameValue(L,N,Result);
  182. end;
  183. Function TStrings.GetValueFromIndex(Index: Integer): string;
  184. Var
  185. N : String;
  186. begin
  187. GetNameValue(Index,N,Result);
  188. end;
  189. Procedure TStrings.SetValueFromIndex(Index: Integer; const Value: string);
  190. begin
  191. If (Value='') then
  192. Delete(Index)
  193. else
  194. begin
  195. If (Index<0) then
  196. Index:=Add('');
  197. CheckSpecialChars;
  198. Strings[Index]:=GetName(Index)+FNameValueSeparator+Value;
  199. end;
  200. end;
  201. procedure TStrings.ReadData(Reader: TReader);
  202. begin
  203. Reader.ReadListBegin;
  204. BeginUpdate;
  205. try
  206. Clear;
  207. while not Reader.EndOfList do
  208. Add(Reader.ReadString);
  209. finally
  210. EndUpdate;
  211. end;
  212. Reader.ReadListEnd;
  213. end;
  214. Procedure TStrings.SetDelimitedText(const AValue: string);
  215. var i,j:integer;
  216. aNotFirst:boolean;
  217. begin
  218. CheckSpecialChars;
  219. BeginUpdate;
  220. i:=1;
  221. j:=1;
  222. aNotFirst:=false;
  223. try
  224. Clear;
  225. If StrictDelimiter then
  226. begin
  227. // Easier, faster loop.
  228. While I<=Length(AValue) do
  229. begin
  230. If (AValue[I] in [FDelimiter,#0]) then
  231. begin
  232. Add(Copy(AValue,J,I-J));
  233. J:=I+1;
  234. end;
  235. Inc(i);
  236. end;
  237. If (Length(AValue)>0) then
  238. Add(Copy(AValue,J,I-J));
  239. end
  240. else
  241. begin
  242. while i<=length(AValue) do begin
  243. // skip delimiter
  244. if aNotFirst and (i<=length(AValue)) and (AValue[i]=FDelimiter) then inc(i);
  245. // skip spaces
  246. while (i<=length(AValue)) and (Ord(AValue[i])<=Ord(' ')) do inc(i);
  247. // read next string
  248. if i<=length(AValue) then begin
  249. if AValue[i]=FQuoteChar then begin
  250. // next string is quoted
  251. j:=i+1;
  252. while (j<=length(AValue)) and
  253. ( (AValue[j]<>FQuoteChar) or
  254. ( (j+1<=length(AValue)) and (AValue[j+1]=FQuoteChar) ) ) do begin
  255. if (j<=length(AValue)) and (AValue[j]=FQuoteChar) then inc(j,2)
  256. else inc(j);
  257. end;
  258. // j is position of closing quote
  259. Add( StringReplace (Copy(AValue,i+1,j-i-1),
  260. FQuoteChar+FQuoteChar,FQuoteChar, [rfReplaceAll]));
  261. i:=j+1;
  262. end else begin
  263. // next string is not quoted
  264. j:=i;
  265. while (j<=length(AValue)) and
  266. (Ord(AValue[j])>Ord(' ')) and
  267. (AValue[j]<>FDelimiter) do inc(j);
  268. Add( Copy(AValue,i,j-i));
  269. i:=j;
  270. end;
  271. end else begin
  272. if aNotFirst then Add('');
  273. end;
  274. // skip spaces
  275. while (i<=length(AValue)) and (Ord(AValue[i])<=Ord(' ')) do inc(i);
  276. aNotFirst:=true;
  277. end;
  278. end;
  279. finally
  280. EndUpdate;
  281. end;
  282. end;
  283. Procedure TStrings.SetCommaText(const Value: string);
  284. Var
  285. C1,C2 : Char;
  286. begin
  287. CheckSpecialChars;
  288. C1:=Delimiter;
  289. C2:=QuoteChar;
  290. Delimiter:=',';
  291. QuoteChar:='"';
  292. Try
  293. SetDelimitedText(Value);
  294. Finally
  295. Delimiter:=C1;
  296. QuoteChar:=C2;
  297. end;
  298. end;
  299. Procedure TStrings.SetStringsAdapter(const Value: IStringsAdapter);
  300. begin
  301. end;
  302. Procedure TStrings.SetValue(const Name, Value: string);
  303. Var L : longint;
  304. begin
  305. CheckSpecialChars;
  306. L:=IndexOfName(Name);
  307. if L=-1 then
  308. Add (Name+FNameValueSeparator+Value)
  309. else
  310. Strings[L]:=Name+FNameValueSeparator+value;
  311. end;
  312. procedure TStrings.WriteData(Writer: TWriter);
  313. var
  314. i: Integer;
  315. begin
  316. Writer.WriteListBegin;
  317. for i := 0 to Count - 1 do
  318. Writer.WriteString(Strings[i]);
  319. Writer.WriteListEnd;
  320. end;
  321. procedure TStrings.DefineProperties(Filer: TFiler);
  322. var
  323. HasData: Boolean;
  324. begin
  325. if Assigned(Filer.Ancestor) then
  326. // Only serialize if string list is different from ancestor
  327. if Filer.Ancestor.InheritsFrom(TStrings) then
  328. HasData := not Equals(TStrings(Filer.Ancestor))
  329. else
  330. HasData := True
  331. else
  332. HasData := Count > 0;
  333. Filer.DefineProperty('Strings', @ReadData, @WriteData, HasData);
  334. end;
  335. Procedure TStrings.Error(const Msg: string; Data: Integer);
  336. begin
  337. Raise EStringListError.CreateFmt(Msg,[Data]) at get_caller_addr(get_frame);
  338. end;
  339. Procedure TStrings.Error(const Msg: pstring; Data: Integer);
  340. begin
  341. Raise EStringListError.CreateFmt(Msg^,[Data]) at get_caller_addr(get_frame);
  342. end;
  343. Function TStrings.GetCapacity: Integer;
  344. begin
  345. Result:=Count;
  346. end;
  347. Function TStrings.GetObject(Index: Integer): TObject;
  348. begin
  349. Result:=Nil;
  350. end;
  351. Function TStrings.GetTextStr: string;
  352. Var P : Pchar;
  353. I,L,NLS : Longint;
  354. S,NL : String;
  355. begin
  356. CheckSpecialChars;
  357. // Determine needed place
  358. Case FLBS of
  359. tlbsLF : NL:=#10;
  360. tlbsCRLF : NL:=#13#10;
  361. tlbsCR : NL:=#13;
  362. end;
  363. L:=0;
  364. NLS:=Length(NL);
  365. For I:=0 to count-1 do
  366. L:=L+Length(Strings[I])+NLS;
  367. Setlength(Result,L);
  368. P:=Pointer(Result);
  369. For i:=0 To count-1 do
  370. begin
  371. S:=Strings[I];
  372. L:=Length(S);
  373. if L<>0 then
  374. System.Move(Pointer(S)^,P^,L);
  375. P:=P+L;
  376. For L:=1 to NLS do
  377. begin
  378. P^:=NL[L];
  379. inc(P);
  380. end;
  381. end;
  382. end;
  383. Procedure TStrings.Put(Index: Integer; const S: string);
  384. Var Obj : TObject;
  385. begin
  386. Obj:=Objects[Index];
  387. Delete(Index);
  388. InsertObject(Index,S,Obj);
  389. end;
  390. Procedure TStrings.PutObject(Index: Integer; AObject: TObject);
  391. begin
  392. // Empty.
  393. end;
  394. Procedure TStrings.SetCapacity(NewCapacity: Integer);
  395. begin
  396. // Empty.
  397. end;
  398. Function GetNextLine (Const Value : String; Var S : String; Var P : Integer) : Boolean;
  399. Var
  400. PS : PChar;
  401. IP,L : Integer;
  402. begin
  403. L:=Length(Value);
  404. S:='';
  405. Result:=False;
  406. If ((L-P)<0) then
  407. exit;
  408. if ((L-P)=0) and (not (value[P] in [#10,#13])) Then
  409. Begin
  410. s:=value[P];
  411. inc(P);
  412. Exit(True);
  413. End;
  414. PS:=PChar(Value)+P-1;
  415. IP:=P;
  416. While ((L-P)>=0) and (not (PS^ in [#10,#13])) do
  417. begin
  418. P:=P+1;
  419. Inc(PS);
  420. end;
  421. SetLength (S,P-IP);
  422. System.Move (Value[IP],Pointer(S)^,P-IP);
  423. If (P<=L) and (Value[P]=#13) then
  424. Inc(P);
  425. If (P<=L) and (Value[P]=#10) then
  426. Inc(P); // Point to character after #10(#13)
  427. Result:=True;
  428. end;
  429. Procedure TStrings.SetTextStr(const Value: string);
  430. Var
  431. S : String;
  432. P : Integer;
  433. begin
  434. Try
  435. beginUpdate;
  436. Clear;
  437. P:=1;
  438. While GetNextLine (Value,S,P) do
  439. Add(S);
  440. finally
  441. EndUpdate;
  442. end;
  443. end;
  444. Procedure TStrings.SetUpdateState(Updating: Boolean);
  445. begin
  446. end;
  447. destructor TSTrings.Destroy;
  448. begin
  449. inherited destroy;
  450. end;
  451. Function TStrings.Add(const S: string): Integer;
  452. begin
  453. Result:=Count;
  454. Insert (Count,S);
  455. end;
  456. Function TStrings.AddObject(const S: string; AObject: TObject): Integer;
  457. begin
  458. Result:=Add(S);
  459. Objects[result]:=AObject;
  460. end;
  461. Procedure TStrings.Append(const S: string);
  462. begin
  463. Add (S);
  464. end;
  465. Procedure TStrings.AddStrings(TheStrings: TStrings);
  466. Var Runner : longint;
  467. begin
  468. try
  469. beginupdate;
  470. For Runner:=0 to TheStrings.Count-1 do
  471. self.AddObject (Thestrings[Runner],TheStrings.Objects[Runner]);
  472. finally
  473. EndUpdate;
  474. end;
  475. end;
  476. Procedure TStrings.Assign(Source: TPersistent);
  477. Var
  478. S : TStrings;
  479. begin
  480. If Source is TStrings then
  481. begin
  482. S:=TStrings(Source);
  483. BeginUpdate;
  484. Try
  485. clear;
  486. FSpecialCharsInited:=S.FSpecialCharsInited;
  487. FQuoteChar:=S.FQuoteChar;
  488. FDelimiter:=S.FDelimiter;
  489. FNameValueSeparator:=S.FNameValueSeparator;
  490. FLBS:=S.FLBS;
  491. AddStrings(S);
  492. finally
  493. EndUpdate;
  494. end;
  495. end
  496. else
  497. Inherited Assign(Source);
  498. end;
  499. Procedure TStrings.BeginUpdate;
  500. begin
  501. if FUpdateCount = 0 then SetUpdateState(true);
  502. inc(FUpdateCount);
  503. end;
  504. Procedure TStrings.EndUpdate;
  505. begin
  506. If FUpdateCount>0 then
  507. Dec(FUpdateCount);
  508. if FUpdateCount=0 then
  509. SetUpdateState(False);
  510. end;
  511. Function TStrings.Equals(TheStrings: TStrings): Boolean;
  512. Var Runner,Nr : Longint;
  513. begin
  514. Result:=False;
  515. Nr:=Self.Count;
  516. if Nr<>TheStrings.Count then exit;
  517. For Runner:=0 to Nr-1 do
  518. If Strings[Runner]<>TheStrings[Runner] then exit;
  519. Result:=True;
  520. end;
  521. Procedure TStrings.Exchange(Index1, Index2: Integer);
  522. Var
  523. Obj : TObject;
  524. Str : String;
  525. begin
  526. Try
  527. beginUpdate;
  528. Obj:=Objects[Index1];
  529. Str:=Strings[Index1];
  530. Objects[Index1]:=Objects[Index2];
  531. Strings[Index1]:=Strings[Index2];
  532. Objects[Index2]:=Obj;
  533. Strings[Index2]:=Str;
  534. finally
  535. EndUpdate;
  536. end;
  537. end;
  538. function TStrings.GetEnumerator: TStringsEnumerator;
  539. begin
  540. Result:=TStringsEnumerator.Create(Self);
  541. end;
  542. Function TStrings.GetText: PChar;
  543. begin
  544. Result:=StrNew(Pchar(Self.Text));
  545. end;
  546. Function TStrings.DoCompareText(const s1,s2 : string) : PtrInt;
  547. begin
  548. result:=CompareText(s1,s2);
  549. end;
  550. Function TStrings.IndexOf(const S: string): Integer;
  551. begin
  552. Result:=0;
  553. While (Result<Count) and (DoCompareText(Strings[Result],S)<>0) do Result:=Result+1;
  554. if Result=Count then Result:=-1;
  555. end;
  556. Function TStrings.IndexOfName(const Name: string): Integer;
  557. Var
  558. len : longint;
  559. S : String;
  560. begin
  561. CheckSpecialChars;
  562. Result:=0;
  563. while (Result<Count) do
  564. begin
  565. S:=Strings[Result];
  566. len:=pos(FNameValueSeparator,S)-1;
  567. if (len>0) and (DoCompareText(Name,Copy(S,1,Len))=0) then
  568. exit;
  569. inc(result);
  570. end;
  571. result:=-1;
  572. end;
  573. Function TStrings.IndexOfObject(AObject: TObject): Integer;
  574. begin
  575. Result:=0;
  576. While (Result<count) and (Objects[Result]<>AObject) do Result:=Result+1;
  577. If Result=Count then Result:=-1;
  578. end;
  579. Procedure TStrings.InsertObject(Index: Integer; const S: string;
  580. AObject: TObject);
  581. begin
  582. Insert (Index,S);
  583. Objects[Index]:=AObject;
  584. end;
  585. Procedure TStrings.LoadFromFile(const FileName: string);
  586. Var
  587. TheStream : TFileStream;
  588. begin
  589. TheStream:=TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite);
  590. try
  591. LoadFromStream(TheStream);
  592. finally
  593. TheStream.Free;
  594. end;
  595. end;
  596. Procedure TStrings.LoadFromStream(Stream: TStream);
  597. {
  598. Borlands method is no good, since a pipe for
  599. instance doesn't have a size.
  600. So we must do it the hard way.
  601. }
  602. Const
  603. BufSize = 1024;
  604. MaxGrow = 1 shl 29;
  605. Var
  606. Buffer : AnsiString;
  607. BytesRead,
  608. BufLen,
  609. I,BufDelta : Longint;
  610. begin
  611. // reread into a buffer
  612. try
  613. beginupdate;
  614. Buffer:='';
  615. BufLen:=0;
  616. I:=1;
  617. Repeat
  618. BufDelta:=BufSize*I;
  619. SetLength(Buffer,BufLen+BufDelta);
  620. BytesRead:=Stream.Read(Buffer[BufLen+1],BufDelta);
  621. inc(BufLen,BufDelta);
  622. If I<MaxGrow then
  623. I:=I shl 1;
  624. Until BytesRead<>BufDelta;
  625. SetLength(Buffer, BufLen-BufDelta+BytesRead);
  626. SetTextStr(Buffer);
  627. SetLength(Buffer,0);
  628. finally
  629. EndUpdate;
  630. end;
  631. end;
  632. Procedure TStrings.Move(CurIndex, NewIndex: Integer);
  633. Var
  634. Obj : TObject;
  635. Str : String;
  636. begin
  637. BeginUpdate;
  638. Obj:=Objects[CurIndex];
  639. Str:=Strings[CurIndex];
  640. Delete(Curindex);
  641. InsertObject(NewIndex,Str,Obj);
  642. EndUpdate;
  643. end;
  644. Procedure TStrings.SaveToFile(const FileName: string);
  645. Var TheStream : TFileStream;
  646. begin
  647. TheStream:=TFileStream.Create(FileName,fmCreate);
  648. try
  649. SaveToStream(TheStream);
  650. finally
  651. TheStream.Free;
  652. end;
  653. end;
  654. Procedure TStrings.SaveToStream(Stream: TStream);
  655. Var
  656. S : String;
  657. begin
  658. S:=Text;
  659. Stream.WriteBuffer(Pointer(S)^,Length(S));
  660. end;
  661. Procedure TStrings.SetText(TheText: PChar);
  662. Var S : String;
  663. begin
  664. If TheText<>Nil then
  665. S:=StrPas(TheText)
  666. else
  667. S:='';
  668. SetTextStr(S);
  669. end;
  670. {****************************************************************************}
  671. {* TStringList *}
  672. {****************************************************************************}
  673. {$if defined(VER2_0) or not defined(FPC_TESTGENERICS)}
  674. Procedure TStringList.ExchangeItems(Index1, Index2: Integer);
  675. Var P1,P2 : Pointer;
  676. begin
  677. P1:=Pointer(Flist^[Index1].FString);
  678. P2:=Pointer(Flist^[Index1].FObject);
  679. Pointer(Flist^[Index1].Fstring):=Pointer(Flist^[Index2].Fstring);
  680. Pointer(Flist^[Index1].FObject):=Pointer(Flist^[Index2].FObject);
  681. Pointer(Flist^[Index2].Fstring):=P1;
  682. Pointer(Flist^[Index2].FObject):=P2;
  683. end;
  684. Procedure TStringList.Grow;
  685. Var
  686. NC : Integer;
  687. begin
  688. NC:=FCapacity;
  689. If NC>=256 then
  690. NC:=NC+(NC Div 4)
  691. else if NC=0 then
  692. NC:=4
  693. else
  694. NC:=NC*4;
  695. SetCapacity(NC);
  696. end;
  697. Procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare);
  698. var
  699. Pivot, vL, vR: Integer;
  700. begin
  701. if R - L <= 1 then begin // a little bit of time saver
  702. if L < R then
  703. if CompareFn(Self, L, R) > 0 then
  704. ExchangeItems(L, R);
  705. Exit;
  706. end;
  707. vL := L;
  708. vR := R;
  709. Pivot := L + Random(R - L); // they say random is best
  710. while vL < vR do begin
  711. while (vL < Pivot) and (CompareFn(Self, vL, Pivot) <= 0) do
  712. Inc(vL);
  713. while (vR > Pivot) and (CompareFn(Self, vR, Pivot) > 0) do
  714. Dec(vR);
  715. ExchangeItems(vL, vR);
  716. if Pivot = vL then // swap pivot if we just hit it from one side
  717. Pivot := vR
  718. else if Pivot = vR then
  719. Pivot := vL;
  720. end;
  721. if Pivot - 1 >= L then
  722. QuickSort(L, Pivot - 1, CompareFn);
  723. if Pivot + 1 <= R then
  724. QuickSort(Pivot + 1, R, CompareFn);
  725. end;
  726. Procedure TStringList.InsertItem(Index: Integer; const S: string);
  727. begin
  728. Changing;
  729. If FCount=Fcapacity then Grow;
  730. If Index<FCount then
  731. System.Move (FList^[Index],FList^[Index+1],
  732. (FCount-Index)*SizeOf(TStringItem));
  733. Pointer(Flist^[Index].Fstring):=Nil; // Needed to initialize...
  734. Flist^[Index].FString:=S;
  735. Flist^[Index].Fobject:=Nil;
  736. Inc(FCount);
  737. Changed;
  738. end;
  739. Procedure TStringList.InsertItem(Index: Integer; const S: string; O: TObject);
  740. begin
  741. Changing;
  742. If FCount=Fcapacity then Grow;
  743. If Index<FCount then
  744. System.Move (FList^[Index],FList^[Index+1],
  745. (FCount-Index)*SizeOf(TStringItem));
  746. Pointer(Flist^[Index].Fstring):=Nil; // Needed to initialize...
  747. Flist^[Index].FString:=S;
  748. Flist^[Index].FObject:=O;
  749. Inc(FCount);
  750. Changed;
  751. end;
  752. Procedure TStringList.SetSorted(Value: Boolean);
  753. begin
  754. If FSorted<>Value then
  755. begin
  756. If Value then sort;
  757. FSorted:=VAlue
  758. end;
  759. end;
  760. Procedure TStringList.Changed;
  761. begin
  762. If (FUpdateCount=0) Then
  763. If Assigned(FOnChange) then
  764. FOnchange(Self);
  765. end;
  766. Procedure TStringList.Changing;
  767. begin
  768. If FUpdateCount=0 then
  769. if Assigned(FOnChanging) then
  770. FOnchanging(Self);
  771. end;
  772. Function TStringList.Get(Index: Integer): string;
  773. begin
  774. If (Index<0) or (INdex>=Fcount) then
  775. Error (SListIndexError,Index);
  776. Result:=Flist^[Index].FString;
  777. end;
  778. Function TStringList.GetCapacity: Integer;
  779. begin
  780. Result:=FCapacity;
  781. end;
  782. Function TStringList.GetCount: Integer;
  783. begin
  784. Result:=FCount;
  785. end;
  786. Function TStringList.GetObject(Index: Integer): TObject;
  787. begin
  788. If (Index<0) or (INdex>=Fcount) then
  789. Error (SListIndexError,Index);
  790. Result:=Flist^[Index].FObject;
  791. end;
  792. Procedure TStringList.Put(Index: Integer; const S: string);
  793. begin
  794. If Sorted then
  795. Error(SSortedListError,0);
  796. If (Index<0) or (INdex>=Fcount) then
  797. Error (SListIndexError,Index);
  798. Changing;
  799. Flist^[Index].FString:=S;
  800. Changed;
  801. end;
  802. Procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  803. begin
  804. If (Index<0) or (INdex>=Fcount) then
  805. Error (SListIndexError,Index);
  806. Changing;
  807. Flist^[Index].FObject:=AObject;
  808. Changed;
  809. end;
  810. Procedure TStringList.SetCapacity(NewCapacity: Integer);
  811. Var NewList : Pointer;
  812. MSize : Longint;
  813. begin
  814. If (NewCapacity<0) then
  815. Error (SListCapacityError,NewCapacity);
  816. If NewCapacity>FCapacity then
  817. begin
  818. GetMem (NewList,NewCapacity*SizeOf(TStringItem));
  819. If NewList=Nil then
  820. Error (SListCapacityError,NewCapacity);
  821. If Assigned(FList) then
  822. begin
  823. MSize:=FCapacity*Sizeof(TStringItem);
  824. System.Move (FList^,NewList^,MSize);
  825. FillWord (Pchar(NewList)[MSize],(NewCapacity-FCapacity)*WordRatio, 0);
  826. FreeMem (Flist,MSize);
  827. end;
  828. Flist:=NewList;
  829. FCapacity:=NewCapacity;
  830. end
  831. else if NewCapacity<FCapacity then
  832. begin
  833. if NewCapacity = 0 then
  834. begin
  835. FreeMem(FList);
  836. FList := nil;
  837. end else
  838. begin
  839. GetMem(NewList, NewCapacity * SizeOf(TStringItem));
  840. System.Move(FList^, NewList^, NewCapacity * SizeOf(TStringItem));
  841. FreeMem(FList);
  842. FList := NewList;
  843. end;
  844. FCapacity:=NewCapacity;
  845. end;
  846. end;
  847. Procedure TStringList.SetUpdateState(Updating: Boolean);
  848. begin
  849. If Updating then
  850. Changing
  851. else
  852. Changed
  853. end;
  854. destructor TStringList.Destroy;
  855. Var I : Longint;
  856. begin
  857. FOnChange:=Nil;
  858. FOnChanging:=Nil;
  859. // This will force a dereference. Can be done better...
  860. For I:=0 to FCount-1 do
  861. FList^[I].FString:='';
  862. FCount:=0;
  863. SetCapacity(0);
  864. Inherited destroy;
  865. end;
  866. Function TStringList.Add(const S: string): Integer;
  867. begin
  868. If Not Sorted then
  869. Result:=FCount
  870. else
  871. If Find (S,Result) then
  872. Case DUplicates of
  873. DupIgnore : Exit;
  874. DupError : Error(SDuplicateString,0)
  875. end;
  876. InsertItem (Result,S);
  877. end;
  878. Procedure TStringList.Clear;
  879. Var I : longint;
  880. begin
  881. if FCount = 0 then Exit;
  882. Changing;
  883. For I:=0 to FCount-1 do
  884. Flist^[I].FString:='';
  885. FCount:=0;
  886. SetCapacity(0);
  887. Changed;
  888. end;
  889. Procedure TStringList.Delete(Index: Integer);
  890. begin
  891. If (Index<0) or (Index>=FCount) then
  892. Error(SlistINdexError,Index);
  893. Changing;
  894. Flist^[Index].FString:='';
  895. Dec(FCount);
  896. If Index<FCount then
  897. System.Move(Flist^[Index+1],
  898. Flist^[Index],
  899. (Fcount-Index)*SizeOf(TStringItem));
  900. Changed;
  901. end;
  902. Procedure TStringList.Exchange(Index1, Index2: Integer);
  903. begin
  904. If (Index1<0) or (Index1>=FCount) then
  905. Error(SListIndexError,Index1);
  906. If (Index2<0) or (Index2>=FCount) then
  907. Error(SListIndexError,Index2);
  908. Changing;
  909. ExchangeItems(Index1,Index2);
  910. changed;
  911. end;
  912. procedure TStringList.SetCaseSensitive(b : boolean);
  913. begin
  914. if b<>FCaseSensitive then
  915. begin
  916. FCaseSensitive:=b;
  917. if FSorted then
  918. sort;
  919. end;
  920. end;
  921. Function TStringList.DoCompareText(const s1,s2 : string) : PtrInt;
  922. begin
  923. if FCaseSensitive then
  924. result:=AnsiCompareStr(s1,s2)
  925. else
  926. result:=AnsiCompareText(s1,s2);
  927. end;
  928. Function TStringList.Find(const S: string; var Index: Integer): Boolean;
  929. var
  930. L, R, I: Integer;
  931. CompareRes: PtrInt;
  932. begin
  933. Result := false;
  934. // Use binary search.
  935. L := 0;
  936. R := Count - 1;
  937. while (L<=R) do
  938. begin
  939. I := L + (R - L) div 2;
  940. CompareRes := DoCompareText(S, Flist^[I].FString);
  941. if (CompareRes>0) then
  942. L := I+1
  943. else begin
  944. R := I-1;
  945. if (CompareRes=0) then begin
  946. Result := true;
  947. if (Duplicates<>dupAccept) then
  948. L := I; // forces end of while loop
  949. end;
  950. end;
  951. end;
  952. Index := L;
  953. end;
  954. Function TStringList.IndexOf(const S: string): Integer;
  955. begin
  956. If Not Sorted then
  957. Result:=Inherited indexOf(S)
  958. else
  959. // faster using binary search...
  960. If Not Find (S,Result) then
  961. Result:=-1;
  962. end;
  963. Procedure TStringList.Insert(Index: Integer; const S: string);
  964. begin
  965. If Sorted then
  966. Error (SSortedListError,0)
  967. else
  968. If (Index<0) or (Index>FCount) then
  969. Error (SListIndexError,Index)
  970. else
  971. InsertItem (Index,S);
  972. end;
  973. Procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  974. begin
  975. If Not Sorted and (FCount>1) then
  976. begin
  977. Changing;
  978. QuickSort(0,FCount-1, CompareFn);
  979. Changed;
  980. end;
  981. end;
  982. function StringListAnsiCompare(List: TStringList; Index1, Index: Integer): Integer;
  983. begin
  984. Result := List.DoCompareText(List.FList^[Index1].FString,
  985. List.FList^[Index].FString);
  986. end;
  987. Procedure TStringList.Sort;
  988. begin
  989. CustomSort(@StringListAnsiCompare);
  990. end;
  991. {$else}
  992. { generics based implementation of TStringList follows }
  993. function StringListAnsiCompare(List: TStringList; Index1, Index2: Integer): Integer;
  994. begin
  995. Result := List.DoCompareText(List.Strings[Index1], List.Strings[Index2]);
  996. end;
  997. constructor TStringList.Create;
  998. begin
  999. inherited;
  1000. FMap := TFPStrObjMap.Create;
  1001. FMap.OnPtrCompare := @MapPtrCompare;
  1002. FOnCompareText := @DefaultCompareText;
  1003. end;
  1004. destructor TStringList.Destroy;
  1005. begin
  1006. FMap.Free;
  1007. inherited;
  1008. end;
  1009. function TStringList.GetDuplicates: TDuplicates;
  1010. begin
  1011. Result := FMap.Duplicates;
  1012. end;
  1013. function TStringList.GetSorted: boolean;
  1014. begin
  1015. Result := FMap.Sorted;
  1016. end;
  1017. procedure TStringList.SetDuplicates(NewDuplicates: TDuplicates);
  1018. begin
  1019. FMap.Duplicates := NewDuplicates;
  1020. end;
  1021. procedure TStringList.SetSorted(NewSorted: Boolean);
  1022. begin
  1023. FMap.Sorted := NewSorted;
  1024. end;
  1025. procedure TStringList.Changed;
  1026. begin
  1027. if FUpdateCount = 0 then
  1028. if Assigned(FOnChange) then
  1029. FOnChange(Self);
  1030. end;
  1031. procedure TStringList.Changing;
  1032. begin
  1033. if FUpdateCount = 0 then
  1034. if Assigned(FOnChanging) then
  1035. FOnChanging(Self);
  1036. end;
  1037. function TStringList.Get(Index: Integer): string;
  1038. begin
  1039. Result := FMap.Keys[Index];
  1040. end;
  1041. function TStringList.GetCapacity: Integer;
  1042. begin
  1043. Result := FMap.Capacity;
  1044. end;
  1045. function TStringList.GetCount: Integer;
  1046. begin
  1047. Result := FMap.Count;
  1048. end;
  1049. function TStringList.GetObject(Index: Integer): TObject;
  1050. begin
  1051. Result := FMap.Data[Index];
  1052. end;
  1053. procedure TStringList.Put(Index: Integer; const S: string);
  1054. begin
  1055. Changing;
  1056. FMap.Keys[Index] := S;
  1057. Changed;
  1058. end;
  1059. procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  1060. begin
  1061. Changing;
  1062. FMap.Data[Index] := AObject;
  1063. Changed;
  1064. end;
  1065. procedure TStringList.SetCapacity(NewCapacity: Integer);
  1066. begin
  1067. FMap.Capacity := NewCapacity;
  1068. end;
  1069. procedure TStringList.SetUpdateState(Updating: Boolean);
  1070. begin
  1071. if Updating then
  1072. Changing
  1073. else
  1074. Changed
  1075. end;
  1076. function TStringList.Add(const S: string): Integer;
  1077. begin
  1078. Result := FMap.Add(S);
  1079. end;
  1080. procedure TStringList.Clear;
  1081. begin
  1082. if FMap.Count = 0 then exit;
  1083. Changing;
  1084. FMap.Clear;
  1085. Changed;
  1086. end;
  1087. procedure TStringList.Delete(Index: Integer);
  1088. begin
  1089. if (Index < 0) or (Index >= FMap.Count) then
  1090. Error(SListIndexError, Index);
  1091. Changing;
  1092. FMap.Delete(Index);
  1093. Changed;
  1094. end;
  1095. procedure TStringList.Exchange(Index1, Index2: Integer);
  1096. begin
  1097. if (Index1 < 0) or (Index1 >= FMap.Count) then
  1098. Error(SListIndexError, Index1);
  1099. if (Index2 < 0) or (Index2 >= FMap.Count) then
  1100. Error(SListIndexError, Index2);
  1101. Changing;
  1102. FMap.InternalExchange(Index1, Index2);
  1103. Changed;
  1104. end;
  1105. procedure TStringList.SetCaseSensitive(NewSensitive: Boolean);
  1106. begin
  1107. if NewSensitive <> FCaseSensitive then
  1108. begin
  1109. FCaseSensitive := NewSensitive;
  1110. if Sorted then
  1111. Sort;
  1112. end;
  1113. end;
  1114. function TStringList.MapPtrCompare(Key1, Key2: Pointer): Integer;
  1115. begin
  1116. Result := FOnCompareText(string(Key1^), string(Key2^));
  1117. end;
  1118. function TStringList.DefaultCompareText(const s1, s2: string): PtrInt;
  1119. begin
  1120. if FCaseSensitive then
  1121. Result := AnsiCompareStr(s1, s2)
  1122. else
  1123. Result := AnsiCompareText(s1, s2);
  1124. end;
  1125. function TStringList.DoCompareText(const s1, s2: string): PtrInt;
  1126. begin
  1127. Result := FOnCompareText(s1, s2);
  1128. end;
  1129. function TStringList.Find(const S: string; var Index: Integer): Boolean;
  1130. begin
  1131. Result := FMap.Find(S, Index);
  1132. end;
  1133. function TStringList.IndexOf(const S: string): Integer;
  1134. begin
  1135. Result := FMap.IndexOf(S);
  1136. end;
  1137. procedure TStringList.Insert(Index: Integer; const S: string);
  1138. begin
  1139. if not Sorted and (0 <= Index) and (Index < FMap.Count) then
  1140. Changing;
  1141. FMap.InsertKey(Index, S);
  1142. Changed;
  1143. end;
  1144. procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare);
  1145. var
  1146. I, J, Pivot: Integer;
  1147. begin
  1148. repeat
  1149. I := L;
  1150. J := R;
  1151. Pivot := (L + R) div 2;
  1152. repeat
  1153. while CompareFn(Self, I, Pivot) < 0 do Inc(I);
  1154. while CompareFn(Self, J, Pivot) > 0 do Dec(J);
  1155. if I <= J then
  1156. begin
  1157. FMap.InternalExchange(I, J); // No check, indices are correct.
  1158. if Pivot = I then
  1159. Pivot := J
  1160. else if Pivot = J then
  1161. Pivot := I;
  1162. Inc(I);
  1163. Dec(j);
  1164. end;
  1165. until I > J;
  1166. if L < J then
  1167. QuickSort(L,J, CompareFn);
  1168. L := I;
  1169. until I >= R;
  1170. end;
  1171. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1172. begin
  1173. if not Sorted and (FMap.Count > 1) then
  1174. begin
  1175. Changing;
  1176. QuickSort(0, FMap.Count-1, CompareFn);
  1177. Changed;
  1178. end;
  1179. end;
  1180. procedure TStringList.Sort;
  1181. begin
  1182. if not Sorted and (FMap.Count > 1) then
  1183. begin
  1184. Changing;
  1185. FMap.Sort;
  1186. Changed;
  1187. end;
  1188. end;
  1189. {$endif}