stringl.inc 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980
  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. function TStrings.AddPair(const AName, AValue: string): TStrings;
  614. begin
  615. Result:=AddPair(AName,AValue,Nil);
  616. end;
  617. function TStrings.AddPair(const AName, AValue: string; AObject: TObject): TStrings;
  618. begin
  619. Result := Self;
  620. AddObject(Concat(AName, NameValueSeparator, AValue), AObject);
  621. end;
  622. Procedure TStrings.Append(const S: string);
  623. begin
  624. Add (S);
  625. end;
  626. Procedure TStrings.AddStrings(TheStrings: TStrings; ClearFirst : Boolean);
  627. Var Runner : longint;
  628. begin
  629. beginupdate;
  630. try
  631. if ClearFirst then
  632. Clear;
  633. if Count + TheStrings.Count > Capacity then
  634. Capacity := Count + TheStrings.Count;
  635. For Runner:=0 to TheStrings.Count-1 do
  636. self.AddObject (Thestrings[Runner],TheStrings.Objects[Runner]);
  637. finally
  638. EndUpdate;
  639. end;
  640. end;
  641. Procedure TStrings.AddStrings(TheStrings: TStrings);
  642. begin
  643. AddStrings(TheStrings, False);
  644. end;
  645. Procedure TStrings.AddStrings(const TheStrings: array of string);
  646. begin
  647. AddStrings(TheStrings, False);
  648. end;
  649. Procedure TStrings.AddStrings(const TheStrings: array of string; ClearFirst : Boolean);
  650. Var Runner : longint;
  651. begin
  652. beginupdate;
  653. try
  654. if ClearFirst then
  655. Clear;
  656. if Count + High(TheStrings)+1 > Capacity then
  657. Capacity := Count + High(TheStrings)+1;
  658. For Runner:=Low(TheStrings) to High(TheStrings) do
  659. self.Add(Thestrings[Runner]);
  660. finally
  661. EndUpdate;
  662. end;
  663. end;
  664. Procedure TStrings.Assign(Source: TPersistent);
  665. Var
  666. S : TStrings;
  667. begin
  668. If Source is TStrings then
  669. begin
  670. S:=TStrings(Source);
  671. BeginUpdate;
  672. Try
  673. clear;
  674. FSpecialCharsInited:=S.FSpecialCharsInited;
  675. FQuoteChar:=S.FQuoteChar;
  676. FDelimiter:=S.FDelimiter;
  677. FNameValueSeparator:=S.FNameValueSeparator;
  678. FLBS:=S.FLBS;
  679. FLineBreak:=S.FLineBreak;
  680. FWriteBOM:=S.FWriteBOM;
  681. DefaultEncoding:=S.DefaultEncoding;
  682. SetEncoding(S.Encoding);
  683. AddStrings(S);
  684. finally
  685. EndUpdate;
  686. end;
  687. end
  688. else
  689. Inherited Assign(Source);
  690. end;
  691. Procedure TStrings.BeginUpdate;
  692. begin
  693. if FUpdateCount = 0 then SetUpdateState(true);
  694. inc(FUpdateCount);
  695. end;
  696. Procedure TStrings.EndUpdate;
  697. begin
  698. If FUpdateCount>0 then
  699. Dec(FUpdateCount);
  700. if FUpdateCount=0 then
  701. SetUpdateState(False);
  702. end;
  703. Function TStrings.Equals(Obj: TObject): Boolean;
  704. begin
  705. if Obj is TStrings then
  706. Result := Equals(TStrings(Obj))
  707. else
  708. Result := inherited Equals(Obj);
  709. end;
  710. Function TStrings.Equals(TheStrings: TStrings): Boolean;
  711. Var Runner,Nr : Longint;
  712. begin
  713. Result:=False;
  714. Nr:=Self.Count;
  715. if Nr<>TheStrings.Count then exit;
  716. For Runner:=0 to Nr-1 do
  717. If Strings[Runner]<>TheStrings[Runner] then exit;
  718. Result:=True;
  719. end;
  720. Procedure TStrings.Exchange(Index1, Index2: Integer);
  721. Var
  722. Obj : TObject;
  723. Str : String;
  724. begin
  725. beginUpdate;
  726. Try
  727. Obj:=Objects[Index1];
  728. Str:=Strings[Index1];
  729. Objects[Index1]:=Objects[Index2];
  730. Strings[Index1]:=Strings[Index2];
  731. Objects[Index2]:=Obj;
  732. Strings[Index2]:=Str;
  733. finally
  734. EndUpdate;
  735. end;
  736. end;
  737. function TStrings.GetEnumerator: TStringsEnumerator;
  738. begin
  739. Result:=TStringsEnumerator.Create(Self);
  740. end;
  741. Function TStrings.GetText: PChar;
  742. begin
  743. Result:=StrNew(Pchar(Self.Text));
  744. end;
  745. Function TStrings.DoCompareText(const s1,s2 : string) : PtrInt;
  746. begin
  747. result:=CompareText(s1,s2);
  748. end;
  749. Function TStrings.IndexOf(const S: string): Integer;
  750. begin
  751. Result:=0;
  752. While (Result<Count) and (DoCompareText(Strings[Result],S)<>0) do Result:=Result+1;
  753. if Result=Count then Result:=-1;
  754. end;
  755. Function TStrings.IndexOfName(const Name: string): Integer;
  756. Var
  757. len : longint;
  758. S : String;
  759. begin
  760. CheckSpecialChars;
  761. Result:=0;
  762. while (Result<Count) do
  763. begin
  764. S:=Strings[Result];
  765. len:=pos(FNameValueSeparator,S)-1;
  766. if (len>=0) and (DoCompareText(Name,Copy(S,1,Len))=0) then
  767. exit;
  768. inc(result);
  769. end;
  770. result:=-1;
  771. end;
  772. Function TStrings.IndexOfObject(AObject: TObject): Integer;
  773. begin
  774. Result:=0;
  775. While (Result<count) and (Objects[Result]<>AObject) do Result:=Result+1;
  776. If Result=Count then Result:=-1;
  777. end;
  778. Procedure TStrings.InsertObject(Index: Integer; const S: string;
  779. AObject: TObject);
  780. begin
  781. Insert (Index,S);
  782. Objects[Index]:=AObject;
  783. end;
  784. Procedure TStrings.LoadFromFile(const FileName: string);
  785. begin
  786. LoadFromFile(FileName,False)
  787. end;
  788. Procedure TStrings.LoadFromFile(const FileName: string; IgnoreEncoding : Boolean);
  789. Var
  790. TheStream : TFileStream;
  791. begin
  792. TheStream:=TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite);
  793. try
  794. LoadFromStream(TheStream, IgnoreEncoding);
  795. finally
  796. TheStream.Free;
  797. end;
  798. end;
  799. Procedure TStrings.LoadFromFile(const FileName: string; AEncoding: TEncoding);
  800. Var
  801. TheStream : TFileStream;
  802. begin
  803. TheStream:=TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite);
  804. try
  805. LoadFromStream(TheStream,AEncoding);
  806. finally
  807. TheStream.Free;
  808. end;
  809. end;
  810. Procedure TStrings.LoadFromStream(Stream: TStream);
  811. begin
  812. LoadFromStream(Stream,False);
  813. end;
  814. Procedure TStrings.LoadFromStream(Stream: TStream; IgnoreEncoding : Boolean);
  815. {
  816. Borlands method is no good, since a pipe for
  817. instance doesn't have a size.
  818. So we must do it the hard way.
  819. }
  820. Const
  821. BufSize = 1024;
  822. MaxGrow = 1 shl 29;
  823. Var
  824. Buffer : AnsiString;
  825. BytesRead,
  826. BufLen,
  827. I,BufDelta : Longint;
  828. begin
  829. if not IgnoreEncoding then
  830. begin
  831. LoadFromStream(Stream,Nil);
  832. Exit;
  833. end;
  834. // reread into a buffer
  835. beginupdate;
  836. try
  837. Buffer:='';
  838. BufLen:=0;
  839. I:=1;
  840. Repeat
  841. BufDelta:=BufSize*I;
  842. SetLength(Buffer,BufLen+BufDelta);
  843. BytesRead:=Stream.Read(Buffer[BufLen+1],BufDelta);
  844. inc(BufLen,BufDelta);
  845. If I<MaxGrow then
  846. I:=I shl 1;
  847. Until BytesRead<>BufDelta;
  848. SetLength(Buffer, BufLen-BufDelta+BytesRead);
  849. SetTextStr(Buffer);
  850. SetLength(Buffer,0);
  851. finally
  852. EndUpdate;
  853. end;
  854. end;
  855. Procedure TStrings.LoadFromStream(Stream: TStream; AEncoding: TEncoding);
  856. {
  857. Borlands method is no good, since a pipe for
  858. instance doesn't have a size.
  859. So we must do it the hard way.
  860. }
  861. Const
  862. BufSize = 1024;
  863. MaxGrow = 1 shl 29;
  864. Var
  865. Buffer : TBytes;
  866. T : string;
  867. BytesRead,
  868. BufLen,
  869. I,BufDelta,
  870. PreambleLength : Longint;
  871. begin
  872. // reread into a buffer
  873. beginupdate;
  874. try
  875. SetLength(Buffer,0);
  876. BufLen:=0;
  877. I:=1;
  878. Repeat
  879. BufDelta:=BufSize*I;
  880. SetLength(Buffer,BufLen+BufDelta);
  881. BytesRead:=Stream.Read(Buffer[BufLen],BufDelta);
  882. inc(BufLen,BufDelta);
  883. If I<MaxGrow then
  884. I:=I shl 1;
  885. Until BytesRead<>BufDelta;
  886. SetLength(Buffer,BufLen-BufDelta+BytesRead);
  887. PreambleLength:=TEncoding.GetBufferEncoding(Buffer,AEncoding,FDefaultEncoding);
  888. T:=AEncoding.GetAnsiString(Buffer,PreambleLength,Length(Buffer)-PreambleLength);
  889. SetEncoding(AEncoding);
  890. SetLength(Buffer,0);
  891. SetTextStr(T);
  892. finally
  893. EndUpdate;
  894. end;
  895. end;
  896. Procedure TStrings.Move(CurIndex, NewIndex: Integer);
  897. Var
  898. Obj : TObject;
  899. Str : String;
  900. begin
  901. BeginUpdate;
  902. Try
  903. Obj:=Objects[CurIndex];
  904. Str:=Strings[CurIndex];
  905. Objects[CurIndex]:=Nil; // Prevent Delete from freeing.
  906. Delete(Curindex);
  907. InsertObject(NewIndex,Str,Obj);
  908. finally
  909. EndUpdate;
  910. end;
  911. end;
  912. Procedure TStrings.SaveToFile(const FileName: string);
  913. Var TheStream : TFileStream;
  914. begin
  915. TheStream:=TFileStream.Create(FileName,fmCreate);
  916. try
  917. SaveToStream(TheStream);
  918. finally
  919. TheStream.Free;
  920. end;
  921. end;
  922. Procedure TStrings.SaveToFile(const FileName: string; AEncoding: TEncoding);
  923. Var TheStream : TFileStream;
  924. begin
  925. TheStream:=TFileStream.Create(FileName,fmCreate);
  926. try
  927. SaveToStream(TheStream,AEncoding);
  928. finally
  929. TheStream.Free;
  930. end;
  931. end;
  932. Procedure TStrings.SaveToStream(Stream: TStream);
  933. Var
  934. S : String;
  935. begin
  936. if Encoding<>nil then
  937. SaveToStream(Stream,Encoding)
  938. else
  939. begin
  940. S:=Text;
  941. if S = '' then Exit;
  942. Stream.WriteBuffer(Pointer(S)^,Length(S));
  943. end;
  944. end;
  945. Procedure TStrings.SaveToStream(Stream: TStream; AEncoding: TEncoding);
  946. Var B : TBytes;
  947. begin
  948. if AEncoding=nil then
  949. AEncoding:=FDefaultEncoding;
  950. if FWriteBOM then
  951. begin
  952. B:=AEncoding.GetPreamble;
  953. if Length(B)>0 then
  954. Stream.WriteBuffer(B[0],Length(B));
  955. end;
  956. B:=AEncoding.GetAnsiBytes(Text);
  957. if Length(B)>0 then
  958. Stream.WriteBuffer(B[0],Length(B));
  959. end;
  960. Procedure TStrings.SetText(TheText: PChar);
  961. Var S : String;
  962. begin
  963. If TheText<>Nil then
  964. S:=StrPas(TheText)
  965. else
  966. S:='';
  967. SetTextStr(S);
  968. end;
  969. {****************************************************************************}
  970. {* TStringList *}
  971. {****************************************************************************}
  972. {$if not defined(FPC_TESTGENERICS)}
  973. procedure TStringList.ExchangeItemsInt(Index1, Index2: Integer);
  974. Var P1,P2 : Pointer;
  975. begin
  976. P1:=Pointer(Flist^[Index1].FString);
  977. P2:=Pointer(Flist^[Index1].FObject);
  978. Pointer(Flist^[Index1].Fstring):=Pointer(Flist^[Index2].Fstring);
  979. Pointer(Flist^[Index1].FObject):=Pointer(Flist^[Index2].FObject);
  980. Pointer(Flist^[Index2].Fstring):=P1;
  981. Pointer(Flist^[Index2].FObject):=P2;
  982. end;
  983. function TStringList.GetSorted: Boolean;
  984. begin
  985. Result:=FSortStyle in [sslUser,sslAuto];
  986. end;
  987. procedure TStringList.ExchangeItems(Index1, Index2: Integer);
  988. begin
  989. ExchangeItemsInt(Index1, Index2);
  990. end;
  991. procedure TStringList.Grow;
  992. Var
  993. NC : Integer;
  994. begin
  995. NC:=FCapacity;
  996. If NC>=256 then
  997. NC:=NC+(NC Div 4)
  998. else if NC=0 then
  999. NC:=4
  1000. else
  1001. NC:=NC*4;
  1002. SetCapacity(NC);
  1003. end;
  1004. procedure TStringList.InternalClear(FromIndex: Integer; ClearOnly: Boolean);
  1005. Var
  1006. I: Integer;
  1007. begin
  1008. if FromIndex < FCount then
  1009. begin
  1010. if FOwnsObjects then
  1011. begin
  1012. For I:=FromIndex to FCount-1 do
  1013. begin
  1014. Flist^[I].FString:='';
  1015. freeandnil(Flist^[i].FObject);
  1016. end;
  1017. end
  1018. else
  1019. begin
  1020. For I:=FromIndex to FCount-1 do
  1021. Flist^[I].FString:='';
  1022. end;
  1023. FCount:=FromIndex;
  1024. end;
  1025. if Not ClearOnly then
  1026. SetCapacity(0);
  1027. end;
  1028. procedure TStringList.InsertItem(Index: Integer; const S: string);
  1029. begin
  1030. InsertItem(Index, S, nil);
  1031. end;
  1032. procedure TStringList.InsertItem(Index: Integer; const S: string; O: TObject);
  1033. begin
  1034. Changing;
  1035. If FCount=Fcapacity then Grow;
  1036. If Index<FCount then
  1037. System.Move (FList^[Index],FList^[Index+1],
  1038. (FCount-Index)*SizeOf(TStringItem));
  1039. Pointer(Flist^[Index].Fstring):=Nil; // Needed to initialize...
  1040. Flist^[Index].FString:=S;
  1041. Flist^[Index].FObject:=O;
  1042. Inc(FCount);
  1043. Changed;
  1044. end;
  1045. procedure TStringList.SetSorted(Value: Boolean);
  1046. begin
  1047. If Value then
  1048. SortStyle:=sslAuto
  1049. else
  1050. SortStyle:=sslNone
  1051. end;
  1052. procedure TStringList.Changed;
  1053. begin
  1054. If (FUpdateCount=0) Then
  1055. begin
  1056. If Assigned(FOnChange) then
  1057. FOnchange(Self);
  1058. FPONotifyObservers(Self,ooChange,Nil);
  1059. end;
  1060. end;
  1061. procedure TStringList.Changing;
  1062. begin
  1063. If FUpdateCount=0 then
  1064. if Assigned(FOnChanging) then
  1065. FOnchanging(Self);
  1066. end;
  1067. function TStringList.Get(Index: Integer): string;
  1068. begin
  1069. CheckIndex(Index);
  1070. Result:=Flist^[Index].FString;
  1071. end;
  1072. function TStringList.GetCapacity: Integer;
  1073. begin
  1074. Result:=FCapacity;
  1075. end;
  1076. function TStringList.GetCount: Integer;
  1077. begin
  1078. Result:=FCount;
  1079. end;
  1080. function TStringList.GetObject(Index: Integer): TObject;
  1081. begin
  1082. CheckIndex(Index);
  1083. Result:=Flist^[Index].FObject;
  1084. end;
  1085. procedure TStringList.Put(Index: Integer; const S: string);
  1086. begin
  1087. If Sorted then
  1088. Error(SSortedListError,0);
  1089. CheckIndex(Index);
  1090. Changing;
  1091. Flist^[Index].FString:=S;
  1092. Changed;
  1093. end;
  1094. procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  1095. begin
  1096. CheckIndex(Index);
  1097. Changing;
  1098. Flist^[Index].FObject:=AObject;
  1099. Changed;
  1100. end;
  1101. procedure TStringList.SetCapacity(NewCapacity: Integer);
  1102. Var NewList : Pointer;
  1103. MSize : Longint;
  1104. begin
  1105. If (NewCapacity<0) then
  1106. Error (SListCapacityError,NewCapacity);
  1107. If NewCapacity>FCapacity then
  1108. begin
  1109. GetMem (NewList,NewCapacity*SizeOf(TStringItem));
  1110. If NewList=Nil then
  1111. Error (SListCapacityError,NewCapacity);
  1112. If Assigned(FList) then
  1113. begin
  1114. MSize:=FCapacity*Sizeof(TStringItem);
  1115. System.Move (FList^,NewList^,MSize);
  1116. FillWord (Pchar(NewList)[MSize],(NewCapacity-FCapacity)*(SizeOf(TStringItem) div SizeOf(Word)), 0);
  1117. FreeMem (Flist,MSize);
  1118. end;
  1119. Flist:=NewList;
  1120. FCapacity:=NewCapacity;
  1121. end
  1122. else if NewCapacity<FCapacity then
  1123. begin
  1124. if NewCapacity = 0 then
  1125. begin
  1126. if FCount > 0 then
  1127. InternalClear(0,True);
  1128. FreeMem(FList);
  1129. FList := nil;
  1130. end else
  1131. begin
  1132. InternalClear(NewCapacity,True);
  1133. GetMem(NewList, NewCapacity * SizeOf(TStringItem));
  1134. System.Move(FList^, NewList^, NewCapacity * SizeOf(TStringItem));
  1135. FreeMem(FList);
  1136. FList := NewList;
  1137. end;
  1138. FCapacity:=NewCapacity;
  1139. end;
  1140. end;
  1141. procedure TStringList.SetUpdateState(Updating: Boolean);
  1142. begin
  1143. If Updating then
  1144. Changing
  1145. else
  1146. Changed
  1147. end;
  1148. destructor TStringList.Destroy;
  1149. begin
  1150. InternalClear;
  1151. Inherited destroy;
  1152. end;
  1153. function TStringList.Add(const S: string): Integer;
  1154. begin
  1155. If Not (SortStyle=sslAuto) then
  1156. Result:=FCount
  1157. else
  1158. If Find (S,Result) then
  1159. Case DUplicates of
  1160. DupIgnore : Exit;
  1161. DupError : Error(SDuplicateString,0)
  1162. end;
  1163. InsertItem (Result,S);
  1164. end;
  1165. procedure TStringList.Clear;
  1166. begin
  1167. if FCount = 0 then Exit;
  1168. Changing;
  1169. InternalClear;
  1170. Changed;
  1171. end;
  1172. procedure TStringList.Delete(Index: Integer);
  1173. begin
  1174. CheckIndex(Index);
  1175. Changing;
  1176. Flist^[Index].FString:='';
  1177. if FOwnsObjects then
  1178. FreeAndNil(Flist^[Index].FObject);
  1179. Dec(FCount);
  1180. If Index<FCount then
  1181. System.Move(Flist^[Index+1],
  1182. Flist^[Index],
  1183. (Fcount-Index)*SizeOf(TStringItem));
  1184. Changed;
  1185. end;
  1186. procedure TStringList.Exchange(Index1, Index2: Integer);
  1187. begin
  1188. CheckIndex(Index1);
  1189. CheckIndex(Index2);
  1190. Changing;
  1191. ExchangeItemsInt(Index1,Index2);
  1192. changed;
  1193. end;
  1194. procedure TStringList.SetCaseSensitive(b : boolean);
  1195. begin
  1196. if b=FCaseSensitive then
  1197. Exit;
  1198. FCaseSensitive:=b;
  1199. if FSortStyle=sslAuto then
  1200. begin
  1201. FForceSort:=True;
  1202. try
  1203. Sort;
  1204. finally
  1205. FForceSort:=False;
  1206. end;
  1207. end;
  1208. end;
  1209. procedure TStringList.SetSortStyle(AValue: TStringsSortStyle);
  1210. begin
  1211. if FSortStyle=AValue then Exit;
  1212. if (AValue=sslAuto) then
  1213. Sort;
  1214. FSortStyle:=AValue;
  1215. end;
  1216. procedure TStringList.CheckIndex(AIndex: Integer);
  1217. begin
  1218. If (AIndex<0) or (AIndex>=FCount) then
  1219. Error(SListIndexError,AIndex);
  1220. end;
  1221. function TStringList.DoCompareText(const s1, s2: string): PtrInt;
  1222. begin
  1223. if FCaseSensitive then
  1224. result:=AnsiCompareStr(s1,s2)
  1225. else
  1226. result:=AnsiCompareText(s1,s2);
  1227. end;
  1228. function TStringList.CompareStrings(const s1,s2 : string) : Integer;
  1229. begin
  1230. Result := DoCompareText(s1, s2);
  1231. end;
  1232. function TStringList.Find(const S: string; out Index: Integer): Boolean;
  1233. var
  1234. L, R, I: Integer;
  1235. CompareRes: PtrInt;
  1236. begin
  1237. Result := false;
  1238. Index:=-1;
  1239. if Not Sorted then
  1240. Raise EListError.Create(SErrFindNeedsSortedList);
  1241. // Use binary search.
  1242. L := 0;
  1243. R := Count - 1;
  1244. while (L<=R) do
  1245. begin
  1246. I := L + (R - L) div 2;
  1247. CompareRes := DoCompareText(S, Flist^[I].FString);
  1248. if (CompareRes>0) then
  1249. L := I+1
  1250. else begin
  1251. R := I-1;
  1252. if (CompareRes=0) then begin
  1253. Result := true;
  1254. if (Duplicates<>dupAccept) then
  1255. L := I; // forces end of while loop
  1256. end;
  1257. end;
  1258. end;
  1259. Index := L;
  1260. end;
  1261. function TStringList.IndexOf(const S: string): Integer;
  1262. begin
  1263. If Not Sorted then
  1264. Result:=Inherited indexOf(S)
  1265. else
  1266. // faster using binary search...
  1267. If Not Find (S,Result) then
  1268. Result:=-1;
  1269. end;
  1270. procedure TStringList.Insert(Index: Integer; const S: string);
  1271. begin
  1272. If SortStyle=sslAuto then
  1273. Error (SSortedListError,0)
  1274. else
  1275. begin
  1276. If (Index<0) or (Index>FCount) then
  1277. Error(SListIndexError,Index); // Cannot use CheckIndex, because there >= FCount...
  1278. InsertItem (Index,S);
  1279. end;
  1280. end;
  1281. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1282. begin
  1283. CustomSort(CompareFn, SortBase.DefaultSortingAlgorithm);
  1284. end;
  1285. type
  1286. PStringList_CustomSort_Context = ^TStringList_CustomSort_Context;
  1287. TStringList_CustomSort_Context = record
  1288. List: TStringList;
  1289. ListStartPtr: Pointer;
  1290. CompareFn: TStringListSortCompare;
  1291. end;
  1292. function TStringList_CustomSort_Comparer(Item1, Item2, Context: Pointer): Integer;
  1293. begin
  1294. with PStringList_CustomSort_Context(Context)^ do
  1295. Result := CompareFn(List,
  1296. (Item1 - ListStartPtr) div SizeOf(TStringItem),
  1297. (Item2 - ListStartPtr) div SizeOf(TStringItem));
  1298. end;
  1299. procedure TStringList_CustomSort_Exchanger(Item1, Item2, Context: Pointer);
  1300. begin
  1301. with PStringList_CustomSort_Context(Context)^ do
  1302. List.ExchangeItems((Item1 - ListStartPtr) div SizeOf(TStringItem),
  1303. (Item2 - ListStartPtr) div SizeOf(TStringItem));
  1304. end;
  1305. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare; SortingAlgorithm: PSortingAlgorithm);
  1306. var
  1307. Context: TStringList_CustomSort_Context;
  1308. begin
  1309. If (FForceSort or (Not (FSortStyle=sslAuto))) and (FCount>1) then
  1310. begin
  1311. Changing;
  1312. Context.List := Self;
  1313. Context.ListStartPtr := FList;
  1314. Context.CompareFn := CompareFn;
  1315. //if ExchangeItems is overriden call that, else call (faster) ItemListSorter_ContextComparer
  1316. if TMethod(@Self.ExchangeItems).Code = CodePointer(@TStringList.ExchangeItems) then
  1317. SortingAlgorithm^.ItemListSorter_ContextComparer(
  1318. FList, FCount, SizeOf(TStringItem), @TStringList_CustomSort_Comparer,
  1319. @Context)
  1320. else
  1321. SortingAlgorithm^.ItemListSorter_CustomItemExchanger_ContextComparer(
  1322. FList, FCount, SizeOf(TStringItem), @TStringList_CustomSort_Comparer,
  1323. @TStringList_CustomSort_Exchanger, @Context);
  1324. Changed;
  1325. end;
  1326. end;
  1327. function StringListAnsiCompare(List: TStringList; Index1, Index: Integer): Integer;
  1328. begin
  1329. Result := List.DoCompareText(List.FList^[Index1].FString,
  1330. List.FList^[Index].FString);
  1331. end;
  1332. procedure TStringList.Sort;
  1333. begin
  1334. CustomSort(@StringListAnsiCompare);
  1335. end;
  1336. procedure TStringList.Sort(SortingAlgorithm: PSortingAlgorithm);
  1337. begin
  1338. CustomSort(@StringListAnsiCompare, SortingAlgorithm);
  1339. end;
  1340. {$else}
  1341. { generics based implementation of TStringList follows }
  1342. function StringListAnsiCompare(List: TStringList; Index1, Index2: Integer): Integer;
  1343. begin
  1344. Result := List.DoCompareText(List.Strings[Index1], List.Strings[Index2]);
  1345. end;
  1346. constructor TStringList.Create;
  1347. begin
  1348. inherited;
  1349. FOwnsObjects:=false;
  1350. FMap := TFPStrObjMap.Create;
  1351. FMap.OnPtrCompare := @MapPtrCompare;
  1352. FOnCompareText := @DefaultCompareText;
  1353. NameValueSeparator:='=';
  1354. CheckSpecialChars;
  1355. end;
  1356. destructor TStringList.Destroy;
  1357. begin
  1358. FMap.Free;
  1359. inherited;
  1360. end;
  1361. function TStringList.GetDuplicates: TDuplicates;
  1362. begin
  1363. Result := FMap.Duplicates;
  1364. end;
  1365. function TStringList.GetSorted: boolean;
  1366. begin
  1367. Result := FMap.Sorted;
  1368. end;
  1369. procedure TStringList.SetDuplicates(NewDuplicates: TDuplicates);
  1370. begin
  1371. FMap.Duplicates := NewDuplicates;
  1372. end;
  1373. procedure TStringList.SetSorted(NewSorted: Boolean);
  1374. begin
  1375. FMap.Sorted := NewSorted;
  1376. end;
  1377. procedure TStringList.Changed;
  1378. begin
  1379. if FUpdateCount = 0 then
  1380. if Assigned(FOnChange) then
  1381. FOnChange(Self);
  1382. end;
  1383. procedure TStringList.Changing;
  1384. begin
  1385. if FUpdateCount = 0 then
  1386. if Assigned(FOnChanging) then
  1387. FOnChanging(Self);
  1388. end;
  1389. function TStringList.Get(Index: Integer): string;
  1390. begin
  1391. Result := FMap.Keys[Index];
  1392. end;
  1393. function TStringList.GetCapacity: Integer;
  1394. begin
  1395. Result := FMap.Capacity;
  1396. end;
  1397. function TStringList.GetCount: Integer;
  1398. begin
  1399. Result := FMap.Count;
  1400. end;
  1401. function TStringList.GetObject(Index: Integer): TObject;
  1402. begin
  1403. Result := FMap.Data[Index];
  1404. end;
  1405. procedure TStringList.Put(Index: Integer; const S: string);
  1406. begin
  1407. Changing;
  1408. FMap.Keys[Index] := S;
  1409. Changed;
  1410. end;
  1411. procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  1412. begin
  1413. Changing;
  1414. FMap.Data[Index] := AObject;
  1415. Changed;
  1416. end;
  1417. procedure TStringList.SetCapacity(NewCapacity: Integer);
  1418. begin
  1419. FMap.Capacity := NewCapacity;
  1420. end;
  1421. procedure TStringList.SetUpdateState(Updating: Boolean);
  1422. begin
  1423. if Updating then
  1424. Changing
  1425. else
  1426. Changed
  1427. end;
  1428. function TStringList.Add(const S: string): Integer;
  1429. begin
  1430. Result := FMap.Add(S);
  1431. end;
  1432. procedure TStringList.Clear;
  1433. begin
  1434. if FMap.Count = 0 then exit;
  1435. Changing;
  1436. FMap.Clear;
  1437. Changed;
  1438. end;
  1439. procedure TStringList.Delete(Index: Integer);
  1440. begin
  1441. if (Index < 0) or (Index >= FMap.Count) then
  1442. Error(SListIndexError, Index);
  1443. Changing;
  1444. FMap.Delete(Index);
  1445. Changed;
  1446. end;
  1447. procedure TStringList.Exchange(Index1, Index2: Integer);
  1448. begin
  1449. if (Index1 < 0) or (Index1 >= FMap.Count) then
  1450. Error(SListIndexError, Index1);
  1451. if (Index2 < 0) or (Index2 >= FMap.Count) then
  1452. Error(SListIndexError, Index2);
  1453. Changing;
  1454. FMap.InternalExchange(Index1, Index2);
  1455. Changed;
  1456. end;
  1457. procedure TStringList.SetCaseSensitive(NewSensitive: Boolean);
  1458. begin
  1459. if NewSensitive <> FCaseSensitive then
  1460. begin
  1461. FCaseSensitive := NewSensitive;
  1462. if Sorted then
  1463. Sort;
  1464. end;
  1465. end;
  1466. function TStringList.MapPtrCompare(Key1, Key2: Pointer): Integer;
  1467. begin
  1468. Result := FOnCompareText(string(Key1^), string(Key2^));
  1469. end;
  1470. function TStringList.DefaultCompareText(const s1, s2: string): PtrInt;
  1471. begin
  1472. if FCaseSensitive then
  1473. Result := AnsiCompareStr(s1, s2)
  1474. else
  1475. Result := AnsiCompareText(s1, s2);
  1476. end;
  1477. function TStringList.DoCompareText(const s1, s2: string): PtrInt;
  1478. begin
  1479. Result := FOnCompareText(s1, s2);
  1480. end;
  1481. function TStringList.Find(const S: string; var Index: Integer): Boolean;
  1482. begin
  1483. Result := FMap.Find(S, Index);
  1484. end;
  1485. function TStringList.IndexOf(const S: string): Integer;
  1486. begin
  1487. Result := FMap.IndexOf(S);
  1488. end;
  1489. procedure TStringList.Insert(Index: Integer; const S: string);
  1490. begin
  1491. if not Sorted and (0 <= Index) and (Index < FMap.Count) then
  1492. Changing;
  1493. FMap.InsertKey(Index, S);
  1494. Changed;
  1495. end;
  1496. type
  1497. PStringList_CustomSort_Context = ^TStringList_CustomSort_Context;
  1498. TStringList_CustomSort_Context = record
  1499. List: TStringList;
  1500. ListStartPtr: Pointer;
  1501. ItemSize: SizeUInt;
  1502. IndexBase: Integer;
  1503. CompareFn: TStringListSortCompare;
  1504. end;
  1505. function TStringList_CustomSort_Comparer(Item1, Item2, Context: Pointer): Integer;
  1506. begin
  1507. with PStringList_CustomSort_Context(Context)^ do
  1508. Result := CompareFn(List,
  1509. ((Item1 - ListStartPtr) div ItemSize) + IndexBase,
  1510. ((Item2 - ListStartPtr) div ItemSize) + IndexBase);
  1511. end;
  1512. procedure TStringList_CustomSort_Exchanger(Item1, Item2, Context: Pointer);
  1513. begin
  1514. with PStringList_CustomSort_Context(Context)^ do
  1515. List.Exchange(((Item1 - ListStartPtr) div ItemSize) + IndexBase,
  1516. ((Item2 - ListStartPtr) div ItemSize) + IndexBase);
  1517. end;
  1518. procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare);
  1519. var
  1520. Context: TStringList_CustomSort_Context;
  1521. begin
  1522. if L > R then
  1523. exit;
  1524. Context.List := Self;
  1525. Context.ListStartPtr := FMap.Items[L];
  1526. Context.CompareFn := CompareFn;
  1527. Context.ItemSize := FMap.KeySize + FMap.DataSize;
  1528. Context.IndexBase := L;
  1529. DefaultSortingAlgorithm^.ItemListSorter_CustomItemExchanger_ContextComparer(
  1530. Context.ListStartPtr, R - L + 1, Context.ItemSize, @TStringList_CustomSort_Comparer,
  1531. @TStringList_CustomSort_Exchanger, @Context);
  1532. end;
  1533. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1534. begin
  1535. if not Sorted and (FMap.Count > 1) then
  1536. begin
  1537. Changing;
  1538. QuickSort(0, FMap.Count-1, CompareFn);
  1539. Changed;
  1540. end;
  1541. end;
  1542. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare; SortingAlgorithm: PSortingAlgorithm);
  1543. var
  1544. Context: TStringList_CustomSort_Context;
  1545. begin
  1546. if not Sorted and (FMap.Count > 1) then
  1547. begin
  1548. Changing;
  1549. Context.List := Self;
  1550. Context.ListStartPtr := FMap.Items[0];
  1551. Context.CompareFn := CompareFn;
  1552. Context.ItemSize := FMap.KeySize + FMap.DataSize;
  1553. Context.IndexBase := 0;
  1554. SortingAlgorithm^.ItemListSorter_CustomItemExchanger_ContextComparer(
  1555. Context.ListStartPtr, FMap.Count, Context.ItemSize, @TStringList_CustomSort_Comparer,
  1556. @TStringList_CustomSort_Exchanger, @Context);
  1557. Changed;
  1558. end;
  1559. end;
  1560. procedure TStringList.Sort;
  1561. begin
  1562. if not Sorted and (FMap.Count > 1) then
  1563. begin
  1564. Changing;
  1565. FMap.Sort;
  1566. Changed;
  1567. end;
  1568. end;
  1569. procedure TStringList.Sort(SortingAlgorithm: PSortingAlgorithm);
  1570. begin
  1571. if not Sorted and (FMap.Count > 1) then
  1572. begin
  1573. Changing;
  1574. FMap.Sort(SortingAlgorithm);
  1575. Changed;
  1576. end;
  1577. end;
  1578. {$endif}