stringl.inc 43 KB

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