stringl.inc 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551
  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(Obj: TObject): Boolean;
  512. begin
  513. if Obj is TStrings then
  514. Result := Equals(TStrings(Obj))
  515. else
  516. Result := inherited Equals(Obj);
  517. end;
  518. Function TStrings.Equals(TheStrings: TStrings): Boolean;
  519. Var Runner,Nr : Longint;
  520. begin
  521. Result:=False;
  522. Nr:=Self.Count;
  523. if Nr<>TheStrings.Count then exit;
  524. For Runner:=0 to Nr-1 do
  525. If Strings[Runner]<>TheStrings[Runner] then exit;
  526. Result:=True;
  527. end;
  528. Procedure TStrings.Exchange(Index1, Index2: Integer);
  529. Var
  530. Obj : TObject;
  531. Str : String;
  532. begin
  533. Try
  534. beginUpdate;
  535. Obj:=Objects[Index1];
  536. Str:=Strings[Index1];
  537. Objects[Index1]:=Objects[Index2];
  538. Strings[Index1]:=Strings[Index2];
  539. Objects[Index2]:=Obj;
  540. Strings[Index2]:=Str;
  541. finally
  542. EndUpdate;
  543. end;
  544. end;
  545. function TStrings.GetEnumerator: TStringsEnumerator;
  546. begin
  547. Result:=TStringsEnumerator.Create(Self);
  548. end;
  549. Function TStrings.GetText: PChar;
  550. begin
  551. Result:=StrNew(Pchar(Self.Text));
  552. end;
  553. Function TStrings.DoCompareText(const s1,s2 : string) : PtrInt;
  554. begin
  555. result:=CompareText(s1,s2);
  556. end;
  557. Function TStrings.IndexOf(const S: string): Integer;
  558. begin
  559. Result:=0;
  560. While (Result<Count) and (DoCompareText(Strings[Result],S)<>0) do Result:=Result+1;
  561. if Result=Count then Result:=-1;
  562. end;
  563. Function TStrings.IndexOfName(const Name: string): Integer;
  564. Var
  565. len : longint;
  566. S : String;
  567. begin
  568. CheckSpecialChars;
  569. Result:=0;
  570. while (Result<Count) do
  571. begin
  572. S:=Strings[Result];
  573. len:=pos(FNameValueSeparator,S)-1;
  574. if (len>0) and (DoCompareText(Name,Copy(S,1,Len))=0) then
  575. exit;
  576. inc(result);
  577. end;
  578. result:=-1;
  579. end;
  580. Function TStrings.IndexOfObject(AObject: TObject): Integer;
  581. begin
  582. Result:=0;
  583. While (Result<count) and (Objects[Result]<>AObject) do Result:=Result+1;
  584. If Result=Count then Result:=-1;
  585. end;
  586. Procedure TStrings.InsertObject(Index: Integer; const S: string;
  587. AObject: TObject);
  588. begin
  589. Insert (Index,S);
  590. Objects[Index]:=AObject;
  591. end;
  592. Procedure TStrings.LoadFromFile(const FileName: string);
  593. Var
  594. TheStream : TFileStream;
  595. begin
  596. TheStream:=TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite);
  597. try
  598. LoadFromStream(TheStream);
  599. finally
  600. TheStream.Free;
  601. end;
  602. end;
  603. Procedure TStrings.LoadFromStream(Stream: TStream);
  604. {
  605. Borlands method is no good, since a pipe for
  606. instance doesn't have a size.
  607. So we must do it the hard way.
  608. }
  609. Const
  610. BufSize = 1024;
  611. MaxGrow = 1 shl 29;
  612. Var
  613. Buffer : AnsiString;
  614. BytesRead,
  615. BufLen,
  616. I,BufDelta : Longint;
  617. begin
  618. // reread into a buffer
  619. try
  620. beginupdate;
  621. Buffer:='';
  622. BufLen:=0;
  623. I:=1;
  624. Repeat
  625. BufDelta:=BufSize*I;
  626. SetLength(Buffer,BufLen+BufDelta);
  627. BytesRead:=Stream.Read(Buffer[BufLen+1],BufDelta);
  628. inc(BufLen,BufDelta);
  629. If I<MaxGrow then
  630. I:=I shl 1;
  631. Until BytesRead<>BufDelta;
  632. SetLength(Buffer, BufLen-BufDelta+BytesRead);
  633. SetTextStr(Buffer);
  634. SetLength(Buffer,0);
  635. finally
  636. EndUpdate;
  637. end;
  638. end;
  639. Procedure TStrings.Move(CurIndex, NewIndex: Integer);
  640. Var
  641. Obj : TObject;
  642. Str : String;
  643. begin
  644. BeginUpdate;
  645. Obj:=Objects[CurIndex];
  646. Str:=Strings[CurIndex];
  647. Delete(Curindex);
  648. InsertObject(NewIndex,Str,Obj);
  649. EndUpdate;
  650. end;
  651. Procedure TStrings.SaveToFile(const FileName: string);
  652. Var TheStream : TFileStream;
  653. begin
  654. TheStream:=TFileStream.Create(FileName,fmCreate);
  655. try
  656. SaveToStream(TheStream);
  657. finally
  658. TheStream.Free;
  659. end;
  660. end;
  661. Procedure TStrings.SaveToStream(Stream: TStream);
  662. Var
  663. S : String;
  664. begin
  665. S:=Text;
  666. if S = '' then Exit;
  667. Stream.WriteBuffer(Pointer(S)^,Length(S));
  668. end;
  669. Procedure TStrings.SetText(TheText: PChar);
  670. Var S : String;
  671. begin
  672. If TheText<>Nil then
  673. S:=StrPas(TheText)
  674. else
  675. S:='';
  676. SetTextStr(S);
  677. end;
  678. {****************************************************************************}
  679. {* TStringList *}
  680. {****************************************************************************}
  681. {$if defined(VER2_0) or not defined(FPC_TESTGENERICS)}
  682. Procedure TStringList.ExchangeItems(Index1, Index2: Integer);
  683. Var P1,P2 : Pointer;
  684. begin
  685. P1:=Pointer(Flist^[Index1].FString);
  686. P2:=Pointer(Flist^[Index1].FObject);
  687. Pointer(Flist^[Index1].Fstring):=Pointer(Flist^[Index2].Fstring);
  688. Pointer(Flist^[Index1].FObject):=Pointer(Flist^[Index2].FObject);
  689. Pointer(Flist^[Index2].Fstring):=P1;
  690. Pointer(Flist^[Index2].FObject):=P2;
  691. end;
  692. Procedure TStringList.Grow;
  693. Var
  694. NC : Integer;
  695. begin
  696. NC:=FCapacity;
  697. If NC>=256 then
  698. NC:=NC+(NC Div 4)
  699. else if NC=0 then
  700. NC:=4
  701. else
  702. NC:=NC*4;
  703. SetCapacity(NC);
  704. end;
  705. Procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare);
  706. var
  707. Pivot, vL, vR: Integer;
  708. begin
  709. if R - L <= 1 then begin // a little bit of time saver
  710. if L < R then
  711. if CompareFn(Self, L, R) > 0 then
  712. ExchangeItems(L, R);
  713. Exit;
  714. end;
  715. vL := L;
  716. vR := R;
  717. Pivot := L + Random(R - L); // they say random is best
  718. while vL < vR do begin
  719. while (vL < Pivot) and (CompareFn(Self, vL, Pivot) <= 0) do
  720. Inc(vL);
  721. while (vR > Pivot) and (CompareFn(Self, vR, Pivot) > 0) do
  722. Dec(vR);
  723. ExchangeItems(vL, vR);
  724. if Pivot = vL then // swap pivot if we just hit it from one side
  725. Pivot := vR
  726. else if Pivot = vR then
  727. Pivot := vL;
  728. end;
  729. if Pivot - 1 >= L then
  730. QuickSort(L, Pivot - 1, CompareFn);
  731. if Pivot + 1 <= R then
  732. QuickSort(Pivot + 1, R, CompareFn);
  733. end;
  734. Procedure TStringList.InsertItem(Index: Integer; const S: string);
  735. begin
  736. Changing;
  737. If FCount=Fcapacity then Grow;
  738. If Index<FCount then
  739. System.Move (FList^[Index],FList^[Index+1],
  740. (FCount-Index)*SizeOf(TStringItem));
  741. Pointer(Flist^[Index].Fstring):=Nil; // Needed to initialize...
  742. Flist^[Index].FString:=S;
  743. Flist^[Index].Fobject:=Nil;
  744. Inc(FCount);
  745. Changed;
  746. end;
  747. Procedure TStringList.InsertItem(Index: Integer; const S: string; O: TObject);
  748. begin
  749. Changing;
  750. If FCount=Fcapacity then Grow;
  751. If Index<FCount then
  752. System.Move (FList^[Index],FList^[Index+1],
  753. (FCount-Index)*SizeOf(TStringItem));
  754. Pointer(Flist^[Index].Fstring):=Nil; // Needed to initialize...
  755. Flist^[Index].FString:=S;
  756. Flist^[Index].FObject:=O;
  757. Inc(FCount);
  758. Changed;
  759. end;
  760. Procedure TStringList.SetSorted(Value: Boolean);
  761. begin
  762. If FSorted<>Value then
  763. begin
  764. If Value then sort;
  765. FSorted:=VAlue
  766. end;
  767. end;
  768. Procedure TStringList.Changed;
  769. begin
  770. If (FUpdateCount=0) Then
  771. If Assigned(FOnChange) then
  772. FOnchange(Self);
  773. end;
  774. Procedure TStringList.Changing;
  775. begin
  776. If FUpdateCount=0 then
  777. if Assigned(FOnChanging) then
  778. FOnchanging(Self);
  779. end;
  780. Function TStringList.Get(Index: Integer): string;
  781. begin
  782. If (Index<0) or (INdex>=Fcount) then
  783. Error (SListIndexError,Index);
  784. Result:=Flist^[Index].FString;
  785. end;
  786. Function TStringList.GetCapacity: Integer;
  787. begin
  788. Result:=FCapacity;
  789. end;
  790. Function TStringList.GetCount: Integer;
  791. begin
  792. Result:=FCount;
  793. end;
  794. Function TStringList.GetObject(Index: Integer): TObject;
  795. begin
  796. If (Index<0) or (INdex>=Fcount) then
  797. Error (SListIndexError,Index);
  798. Result:=Flist^[Index].FObject;
  799. end;
  800. Procedure TStringList.Put(Index: Integer; const S: string);
  801. begin
  802. If Sorted then
  803. Error(SSortedListError,0);
  804. If (Index<0) or (INdex>=Fcount) then
  805. Error (SListIndexError,Index);
  806. Changing;
  807. Flist^[Index].FString:=S;
  808. Changed;
  809. end;
  810. Procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  811. begin
  812. If (Index<0) or (INdex>=Fcount) then
  813. Error (SListIndexError,Index);
  814. Changing;
  815. Flist^[Index].FObject:=AObject;
  816. Changed;
  817. end;
  818. Procedure TStringList.SetCapacity(NewCapacity: Integer);
  819. Var NewList : Pointer;
  820. MSize : Longint;
  821. begin
  822. If (NewCapacity<0) then
  823. Error (SListCapacityError,NewCapacity);
  824. If NewCapacity>FCapacity then
  825. begin
  826. GetMem (NewList,NewCapacity*SizeOf(TStringItem));
  827. If NewList=Nil then
  828. Error (SListCapacityError,NewCapacity);
  829. If Assigned(FList) then
  830. begin
  831. MSize:=FCapacity*Sizeof(TStringItem);
  832. System.Move (FList^,NewList^,MSize);
  833. FillWord (Pchar(NewList)[MSize],(NewCapacity-FCapacity)*WordRatio, 0);
  834. FreeMem (Flist,MSize);
  835. end;
  836. Flist:=NewList;
  837. FCapacity:=NewCapacity;
  838. end
  839. else if NewCapacity<FCapacity then
  840. begin
  841. if NewCapacity = 0 then
  842. begin
  843. FreeMem(FList);
  844. FList := nil;
  845. end else
  846. begin
  847. GetMem(NewList, NewCapacity * SizeOf(TStringItem));
  848. System.Move(FList^, NewList^, NewCapacity * SizeOf(TStringItem));
  849. FreeMem(FList);
  850. FList := NewList;
  851. end;
  852. FCapacity:=NewCapacity;
  853. end;
  854. end;
  855. Procedure TStringList.SetUpdateState(Updating: Boolean);
  856. begin
  857. If Updating then
  858. Changing
  859. else
  860. Changed
  861. end;
  862. destructor TStringList.Destroy;
  863. Var I : Longint;
  864. begin
  865. FOnChange:=Nil;
  866. FOnChanging:=Nil;
  867. // This will force a dereference. Can be done better...
  868. For I:=0 to FCount-1 do
  869. FList^[I].FString:='';
  870. FCount:=0;
  871. SetCapacity(0);
  872. Inherited destroy;
  873. end;
  874. Function TStringList.Add(const S: string): Integer;
  875. begin
  876. If Not Sorted then
  877. Result:=FCount
  878. else
  879. If Find (S,Result) then
  880. Case DUplicates of
  881. DupIgnore : Exit;
  882. DupError : Error(SDuplicateString,0)
  883. end;
  884. InsertItem (Result,S);
  885. end;
  886. Procedure TStringList.Clear;
  887. Var I : longint;
  888. begin
  889. if FCount = 0 then Exit;
  890. Changing;
  891. if FOwnsObjects then
  892. begin
  893. For I:=0 to FCount-1 do
  894. begin
  895. Flist^[I].FString:='';
  896. freeandnil(Flist^[i].FObject);
  897. end;
  898. end
  899. else
  900. begin
  901. For I:=0 to FCount-1 do
  902. Flist^[I].FString:='';
  903. end;
  904. FCount:=0;
  905. SetCapacity(0);
  906. Changed;
  907. end;
  908. Procedure TStringList.Delete(Index: Integer);
  909. begin
  910. If (Index<0) or (Index>=FCount) then
  911. Error(SlistINdexError,Index);
  912. Changing;
  913. Flist^[Index].FString:='';
  914. if FOwnsObjects then
  915. FreeAndNil(Flist^[Index].FObject);
  916. Dec(FCount);
  917. If Index<FCount then
  918. System.Move(Flist^[Index+1],
  919. Flist^[Index],
  920. (Fcount-Index)*SizeOf(TStringItem));
  921. Changed;
  922. end;
  923. Procedure TStringList.Exchange(Index1, Index2: Integer);
  924. begin
  925. If (Index1<0) or (Index1>=FCount) then
  926. Error(SListIndexError,Index1);
  927. If (Index2<0) or (Index2>=FCount) then
  928. Error(SListIndexError,Index2);
  929. Changing;
  930. ExchangeItems(Index1,Index2);
  931. changed;
  932. end;
  933. procedure TStringList.SetCaseSensitive(b : boolean);
  934. begin
  935. if b<>FCaseSensitive then
  936. begin
  937. FCaseSensitive:=b;
  938. if FSorted then
  939. sort;
  940. end;
  941. end;
  942. Function TStringList.DoCompareText(const s1,s2 : string) : PtrInt;
  943. begin
  944. if FCaseSensitive then
  945. result:=AnsiCompareStr(s1,s2)
  946. else
  947. result:=AnsiCompareText(s1,s2);
  948. end;
  949. Function TStringList.Find(const S: string; Out Index: Integer): Boolean;
  950. var
  951. L, R, I: Integer;
  952. CompareRes: PtrInt;
  953. begin
  954. Result := false;
  955. // Use binary search.
  956. L := 0;
  957. R := Count - 1;
  958. while (L<=R) do
  959. begin
  960. I := L + (R - L) div 2;
  961. CompareRes := DoCompareText(S, Flist^[I].FString);
  962. if (CompareRes>0) then
  963. L := I+1
  964. else begin
  965. R := I-1;
  966. if (CompareRes=0) then begin
  967. Result := true;
  968. if (Duplicates<>dupAccept) then
  969. L := I; // forces end of while loop
  970. end;
  971. end;
  972. end;
  973. Index := L;
  974. end;
  975. Function TStringList.IndexOf(const S: string): Integer;
  976. begin
  977. If Not Sorted then
  978. Result:=Inherited indexOf(S)
  979. else
  980. // faster using binary search...
  981. If Not Find (S,Result) then
  982. Result:=-1;
  983. end;
  984. Procedure TStringList.Insert(Index: Integer; const S: string);
  985. begin
  986. If Sorted then
  987. Error (SSortedListError,0)
  988. else
  989. If (Index<0) or (Index>FCount) then
  990. Error (SListIndexError,Index)
  991. else
  992. InsertItem (Index,S);
  993. end;
  994. Procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  995. begin
  996. If Not Sorted and (FCount>1) then
  997. begin
  998. Changing;
  999. QuickSort(0,FCount-1, CompareFn);
  1000. Changed;
  1001. end;
  1002. end;
  1003. function StringListAnsiCompare(List: TStringList; Index1, Index: Integer): Integer;
  1004. begin
  1005. Result := List.DoCompareText(List.FList^[Index1].FString,
  1006. List.FList^[Index].FString);
  1007. end;
  1008. Procedure TStringList.Sort;
  1009. begin
  1010. CustomSort(@StringListAnsiCompare);
  1011. end;
  1012. {$else}
  1013. { generics based implementation of TStringList follows }
  1014. function StringListAnsiCompare(List: TStringList; Index1, Index2: Integer): Integer;
  1015. begin
  1016. Result := List.DoCompareText(List.Strings[Index1], List.Strings[Index2]);
  1017. end;
  1018. constructor TStringList.Create;
  1019. begin
  1020. inherited;
  1021. FOwnsObjects:=false;
  1022. FMap := TFPStrObjMap.Create;
  1023. FMap.OnPtrCompare := @MapPtrCompare;
  1024. FOnCompareText := @DefaultCompareText;
  1025. CheckSpecialChars;
  1026. end;
  1027. destructor TStringList.Destroy;
  1028. begin
  1029. FMap.Free;
  1030. inherited;
  1031. end;
  1032. function TStringList.GetDuplicates: TDuplicates;
  1033. begin
  1034. Result := FMap.Duplicates;
  1035. end;
  1036. function TStringList.GetSorted: boolean;
  1037. begin
  1038. Result := FMap.Sorted;
  1039. end;
  1040. procedure TStringList.SetDuplicates(NewDuplicates: TDuplicates);
  1041. begin
  1042. FMap.Duplicates := NewDuplicates;
  1043. end;
  1044. procedure TStringList.SetSorted(NewSorted: Boolean);
  1045. begin
  1046. FMap.Sorted := NewSorted;
  1047. end;
  1048. procedure TStringList.Changed;
  1049. begin
  1050. if FUpdateCount = 0 then
  1051. if Assigned(FOnChange) then
  1052. FOnChange(Self);
  1053. end;
  1054. procedure TStringList.Changing;
  1055. begin
  1056. if FUpdateCount = 0 then
  1057. if Assigned(FOnChanging) then
  1058. FOnChanging(Self);
  1059. end;
  1060. function TStringList.Get(Index: Integer): string;
  1061. begin
  1062. Result := FMap.Keys[Index];
  1063. end;
  1064. function TStringList.GetCapacity: Integer;
  1065. begin
  1066. Result := FMap.Capacity;
  1067. end;
  1068. function TStringList.GetCount: Integer;
  1069. begin
  1070. Result := FMap.Count;
  1071. end;
  1072. function TStringList.GetObject(Index: Integer): TObject;
  1073. begin
  1074. Result := FMap.Data[Index];
  1075. end;
  1076. procedure TStringList.Put(Index: Integer; const S: string);
  1077. begin
  1078. Changing;
  1079. FMap.Keys[Index] := S;
  1080. Changed;
  1081. end;
  1082. procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  1083. begin
  1084. Changing;
  1085. FMap.Data[Index] := AObject;
  1086. Changed;
  1087. end;
  1088. procedure TStringList.SetCapacity(NewCapacity: Integer);
  1089. begin
  1090. FMap.Capacity := NewCapacity;
  1091. end;
  1092. procedure TStringList.SetUpdateState(Updating: Boolean);
  1093. begin
  1094. if Updating then
  1095. Changing
  1096. else
  1097. Changed
  1098. end;
  1099. function TStringList.Add(const S: string): Integer;
  1100. begin
  1101. Result := FMap.Add(S);
  1102. end;
  1103. procedure TStringList.Clear;
  1104. begin
  1105. if FMap.Count = 0 then exit;
  1106. Changing;
  1107. FMap.Clear;
  1108. Changed;
  1109. end;
  1110. procedure TStringList.Delete(Index: Integer);
  1111. begin
  1112. if (Index < 0) or (Index >= FMap.Count) then
  1113. Error(SListIndexError, Index);
  1114. Changing;
  1115. FMap.Delete(Index);
  1116. Changed;
  1117. end;
  1118. procedure TStringList.Exchange(Index1, Index2: Integer);
  1119. begin
  1120. if (Index1 < 0) or (Index1 >= FMap.Count) then
  1121. Error(SListIndexError, Index1);
  1122. if (Index2 < 0) or (Index2 >= FMap.Count) then
  1123. Error(SListIndexError, Index2);
  1124. Changing;
  1125. FMap.InternalExchange(Index1, Index2);
  1126. Changed;
  1127. end;
  1128. procedure TStringList.SetCaseSensitive(NewSensitive: Boolean);
  1129. begin
  1130. if NewSensitive <> FCaseSensitive then
  1131. begin
  1132. FCaseSensitive := NewSensitive;
  1133. if Sorted then
  1134. Sort;
  1135. end;
  1136. end;
  1137. function TStringList.MapPtrCompare(Key1, Key2: Pointer): Integer;
  1138. begin
  1139. Result := FOnCompareText(string(Key1^), string(Key2^));
  1140. end;
  1141. function TStringList.DefaultCompareText(const s1, s2: string): PtrInt;
  1142. begin
  1143. if FCaseSensitive then
  1144. Result := AnsiCompareStr(s1, s2)
  1145. else
  1146. Result := AnsiCompareText(s1, s2);
  1147. end;
  1148. function TStringList.DoCompareText(const s1, s2: string): PtrInt;
  1149. begin
  1150. Result := FOnCompareText(s1, s2);
  1151. end;
  1152. function TStringList.Find(const S: string; var Index: Integer): Boolean;
  1153. begin
  1154. Result := FMap.Find(S, Index);
  1155. end;
  1156. function TStringList.IndexOf(const S: string): Integer;
  1157. begin
  1158. Result := FMap.IndexOf(S);
  1159. end;
  1160. procedure TStringList.Insert(Index: Integer; const S: string);
  1161. begin
  1162. if not Sorted and (0 <= Index) and (Index < FMap.Count) then
  1163. Changing;
  1164. FMap.InsertKey(Index, S);
  1165. Changed;
  1166. end;
  1167. procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare);
  1168. var
  1169. I, J, Pivot: Integer;
  1170. begin
  1171. repeat
  1172. I := L;
  1173. J := R;
  1174. Pivot := (L + R) div 2;
  1175. repeat
  1176. while CompareFn(Self, I, Pivot) < 0 do Inc(I);
  1177. while CompareFn(Self, J, Pivot) > 0 do Dec(J);
  1178. if I <= J then
  1179. begin
  1180. FMap.InternalExchange(I, J); // No check, indices are correct.
  1181. if Pivot = I then
  1182. Pivot := J
  1183. else if Pivot = J then
  1184. Pivot := I;
  1185. Inc(I);
  1186. Dec(j);
  1187. end;
  1188. until I > J;
  1189. if L < J then
  1190. QuickSort(L,J, CompareFn);
  1191. L := I;
  1192. until I >= R;
  1193. end;
  1194. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1195. begin
  1196. if not Sorted and (FMap.Count > 1) then
  1197. begin
  1198. Changing;
  1199. QuickSort(0, FMap.Count-1, CompareFn);
  1200. Changed;
  1201. end;
  1202. end;
  1203. procedure TStringList.Sort;
  1204. begin
  1205. if not Sorted and (FMap.Count > 1) then
  1206. begin
  1207. Changing;
  1208. FMap.Sort;
  1209. Changed;
  1210. end;
  1211. end;
  1212. {$endif}