stringl.inc 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373
  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. Result:=not TrailingLineBreak;
  71. end;
  72. procedure TStrings.SetSkipLastLineBreak(const AValue : Boolean);
  73. begin
  74. TrailingLineBreak:=not AValue;
  75. end;
  76. Function TStrings.GetLBS : TTextLineBreakStyle;
  77. begin
  78. CheckSpecialChars;
  79. Result:=FLBS;
  80. end;
  81. Procedure TStrings.SetLBS (AValue : TTextLineBreakStyle);
  82. begin
  83. CheckSpecialChars;
  84. FLBS:=AValue;
  85. end;
  86. procedure TStrings.SetDelimiter(c:Char);
  87. begin
  88. CheckSpecialChars;
  89. FDelimiter:=c;
  90. end;
  91. Procedure TStrings.SetEncoding(const AEncoding: TEncoding);
  92. begin
  93. if (FEncoding<>nil) and not TEncoding.IsStandardEncoding(FEncoding) then
  94. FEncoding.Free;
  95. if TEncoding.IsStandardEncoding(AEncoding) then
  96. FEncoding:=AEncoding
  97. else if AEncoding<>nil then
  98. FEncoding:=AEncoding.Clone
  99. else
  100. FEncoding:=nil;
  101. end;
  102. Function TStrings.GetDelimiter : Char;
  103. begin
  104. CheckSpecialChars;
  105. Result:=FDelimiter;
  106. end;
  107. procedure TStrings.SetLineBreak(Const S : String);
  108. begin
  109. CheckSpecialChars;
  110. FLineBreak:=S;
  111. end;
  112. Function TStrings.GetLineBreak : String;
  113. begin
  114. CheckSpecialChars;
  115. Result:=FLineBreak;
  116. end;
  117. procedure TStrings.SetQuoteChar(c:Char);
  118. begin
  119. CheckSpecialChars;
  120. FQuoteChar:=c;
  121. end;
  122. Function TStrings.GetQuoteChar :Char;
  123. begin
  124. CheckSpecialChars;
  125. Result:=FQuoteChar;
  126. end;
  127. procedure TStrings.SetNameValueSeparator(c:Char);
  128. begin
  129. CheckSpecialChars;
  130. FNameValueSeparator:=c;
  131. end;
  132. Function TStrings.GetNameValueSeparator :Char;
  133. begin
  134. CheckSpecialChars;
  135. Result:=FNameValueSeparator;
  136. end;
  137. function TStrings.GetCommaText: string;
  138. Var
  139. C1,C2 : Char;
  140. FSD : Boolean;
  141. begin
  142. CheckSpecialChars;
  143. FSD:=StrictDelimiter;
  144. C1:=Delimiter;
  145. C2:=QuoteChar;
  146. Delimiter:=',';
  147. QuoteChar:='"';
  148. StrictDelimiter:=False;
  149. Try
  150. Result:=GetDelimitedText;
  151. Finally
  152. Delimiter:=C1;
  153. QuoteChar:=C2;
  154. StrictDelimiter:=FSD;
  155. end;
  156. end;
  157. function TStrings.GetLineBreakCharLBS: string;
  158. begin
  159. CheckSpecialChars;
  160. if FLineBreak<>sLineBreak then
  161. Result:=FLineBreak
  162. else
  163. Case FLBS of
  164. tlbsLF : Result:=#10;
  165. tlbsCRLF : Result:=#13#10;
  166. tlbsCR : Result:=#13;
  167. end;
  168. end;
  169. function TStrings.GetMissingNameValueSeparatorAction: TMissingNameValueSeparatorAction;
  170. begin
  171. CheckSpecialChars;
  172. Result:=FMissingNameValueSeparatorAction;
  173. end;
  174. Function TStrings.GetDelimitedText: string;
  175. Var
  176. I : integer;
  177. p : pchar;
  178. BreakChars : set of char;
  179. S : String;
  180. doQuote : Boolean;
  181. begin
  182. CheckSpecialChars;
  183. result:='';
  184. if StrictDelimiter then
  185. BreakChars:=[#0,QuoteChar,Delimiter]
  186. else
  187. BreakChars:=[#0..' ',QuoteChar,Delimiter];
  188. // Check for break characters and quote if required.
  189. For i:=0 to count-1 do
  190. begin
  191. S:=Strings[i];
  192. doQuote:=FAlwaysQuote;
  193. If not DoQuote then
  194. begin
  195. p:=pchar(S);
  196. //Quote strings that include BreakChars:
  197. while not(p^ in BreakChars) do
  198. inc(p);
  199. DoQuote:=(p<>pchar(S)+length(S));
  200. end;
  201. if DoQuote and (QuoteChar<>#0) then
  202. Result:=Result+QuoteString(S,QuoteChar)
  203. else
  204. Result:=Result+S;
  205. if I<Count-1 then
  206. Result:=Result+Delimiter;
  207. end;
  208. // Quote empty string:
  209. If (Length(Result)=0) and (Count=1) and (QuoteChar<>#0) then
  210. Result:=QuoteChar+QuoteChar;
  211. end;
  212. procedure TStrings.GetNameValue(Index : Integer; Out AName,AValue : String);
  213. Var L : longint;
  214. begin
  215. aName:='';
  216. CheckSpecialChars;
  217. AValue:=Strings[Index];
  218. L:=Pos(FNameValueSeparator,AValue);
  219. If L<>0 then
  220. begin
  221. AName:=Copy(AValue,1,L-1);
  222. System.Delete(AValue,1,L);
  223. end
  224. else
  225. case FMissingNameValueSeparatorAction of
  226. mnvaValue : ;
  227. mnvaName :
  228. begin
  229. aName:=aValue;
  230. aValue:='';
  231. end;
  232. mnvaEmpty :
  233. aValue:='';
  234. mnvaError :
  235. Raise EStringListError.CreateFmt(SErrNoNameValuePairAt,[Index]);
  236. end;
  237. end;
  238. function TStrings.ExtractName(const s:String):String;
  239. var
  240. L: Longint;
  241. begin
  242. CheckSpecialChars;
  243. L:=Pos(FNameValueSeparator,S);
  244. If L<>0 then
  245. Result:=Copy(S,1,L-1)
  246. else
  247. Result:='';
  248. end;
  249. procedure TStrings.Filter(aFilter: TStringsFilterMethod; aList: TStrings);
  250. var
  251. S : string;
  252. begin
  253. for S in self do
  254. if aFilter(S) then
  255. aList.Add(S);
  256. end;
  257. procedure TStrings.ForEach(aCallback: TStringsForeachMethod);
  258. var
  259. S : String;
  260. begin
  261. for S in self do
  262. aCallBack(S);
  263. end;
  264. procedure TStrings.ForEach(aCallback: TStringsForeachMethodEx);
  265. var
  266. i: integer;
  267. begin
  268. for i:=0 to Count-1 do
  269. aCallBack(Strings[i],i);
  270. end;
  271. procedure TStrings.ForEach(aCallback: TStringsForeachMethodExObj);
  272. var
  273. i: integer;
  274. begin
  275. for i:=0 to Count-1 do
  276. aCallback(Strings[i],i,Objects[i]);
  277. end;
  278. function TStrings.Filter(aFilter: TStringsFilterMethod): TStrings;
  279. begin
  280. Result:=TStringsClass(Self.ClassType).Create;
  281. try
  282. Filter(aFilter,Result);
  283. except
  284. FreeAndNil(Result);
  285. Raise;
  286. end;
  287. end;
  288. procedure TStrings.Fill(const aValue: String; aStart, aEnd: Integer);
  289. var
  290. i: integer;
  291. begin
  292. if aEnd<0 then
  293. aEnd:=Self.Count+aEnd;
  294. if aEnd>=Count then
  295. aEnd:=Count-1;
  296. for i:=aStart to aEnd do
  297. Strings[i]:=aValue;
  298. end;
  299. Procedure TStrings.Map(aMap: TStringsMapMethod; aList : TStrings);
  300. Var
  301. S : String;
  302. begin
  303. For S in self do
  304. aList.Add(aMap(S));
  305. end;
  306. Function TStrings.Map(aMap: TStringsMapMethod) : TStrings;
  307. begin
  308. Result:=TStringsClass(Self.ClassType).Create;
  309. try
  310. Map(aMap,Result);
  311. except
  312. FreeAndNil(Result);
  313. Raise;
  314. end;
  315. end;
  316. function TStrings.Reduce(aReduceMethod: TStringsReduceMethod; const startingValue: string): string;
  317. var
  318. S : String;
  319. begin
  320. Result:=startingValue;
  321. for S in self do
  322. Result:=aReduceMethod(Result, S);
  323. end;
  324. Function TStrings.Reverse : TStrings;
  325. begin
  326. Result:=TStringsClass(Self.ClassType).Create;
  327. try
  328. Reverse(Result);
  329. except
  330. FreeAndNil(Result);
  331. Raise;
  332. end;
  333. end;
  334. Procedure TStrings.Reverse(aList : TStrings);
  335. Var
  336. I : Integer;
  337. begin
  338. for I:=Count-1 downto 0 do
  339. aList.Add(Strings[i]);
  340. end;
  341. Procedure TStrings.Slice(fromIndex: integer; aList : TStrings);
  342. var
  343. i: integer;
  344. begin
  345. for i:=fromIndex to Count-1 do
  346. aList.Add(Self[i]);
  347. end;
  348. Function TStrings.Slice(fromIndex: integer) : TStrings;
  349. begin
  350. Result:=TStringsClass(Self.ClassType).Create;
  351. try
  352. Slice(FromIndex,Result);
  353. except
  354. FreeAndNil(Result);
  355. Raise;
  356. end;
  357. end;
  358. function TStrings.GetName(Index: Integer): string;
  359. Var
  360. V : String;
  361. begin
  362. GetNameValue(Index,Result,V);
  363. end;
  364. function TStrings.GetStrictDelimiter: Boolean;
  365. begin
  366. Result:=soStrictDelimiter in FOptions;
  367. end;
  368. function TStrings.GetTrailingLineBreak: Boolean;
  369. begin
  370. Result:=soTrailingLineBreak in FOptions;
  371. end;
  372. function TStrings.GetUseLocale: Boolean;
  373. begin
  374. Result:=soUseLocale in FOptions;
  375. end;
  376. function TStrings.GetWriteBOM: Boolean;
  377. begin
  378. Result:=soWriteBOM in FOptions;
  379. end;
  380. Function TStrings.GetValue(const Name: string): string;
  381. Var
  382. L : longint;
  383. N : String;
  384. begin
  385. Result:='';
  386. L:=IndexOfName(Name);
  387. If L<>-1 then
  388. GetNameValue(L,N,Result);
  389. end;
  390. Function TStrings.GetValueFromIndex(Index: Integer): string;
  391. Var
  392. N : String;
  393. begin
  394. GetNameValue(Index,N,Result);
  395. end;
  396. Procedure TStrings.SetValueFromIndex(Index: Integer; const Value: string);
  397. begin
  398. If (Value='') then
  399. Delete(Index)
  400. else
  401. begin
  402. If (Index<0) then
  403. Index:=Add('');
  404. CheckSpecialChars;
  405. Strings[Index]:=GetName(Index)+FNameValueSeparator+Value;
  406. end;
  407. end;
  408. procedure TStrings.ReadData(Reader: TReader);
  409. begin
  410. Reader.ReadListBegin;
  411. BeginUpdate;
  412. try
  413. Clear;
  414. while not Reader.EndOfList do
  415. Add(Reader.ReadString);
  416. finally
  417. EndUpdate;
  418. end;
  419. Reader.ReadListEnd;
  420. end;
  421. Procedure TStrings.SetDelimitedText(const AValue: string);
  422. begin
  423. CheckSpecialChars;
  424. DoSetDelimitedText(aValue,True,StrictDelimiter,FQuoteChar,FDelimiter);
  425. end;
  426. Procedure TStrings.DoSetDelimitedText(const AValue: string; DoClear,aStrictDelimiter : Boolean; aQuoteChar,aDelimiter : Char);
  427. var
  428. len,i,j: SizeInt;
  429. aNotFirst:boolean;
  430. Procedure AddQuoted;
  431. begin
  432. Add(StringReplace(Copy(AValue,i+1,j-i-1),aQuoteChar+aQuoteChar,aQuoteChar, [rfReplaceAll]));
  433. end;
  434. Function CheckQuoted : Boolean;
  435. { Paraphrased from Delphi XE2 help:
  436. Strings must be separated by Delimiter characters or spaces.
  437. They may be enclosed in QuoteChars.
  438. QuoteChars in the string must be repeated to distinguish them from the QuoteChars enclosing the string.
  439. }
  440. begin
  441. Result:=(AValue[i]=aQuoteChar) and (aQuoteChar<>#0);
  442. If Not Result then
  443. exit;
  444. // next string is quoted
  445. j:=i+1;
  446. while (j<=len) and
  447. ((AValue[j]<>aQuoteChar) or
  448. ((j+1<=len) and (AValue[j+1]=aQuoteChar))) do
  449. begin
  450. if (j<=len) and (AValue[j]=aQuoteChar) then
  451. inc(j,2)
  452. else
  453. inc(j);
  454. end;
  455. AddQuoted;
  456. i:=j+1;
  457. end;
  458. Procedure MaybeSkipSpaces; inline;
  459. begin
  460. if Not aStrictDelimiter then
  461. while (i<=len) and (Ord(AValue[i])<=Ord(' ')) do
  462. inc(i);
  463. end;
  464. begin
  465. BeginUpdate;
  466. i:=1;
  467. j:=1;
  468. aNotFirst:=false;
  469. try
  470. if DoClear then
  471. Clear;
  472. len:=length(AValue);
  473. while i<=len do
  474. begin
  475. // skip delimiter
  476. if aNotFirst and (i<=len) and (AValue[i]=aDelimiter) then
  477. inc(i);
  478. MaybeSkipSpaces;
  479. // read next string
  480. if i>len then
  481. begin
  482. if aNotFirst then Add('');
  483. end
  484. else
  485. begin
  486. // next string is quoted
  487. if not CheckQuoted then
  488. begin
  489. // next string is not quoted; read until control character/space/delimiter
  490. j:=i;
  491. while (j<=len) and
  492. (aStrictDelimiter or (Ord(AValue[j])>Ord(' '))) and
  493. (AValue[j]<>aDelimiter) do
  494. inc(j);
  495. Add( Copy(AValue,i,j-i));
  496. i:=j;
  497. end;
  498. end;
  499. MaybeSkipSpaces;
  500. aNotFirst:=true;
  501. end; // While I<=Len
  502. finally
  503. EndUpdate;
  504. end;
  505. end;
  506. Procedure TStrings.SetCommaText(const Value: string);
  507. begin
  508. CheckSpecialChars;
  509. DoSetDelimitedText(Value,True,StrictDelimiter,'"',',');
  510. end;
  511. procedure TStrings.SetMissingNameValueSeparatorAction(AValue: TMissingNameValueSeparatorAction);
  512. begin
  513. CheckSpecialChars;
  514. FMissingNameValueSeparatorAction:=aValue;
  515. end;
  516. Procedure TStrings.SetStringsAdapter(const Value: IStringsAdapter);
  517. begin
  518. end;
  519. procedure TStrings.SetStrictDelimiter(AValue: Boolean);
  520. begin
  521. if AValue then
  522. Include(FOptions,soStrictDelimiter)
  523. else
  524. Exclude(FOptions,soStrictDelimiter);
  525. end;
  526. procedure TStrings.SetTrailingLineBreak(AValue: Boolean);
  527. begin
  528. if AValue then
  529. Include(FOptions,soTrailingLineBreak)
  530. else
  531. Exclude(FOptions,soTrailingLineBreak);
  532. end;
  533. procedure TStrings.SetUseLocale(AValue: Boolean);
  534. begin
  535. if AValue then
  536. Include(FOptions,soUseLocale)
  537. else
  538. Exclude(FOptions,soUseLocale);
  539. end;
  540. procedure TStrings.SetWriteBOM(AValue: Boolean);
  541. begin
  542. if AValue then
  543. Include(FOptions,soWriteBOM)
  544. else
  545. Exclude(FOptions,soWriteBOM);
  546. end;
  547. Procedure TStrings.SetDefaultEncoding(const ADefaultEncoding: TEncoding);
  548. begin
  549. if (FDefaultEncoding<>nil) and not TEncoding.IsStandardEncoding(FDefaultEncoding) then
  550. FDefaultEncoding.Free;
  551. if TEncoding.IsStandardEncoding(ADefaultEncoding) then
  552. FDefaultEncoding:=ADefaultEncoding
  553. else if ADefaultEncoding<>nil then
  554. FDefaultEncoding:=ADefaultEncoding.Clone
  555. else
  556. FDefaultEncoding:=TEncoding.Default;
  557. end;
  558. Procedure TStrings.SetValue(const Name, Value: string);
  559. Var L : longint;
  560. begin
  561. CheckSpecialChars;
  562. L:=IndexOfName(Name);
  563. if L=-1 then
  564. Add (Name+FNameValueSeparator+Value)
  565. else
  566. Strings[L]:=Name+FNameValueSeparator+value;
  567. end;
  568. procedure TStrings.WriteData(Writer: TWriter);
  569. var
  570. i: Integer;
  571. begin
  572. Writer.WriteListBegin;
  573. for i := 0 to Count - 1 do
  574. Writer.WriteString(Strings[i]);
  575. Writer.WriteListEnd;
  576. end;
  577. function TStrings.CompareStrings(const s1,s2 : string) : Integer;
  578. begin
  579. Result := DoCompareText(s1, s2);
  580. end;
  581. procedure TStrings.DefineProperties(Filer: TFiler);
  582. var
  583. HasData: Boolean;
  584. begin
  585. if Assigned(Filer.Ancestor) then
  586. // Only serialize if string list is different from ancestor
  587. if Filer.Ancestor.InheritsFrom(TStrings) then
  588. HasData := not Equals(TStrings(Filer.Ancestor))
  589. else
  590. HasData := True
  591. else
  592. HasData := Count > 0;
  593. Filer.DefineProperty('Strings', @ReadData, @WriteData, HasData);
  594. end;
  595. Procedure TStrings.Error(const Msg: string; Data: Integer);
  596. begin
  597. Raise EStringListError.CreateFmt(Msg,[Data]) at get_caller_addr(get_frame), get_caller_frame(get_frame);
  598. end;
  599. Procedure TStrings.Error(const Msg: pstring; Data: Integer);
  600. begin
  601. Raise EStringListError.CreateFmt(Msg^,[Data]) at get_caller_addr(get_frame), get_caller_frame(get_frame);
  602. end;
  603. Function TStrings.GetCapacity: Integer;
  604. begin
  605. Result:=Count;
  606. end;
  607. Function TStrings.GetObject(Index: Integer): TObject;
  608. begin
  609. Result:=Nil;
  610. end;
  611. Function TStrings.GetTextStr: string;
  612. Var P : Pchar;
  613. I,L,NLS : SizeInt;
  614. S,NL : String;
  615. begin
  616. NL:=GetLineBreakCharLBS;
  617. // Determine needed place
  618. L:=0;
  619. NLS:=Length(NL);
  620. For I:=0 to count-1 do
  621. L:=L+Length(Strings[I])+NLS;
  622. if SkipLastLineBreak then
  623. Dec(L,NLS);
  624. Setlength(Result,L);
  625. P:=Pointer(Result);
  626. For i:=0 To count-1 do
  627. begin
  628. S:=Strings[I];
  629. L:=Length(S);
  630. if L<>0 then
  631. System.Move(Pointer(S)^,P^,L);
  632. P:=P+L;
  633. if (I<Count-1) or Not SkipLastLineBreak then
  634. For L:=1 to NLS do
  635. begin
  636. P^:=NL[L];
  637. inc(P);
  638. end;
  639. end;
  640. end;
  641. Procedure TStrings.Put(Index: Integer; const S: string);
  642. Var Obj : TObject;
  643. begin
  644. Obj:=Objects[Index];
  645. Delete(Index);
  646. InsertObject(Index,S,Obj);
  647. end;
  648. Procedure TStrings.PutObject(Index: Integer; AObject: TObject);
  649. begin
  650. // Empty.
  651. end;
  652. Procedure TStrings.SetCapacity(NewCapacity: Integer);
  653. begin
  654. // Empty.
  655. end;
  656. Class Function TStrings.GetNextLine (Const Value : String; Var S : String; Var P : SizeInt) : Boolean;
  657. var
  658. LengthOfValue: SizeInt;
  659. StartPos, FuturePos: SizeInt;
  660. begin
  661. LengthOfValue := Length(Value);
  662. StartPos := P;
  663. if (StartPos <= 0) or (StartPos > LengthOfValue) then // True for LengthOfValue <= 0
  664. begin
  665. S := '';
  666. Exit(False);
  667. end;
  668. FuturePos := StartPos;
  669. while (FuturePos <= LengthOfValue) and not (Value[FuturePos] in [#10, #13]) do
  670. Inc(FuturePos);
  671. // If we use S := Copy(Value, StartPos, FuturePos - StartPos); then compiler
  672. // generate TempS := Copy(...); S := TempS to eliminate side effects and
  673. // implicit "try finally" for TempS finalization
  674. // When we use SetString then no TempS, no try finally generated,
  675. // but we must check case when Value and S is same (side effects)
  676. if Pointer(S) = Pointer(Value) then
  677. System.Delete(S, FuturePos, High(FuturePos))
  678. else
  679. begin
  680. SetString(S, @Value[StartPos], FuturePos - StartPos);
  681. if (FuturePos <= LengthOfValue) and (Value[FuturePos] = #13) then
  682. Inc(FuturePos);
  683. if (FuturePos <= LengthOfValue) and (Value[FuturePos] = #10) then
  684. Inc(FuturePos);
  685. end;
  686. P := FuturePos;
  687. Result := True;
  688. end;
  689. Function TStrings.GetNextLineBreak (Const Value : String; Var S : String; Var P : SizeInt) : Boolean;
  690. var
  691. StartPos, FuturePos: SizeInt;
  692. begin
  693. StartPos := P;
  694. if (StartPos <= 0) or (StartPos > Length(Value)) then // True for Length <= 0
  695. begin
  696. S := '';
  697. Exit(False);
  698. end;
  699. FuturePos := Pos(FLineBreak, Value, StartPos); // Use PosEx in old RTL
  700. // Why we don't use Copy but use SetString read in GetNextLine
  701. if FuturePos = 0 then // No line breaks
  702. begin
  703. FuturePos := Length(Value) + 1;
  704. if Pointer(S) = Pointer(Value) then
  705. // Nothing to do
  706. else
  707. SetString(S, @Value[StartPos], FuturePos - StartPos)
  708. end
  709. else
  710. if Pointer(S) = Pointer(Value) then
  711. System.Delete(S, FuturePos, High(FuturePos))
  712. else
  713. begin
  714. SetString(S, @Value[StartPos], FuturePos - StartPos);
  715. Inc(FuturePos, Length(FLineBreak));
  716. end;
  717. P := FuturePos;
  718. Result := True;
  719. end;
  720. {$IF (SizeOf(Integer) < SizeOf(SizeInt)) }
  721. class function TStrings.GetNextLine(const Value: string; var S: string; var P: Integer) : Boolean;
  722. var
  723. LP: SizeInt;
  724. begin
  725. LP := P;
  726. Result := GetNextLine(Value, S, LP);
  727. P := LP;
  728. end;
  729. function TStrings.GetNextLineBreak(const Value: string; var S: string; var P: Integer) : Boolean;
  730. var
  731. LP: SizeInt;
  732. begin
  733. LP := P;
  734. Result := GetNextLineBreak(Value, S, LP);
  735. P := LP;
  736. end;
  737. {$IFEND}
  738. Procedure TStrings.DoSetTextStr(const Value: string; DoClear : Boolean);
  739. Var
  740. S : String;
  741. P : SizeInt;
  742. begin
  743. Try
  744. beginUpdate;
  745. if DoClear then
  746. Clear;
  747. P:=1;
  748. if FLineBreak=sLineBreak then
  749. begin
  750. While GetNextLine (Value,S,P) do
  751. Add(S)
  752. end
  753. else
  754. While GetNextLineBreak (Value,S,P) do
  755. Add(S);
  756. finally
  757. EndUpdate;
  758. end;
  759. end;
  760. Procedure TStrings.SetTextStr(const Value: string);
  761. begin
  762. CheckSpecialChars;
  763. DoSetTextStr(Value,True);
  764. end;
  765. Procedure TStrings.AddText(const S: string);
  766. begin
  767. CheckSpecialChars;
  768. DoSetTextStr(S,False);
  769. end;
  770. procedure TStrings.AddCommaText(const S: String);
  771. begin
  772. DoSetDelimitedText(S,False,StrictDelimiter,'"',',');
  773. end;
  774. procedure TStrings.AddDelimitedText(const S: String; ADelimiter: Char; AStrictDelimiter: Boolean);
  775. begin
  776. CheckSpecialChars;
  777. DoSetDelimitedText(S,False,AStrictDelimiter,FQuoteChar,ADelimiter);
  778. end;
  779. procedure TStrings.AddDelimitedText(const S: String);
  780. begin
  781. CheckSpecialChars;
  782. DoSetDelimitedText(S,False,StrictDelimiter,FQuoteChar,FDelimiter);
  783. end;
  784. Procedure TStrings.SetUpdateState(Updating: Boolean);
  785. begin
  786. FPONotifyObservers(Self,ooChange,Nil);
  787. end;
  788. destructor TSTrings.Destroy;
  789. begin
  790. if (FEncoding<>nil) and not TEncoding.IsStandardEncoding(FEncoding) then
  791. FreeAndNil(FEncoding);
  792. if (FDefaultEncoding<>nil) and not TEncoding.IsStandardEncoding(FDefaultEncoding) then
  793. FreeAndNil(FDefaultEncoding);
  794. inherited destroy;
  795. end;
  796. function TStrings.ToObjectArray: TObjectDynArray;
  797. begin
  798. Result:=ToObjectArray(0,Count-1);
  799. end;
  800. function TStrings.ToObjectArray(aStart,aEnd : Integer): TObjectDynArray;
  801. Var
  802. I : Integer;
  803. begin
  804. Result:=Nil;
  805. if aStart>aEnd then exit;
  806. SetLength(Result,aEnd-aStart+1);
  807. For I:=aStart to aEnd do
  808. Result[i-aStart]:=Objects[i];
  809. end;
  810. function TStrings.ToStringArray: TStringDynArray;
  811. begin
  812. Result:=ToStringArray(0,Count-1);
  813. end;
  814. function TStrings.ToStringArray(aStart,aEnd : Integer): TStringDynArray;
  815. Var
  816. I : Integer;
  817. begin
  818. Result:=Nil;
  819. if aStart>aEnd then exit;
  820. SetLength(Result,aEnd-aStart+1);
  821. For I:=aStart to aEnd do
  822. Result[i-aStart]:=Strings[i];
  823. end;
  824. constructor TStrings.Create;
  825. begin
  826. inherited Create;
  827. FDefaultEncoding:=TEncoding.Default;
  828. FEncoding:=nil;
  829. FOptions := [soTrailingLineBreak,soUseLocale,soPreserveBOM];
  830. FAlwaysQuote:=False;
  831. end;
  832. Function TStrings.Add(const S: string): Integer;
  833. begin
  834. Result:=Count;
  835. Insert (Count,S);
  836. end;
  837. function TStrings.Add(const Fmt : string; const Args : Array of const): Integer;
  838. begin
  839. Result:=Add(Format(Fmt,Args));
  840. end;
  841. Function TStrings.AddObject(const S: string; AObject: TObject): Integer;
  842. begin
  843. Result:=Add(S);
  844. Objects[result]:=AObject;
  845. end;
  846. function TStrings.AddObject(const Fmt: string; Args : Array of const; AObject: TObject): Integer;
  847. begin
  848. Result:=AddObject(Format(Fmt,Args),AObject);
  849. end;
  850. function TStrings.AddPair(const AName, AValue: string): TStrings;
  851. begin
  852. Result:=AddPair(AName,AValue,Nil);
  853. end;
  854. function TStrings.AddPair(const AName, AValue: string; AObject: TObject): TStrings;
  855. begin
  856. Result := Self;
  857. AddObject(Concat(AName, NameValueSeparator, AValue), AObject);
  858. end;
  859. Procedure TStrings.Append(const S: string);
  860. begin
  861. Add (S);
  862. end;
  863. Procedure TStrings.AddStrings(TheStrings: TStrings; ClearFirst : Boolean);
  864. Var Runner : longint;
  865. begin
  866. beginupdate;
  867. try
  868. if ClearFirst then
  869. Clear;
  870. if Count + TheStrings.Count > Capacity then
  871. Capacity := Count + TheStrings.Count;
  872. For Runner:=0 to TheStrings.Count-1 do
  873. self.AddObject (Thestrings[Runner],TheStrings.Objects[Runner]);
  874. finally
  875. EndUpdate;
  876. end;
  877. end;
  878. Procedure TStrings.AddStrings(TheStrings: TStrings);
  879. begin
  880. AddStrings(TheStrings, False);
  881. end;
  882. Procedure TStrings.AddStrings(const TheStrings: array of string);
  883. begin
  884. AddStrings(TheStrings, False);
  885. end;
  886. Procedure TStrings.AddStrings(const TheStrings: array of string; ClearFirst : Boolean);
  887. Var Runner : longint;
  888. begin
  889. beginupdate;
  890. try
  891. if ClearFirst then
  892. Clear;
  893. if Count + High(TheStrings)+1 > Capacity then
  894. Capacity := Count + High(TheStrings)+1;
  895. For Runner:=Low(TheStrings) to High(TheStrings) do
  896. self.Add(Thestrings[Runner]);
  897. finally
  898. EndUpdate;
  899. end;
  900. end;
  901. procedure TStrings.SetStrings(TheStrings: TStrings);
  902. begin
  903. AddStrings(TheStrings,True);
  904. end;
  905. procedure TStrings.SetStrings(TheStrings: array of string);
  906. begin
  907. AddStrings(TheStrings,True);
  908. end;
  909. Procedure TStrings.Assign(Source: TPersistent);
  910. Var
  911. S : TStrings;
  912. begin
  913. If Source is TStrings then
  914. begin
  915. S:=TStrings(Source);
  916. BeginUpdate;
  917. Try
  918. clear;
  919. FSpecialCharsInited:=S.FSpecialCharsInited;
  920. FQuoteChar:=S.FQuoteChar;
  921. FDelimiter:=S.FDelimiter;
  922. FNameValueSeparator:=S.FNameValueSeparator;
  923. FLBS:=S.FLBS;
  924. FLineBreak:=S.FLineBreak;
  925. FOptions:=S.FOptions;
  926. DefaultEncoding:=S.DefaultEncoding;
  927. SetEncoding(S.Encoding);
  928. AddStrings(S);
  929. finally
  930. EndUpdate;
  931. end;
  932. end
  933. else
  934. Inherited Assign(Source);
  935. end;
  936. Procedure TStrings.BeginUpdate;
  937. begin
  938. if FUpdateCount = 0 then SetUpdateState(true);
  939. inc(FUpdateCount);
  940. end;
  941. Procedure TStrings.EndUpdate;
  942. begin
  943. If FUpdateCount>0 then
  944. Dec(FUpdateCount);
  945. if FUpdateCount=0 then
  946. SetUpdateState(False);
  947. end;
  948. Function TStrings.Equals(Obj: TObject): Boolean;
  949. begin
  950. if Obj is TStrings then
  951. Result := Equals(TStrings(Obj))
  952. else
  953. Result := inherited Equals(Obj);
  954. end;
  955. Function TStrings.Equals(TheStrings: TStrings): Boolean;
  956. Var Runner,Nr : Longint;
  957. begin
  958. Result:=False;
  959. Nr:=Self.Count;
  960. if Nr<>TheStrings.Count then exit;
  961. For Runner:=0 to Nr-1 do
  962. If Strings[Runner]<>TheStrings[Runner] then exit;
  963. Result:=True;
  964. end;
  965. Procedure TStrings.Exchange(Index1, Index2: Integer);
  966. Var
  967. Obj : TObject;
  968. Str : String;
  969. begin
  970. beginUpdate;
  971. Try
  972. Obj:=Objects[Index1];
  973. Str:=Strings[Index1];
  974. Objects[Index1]:=Objects[Index2];
  975. Strings[Index1]:=Strings[Index2];
  976. Objects[Index2]:=Obj;
  977. Strings[Index2]:=Str;
  978. finally
  979. EndUpdate;
  980. end;
  981. end;
  982. function TStrings.GetEnumerator: TStringsEnumerator;
  983. begin
  984. Result:=TStringsEnumerator.Create(Self);
  985. end;
  986. Function TStrings.GetText: PChar;
  987. begin
  988. Result:=StrNew(Pchar(Self.Text));
  989. end;
  990. Function TStrings.DoCompareText(const s1,s2 : string) : PtrInt;
  991. begin
  992. if UseLocale then
  993. result:=AnsiCompareText(s1,s2)
  994. else
  995. result:=CompareText(s1,s2);
  996. end;
  997. Function TStrings.IndexOf(const S: string): Integer;
  998. begin
  999. Result:=0;
  1000. While (Result<Count) and (DoCompareText(Strings[Result],S)<>0) do Result:=Result+1;
  1001. if Result=Count then Result:=-1;
  1002. end;
  1003. function TStrings.IndexOf(const S: string; aStart: Integer): Integer;
  1004. begin
  1005. if aStart<0 then
  1006. begin
  1007. aStart:=Count+aStart;
  1008. if aStart<0 then
  1009. aStart:=0;
  1010. end;
  1011. Result:=aStart;
  1012. While (Result<Count) and (DoCompareText(Strings[Result],S)<>0) do Result:=Result+1;
  1013. if Result=Count then Result:=-1;
  1014. end;
  1015. Function TStrings.IndexOfName(const Name: string): Integer;
  1016. Var
  1017. len : longint;
  1018. S : String;
  1019. begin
  1020. CheckSpecialChars;
  1021. Result:=0;
  1022. while (Result<Count) do
  1023. begin
  1024. S:=Strings[Result];
  1025. len:=pos(FNameValueSeparator,S)-1;
  1026. if (len>=0) and (DoCompareText(Name,Copy(S,1,Len))=0) then
  1027. exit;
  1028. inc(result);
  1029. end;
  1030. result:=-1;
  1031. end;
  1032. Function TStrings.IndexOfObject(AObject: TObject): Integer;
  1033. begin
  1034. Result:=0;
  1035. While (Result<count) and (Objects[Result]<>AObject) do Result:=Result+1;
  1036. If Result=Count then Result:=-1;
  1037. end;
  1038. Procedure TStrings.InsertObject(Index: Integer; const S: string;
  1039. AObject: TObject);
  1040. begin
  1041. Insert (Index,S);
  1042. Objects[Index]:=AObject;
  1043. end;
  1044. function TStrings.LastIndexOf(const S: string): Integer;
  1045. begin
  1046. Result:=LastIndexOf(S,Count-1);
  1047. end;
  1048. function TStrings.LastIndexOf(const S: string; aStart : Integer): Integer;
  1049. begin
  1050. if aStart<0 then
  1051. begin
  1052. aStart:=Count+aStart;
  1053. if aStart<0 then
  1054. aStart:=0;
  1055. end;
  1056. Result:=aStart;
  1057. if Result>=Count-1 then
  1058. Result:=Count-1;
  1059. While (Result>=0) and (DoCompareText(Strings[Result],S)<>0) do
  1060. Result:=Result-1;
  1061. end;
  1062. Procedure TStrings.LoadFromFile(const FileName: string);
  1063. begin
  1064. LoadFromFile(FileName,False)
  1065. end;
  1066. Procedure TStrings.LoadFromFile(const FileName: string; IgnoreEncoding : Boolean);
  1067. Var
  1068. TheStream : TFileStream;
  1069. begin
  1070. TheStream:=TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite);
  1071. try
  1072. LoadFromStream(TheStream, IgnoreEncoding);
  1073. finally
  1074. TheStream.Free;
  1075. end;
  1076. end;
  1077. Procedure TStrings.LoadFromFile(const FileName: string; AEncoding: TEncoding);
  1078. Var
  1079. TheStream : TFileStream;
  1080. begin
  1081. TheStream:=TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite);
  1082. try
  1083. LoadFromStream(TheStream,AEncoding);
  1084. finally
  1085. TheStream.Free;
  1086. end;
  1087. end;
  1088. Procedure TStrings.LoadFromStream(Stream: TStream);
  1089. begin
  1090. LoadFromStream(Stream,False);
  1091. end;
  1092. Const
  1093. LoadBufSize = 1024;
  1094. LoadMaxGrow = MaxInt Div 2;
  1095. Procedure TStrings.LoadFromStream(Stream: TStream; IgnoreEncoding : Boolean);
  1096. {
  1097. Borlands method is no good, since a pipe for
  1098. instance doesn't have a size.
  1099. So we must do it the hard way.
  1100. }
  1101. Var
  1102. Buffer : AnsiString;
  1103. BufLen : SizeInt;
  1104. BytesRead, I, BufDelta : Longint;
  1105. begin
  1106. if not IgnoreEncoding then
  1107. begin
  1108. LoadFromStream(Stream,Nil);
  1109. Exit;
  1110. end;
  1111. // reread into a buffer
  1112. beginupdate;
  1113. try
  1114. Buffer:='';
  1115. BufLen:=0;
  1116. I:=1;
  1117. Repeat
  1118. BufDelta:=LoadBufSize*I;
  1119. SetLength(Buffer,BufLen+BufDelta);
  1120. BytesRead:=Stream.Read(Buffer[BufLen+1],BufDelta);
  1121. inc(BufLen,BufDelta);
  1122. If I<LoadMaxGrow then
  1123. I:=I shl 1;
  1124. Until BytesRead<>BufDelta;
  1125. SetLength(Buffer, BufLen-BufDelta+BytesRead);
  1126. SetTextStr(Buffer);
  1127. SetLength(Buffer,0);
  1128. finally
  1129. EndUpdate;
  1130. end;
  1131. if soPreserveBOM in FOptions then
  1132. WriteBOM:=False;
  1133. end;
  1134. Procedure TStrings.LoadFromStream(Stream: TStream; AEncoding: TEncoding);
  1135. {
  1136. Borlands method is no good, since a pipe for
  1137. instance doesn't have a size.
  1138. So we must do it the hard way.
  1139. }
  1140. Var
  1141. Buffer : TBytes;
  1142. T : string;
  1143. BufLen : SizeInt;
  1144. BytesRead, I, BufDelta, PreambleLength : Longint;
  1145. begin
  1146. // reread into a buffer
  1147. beginupdate;
  1148. try
  1149. SetLength(Buffer,0);
  1150. BufLen:=0;
  1151. I:=1;
  1152. Repeat
  1153. BufDelta:=LoadBufSize*I;
  1154. SetLength(Buffer,BufLen+BufDelta);
  1155. BytesRead:=Stream.Read(Buffer[BufLen],BufDelta);
  1156. inc(BufLen,BufDelta);
  1157. If I<LoadMaxGrow then
  1158. I:=I shl 1;
  1159. Until BytesRead<>BufDelta;
  1160. SetLength(Buffer,BufLen-BufDelta+BytesRead);
  1161. PreambleLength:=TEncoding.GetBufferEncoding(Buffer,AEncoding,FDefaultEncoding);
  1162. T:=AEncoding.GetAnsiString(Buffer,PreambleLength,Length(Buffer)-PreambleLength);
  1163. if soPreserveBOM in FOptions then
  1164. WriteBOM:=PreambleLength>0;
  1165. SetEncoding(AEncoding);
  1166. SetLength(Buffer,0);
  1167. SetTextStr(T);
  1168. finally
  1169. EndUpdate;
  1170. end;
  1171. end;
  1172. Procedure TStrings.Move(CurIndex, NewIndex: Integer);
  1173. Var
  1174. Obj : TObject;
  1175. Str : String;
  1176. begin
  1177. BeginUpdate;
  1178. Try
  1179. Obj:=Objects[CurIndex];
  1180. Str:=Strings[CurIndex];
  1181. Objects[CurIndex]:=Nil; // Prevent Delete from freeing.
  1182. Delete(Curindex);
  1183. InsertObject(NewIndex,Str,Obj);
  1184. finally
  1185. EndUpdate;
  1186. end;
  1187. end;
  1188. function TStrings.Pop: string;
  1189. var
  1190. C : Integer;
  1191. begin
  1192. Result:='';
  1193. C:=Count-1;
  1194. if (C>=0) then
  1195. begin
  1196. Result:=Strings[C];
  1197. Delete(C);
  1198. end;
  1199. end;
  1200. function TStrings.Shift: String;
  1201. begin
  1202. Result:='';
  1203. if (Count > 0) then
  1204. begin
  1205. Result:=Strings[0];
  1206. Delete(0);
  1207. end;
  1208. end;
  1209. Procedure TStrings.SaveToFile(const FileName: string);
  1210. Var TheStream : TFileStream;
  1211. begin
  1212. TheStream:=TFileStream.Create(FileName,fmCreate);
  1213. try
  1214. SaveToStream(TheStream);
  1215. finally
  1216. TheStream.Free;
  1217. end;
  1218. end;
  1219. Procedure TStrings.SaveToFile(const FileName: string; IgnoreEncoding : Boolean);
  1220. Var TheStream : TFileStream;
  1221. begin
  1222. TheStream:=TFileStream.Create(FileName,fmCreate);
  1223. try
  1224. SaveToStream(TheStream, IgnoreEncoding);
  1225. finally
  1226. TheStream.Free;
  1227. end;
  1228. end;
  1229. Procedure TStrings.SaveToFile(const FileName: string; AEncoding: TEncoding);
  1230. Var TheStream : TFileStream;
  1231. begin
  1232. TheStream:=TFileStream.Create(FileName,fmCreate);
  1233. try
  1234. SaveToStream(TheStream,AEncoding);
  1235. finally
  1236. TheStream.Free;
  1237. end;
  1238. end;
  1239. Procedure TStrings.SaveToStream(Stream: TStream);
  1240. begin
  1241. SaveToStream(Stream,False)
  1242. end;
  1243. Procedure TStrings.SaveToStream(Stream: TStream; IgnoreEncoding: Boolean);
  1244. Var
  1245. I,L,NLS : SizeInt;
  1246. S,NL : String;
  1247. begin
  1248. if not IgnoreEncoding then
  1249. begin
  1250. SaveToStream(Stream,FEncoding);
  1251. Exit;
  1252. end;
  1253. NL:=GetLineBreakCharLBS;
  1254. NLS:=Length(NL)*SizeOf(Char);
  1255. For i:=0 To count-1 do
  1256. begin
  1257. S:=Strings[I];
  1258. L:=Length(S);
  1259. if L<>0 then
  1260. Stream.WriteBuffer(S[1], L*SizeOf(Char));
  1261. if (I<Count-1) or Not SkipLastLineBreak then
  1262. Stream.WriteBuffer(NL[1], NLS);
  1263. end;
  1264. end;
  1265. Procedure TStrings.SaveToStream(Stream: TStream; AEncoding: TEncoding);
  1266. Var B,BNL : TBytes;
  1267. NL,S: string;
  1268. i,BNLS: SizeInt;
  1269. begin
  1270. if AEncoding=nil then
  1271. AEncoding:=FDefaultEncoding;
  1272. if WriteBOM then
  1273. begin
  1274. B:=AEncoding.GetPreamble;
  1275. if Length(B)>0 then
  1276. Stream.WriteBuffer(B[0],Length(B));
  1277. end;
  1278. NL := GetLineBreakCharLBS;
  1279. BNL:=AEncoding.GetAnsiBytes(NL);
  1280. BNLS:=Length(BNL);
  1281. For i:=0 To count-1 do
  1282. begin
  1283. S:=Strings[I];
  1284. if S<>'' then
  1285. begin
  1286. B:=AEncoding.GetAnsiBytes(S);
  1287. Stream.WriteBuffer(B[0],Length(B));
  1288. end;
  1289. if (I<Count-1) or Not SkipLastLineBreak then
  1290. Stream.WriteBuffer(BNL[0],BNLS);
  1291. end;
  1292. end;
  1293. Procedure TStrings.SetText(TheText: PChar);
  1294. Var S : String;
  1295. begin
  1296. If TheText<>Nil then
  1297. S:=StrPas(TheText)
  1298. else
  1299. S:='';
  1300. SetTextStr(S);
  1301. end;
  1302. {****************************************************************************}
  1303. {* TStringList *}
  1304. {****************************************************************************}
  1305. {$if not defined(FPC_TESTGENERICS)}
  1306. procedure TStringList.ExchangeItemsInt(Index1, Index2: Integer);
  1307. Var P1,P2 : Pointer;
  1308. begin
  1309. P1:=Pointer(Flist^[Index1].FString);
  1310. P2:=Pointer(Flist^[Index1].FObject);
  1311. Pointer(Flist^[Index1].Fstring):=Pointer(Flist^[Index2].Fstring);
  1312. Pointer(Flist^[Index1].FObject):=Pointer(Flist^[Index2].FObject);
  1313. Pointer(Flist^[Index2].Fstring):=P1;
  1314. Pointer(Flist^[Index2].FObject):=P2;
  1315. end;
  1316. function TStringList.GetSorted: Boolean;
  1317. begin
  1318. Result:=FSortStyle in [sslUser,sslAuto];
  1319. end;
  1320. procedure TStringList.ExchangeItems(Index1, Index2: Integer);
  1321. begin
  1322. ExchangeItemsInt(Index1, Index2);
  1323. end;
  1324. procedure TStringList.Grow;
  1325. Var
  1326. NC : Integer;
  1327. begin
  1328. NC:=FCapacity;
  1329. If NC>=256 then
  1330. NC:=NC+(NC Div 4)
  1331. else if NC=0 then
  1332. NC:=4
  1333. else
  1334. NC:=NC*4;
  1335. SetCapacity(NC);
  1336. end;
  1337. procedure TStringList.InternalClear(FromIndex: Integer; ClearOnly: Boolean);
  1338. Var
  1339. I: Integer;
  1340. begin
  1341. if FromIndex < FCount then
  1342. begin
  1343. if FOwnsObjects then
  1344. begin
  1345. For I:=FromIndex to FCount-1 do
  1346. begin
  1347. Flist^[I].FString:='';
  1348. freeandnil(Flist^[i].FObject);
  1349. end;
  1350. end
  1351. else
  1352. begin
  1353. For I:=FromIndex to FCount-1 do
  1354. Flist^[I].FString:='';
  1355. end;
  1356. FCount:=FromIndex;
  1357. end;
  1358. if Not ClearOnly then
  1359. SetCapacity(0);
  1360. end;
  1361. procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare
  1362. );
  1363. var
  1364. Pivot, vL, vR: Integer;
  1365. ExchangeProc: procedure(Left, Right: Integer) of object;
  1366. begin
  1367. //if ExchangeItems is override call that, else call (faster) ExchangeItemsInt
  1368. if TMethod(@Self.ExchangeItems).Code = CodePointer(@TStringList.ExchangeItems) then
  1369. ExchangeProc := @ExchangeItemsInt
  1370. else
  1371. ExchangeProc := @ExchangeItems;
  1372. if R - L <= 1 then begin // a little bit of time saver
  1373. if L < R then
  1374. if CompareFn(Self, L, R) > 0 then
  1375. ExchangeProc(L, R);
  1376. Exit;
  1377. end;
  1378. vL := L;
  1379. vR := R;
  1380. Pivot := L + Random(R - L); // they say random is best
  1381. while vL < vR do begin
  1382. while (vL < Pivot) and (CompareFn(Self, vL, Pivot) <= 0) do
  1383. Inc(vL);
  1384. while (vR > Pivot) and (CompareFn(Self, vR, Pivot) > 0) do
  1385. Dec(vR);
  1386. ExchangeProc(vL, vR);
  1387. if Pivot = vL then // swap pivot if we just hit it from one side
  1388. Pivot := vR
  1389. else if Pivot = vR then
  1390. Pivot := vL;
  1391. end;
  1392. if Pivot - 1 >= L then
  1393. QuickSort(L, Pivot - 1, CompareFn);
  1394. if Pivot + 1 <= R then
  1395. QuickSort(Pivot + 1, R, CompareFn);
  1396. end;
  1397. procedure TStringList.InsertItem(Index: Integer; const S: string);
  1398. begin
  1399. InsertItem(Index, S, nil);
  1400. end;
  1401. procedure TStringList.InsertItem(Index: Integer; const S: string; O: TObject);
  1402. begin
  1403. Changing;
  1404. If FCount=Fcapacity then Grow;
  1405. If Index<FCount then
  1406. System.Move (FList^[Index],FList^[Index+1],
  1407. (FCount-Index)*SizeOf(TStringItem));
  1408. Pointer(Flist^[Index].Fstring):=Nil; // Needed to initialize...
  1409. Flist^[Index].FString:=S;
  1410. Flist^[Index].FObject:=O;
  1411. Inc(FCount);
  1412. Changed;
  1413. end;
  1414. procedure TStringList.SetSorted(Value: Boolean);
  1415. begin
  1416. If Value then
  1417. SortStyle:=sslAuto
  1418. else
  1419. SortStyle:=sslNone
  1420. end;
  1421. procedure TStringList.Changed;
  1422. begin
  1423. If (FUpdateCount=0) Then
  1424. begin
  1425. If Assigned(FOnChange) then
  1426. FOnchange(Self);
  1427. FPONotifyObservers(Self,ooChange,Nil);
  1428. end;
  1429. end;
  1430. procedure TStringList.Changing;
  1431. begin
  1432. If FUpdateCount=0 then
  1433. if Assigned(FOnChanging) then
  1434. FOnchanging(Self);
  1435. end;
  1436. function TStringList.Get(Index: Integer): string;
  1437. begin
  1438. CheckIndex(Index);
  1439. Result:=Flist^[Index].FString;
  1440. end;
  1441. function TStringList.GetCapacity: Integer;
  1442. begin
  1443. Result:=FCapacity;
  1444. end;
  1445. function TStringList.GetCount: Integer;
  1446. begin
  1447. Result:=FCount;
  1448. end;
  1449. function TStringList.GetObject(Index: Integer): TObject;
  1450. begin
  1451. CheckIndex(Index);
  1452. Result:=Flist^[Index].FObject;
  1453. end;
  1454. procedure TStringList.Put(Index: Integer; const S: string);
  1455. begin
  1456. If Sorted then
  1457. Error(SSortedListError,0);
  1458. CheckIndex(Index);
  1459. Changing;
  1460. Flist^[Index].FString:=S;
  1461. Changed;
  1462. end;
  1463. procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  1464. begin
  1465. CheckIndex(Index);
  1466. Changing;
  1467. Flist^[Index].FObject:=AObject;
  1468. Changed;
  1469. end;
  1470. procedure TStringList.SetCapacity(NewCapacity: Integer);
  1471. Var NewList : Pointer;
  1472. MSize : Longint;
  1473. begin
  1474. If (NewCapacity<0) then
  1475. Error (SListCapacityError,NewCapacity);
  1476. If NewCapacity>FCapacity then
  1477. begin
  1478. GetMem (NewList,NewCapacity*SizeOf(TStringItem));
  1479. If NewList=Nil then
  1480. Error (SListCapacityError,NewCapacity);
  1481. If Assigned(FList) then
  1482. begin
  1483. MSize:=FCapacity*Sizeof(TStringItem);
  1484. System.Move (FList^,NewList^,MSize);
  1485. FillWord (Pchar(NewList)[MSize],(NewCapacity-FCapacity)*(SizeOf(TStringItem) div SizeOf(Word)), 0);
  1486. FreeMem (Flist,MSize);
  1487. end;
  1488. Flist:=NewList;
  1489. FCapacity:=NewCapacity;
  1490. end
  1491. else if NewCapacity<FCapacity then
  1492. begin
  1493. if NewCapacity = 0 then
  1494. begin
  1495. if FCount > 0 then
  1496. InternalClear(0,True);
  1497. FreeMem(FList);
  1498. FList := nil;
  1499. end else
  1500. begin
  1501. InternalClear(NewCapacity,True);
  1502. GetMem(NewList, NewCapacity * SizeOf(TStringItem));
  1503. System.Move(FList^, NewList^, NewCapacity * SizeOf(TStringItem));
  1504. FreeMem(FList);
  1505. FList := NewList;
  1506. end;
  1507. FCapacity:=NewCapacity;
  1508. end;
  1509. end;
  1510. procedure TStringList.SetUpdateState(Updating: Boolean);
  1511. begin
  1512. If Updating then
  1513. Changing
  1514. else
  1515. Changed
  1516. end;
  1517. Constructor TStringList.Create;
  1518. begin
  1519. inherited Create;
  1520. end;
  1521. Constructor TStringList.Create(anOwnsObjects : Boolean);
  1522. begin
  1523. inherited Create;
  1524. FOwnsObjects:=anOwnsObjects;
  1525. end;
  1526. destructor TStringList.Destroy;
  1527. begin
  1528. InternalClear;
  1529. Inherited destroy;
  1530. end;
  1531. function TStringList.Add(const S: string): Integer;
  1532. begin
  1533. If (SortStyle<>sslAuto) then
  1534. Result:=FCount
  1535. else
  1536. If Find (S,Result) then
  1537. Case DUplicates of
  1538. DupIgnore : Exit;
  1539. DupError : Error(SDuplicateString,0)
  1540. end;
  1541. InsertItem (Result,S);
  1542. end;
  1543. procedure TStringList.Clear;
  1544. begin
  1545. if FCount = 0 then Exit;
  1546. Changing;
  1547. InternalClear;
  1548. Changed;
  1549. end;
  1550. procedure TStringList.Delete(Index: Integer);
  1551. begin
  1552. CheckIndex(Index);
  1553. Changing;
  1554. Flist^[Index].FString:='';
  1555. if FOwnsObjects then
  1556. FreeAndNil(Flist^[Index].FObject);
  1557. Dec(FCount);
  1558. If Index<FCount then
  1559. System.Move(Flist^[Index+1],
  1560. Flist^[Index],
  1561. (Fcount-Index)*SizeOf(TStringItem));
  1562. Changed;
  1563. end;
  1564. procedure TStringList.Exchange(Index1, Index2: Integer);
  1565. begin
  1566. CheckIndex(Index1);
  1567. CheckIndex(Index2);
  1568. Changing;
  1569. ExchangeItemsInt(Index1,Index2);
  1570. changed;
  1571. end;
  1572. procedure TStringList.SetCaseSensitive(b : boolean);
  1573. begin
  1574. if b=FCaseSensitive then
  1575. Exit;
  1576. FCaseSensitive:=b;
  1577. if FSortStyle=sslAuto then
  1578. begin
  1579. FForceSort:=True;
  1580. try
  1581. Sort;
  1582. finally
  1583. FForceSort:=False;
  1584. end;
  1585. end;
  1586. end;
  1587. procedure TStringList.SetSortStyle(AValue: TStringsSortStyle);
  1588. begin
  1589. if FSortStyle=AValue then Exit;
  1590. if (AValue=sslAuto) then
  1591. Sort;
  1592. FSortStyle:=AValue;
  1593. end;
  1594. procedure TStringList.CheckIndex(AIndex: Integer);
  1595. begin
  1596. If (AIndex<0) or (AIndex>=FCount) then
  1597. Error(SListIndexError,AIndex);
  1598. end;
  1599. function TStringList.DoCompareText(const s1, s2: string): PtrInt;
  1600. begin
  1601. if FCaseSensitive then
  1602. begin
  1603. if UseLocale then
  1604. result:=AnsiCompareStr(s1,s2)
  1605. else
  1606. result:=CompareStr(s1,s2);
  1607. end else
  1608. begin
  1609. if UseLocale then
  1610. result:=AnsiCompareText(s1,s2)
  1611. else
  1612. result:=CompareText(s1,s2);
  1613. end;
  1614. end;
  1615. function TStringList.Find(const S: string; out Index: Integer): Boolean;
  1616. var
  1617. L, R, I: Integer;
  1618. CompareRes: PtrInt;
  1619. begin
  1620. Result := false;
  1621. Index:=-1;
  1622. if Not Sorted then
  1623. Raise EListError.Create(SErrFindNeedsSortedList);
  1624. // Use binary search.
  1625. L := 0;
  1626. R := Count - 1;
  1627. while (L<=R) do
  1628. begin
  1629. I := L + (R - L) div 2;
  1630. CompareRes := DoCompareText(S, Flist^[I].FString);
  1631. if (CompareRes>0) then
  1632. L := I+1
  1633. else begin
  1634. R := I-1;
  1635. if (CompareRes=0) then begin
  1636. Result := true;
  1637. if (Duplicates<>dupAccept) then
  1638. L := I; // forces end of while loop
  1639. end;
  1640. end;
  1641. end;
  1642. Index := L;
  1643. end;
  1644. function TStringList.IndexOf(const S: string): Integer;
  1645. begin
  1646. If Not Sorted then
  1647. Result:=Inherited indexOf(S)
  1648. else
  1649. // faster using binary search...
  1650. If Not Find (S,Result) then
  1651. Result:=-1;
  1652. end;
  1653. procedure TStringList.Insert(Index: Integer; const S: string);
  1654. begin
  1655. If SortStyle=sslAuto then
  1656. Error (SSortedListError,0)
  1657. else
  1658. begin
  1659. If (Index<0) or (Index>FCount) then
  1660. Error(SListIndexError,Index); // Cannot use CheckIndex, because there >= FCount...
  1661. InsertItem (Index,S);
  1662. end;
  1663. end;
  1664. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1665. begin
  1666. If (FCount>1) and (FForceSort or (FSortStyle<>sslAuto)) then
  1667. begin
  1668. Changing;
  1669. QuickSort(0,FCount-1, CompareFn);
  1670. Changed;
  1671. end;
  1672. end;
  1673. function StringListAnsiCompare(List: TStringList; Index1, Index: Integer): Integer;
  1674. begin
  1675. Result := List.DoCompareText(List.FList^[Index1].FString,
  1676. List.FList^[Index].FString);
  1677. end;
  1678. procedure TStringList.Sort;
  1679. begin
  1680. CustomSort(@StringListAnsiCompare);
  1681. end;
  1682. {$else}
  1683. { generics based implementation of TStringList follows }
  1684. function StringListAnsiCompare(List: TStringList; Index1, Index2: Integer): Integer;
  1685. begin
  1686. Result := List.DoCompareText(List.Strings[Index1], List.Strings[Index2]);
  1687. end;
  1688. constructor TStringList.Create;
  1689. begin
  1690. inherited;
  1691. FOwnsObjects:=false;
  1692. FMap := TFPStrObjMap.Create;
  1693. FMap.OnPtrCompare := @MapPtrCompare;
  1694. FOnCompareText := @DefaultCompareText;
  1695. NameValueSeparator:='=';
  1696. CheckSpecialChars;
  1697. end;
  1698. destructor TStringList.Destroy;
  1699. begin
  1700. FMap.Free;
  1701. inherited;
  1702. end;
  1703. function TStringList.GetDuplicates: TDuplicates;
  1704. begin
  1705. Result := FMap.Duplicates;
  1706. end;
  1707. function TStringList.GetSorted: boolean;
  1708. begin
  1709. Result := FMap.Sorted;
  1710. end;
  1711. procedure TStringList.SetDuplicates(NewDuplicates: TDuplicates);
  1712. begin
  1713. FMap.Duplicates := NewDuplicates;
  1714. end;
  1715. procedure TStringList.SetSorted(NewSorted: Boolean);
  1716. begin
  1717. FMap.Sorted := NewSorted;
  1718. end;
  1719. procedure TStringList.Changed;
  1720. begin
  1721. if FUpdateCount = 0 then
  1722. if Assigned(FOnChange) then
  1723. FOnChange(Self);
  1724. end;
  1725. procedure TStringList.Changing;
  1726. begin
  1727. if FUpdateCount = 0 then
  1728. if Assigned(FOnChanging) then
  1729. FOnChanging(Self);
  1730. end;
  1731. function TStringList.Get(Index: Integer): string;
  1732. begin
  1733. Result := FMap.Keys[Index];
  1734. end;
  1735. function TStringList.GetCapacity: Integer;
  1736. begin
  1737. Result := FMap.Capacity;
  1738. end;
  1739. function TStringList.GetCount: Integer;
  1740. begin
  1741. Result := FMap.Count;
  1742. end;
  1743. function TStringList.GetObject(Index: Integer): TObject;
  1744. begin
  1745. Result := FMap.Data[Index];
  1746. end;
  1747. procedure TStringList.Put(Index: Integer; const S: string);
  1748. begin
  1749. Changing;
  1750. FMap.Keys[Index] := S;
  1751. Changed;
  1752. end;
  1753. procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  1754. begin
  1755. Changing;
  1756. FMap.Data[Index] := AObject;
  1757. Changed;
  1758. end;
  1759. procedure TStringList.SetCapacity(NewCapacity: Integer);
  1760. begin
  1761. FMap.Capacity := NewCapacity;
  1762. end;
  1763. procedure TStringList.SetUpdateState(Updating: Boolean);
  1764. begin
  1765. if Updating then
  1766. Changing
  1767. else
  1768. Changed
  1769. end;
  1770. function TStringList.Add(const S: string): Integer;
  1771. begin
  1772. Result := FMap.Add(S);
  1773. end;
  1774. procedure TStringList.Clear;
  1775. begin
  1776. if FMap.Count = 0 then exit;
  1777. Changing;
  1778. FMap.Clear;
  1779. Changed;
  1780. end;
  1781. procedure TStringList.Delete(Index: Integer);
  1782. begin
  1783. if (Index < 0) or (Index >= FMap.Count) then
  1784. Error(SListIndexError, Index);
  1785. Changing;
  1786. FMap.Delete(Index);
  1787. Changed;
  1788. end;
  1789. procedure TStringList.Exchange(Index1, Index2: Integer);
  1790. begin
  1791. if (Index1 < 0) or (Index1 >= FMap.Count) then
  1792. Error(SListIndexError, Index1);
  1793. if (Index2 < 0) or (Index2 >= FMap.Count) then
  1794. Error(SListIndexError, Index2);
  1795. Changing;
  1796. FMap.InternalExchange(Index1, Index2);
  1797. Changed;
  1798. end;
  1799. procedure TStringList.SetCaseSensitive(NewSensitive: Boolean);
  1800. begin
  1801. if NewSensitive <> FCaseSensitive then
  1802. begin
  1803. FCaseSensitive := NewSensitive;
  1804. if Sorted then
  1805. Sort;
  1806. end;
  1807. end;
  1808. function TStringList.MapPtrCompare(Key1, Key2: Pointer): Integer;
  1809. begin
  1810. Result := FOnCompareText(string(Key1^), string(Key2^));
  1811. end;
  1812. function TStringList.DefaultCompareText(const s1, s2: string): PtrInt;
  1813. begin
  1814. if FCaseSensitive then
  1815. Result := AnsiCompareStr(s1, s2)
  1816. else
  1817. Result := AnsiCompareText(s1, s2);
  1818. end;
  1819. function TStringList.DoCompareText(const s1, s2: string): PtrInt;
  1820. begin
  1821. Result := FOnCompareText(s1, s2);
  1822. end;
  1823. function TStringList.Find(const S: string; var Index: Integer): Boolean;
  1824. begin
  1825. Result := FMap.Find(S, Index);
  1826. end;
  1827. function TStringList.IndexOf(const S: string): Integer;
  1828. begin
  1829. Result := FMap.IndexOf(S);
  1830. end;
  1831. procedure TStringList.Insert(Index: Integer; const S: string);
  1832. begin
  1833. if not Sorted and (0 <= Index) and (Index < FMap.Count) then
  1834. Changing;
  1835. FMap.InsertKey(Index, S);
  1836. Changed;
  1837. end;
  1838. procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare);
  1839. var
  1840. I, J, Pivot: Integer;
  1841. begin
  1842. repeat
  1843. I := L;
  1844. J := R;
  1845. Pivot := (L + R) div 2;
  1846. repeat
  1847. while CompareFn(Self, I, Pivot) < 0 do Inc(I);
  1848. while CompareFn(Self, J, Pivot) > 0 do Dec(J);
  1849. if I <= J then
  1850. begin
  1851. FMap.InternalExchange(I, J); // No check, indices are correct.
  1852. if Pivot = I then
  1853. Pivot := J
  1854. else if Pivot = J then
  1855. Pivot := I;
  1856. Inc(I);
  1857. Dec(j);
  1858. end;
  1859. until I > J;
  1860. if L < J then
  1861. QuickSort(L,J, CompareFn);
  1862. L := I;
  1863. until I >= R;
  1864. end;
  1865. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1866. begin
  1867. if not Sorted and (FMap.Count > 1) then
  1868. begin
  1869. Changing;
  1870. QuickSort(0, FMap.Count-1, CompareFn);
  1871. Changed;
  1872. end;
  1873. end;
  1874. procedure TStringList.Sort;
  1875. begin
  1876. if not Sorted and (FMap.Count > 1) then
  1877. begin
  1878. Changing;
  1879. FMap.Sort;
  1880. Changed;
  1881. end;
  1882. end;
  1883. {$endif}