stringl.inc 46 KB

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