stringl.inc 27 KB

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