stringl.inc 29 KB

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