stringl.inc 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920
  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; Const Quote : String) : String;
  35. Var
  36. I,J : Integer;
  37. begin
  38. J:=0;
  39. Result:=S;
  40. for i:=1 to 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. FLBS:=DefaultTextLineBreakStyle;
  64. FSpecialCharsInited:=true;
  65. FLineBreak:=sLineBreak;
  66. end;
  67. end;
  68. Function TStrings.GetSkipLastLineBreak : Boolean;
  69. begin
  70. CheckSpecialChars;
  71. Result:=FSkipLastLineBreak;
  72. end;
  73. procedure TStrings.SetSkipLastLineBreak(const AValue : Boolean);
  74. begin
  75. CheckSpecialChars;
  76. FSkipLastLineBreak:=AValue;
  77. end;
  78. Function TStrings.GetLBS : TTextLineBreakStyle;
  79. begin
  80. CheckSpecialChars;
  81. Result:=FLBS;
  82. end;
  83. Procedure TStrings.SetLBS (AValue : TTextLineBreakStyle);
  84. begin
  85. CheckSpecialChars;
  86. FLBS:=AValue;
  87. end;
  88. procedure TStrings.SetDelimiter(c:Char);
  89. begin
  90. CheckSpecialChars;
  91. FDelimiter:=c;
  92. end;
  93. Procedure TStrings.SetEncoding(const AEncoding: TEncoding);
  94. begin
  95. if (FEncoding<>nil) and not TEncoding.IsStandardEncoding(FEncoding) then
  96. FEncoding.Free;
  97. if TEncoding.IsStandardEncoding(AEncoding) then
  98. FEncoding:=AEncoding
  99. else if AEncoding<>nil then
  100. FEncoding:=AEncoding.Clone
  101. else
  102. FEncoding:=nil;
  103. end;
  104. Function TStrings.GetDelimiter : Char;
  105. begin
  106. CheckSpecialChars;
  107. Result:=FDelimiter;
  108. end;
  109. procedure TStrings.SetLineBreak(Const S : String);
  110. begin
  111. CheckSpecialChars;
  112. FLineBreak:=S;
  113. end;
  114. Function TStrings.GetLineBreak : String;
  115. begin
  116. CheckSpecialChars;
  117. Result:=FLineBreak;
  118. end;
  119. procedure TStrings.SetQuoteChar(c:Char);
  120. begin
  121. CheckSpecialChars;
  122. FQuoteChar:=c;
  123. end;
  124. Function TStrings.GetQuoteChar :Char;
  125. begin
  126. CheckSpecialChars;
  127. Result:=FQuoteChar;
  128. end;
  129. procedure TStrings.SetNameValueSeparator(c:Char);
  130. begin
  131. CheckSpecialChars;
  132. FNameValueSeparator:=c;
  133. end;
  134. Function TStrings.GetNameValueSeparator :Char;
  135. begin
  136. CheckSpecialChars;
  137. Result:=FNameValueSeparator;
  138. end;
  139. function TStrings.GetCommaText: string;
  140. Var
  141. C1,C2 : Char;
  142. FSD : Boolean;
  143. begin
  144. CheckSpecialChars;
  145. FSD:=StrictDelimiter;
  146. C1:=Delimiter;
  147. C2:=QuoteChar;
  148. Delimiter:=',';
  149. QuoteChar:='"';
  150. StrictDelimiter:=False;
  151. Try
  152. Result:=GetDelimitedText;
  153. Finally
  154. Delimiter:=C1;
  155. QuoteChar:=C2;
  156. StrictDelimiter:=FSD;
  157. end;
  158. end;
  159. Function TStrings.GetDelimitedText: string;
  160. Var
  161. I : integer;
  162. p : pchar;
  163. BreakChars : set of char;
  164. S : String;
  165. doQuote : Boolean;
  166. begin
  167. CheckSpecialChars;
  168. result:='';
  169. if StrictDelimiter then
  170. BreakChars:=[#0,QuoteChar,Delimiter]
  171. else
  172. BreakChars:=[#0..' ',QuoteChar,Delimiter];
  173. // Check for break characters and quote if required.
  174. For i:=0 to count-1 do
  175. begin
  176. S:=Strings[i];
  177. doQuote:=FAlwaysQuote;
  178. If not DoQuote then
  179. begin
  180. p:=pchar(S);
  181. //Quote strings that include BreakChars:
  182. while not(p^ in BreakChars) do
  183. inc(p);
  184. DoQuote:=(p<>pchar(S)+length(S));
  185. end;
  186. if DoQuote then
  187. Result:=Result+QuoteString(S,QuoteChar)
  188. else
  189. Result:=Result+S;
  190. if I<Count-1 then
  191. Result:=Result+Delimiter;
  192. end;
  193. // Quote empty string:
  194. If (Length(Result)=0) and (Count=1) then
  195. Result:=QuoteChar+QuoteChar;
  196. end;
  197. procedure TStrings.GetNameValue(Index : Integer; Out AName,AValue : String);
  198. Var L : longint;
  199. begin
  200. CheckSpecialChars;
  201. AValue:=Strings[Index];
  202. L:=Pos(FNameValueSeparator,AValue);
  203. If L<>0 then
  204. begin
  205. AName:=Copy(AValue,1,L-1);
  206. System.Delete(AValue,1,L);
  207. end
  208. else
  209. AName:='';
  210. end;
  211. function TStrings.ExtractName(const s:String):String;
  212. var
  213. L: Longint;
  214. begin
  215. CheckSpecialChars;
  216. L:=Pos(FNameValueSeparator,S);
  217. If L<>0 then
  218. Result:=Copy(S,1,L-1)
  219. else
  220. Result:='';
  221. end;
  222. function TStrings.GetName(Index: Integer): string;
  223. Var
  224. V : String;
  225. begin
  226. GetNameValue(Index,Result,V);
  227. end;
  228. Function TStrings.GetValue(const Name: string): string;
  229. Var
  230. L : longint;
  231. N : String;
  232. begin
  233. Result:='';
  234. L:=IndexOfName(Name);
  235. If L<>-1 then
  236. GetNameValue(L,N,Result);
  237. end;
  238. Function TStrings.GetValueFromIndex(Index: Integer): string;
  239. Var
  240. N : String;
  241. begin
  242. GetNameValue(Index,N,Result);
  243. end;
  244. Procedure TStrings.SetValueFromIndex(Index: Integer; const Value: string);
  245. begin
  246. If (Value='') then
  247. Delete(Index)
  248. else
  249. begin
  250. If (Index<0) then
  251. Index:=Add('');
  252. CheckSpecialChars;
  253. Strings[Index]:=GetName(Index)+FNameValueSeparator+Value;
  254. end;
  255. end;
  256. procedure TStrings.ReadData(Reader: TReader);
  257. begin
  258. Reader.ReadListBegin;
  259. BeginUpdate;
  260. try
  261. Clear;
  262. while not Reader.EndOfList do
  263. Add(Reader.ReadString);
  264. finally
  265. EndUpdate;
  266. end;
  267. Reader.ReadListEnd;
  268. end;
  269. Procedure TStrings.SetDelimitedText(const AValue: string);
  270. var i,j:integer;
  271. aNotFirst:boolean;
  272. begin
  273. CheckSpecialChars;
  274. BeginUpdate;
  275. i:=1;
  276. j:=1;
  277. aNotFirst:=false;
  278. { Paraphrased from Delphi XE2 help:
  279. Strings must be separated by Delimiter characters or spaces.
  280. They may be enclosed in QuoteChars.
  281. QuoteChars in the string must be repeated to distinguish them from the QuoteChars enclosing the string.
  282. }
  283. try
  284. Clear;
  285. If StrictDelimiter then
  286. begin
  287. while i<=length(AValue) do begin
  288. // skip delimiter
  289. if aNotFirst and (i<=length(AValue)) and (AValue[i]=FDelimiter) then inc(i);
  290. // read next string
  291. if i<=length(AValue) then begin
  292. if AValue[i]=FQuoteChar then begin
  293. // next string is quoted
  294. j:=i+1;
  295. while (j<=length(AValue)) and
  296. ( (AValue[j]<>FQuoteChar) or
  297. ( (j+1<=length(AValue)) and (AValue[j+1]=FQuoteChar) ) ) do begin
  298. if (j<=length(AValue)) and (AValue[j]=FQuoteChar) then inc(j,2)
  299. else inc(j);
  300. end;
  301. // j is position of closing quote
  302. Add( StringReplace (Copy(AValue,i+1,j-i-1),
  303. FQuoteChar+FQuoteChar,FQuoteChar, [rfReplaceAll]));
  304. i:=j+1;
  305. end else begin
  306. // next string is not quoted; read until delimiter
  307. j:=i;
  308. while (j<=length(AValue)) and
  309. (AValue[j]<>FDelimiter) do inc(j);
  310. Add( Copy(AValue,i,j-i));
  311. i:=j;
  312. end;
  313. end else begin
  314. if aNotFirst then Add('');
  315. end;
  316. aNotFirst:=true;
  317. end;
  318. end
  319. else
  320. begin
  321. while i<=length(AValue) do begin
  322. // skip delimiter
  323. if aNotFirst and (i<=length(AValue)) and (AValue[i]=FDelimiter) then inc(i);
  324. // skip spaces
  325. while (i<=length(AValue)) and (Ord(AValue[i])<=Ord(' ')) do inc(i);
  326. // read next string
  327. if i<=length(AValue) then begin
  328. if AValue[i]=FQuoteChar then begin
  329. // next string is quoted
  330. j:=i+1;
  331. while (j<=length(AValue)) and
  332. ( (AValue[j]<>FQuoteChar) or
  333. ( (j+1<=length(AValue)) and (AValue[j+1]=FQuoteChar) ) ) do begin
  334. if (j<=length(AValue)) and (AValue[j]=FQuoteChar) then inc(j,2)
  335. else inc(j);
  336. end;
  337. // j is position of closing quote
  338. Add( StringReplace (Copy(AValue,i+1,j-i-1),
  339. FQuoteChar+FQuoteChar,FQuoteChar, [rfReplaceAll]));
  340. i:=j+1;
  341. end else begin
  342. // next string is not quoted; read until control character/space/delimiter
  343. j:=i;
  344. while (j<=length(AValue)) and
  345. (Ord(AValue[j])>Ord(' ')) and
  346. (AValue[j]<>FDelimiter) do inc(j);
  347. Add( Copy(AValue,i,j-i));
  348. i:=j;
  349. end;
  350. end else begin
  351. if aNotFirst then Add('');
  352. end;
  353. // skip spaces
  354. while (i<=length(AValue)) and (Ord(AValue[i])<=Ord(' ')) do inc(i);
  355. aNotFirst:=true;
  356. end;
  357. end;
  358. finally
  359. EndUpdate;
  360. end;
  361. end;
  362. Procedure TStrings.SetCommaText(const Value: string);
  363. Var
  364. C1,C2 : Char;
  365. begin
  366. CheckSpecialChars;
  367. C1:=Delimiter;
  368. C2:=QuoteChar;
  369. Delimiter:=',';
  370. QuoteChar:='"';
  371. Try
  372. SetDelimitedText(Value);
  373. Finally
  374. Delimiter:=C1;
  375. QuoteChar:=C2;
  376. end;
  377. end;
  378. Procedure TStrings.SetStringsAdapter(const Value: IStringsAdapter);
  379. begin
  380. end;
  381. Procedure TStrings.SetDefaultEncoding(const ADefaultEncoding: TEncoding);
  382. begin
  383. if (FDefaultEncoding<>nil) and not TEncoding.IsStandardEncoding(FDefaultEncoding) then
  384. FDefaultEncoding.Free;
  385. if TEncoding.IsStandardEncoding(ADefaultEncoding) then
  386. FDefaultEncoding:=ADefaultEncoding
  387. else if ADefaultEncoding<>nil then
  388. FDefaultEncoding:=ADefaultEncoding.Clone
  389. else
  390. FDefaultEncoding:=TEncoding.Default;
  391. end;
  392. Procedure TStrings.SetValue(const Name, Value: string);
  393. Var L : longint;
  394. begin
  395. CheckSpecialChars;
  396. L:=IndexOfName(Name);
  397. if L=-1 then
  398. Add (Name+FNameValueSeparator+Value)
  399. else
  400. Strings[L]:=Name+FNameValueSeparator+value;
  401. end;
  402. procedure TStrings.WriteData(Writer: TWriter);
  403. var
  404. i: Integer;
  405. begin
  406. Writer.WriteListBegin;
  407. for i := 0 to Count - 1 do
  408. Writer.WriteString(Strings[i]);
  409. Writer.WriteListEnd;
  410. end;
  411. procedure TStrings.DefineProperties(Filer: TFiler);
  412. var
  413. HasData: Boolean;
  414. begin
  415. if Assigned(Filer.Ancestor) then
  416. // Only serialize if string list is different from ancestor
  417. if Filer.Ancestor.InheritsFrom(TStrings) then
  418. HasData := not Equals(TStrings(Filer.Ancestor))
  419. else
  420. HasData := True
  421. else
  422. HasData := Count > 0;
  423. Filer.DefineProperty('Strings', @ReadData, @WriteData, HasData);
  424. end;
  425. Procedure TStrings.Error(const Msg: string; Data: Integer);
  426. begin
  427. Raise EStringListError.CreateFmt(Msg,[Data]) at get_caller_addr(get_frame), get_caller_frame(get_frame);
  428. end;
  429. Procedure TStrings.Error(const Msg: pstring; Data: Integer);
  430. begin
  431. Raise EStringListError.CreateFmt(Msg^,[Data]) at get_caller_addr(get_frame), get_caller_frame(get_frame);
  432. end;
  433. Function TStrings.GetCapacity: Integer;
  434. begin
  435. Result:=Count;
  436. end;
  437. Function TStrings.GetObject(Index: Integer): TObject;
  438. begin
  439. Result:=Nil;
  440. end;
  441. Function TStrings.GetTextStr: string;
  442. Var P : Pchar;
  443. I,L,NLS : Longint;
  444. S,NL : String;
  445. begin
  446. CheckSpecialChars;
  447. // Determine needed place
  448. if FLineBreak<>sLineBreak then
  449. NL:=FLineBreak
  450. else
  451. Case FLBS of
  452. tlbsLF : NL:=#10;
  453. tlbsCRLF : NL:=#13#10;
  454. tlbsCR : NL:=#13;
  455. end;
  456. L:=0;
  457. NLS:=Length(NL);
  458. For I:=0 to count-1 do
  459. L:=L+Length(Strings[I])+NLS;
  460. if SkipLastLineBreak then
  461. Dec(L,NLS);
  462. Setlength(Result,L);
  463. P:=Pointer(Result);
  464. For i:=0 To count-1 do
  465. begin
  466. S:=Strings[I];
  467. L:=Length(S);
  468. if L<>0 then
  469. System.Move(Pointer(S)^,P^,L);
  470. P:=P+L;
  471. if (I<Count-1) or Not SkipLastLineBreak then
  472. For L:=1 to NLS do
  473. begin
  474. P^:=NL[L];
  475. inc(P);
  476. end;
  477. end;
  478. end;
  479. Procedure TStrings.Put(Index: Integer; const S: string);
  480. Var Obj : TObject;
  481. begin
  482. Obj:=Objects[Index];
  483. Delete(Index);
  484. InsertObject(Index,S,Obj);
  485. end;
  486. Procedure TStrings.PutObject(Index: Integer; AObject: TObject);
  487. begin
  488. // Empty.
  489. end;
  490. Procedure TStrings.SetCapacity(NewCapacity: Integer);
  491. begin
  492. // Empty.
  493. end;
  494. Class Function TStrings.GetNextLine (Const Value : String; Var S : String; Var P : Integer) : Boolean;
  495. Var
  496. PS : PChar;
  497. IP,L : Integer;
  498. begin
  499. L:=Length(Value);
  500. S:='';
  501. Result:=False;
  502. If ((L-P)<0) then
  503. exit;
  504. if ((L-P)=0) and (not (value[P] in [#10,#13])) Then
  505. Begin
  506. s:=value[P];
  507. inc(P);
  508. Exit(True);
  509. End;
  510. PS:=PChar(Value)+P-1;
  511. IP:=P;
  512. While ((L-P)>=0) and (not (PS^ in [#10,#13])) do
  513. begin
  514. P:=P+1;
  515. Inc(PS);
  516. end;
  517. SetLength (S,P-IP);
  518. System.Move (Value[IP],Pointer(S)^,P-IP);
  519. If (P<=L) and (Value[P]=#13) then
  520. Inc(P);
  521. If (P<=L) and (Value[P]=#10) then
  522. Inc(P); // Point to character after #10(#13)
  523. Result:=True;
  524. end;
  525. Function TStrings.GetNextLineBreak (Const Value : String; Var S : String; Var P : Integer) : Boolean;
  526. Var
  527. PS,PC,PP : PChar;
  528. begin
  529. S:='';
  530. Result:=False;
  531. If ((Length(Value)-P)<0) then
  532. exit;
  533. PS:=@Value[P];
  534. PC:=PS;
  535. PP:=AnsiStrPos(PS,PChar(FLineBreak));
  536. // Stop on #0.
  537. While (PC^<>#0) and (PC<>PP) do
  538. Inc(PC);
  539. P:=P+(PC-PS)+Length(FLineBreak);
  540. SetString(S,PS,PC-PS);
  541. Result:=True;
  542. end;
  543. Procedure TStrings.DoSetTextStr(const Value: string; DoClear : Boolean);
  544. Var
  545. S : String;
  546. P : Integer;
  547. begin
  548. Try
  549. beginUpdate;
  550. if DoClear then
  551. Clear;
  552. P:=1;
  553. if FLineBreak=sLineBreak then
  554. begin
  555. While GetNextLine (Value,S,P) do
  556. Add(S)
  557. end
  558. else
  559. While GetNextLineBreak (Value,S,P) do
  560. Add(S);
  561. finally
  562. EndUpdate;
  563. end;
  564. end;
  565. Procedure TStrings.SetTextStr(const Value: string);
  566. begin
  567. CheckSpecialChars;
  568. DoSetTextStr(Value,True);
  569. end;
  570. Procedure TStrings.AddText(const S: string);
  571. begin
  572. CheckSpecialChars;
  573. DoSetTextStr(S,False);
  574. end;
  575. Procedure TStrings.SetUpdateState(Updating: Boolean);
  576. begin
  577. FPONotifyObservers(Self,ooChange,Nil);
  578. end;
  579. destructor TSTrings.Destroy;
  580. begin
  581. if (FEncoding<>nil) and not TEncoding.IsStandardEncoding(FEncoding) then
  582. FreeAndNil(FEncoding);
  583. if (FDefaultEncoding<>nil) and not TEncoding.IsStandardEncoding(FDefaultEncoding) then
  584. FreeAndNil(FDefaultEncoding);
  585. inherited destroy;
  586. end;
  587. constructor TStrings.Create;
  588. begin
  589. inherited Create;
  590. FDefaultEncoding:=TEncoding.Default;
  591. FEncoding:=nil;
  592. FWriteBOM:=True;
  593. FAlwaysQuote:=False;
  594. end;
  595. Function TStrings.Add(const S: string): Integer;
  596. begin
  597. Result:=Count;
  598. Insert (Count,S);
  599. end;
  600. function TStrings.Add(const Fmt : string; const Args : Array of const): Integer;
  601. begin
  602. Result:=Add(Format(Fmt,Args));
  603. end;
  604. Function TStrings.AddObject(const S: string; AObject: TObject): Integer;
  605. begin
  606. Result:=Add(S);
  607. Objects[result]:=AObject;
  608. end;
  609. function TStrings.AddObject(const Fmt: string; Args : Array of const; AObject: TObject): Integer;
  610. begin
  611. Result:=AddObject(Format(Fmt,Args),AObject);
  612. end;
  613. Procedure TStrings.Append(const S: string);
  614. begin
  615. Add (S);
  616. end;
  617. Procedure TStrings.AddStrings(TheStrings: TStrings; ClearFirst : Boolean);
  618. begin
  619. beginupdate;
  620. try
  621. if ClearFirst then
  622. Clear;
  623. AddStrings(TheStrings);
  624. finally
  625. EndUpdate;
  626. end;
  627. end;
  628. Procedure TStrings.AddStrings(TheStrings: TStrings);
  629. Var Runner : longint;
  630. begin
  631. For Runner:=0 to TheStrings.Count-1 do
  632. self.AddObject (Thestrings[Runner],TheStrings.Objects[Runner]);
  633. end;
  634. Procedure TStrings.AddStrings(const TheStrings: array of string);
  635. Var Runner : longint;
  636. begin
  637. if Count + High(TheStrings)+1 > Capacity then
  638. Capacity := Count + High(TheStrings)+1;
  639. For Runner:=Low(TheStrings) to High(TheStrings) do
  640. self.Add(Thestrings[Runner]);
  641. end;
  642. Procedure TStrings.AddStrings(const TheStrings: array of string; ClearFirst : Boolean);
  643. begin
  644. beginupdate;
  645. try
  646. if ClearFirst then
  647. Clear;
  648. AddStrings(TheStrings);
  649. finally
  650. EndUpdate;
  651. end;
  652. end;
  653. Procedure TStrings.Assign(Source: TPersistent);
  654. Var
  655. S : TStrings;
  656. begin
  657. If Source is TStrings then
  658. begin
  659. S:=TStrings(Source);
  660. BeginUpdate;
  661. Try
  662. clear;
  663. FSpecialCharsInited:=S.FSpecialCharsInited;
  664. FQuoteChar:=S.FQuoteChar;
  665. FDelimiter:=S.FDelimiter;
  666. FNameValueSeparator:=S.FNameValueSeparator;
  667. FLBS:=S.FLBS;
  668. FLineBreak:=S.FLineBreak;
  669. FWriteBOM:=S.FWriteBOM;
  670. DefaultEncoding:=S.DefaultEncoding;
  671. SetEncoding(S.Encoding);
  672. AddStrings(S);
  673. finally
  674. EndUpdate;
  675. end;
  676. end
  677. else
  678. Inherited Assign(Source);
  679. end;
  680. Procedure TStrings.BeginUpdate;
  681. begin
  682. if FUpdateCount = 0 then SetUpdateState(true);
  683. inc(FUpdateCount);
  684. end;
  685. Procedure TStrings.EndUpdate;
  686. begin
  687. If FUpdateCount>0 then
  688. Dec(FUpdateCount);
  689. if FUpdateCount=0 then
  690. SetUpdateState(False);
  691. end;
  692. Function TStrings.Equals(Obj: TObject): Boolean;
  693. begin
  694. if Obj is TStrings then
  695. Result := Equals(TStrings(Obj))
  696. else
  697. Result := inherited Equals(Obj);
  698. end;
  699. Function TStrings.Equals(TheStrings: TStrings): Boolean;
  700. Var Runner,Nr : Longint;
  701. begin
  702. Result:=False;
  703. Nr:=Self.Count;
  704. if Nr<>TheStrings.Count then exit;
  705. For Runner:=0 to Nr-1 do
  706. If Strings[Runner]<>TheStrings[Runner] then exit;
  707. Result:=True;
  708. end;
  709. Procedure TStrings.Exchange(Index1, Index2: Integer);
  710. Var
  711. Obj : TObject;
  712. Str : String;
  713. begin
  714. beginUpdate;
  715. Try
  716. Obj:=Objects[Index1];
  717. Str:=Strings[Index1];
  718. Objects[Index1]:=Objects[Index2];
  719. Strings[Index1]:=Strings[Index2];
  720. Objects[Index2]:=Obj;
  721. Strings[Index2]:=Str;
  722. finally
  723. EndUpdate;
  724. end;
  725. end;
  726. function TStrings.GetEnumerator: TStringsEnumerator;
  727. begin
  728. Result:=TStringsEnumerator.Create(Self);
  729. end;
  730. Function TStrings.GetText: PChar;
  731. begin
  732. Result:=StrNew(Pchar(Self.Text));
  733. end;
  734. Function TStrings.DoCompareText(const s1,s2 : string) : PtrInt;
  735. begin
  736. result:=CompareText(s1,s2);
  737. end;
  738. Function TStrings.IndexOf(const S: string): Integer;
  739. begin
  740. Result:=0;
  741. While (Result<Count) and (DoCompareText(Strings[Result],S)<>0) do Result:=Result+1;
  742. if Result=Count then Result:=-1;
  743. end;
  744. Function TStrings.IndexOfName(const Name: string): Integer;
  745. Var
  746. len : longint;
  747. S : String;
  748. begin
  749. CheckSpecialChars;
  750. Result:=0;
  751. while (Result<Count) do
  752. begin
  753. S:=Strings[Result];
  754. len:=pos(FNameValueSeparator,S)-1;
  755. if (len>=0) and (DoCompareText(Name,Copy(S,1,Len))=0) then
  756. exit;
  757. inc(result);
  758. end;
  759. result:=-1;
  760. end;
  761. Function TStrings.IndexOfObject(AObject: TObject): Integer;
  762. begin
  763. Result:=0;
  764. While (Result<count) and (Objects[Result]<>AObject) do Result:=Result+1;
  765. If Result=Count then Result:=-1;
  766. end;
  767. Procedure TStrings.InsertObject(Index: Integer; const S: string;
  768. AObject: TObject);
  769. begin
  770. Insert (Index,S);
  771. Objects[Index]:=AObject;
  772. end;
  773. Procedure TStrings.LoadFromFile(const FileName: string);
  774. begin
  775. LoadFromFile(FileName,False)
  776. end;
  777. Procedure TStrings.LoadFromFile(const FileName: string; IgnoreEncoding : Boolean);
  778. Var
  779. TheStream : TFileStream;
  780. begin
  781. TheStream:=TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite);
  782. try
  783. LoadFromStream(TheStream, IgnoreEncoding);
  784. finally
  785. TheStream.Free;
  786. end;
  787. end;
  788. Procedure TStrings.LoadFromFile(const FileName: string; AEncoding: TEncoding);
  789. Var
  790. TheStream : TFileStream;
  791. begin
  792. TheStream:=TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite);
  793. try
  794. LoadFromStream(TheStream,AEncoding);
  795. finally
  796. TheStream.Free;
  797. end;
  798. end;
  799. Procedure TStrings.LoadFromStream(Stream: TStream);
  800. begin
  801. LoadFromStream(Stream,False);
  802. end;
  803. Procedure TStrings.LoadFromStream(Stream: TStream; IgnoreEncoding : Boolean);
  804. {
  805. Borlands method is no good, since a pipe for
  806. instance doesn't have a size.
  807. So we must do it the hard way.
  808. }
  809. Const
  810. BufSize = 1024;
  811. MaxGrow = 1 shl 29;
  812. Var
  813. Buffer : AnsiString;
  814. BytesRead,
  815. BufLen,
  816. I,BufDelta : Longint;
  817. begin
  818. if not IgnoreEncoding then
  819. begin
  820. LoadFromStream(Stream,Nil);
  821. Exit;
  822. end;
  823. // reread into a buffer
  824. beginupdate;
  825. try
  826. Buffer:='';
  827. BufLen:=0;
  828. I:=1;
  829. Repeat
  830. BufDelta:=BufSize*I;
  831. SetLength(Buffer,BufLen+BufDelta);
  832. BytesRead:=Stream.Read(Buffer[BufLen+1],BufDelta);
  833. inc(BufLen,BufDelta);
  834. If I<MaxGrow then
  835. I:=I shl 1;
  836. Until BytesRead<>BufDelta;
  837. SetLength(Buffer, BufLen-BufDelta+BytesRead);
  838. SetTextStr(Buffer);
  839. SetLength(Buffer,0);
  840. finally
  841. EndUpdate;
  842. end;
  843. end;
  844. Procedure TStrings.LoadFromStream(Stream: TStream; AEncoding: TEncoding);
  845. {
  846. Borlands method is no good, since a pipe for
  847. instance doesn't have a size.
  848. So we must do it the hard way.
  849. }
  850. Const
  851. BufSize = 1024;
  852. MaxGrow = 1 shl 29;
  853. Var
  854. Buffer : TBytes;
  855. T : string;
  856. BytesRead,
  857. BufLen,
  858. I,BufDelta,
  859. PreambleLength : Longint;
  860. begin
  861. // reread into a buffer
  862. beginupdate;
  863. try
  864. SetLength(Buffer,0);
  865. BufLen:=0;
  866. I:=1;
  867. Repeat
  868. BufDelta:=BufSize*I;
  869. SetLength(Buffer,BufLen+BufDelta);
  870. BytesRead:=Stream.Read(Buffer[BufLen],BufDelta);
  871. inc(BufLen,BufDelta);
  872. If I<MaxGrow then
  873. I:=I shl 1;
  874. Until BytesRead<>BufDelta;
  875. SetLength(Buffer,BufLen-BufDelta+BytesRead);
  876. PreambleLength:=TEncoding.GetBufferEncoding(Buffer,AEncoding,FDefaultEncoding);
  877. T:=AEncoding.GetAnsiString(Buffer,PreambleLength,Length(Buffer)-PreambleLength);
  878. SetEncoding(AEncoding);
  879. SetLength(Buffer,0);
  880. SetTextStr(T);
  881. finally
  882. EndUpdate;
  883. end;
  884. end;
  885. Procedure TStrings.Move(CurIndex, NewIndex: Integer);
  886. Var
  887. Obj : TObject;
  888. Str : String;
  889. begin
  890. BeginUpdate;
  891. Try
  892. Obj:=Objects[CurIndex];
  893. Str:=Strings[CurIndex];
  894. Objects[CurIndex]:=Nil; // Prevent Delete from freeing.
  895. Delete(Curindex);
  896. InsertObject(NewIndex,Str,Obj);
  897. finally
  898. EndUpdate;
  899. end;
  900. end;
  901. Procedure TStrings.SaveToFile(const FileName: string);
  902. Var TheStream : TFileStream;
  903. begin
  904. TheStream:=TFileStream.Create(FileName,fmCreate);
  905. try
  906. SaveToStream(TheStream);
  907. finally
  908. TheStream.Free;
  909. end;
  910. end;
  911. Procedure TStrings.SaveToFile(const FileName: string; AEncoding: TEncoding);
  912. Var TheStream : TFileStream;
  913. begin
  914. TheStream:=TFileStream.Create(FileName,fmCreate);
  915. try
  916. SaveToStream(TheStream,AEncoding);
  917. finally
  918. TheStream.Free;
  919. end;
  920. end;
  921. Procedure TStrings.SaveToStream(Stream: TStream);
  922. Var
  923. S : String;
  924. begin
  925. if Encoding<>nil then
  926. SaveToStream(Stream,Encoding)
  927. else
  928. begin
  929. S:=Text;
  930. if S = '' then Exit;
  931. Stream.WriteBuffer(Pointer(S)^,Length(S));
  932. end;
  933. end;
  934. Procedure TStrings.SaveToStream(Stream: TStream; AEncoding: TEncoding);
  935. Var B : TBytes;
  936. begin
  937. if AEncoding=nil then
  938. AEncoding:=FDefaultEncoding;
  939. if FWriteBOM then
  940. begin
  941. B:=AEncoding.GetPreamble;
  942. if Length(B)>0 then
  943. Stream.WriteBuffer(B[0],Length(B));
  944. end;
  945. B:=AEncoding.GetAnsiBytes(Text);
  946. if Length(B)>0 then
  947. Stream.WriteBuffer(B[0],Length(B));
  948. end;
  949. Procedure TStrings.SetText(TheText: PChar);
  950. Var S : String;
  951. begin
  952. If TheText<>Nil then
  953. S:=StrPas(TheText)
  954. else
  955. S:='';
  956. SetTextStr(S);
  957. end;
  958. {****************************************************************************}
  959. {* TStringList *}
  960. {****************************************************************************}
  961. {$if not defined(FPC_TESTGENERICS)}
  962. procedure TStringList.ExchangeItemsInt(Index1, Index2: Integer);
  963. Var P1,P2 : Pointer;
  964. begin
  965. P1:=Pointer(Flist^[Index1].FString);
  966. P2:=Pointer(Flist^[Index1].FObject);
  967. Pointer(Flist^[Index1].Fstring):=Pointer(Flist^[Index2].Fstring);
  968. Pointer(Flist^[Index1].FObject):=Pointer(Flist^[Index2].FObject);
  969. Pointer(Flist^[Index2].Fstring):=P1;
  970. Pointer(Flist^[Index2].FObject):=P2;
  971. end;
  972. function TStringList.GetSorted: Boolean;
  973. begin
  974. Result:=FSortStyle in [sslUser,sslAuto];
  975. end;
  976. procedure TStringList.ExchangeItems(Index1, Index2: Integer);
  977. begin
  978. ExchangeItemsInt(Index1, Index2);
  979. end;
  980. procedure TStringList.Grow;
  981. Var
  982. NC : Integer;
  983. begin
  984. NC:=FCapacity;
  985. If NC>=256 then
  986. NC:=NC+(NC Div 4)
  987. else if NC=0 then
  988. NC:=4
  989. else
  990. NC:=NC*4;
  991. SetCapacity(NC);
  992. end;
  993. procedure TStringList.InternalClear(FromIndex: Integer; ClearOnly: Boolean);
  994. Var
  995. I: Integer;
  996. begin
  997. if FromIndex < FCount then
  998. begin
  999. if FOwnsObjects then
  1000. begin
  1001. For I:=FromIndex to FCount-1 do
  1002. begin
  1003. Flist^[I].FString:='';
  1004. freeandnil(Flist^[i].FObject);
  1005. end;
  1006. end
  1007. else
  1008. begin
  1009. For I:=FromIndex to FCount-1 do
  1010. Flist^[I].FString:='';
  1011. end;
  1012. FCount:=FromIndex;
  1013. end;
  1014. if Not ClearOnly then
  1015. SetCapacity(0);
  1016. end;
  1017. procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare
  1018. );
  1019. var
  1020. Pivot, vL, vR: Integer;
  1021. ExchangeProc: procedure(Left, Right: Integer) of object;
  1022. begin
  1023. //if ExchangeItems is override call that, else call (faster) ExchangeItemsInt
  1024. if TMethod(@Self.ExchangeItems).Code = CodePointer(@TStringList.ExchangeItems) then
  1025. ExchangeProc := @ExchangeItemsInt
  1026. else
  1027. ExchangeProc := @ExchangeItems;
  1028. if R - L <= 1 then begin // a little bit of time saver
  1029. if L < R then
  1030. if CompareFn(Self, L, R) > 0 then
  1031. ExchangeProc(L, R);
  1032. Exit;
  1033. end;
  1034. vL := L;
  1035. vR := R;
  1036. Pivot := L + Random(R - L); // they say random is best
  1037. while vL < vR do begin
  1038. while (vL < Pivot) and (CompareFn(Self, vL, Pivot) <= 0) do
  1039. Inc(vL);
  1040. while (vR > Pivot) and (CompareFn(Self, vR, Pivot) > 0) do
  1041. Dec(vR);
  1042. ExchangeProc(vL, vR);
  1043. if Pivot = vL then // swap pivot if we just hit it from one side
  1044. Pivot := vR
  1045. else if Pivot = vR then
  1046. Pivot := vL;
  1047. end;
  1048. if Pivot - 1 >= L then
  1049. QuickSort(L, Pivot - 1, CompareFn);
  1050. if Pivot + 1 <= R then
  1051. QuickSort(Pivot + 1, R, CompareFn);
  1052. end;
  1053. procedure TStringList.InsertItem(Index: Integer; const S: string);
  1054. begin
  1055. InsertItem(Index, S, nil);
  1056. end;
  1057. procedure TStringList.InsertItem(Index: Integer; const S: string; O: TObject);
  1058. begin
  1059. Changing;
  1060. If FCount=Fcapacity then Grow;
  1061. If Index<FCount then
  1062. System.Move (FList^[Index],FList^[Index+1],
  1063. (FCount-Index)*SizeOf(TStringItem));
  1064. Pointer(Flist^[Index].Fstring):=Nil; // Needed to initialize...
  1065. Flist^[Index].FString:=S;
  1066. Flist^[Index].FObject:=O;
  1067. Inc(FCount);
  1068. Changed;
  1069. end;
  1070. procedure TStringList.SetSorted(Value: Boolean);
  1071. begin
  1072. If Value then
  1073. SortStyle:=sslAuto
  1074. else
  1075. SortStyle:=sslNone
  1076. end;
  1077. procedure TStringList.Changed;
  1078. begin
  1079. If (FUpdateCount=0) Then
  1080. begin
  1081. If Assigned(FOnChange) then
  1082. FOnchange(Self);
  1083. FPONotifyObservers(Self,ooChange,Nil);
  1084. end;
  1085. end;
  1086. procedure TStringList.Changing;
  1087. begin
  1088. If FUpdateCount=0 then
  1089. if Assigned(FOnChanging) then
  1090. FOnchanging(Self);
  1091. end;
  1092. function TStringList.Get(Index: Integer): string;
  1093. begin
  1094. CheckIndex(Index);
  1095. Result:=Flist^[Index].FString;
  1096. end;
  1097. function TStringList.GetCapacity: Integer;
  1098. begin
  1099. Result:=FCapacity;
  1100. end;
  1101. function TStringList.GetCount: Integer;
  1102. begin
  1103. Result:=FCount;
  1104. end;
  1105. function TStringList.GetObject(Index: Integer): TObject;
  1106. begin
  1107. CheckIndex(Index);
  1108. Result:=Flist^[Index].FObject;
  1109. end;
  1110. procedure TStringList.Put(Index: Integer; const S: string);
  1111. begin
  1112. If Sorted then
  1113. Error(SSortedListError,0);
  1114. CheckIndex(Index);
  1115. Changing;
  1116. Flist^[Index].FString:=S;
  1117. Changed;
  1118. end;
  1119. procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  1120. begin
  1121. CheckIndex(Index);
  1122. Changing;
  1123. Flist^[Index].FObject:=AObject;
  1124. Changed;
  1125. end;
  1126. procedure TStringList.SetCapacity(NewCapacity: Integer);
  1127. Var NewList : Pointer;
  1128. MSize : Longint;
  1129. begin
  1130. If (NewCapacity<0) then
  1131. Error (SListCapacityError,NewCapacity);
  1132. If NewCapacity>FCapacity then
  1133. begin
  1134. GetMem (NewList,NewCapacity*SizeOf(TStringItem));
  1135. If NewList=Nil then
  1136. Error (SListCapacityError,NewCapacity);
  1137. If Assigned(FList) then
  1138. begin
  1139. MSize:=FCapacity*Sizeof(TStringItem);
  1140. System.Move (FList^,NewList^,MSize);
  1141. FillWord (Pchar(NewList)[MSize],(NewCapacity-FCapacity)*(SizeOf(TStringItem) div SizeOf(Word)), 0);
  1142. FreeMem (Flist,MSize);
  1143. end;
  1144. Flist:=NewList;
  1145. FCapacity:=NewCapacity;
  1146. end
  1147. else if NewCapacity<FCapacity then
  1148. begin
  1149. if NewCapacity = 0 then
  1150. begin
  1151. if FCount > 0 then
  1152. InternalClear(0,True);
  1153. FreeMem(FList);
  1154. FList := nil;
  1155. end else
  1156. begin
  1157. InternalClear(NewCapacity,True);
  1158. GetMem(NewList, NewCapacity * SizeOf(TStringItem));
  1159. System.Move(FList^, NewList^, NewCapacity * SizeOf(TStringItem));
  1160. FreeMem(FList);
  1161. FList := NewList;
  1162. end;
  1163. FCapacity:=NewCapacity;
  1164. end;
  1165. end;
  1166. procedure TStringList.SetUpdateState(Updating: Boolean);
  1167. begin
  1168. If Updating then
  1169. Changing
  1170. else
  1171. Changed
  1172. end;
  1173. destructor TStringList.Destroy;
  1174. begin
  1175. InternalClear;
  1176. Inherited destroy;
  1177. end;
  1178. function TStringList.Add(const S: string): Integer;
  1179. begin
  1180. If Not (SortStyle=sslAuto) then
  1181. Result:=FCount
  1182. else
  1183. If Find (S,Result) then
  1184. Case DUplicates of
  1185. DupIgnore : Exit;
  1186. DupError : Error(SDuplicateString,0)
  1187. end;
  1188. InsertItem (Result,S);
  1189. end;
  1190. procedure TStringList.Clear;
  1191. begin
  1192. if FCount = 0 then Exit;
  1193. Changing;
  1194. InternalClear;
  1195. Changed;
  1196. end;
  1197. procedure TStringList.Delete(Index: Integer);
  1198. begin
  1199. CheckIndex(Index);
  1200. Changing;
  1201. Flist^[Index].FString:='';
  1202. if FOwnsObjects then
  1203. FreeAndNil(Flist^[Index].FObject);
  1204. Dec(FCount);
  1205. If Index<FCount then
  1206. System.Move(Flist^[Index+1],
  1207. Flist^[Index],
  1208. (Fcount-Index)*SizeOf(TStringItem));
  1209. Changed;
  1210. end;
  1211. procedure TStringList.Exchange(Index1, Index2: Integer);
  1212. begin
  1213. CheckIndex(Index1);
  1214. CheckIndex(Index2);
  1215. Changing;
  1216. ExchangeItemsInt(Index1,Index2);
  1217. changed;
  1218. end;
  1219. procedure TStringList.SetCaseSensitive(b : boolean);
  1220. begin
  1221. if b=FCaseSensitive then
  1222. Exit;
  1223. FCaseSensitive:=b;
  1224. if FSortStyle=sslAuto then
  1225. begin
  1226. FForceSort:=True;
  1227. try
  1228. Sort;
  1229. finally
  1230. FForceSort:=False;
  1231. end;
  1232. end;
  1233. end;
  1234. procedure TStringList.SetSortStyle(AValue: TStringsSortStyle);
  1235. begin
  1236. if FSortStyle=AValue then Exit;
  1237. if (AValue=sslAuto) then
  1238. Sort;
  1239. FSortStyle:=AValue;
  1240. end;
  1241. procedure TStringList.CheckIndex(AIndex: Integer);
  1242. begin
  1243. If (AIndex<0) or (AIndex>=FCount) then
  1244. Error(SListIndexError,AIndex);
  1245. end;
  1246. function TStringList.DoCompareText(const s1, s2: string): PtrInt;
  1247. begin
  1248. if FCaseSensitive then
  1249. result:=AnsiCompareStr(s1,s2)
  1250. else
  1251. result:=AnsiCompareText(s1,s2);
  1252. end;
  1253. function TStringList.CompareStrings(const s1,s2 : string) : Integer;
  1254. begin
  1255. Result := DoCompareText(s1, s2);
  1256. end;
  1257. function TStringList.Find(const S: string; out Index: Integer): Boolean;
  1258. var
  1259. L, R, I: Integer;
  1260. CompareRes: PtrInt;
  1261. begin
  1262. Result := false;
  1263. Index:=-1;
  1264. if Not Sorted then
  1265. Raise EListError.Create(SErrFindNeedsSortedList);
  1266. // Use binary search.
  1267. L := 0;
  1268. R := Count - 1;
  1269. while (L<=R) do
  1270. begin
  1271. I := L + (R - L) div 2;
  1272. CompareRes := DoCompareText(S, Flist^[I].FString);
  1273. if (CompareRes>0) then
  1274. L := I+1
  1275. else begin
  1276. R := I-1;
  1277. if (CompareRes=0) then begin
  1278. Result := true;
  1279. if (Duplicates<>dupAccept) then
  1280. L := I; // forces end of while loop
  1281. end;
  1282. end;
  1283. end;
  1284. Index := L;
  1285. end;
  1286. function TStringList.IndexOf(const S: string): Integer;
  1287. begin
  1288. If Not Sorted then
  1289. Result:=Inherited indexOf(S)
  1290. else
  1291. // faster using binary search...
  1292. If Not Find (S,Result) then
  1293. Result:=-1;
  1294. end;
  1295. procedure TStringList.Insert(Index: Integer; const S: string);
  1296. begin
  1297. If SortStyle=sslAuto then
  1298. Error (SSortedListError,0)
  1299. else
  1300. begin
  1301. If (Index<0) or (Index>FCount) then
  1302. Error(SListIndexError,Index); // Cannot use CheckIndex, because there >= FCount...
  1303. InsertItem (Index,S);
  1304. end;
  1305. end;
  1306. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1307. begin
  1308. If (FForceSort or (Not (FSortStyle=sslAuto))) and (FCount>1) then
  1309. begin
  1310. Changing;
  1311. QuickSort(0,FCount-1, CompareFn);
  1312. Changed;
  1313. end;
  1314. end;
  1315. function StringListAnsiCompare(List: TStringList; Index1, Index: Integer): Integer;
  1316. begin
  1317. Result := List.DoCompareText(List.FList^[Index1].FString,
  1318. List.FList^[Index].FString);
  1319. end;
  1320. procedure TStringList.Sort;
  1321. begin
  1322. CustomSort(@StringListAnsiCompare);
  1323. end;
  1324. {$else}
  1325. { generics based implementation of TStringList follows }
  1326. function StringListAnsiCompare(List: TStringList; Index1, Index2: Integer): Integer;
  1327. begin
  1328. Result := List.DoCompareText(List.Strings[Index1], List.Strings[Index2]);
  1329. end;
  1330. constructor TStringList.Create;
  1331. begin
  1332. inherited;
  1333. FOwnsObjects:=false;
  1334. FMap := TFPStrObjMap.Create;
  1335. FMap.OnPtrCompare := @MapPtrCompare;
  1336. FOnCompareText := @DefaultCompareText;
  1337. NameValueSeparator:='=';
  1338. CheckSpecialChars;
  1339. end;
  1340. destructor TStringList.Destroy;
  1341. begin
  1342. FMap.Free;
  1343. inherited;
  1344. end;
  1345. function TStringList.GetDuplicates: TDuplicates;
  1346. begin
  1347. Result := FMap.Duplicates;
  1348. end;
  1349. function TStringList.GetSorted: boolean;
  1350. begin
  1351. Result := FMap.Sorted;
  1352. end;
  1353. procedure TStringList.SetDuplicates(NewDuplicates: TDuplicates);
  1354. begin
  1355. FMap.Duplicates := NewDuplicates;
  1356. end;
  1357. procedure TStringList.SetSorted(NewSorted: Boolean);
  1358. begin
  1359. FMap.Sorted := NewSorted;
  1360. end;
  1361. procedure TStringList.Changed;
  1362. begin
  1363. if FUpdateCount = 0 then
  1364. if Assigned(FOnChange) then
  1365. FOnChange(Self);
  1366. end;
  1367. procedure TStringList.Changing;
  1368. begin
  1369. if FUpdateCount = 0 then
  1370. if Assigned(FOnChanging) then
  1371. FOnChanging(Self);
  1372. end;
  1373. function TStringList.Get(Index: Integer): string;
  1374. begin
  1375. Result := FMap.Keys[Index];
  1376. end;
  1377. function TStringList.GetCapacity: Integer;
  1378. begin
  1379. Result := FMap.Capacity;
  1380. end;
  1381. function TStringList.GetCount: Integer;
  1382. begin
  1383. Result := FMap.Count;
  1384. end;
  1385. function TStringList.GetObject(Index: Integer): TObject;
  1386. begin
  1387. Result := FMap.Data[Index];
  1388. end;
  1389. procedure TStringList.Put(Index: Integer; const S: string);
  1390. begin
  1391. Changing;
  1392. FMap.Keys[Index] := S;
  1393. Changed;
  1394. end;
  1395. procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  1396. begin
  1397. Changing;
  1398. FMap.Data[Index] := AObject;
  1399. Changed;
  1400. end;
  1401. procedure TStringList.SetCapacity(NewCapacity: Integer);
  1402. begin
  1403. FMap.Capacity := NewCapacity;
  1404. end;
  1405. procedure TStringList.SetUpdateState(Updating: Boolean);
  1406. begin
  1407. if Updating then
  1408. Changing
  1409. else
  1410. Changed
  1411. end;
  1412. function TStringList.Add(const S: string): Integer;
  1413. begin
  1414. Result := FMap.Add(S);
  1415. end;
  1416. procedure TStringList.Clear;
  1417. begin
  1418. if FMap.Count = 0 then exit;
  1419. Changing;
  1420. FMap.Clear;
  1421. Changed;
  1422. end;
  1423. procedure TStringList.Delete(Index: Integer);
  1424. begin
  1425. if (Index < 0) or (Index >= FMap.Count) then
  1426. Error(SListIndexError, Index);
  1427. Changing;
  1428. FMap.Delete(Index);
  1429. Changed;
  1430. end;
  1431. procedure TStringList.Exchange(Index1, Index2: Integer);
  1432. begin
  1433. if (Index1 < 0) or (Index1 >= FMap.Count) then
  1434. Error(SListIndexError, Index1);
  1435. if (Index2 < 0) or (Index2 >= FMap.Count) then
  1436. Error(SListIndexError, Index2);
  1437. Changing;
  1438. FMap.InternalExchange(Index1, Index2);
  1439. Changed;
  1440. end;
  1441. procedure TStringList.SetCaseSensitive(NewSensitive: Boolean);
  1442. begin
  1443. if NewSensitive <> FCaseSensitive then
  1444. begin
  1445. FCaseSensitive := NewSensitive;
  1446. if Sorted then
  1447. Sort;
  1448. end;
  1449. end;
  1450. function TStringList.MapPtrCompare(Key1, Key2: Pointer): Integer;
  1451. begin
  1452. Result := FOnCompareText(string(Key1^), string(Key2^));
  1453. end;
  1454. function TStringList.DefaultCompareText(const s1, s2: string): PtrInt;
  1455. begin
  1456. if FCaseSensitive then
  1457. Result := AnsiCompareStr(s1, s2)
  1458. else
  1459. Result := AnsiCompareText(s1, s2);
  1460. end;
  1461. function TStringList.DoCompareText(const s1, s2: string): PtrInt;
  1462. begin
  1463. Result := FOnCompareText(s1, s2);
  1464. end;
  1465. function TStringList.Find(const S: string; var Index: Integer): Boolean;
  1466. begin
  1467. Result := FMap.Find(S, Index);
  1468. end;
  1469. function TStringList.IndexOf(const S: string): Integer;
  1470. begin
  1471. Result := FMap.IndexOf(S);
  1472. end;
  1473. procedure TStringList.Insert(Index: Integer; const S: string);
  1474. begin
  1475. if not Sorted and (0 <= Index) and (Index < FMap.Count) then
  1476. Changing;
  1477. FMap.InsertKey(Index, S);
  1478. Changed;
  1479. end;
  1480. procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare);
  1481. var
  1482. I, J, Pivot: Integer;
  1483. begin
  1484. repeat
  1485. I := L;
  1486. J := R;
  1487. Pivot := (L + R) div 2;
  1488. repeat
  1489. while CompareFn(Self, I, Pivot) < 0 do Inc(I);
  1490. while CompareFn(Self, J, Pivot) > 0 do Dec(J);
  1491. if I <= J then
  1492. begin
  1493. FMap.InternalExchange(I, J); // No check, indices are correct.
  1494. if Pivot = I then
  1495. Pivot := J
  1496. else if Pivot = J then
  1497. Pivot := I;
  1498. Inc(I);
  1499. Dec(j);
  1500. end;
  1501. until I > J;
  1502. if L < J then
  1503. QuickSort(L,J, CompareFn);
  1504. L := I;
  1505. until I >= R;
  1506. end;
  1507. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1508. begin
  1509. if not Sorted and (FMap.Count > 1) then
  1510. begin
  1511. Changing;
  1512. QuickSort(0, FMap.Count-1, CompareFn);
  1513. Changed;
  1514. end;
  1515. end;
  1516. procedure TStringList.Sort;
  1517. begin
  1518. if not Sorted and (FMap.Count > 1) then
  1519. begin
  1520. Changing;
  1521. FMap.Sort;
  1522. Changed;
  1523. end;
  1524. end;
  1525. {$endif}