stringl.inc 39 KB

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