stringl.inc 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610
  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; Quote : String) : String;
  35. Var
  36. I,J : Integer;
  37. begin
  38. J:=0;
  39. Result:=S;
  40. for i:=1to 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. FSpecialCharsInited:=true;
  64. FLBS:=DefaultTextLineBreakStyle;
  65. end;
  66. end;
  67. Function TStrings.GetLBS : TTextLineBreakStyle;
  68. begin
  69. CheckSpecialChars;
  70. Result:=FLBS;
  71. end;
  72. Procedure TStrings.SetLBS (AValue : TTextLineBreakStyle);
  73. begin
  74. CheckSpecialChars;
  75. FLBS:=AValue;
  76. end;
  77. procedure TStrings.SetDelimiter(c:Char);
  78. begin
  79. CheckSpecialChars;
  80. FDelimiter:=c;
  81. end;
  82. procedure TStrings.SetQuoteChar(c:Char);
  83. begin
  84. CheckSpecialChars;
  85. FQuoteChar:=c;
  86. end;
  87. procedure TStrings.SetNameValueSeparator(c:Char);
  88. begin
  89. CheckSpecialChars;
  90. FNameValueSeparator:=c;
  91. end;
  92. function TStrings.GetCommaText: string;
  93. Var
  94. C1,C2 : Char;
  95. FSD : Boolean;
  96. begin
  97. CheckSpecialChars;
  98. FSD:=StrictDelimiter;
  99. C1:=Delimiter;
  100. C2:=QuoteChar;
  101. Delimiter:=',';
  102. QuoteChar:='"';
  103. StrictDelimiter:=False;
  104. Try
  105. Result:=GetDelimitedText;
  106. Finally
  107. Delimiter:=C1;
  108. QuoteChar:=C2;
  109. StrictDelimiter:=FSD;
  110. end;
  111. end;
  112. Function TStrings.GetDelimitedText: string;
  113. Var
  114. I : integer;
  115. p : pchar;
  116. BreakChars : set of char;
  117. S : String;
  118. begin
  119. CheckSpecialChars;
  120. result:='';
  121. if StrictDelimiter then
  122. BreakChars:=[#0,QuoteChar,Delimiter]
  123. else
  124. BreakChars:=[#0..' ',QuoteChar,Delimiter];
  125. // Check for break characters and quote if required.
  126. For i:=0 to count-1 do
  127. begin
  128. S:=Strings[i];
  129. p:=pchar(S);
  130. //Quote strings that include BreakChars:
  131. while not(p^ in BreakChars) do
  132. inc(p);
  133. if (p<>pchar(S)+length(S)) then
  134. Result:=Result+QuoteString(S,QuoteChar)
  135. else
  136. Result:=Result+S;
  137. if I<Count-1 then
  138. Result:=Result+Delimiter;
  139. end;
  140. // Quote empty string:
  141. If (Length(Result)=0) and (Count=1) then
  142. Result:=QuoteChar+QuoteChar;
  143. end;
  144. procedure TStrings.GetNameValue(Index : Integer; Out AName,AValue : String);
  145. Var L : longint;
  146. begin
  147. CheckSpecialChars;
  148. AValue:=Strings[Index];
  149. L:=Pos(FNameValueSeparator,AValue);
  150. If L<>0 then
  151. begin
  152. AName:=Copy(AValue,1,L-1);
  153. System.Delete(AValue,1,L);
  154. end
  155. else
  156. AName:='';
  157. end;
  158. function TStrings.ExtractName(const s:String):String;
  159. var
  160. L: Longint;
  161. begin
  162. CheckSpecialChars;
  163. L:=Pos(FNameValueSeparator,S);
  164. If L<>0 then
  165. Result:=Copy(S,1,L-1)
  166. else
  167. Result:='';
  168. end;
  169. function TStrings.GetName(Index: Integer): string;
  170. Var
  171. V : String;
  172. begin
  173. GetNameValue(Index,Result,V);
  174. end;
  175. Function TStrings.GetValue(const Name: string): string;
  176. Var
  177. L : longint;
  178. N : String;
  179. begin
  180. Result:='';
  181. L:=IndexOfName(Name);
  182. If L<>-1 then
  183. GetNameValue(L,N,Result);
  184. end;
  185. Function TStrings.GetValueFromIndex(Index: Integer): string;
  186. Var
  187. N : String;
  188. begin
  189. GetNameValue(Index,N,Result);
  190. end;
  191. Procedure TStrings.SetValueFromIndex(Index: Integer; const Value: string);
  192. begin
  193. If (Value='') then
  194. Delete(Index)
  195. else
  196. begin
  197. If (Index<0) then
  198. Index:=Add('');
  199. CheckSpecialChars;
  200. Strings[Index]:=GetName(Index)+FNameValueSeparator+Value;
  201. end;
  202. end;
  203. procedure TStrings.ReadData(Reader: TReader);
  204. begin
  205. Reader.ReadListBegin;
  206. BeginUpdate;
  207. try
  208. Clear;
  209. while not Reader.EndOfList do
  210. Add(Reader.ReadString);
  211. finally
  212. EndUpdate;
  213. end;
  214. Reader.ReadListEnd;
  215. end;
  216. Procedure TStrings.SetDelimitedText(const AValue: string);
  217. var i,j:integer;
  218. aNotFirst:boolean;
  219. begin
  220. CheckSpecialChars;
  221. BeginUpdate;
  222. i:=1;
  223. j:=1;
  224. aNotFirst:=false;
  225. { Paraphrased from Delphi XE2 help:
  226. Strings must be separated by Delimiter characters or spaces.
  227. They may be enclosed in QuoteChars.
  228. QuoteChars in the string must be repeated to distinguish them from the QuoteChars enclosing the string.
  229. }
  230. try
  231. Clear;
  232. If StrictDelimiter then
  233. begin
  234. while i<=length(AValue) do begin
  235. // skip delimiter
  236. if aNotFirst and (i<=length(AValue)) and (AValue[i]=FDelimiter) then inc(i);
  237. // read next string
  238. if i<=length(AValue) then begin
  239. if AValue[i]=FQuoteChar then begin
  240. // next string is quoted
  241. j:=i+1;
  242. while (j<=length(AValue)) and
  243. ( (AValue[j]<>FQuoteChar) or
  244. ( (j+1<=length(AValue)) and (AValue[j+1]=FQuoteChar) ) ) do begin
  245. if (j<=length(AValue)) and (AValue[j]=FQuoteChar) then inc(j,2)
  246. else inc(j);
  247. end;
  248. // j is position of closing quote
  249. Add( StringReplace (Copy(AValue,i+1,j-i-1),
  250. FQuoteChar+FQuoteChar,FQuoteChar, [rfReplaceAll]));
  251. i:=j+1;
  252. end else begin
  253. // next string is not quoted; read until delimiter
  254. j:=i;
  255. while (j<=length(AValue)) and
  256. (AValue[j]<>FDelimiter) do inc(j);
  257. Add( Copy(AValue,i,j-i));
  258. i:=j;
  259. end;
  260. end else begin
  261. if aNotFirst then Add('');
  262. end;
  263. aNotFirst:=true;
  264. end;
  265. end
  266. else
  267. begin
  268. while i<=length(AValue) do begin
  269. // skip delimiter
  270. if aNotFirst and (i<=length(AValue)) and (AValue[i]=FDelimiter) then inc(i);
  271. // skip spaces
  272. while (i<=length(AValue)) and (Ord(AValue[i])<=Ord(' ')) do inc(i);
  273. // read next string
  274. if i<=length(AValue) then begin
  275. if AValue[i]=FQuoteChar then begin
  276. // next string is quoted
  277. j:=i+1;
  278. while (j<=length(AValue)) and
  279. ( (AValue[j]<>FQuoteChar) or
  280. ( (j+1<=length(AValue)) and (AValue[j+1]=FQuoteChar) ) ) do begin
  281. if (j<=length(AValue)) and (AValue[j]=FQuoteChar) then inc(j,2)
  282. else inc(j);
  283. end;
  284. // j is position of closing quote
  285. Add( StringReplace (Copy(AValue,i+1,j-i-1),
  286. FQuoteChar+FQuoteChar,FQuoteChar, [rfReplaceAll]));
  287. i:=j+1;
  288. end else begin
  289. // next string is not quoted; read until control character/space/delimiter
  290. j:=i;
  291. while (j<=length(AValue)) and
  292. (Ord(AValue[j])>Ord(' ')) and
  293. (AValue[j]<>FDelimiter) do inc(j);
  294. Add( Copy(AValue,i,j-i));
  295. i:=j;
  296. end;
  297. end else begin
  298. if aNotFirst then Add('');
  299. end;
  300. // skip spaces
  301. while (i<=length(AValue)) and (Ord(AValue[i])<=Ord(' ')) do inc(i);
  302. aNotFirst:=true;
  303. end;
  304. end;
  305. finally
  306. EndUpdate;
  307. end;
  308. end;
  309. Procedure TStrings.SetCommaText(const Value: string);
  310. Var
  311. C1,C2 : Char;
  312. begin
  313. CheckSpecialChars;
  314. C1:=Delimiter;
  315. C2:=QuoteChar;
  316. Delimiter:=',';
  317. QuoteChar:='"';
  318. Try
  319. SetDelimitedText(Value);
  320. Finally
  321. Delimiter:=C1;
  322. QuoteChar:=C2;
  323. end;
  324. end;
  325. Procedure TStrings.SetStringsAdapter(const Value: IStringsAdapter);
  326. begin
  327. end;
  328. Procedure TStrings.SetValue(const Name, Value: string);
  329. Var L : longint;
  330. begin
  331. CheckSpecialChars;
  332. L:=IndexOfName(Name);
  333. if L=-1 then
  334. Add (Name+FNameValueSeparator+Value)
  335. else
  336. Strings[L]:=Name+FNameValueSeparator+value;
  337. end;
  338. procedure TStrings.WriteData(Writer: TWriter);
  339. var
  340. i: Integer;
  341. begin
  342. Writer.WriteListBegin;
  343. for i := 0 to Count - 1 do
  344. Writer.WriteString(Strings[i]);
  345. Writer.WriteListEnd;
  346. end;
  347. procedure TStrings.DefineProperties(Filer: TFiler);
  348. var
  349. HasData: Boolean;
  350. begin
  351. if Assigned(Filer.Ancestor) then
  352. // Only serialize if string list is different from ancestor
  353. if Filer.Ancestor.InheritsFrom(TStrings) then
  354. HasData := not Equals(TStrings(Filer.Ancestor))
  355. else
  356. HasData := True
  357. else
  358. HasData := Count > 0;
  359. Filer.DefineProperty('Strings', @ReadData, @WriteData, HasData);
  360. end;
  361. Procedure TStrings.Error(const Msg: string; Data: Integer);
  362. begin
  363. Raise EStringListError.CreateFmt(Msg,[Data]) at get_caller_addr(get_frame);
  364. end;
  365. Procedure TStrings.Error(const Msg: pstring; Data: Integer);
  366. begin
  367. Raise EStringListError.CreateFmt(Msg^,[Data]) at get_caller_addr(get_frame);
  368. end;
  369. Function TStrings.GetCapacity: Integer;
  370. begin
  371. Result:=Count;
  372. end;
  373. Function TStrings.GetObject(Index: Integer): TObject;
  374. begin
  375. Result:=Nil;
  376. end;
  377. Function TStrings.GetTextStr: string;
  378. Var P : Pchar;
  379. I,L,NLS : Longint;
  380. S,NL : String;
  381. begin
  382. CheckSpecialChars;
  383. // Determine needed place
  384. Case FLBS of
  385. tlbsLF : NL:=#10;
  386. tlbsCRLF : NL:=#13#10;
  387. tlbsCR : NL:=#13;
  388. end;
  389. L:=0;
  390. NLS:=Length(NL);
  391. For I:=0 to count-1 do
  392. L:=L+Length(Strings[I])+NLS;
  393. Setlength(Result,L);
  394. P:=Pointer(Result);
  395. For i:=0 To count-1 do
  396. begin
  397. S:=Strings[I];
  398. L:=Length(S);
  399. if L<>0 then
  400. System.Move(Pointer(S)^,P^,L);
  401. P:=P+L;
  402. For L:=1 to NLS do
  403. begin
  404. P^:=NL[L];
  405. inc(P);
  406. end;
  407. end;
  408. end;
  409. Procedure TStrings.Put(Index: Integer; const S: string);
  410. Var Obj : TObject;
  411. begin
  412. Obj:=Objects[Index];
  413. Delete(Index);
  414. InsertObject(Index,S,Obj);
  415. end;
  416. Procedure TStrings.PutObject(Index: Integer; AObject: TObject);
  417. begin
  418. // Empty.
  419. end;
  420. Procedure TStrings.SetCapacity(NewCapacity: Integer);
  421. begin
  422. // Empty.
  423. end;
  424. Function GetNextLine (Const Value : String; Var S : String; Var P : Integer) : Boolean;
  425. Var
  426. PS : PChar;
  427. IP,L : Integer;
  428. begin
  429. L:=Length(Value);
  430. S:='';
  431. Result:=False;
  432. If ((L-P)<0) then
  433. exit;
  434. if ((L-P)=0) and (not (value[P] in [#10,#13])) Then
  435. Begin
  436. s:=value[P];
  437. inc(P);
  438. Exit(True);
  439. End;
  440. PS:=PChar(Value)+P-1;
  441. IP:=P;
  442. While ((L-P)>=0) and (not (PS^ in [#10,#13])) do
  443. begin
  444. P:=P+1;
  445. Inc(PS);
  446. end;
  447. SetLength (S,P-IP);
  448. System.Move (Value[IP],Pointer(S)^,P-IP);
  449. If (P<=L) and (Value[P]=#13) then
  450. Inc(P);
  451. If (P<=L) and (Value[P]=#10) then
  452. Inc(P); // Point to character after #10(#13)
  453. Result:=True;
  454. end;
  455. Procedure TStrings.DoSetTextStr(const Value: string; DoClear : Boolean);
  456. Var
  457. S : String;
  458. P : Integer;
  459. begin
  460. Try
  461. beginUpdate;
  462. if DoClear then
  463. Clear;
  464. P:=1;
  465. While GetNextLine (Value,S,P) do
  466. Add(S);
  467. finally
  468. EndUpdate;
  469. end;
  470. end;
  471. Procedure TStrings.SetTextStr(const Value: string);
  472. begin
  473. DoSetTextStr(Value,True);
  474. end;
  475. Procedure TStrings.AddText(const S: string);
  476. begin
  477. DoSetTextStr(S,False);
  478. end;
  479. Procedure TStrings.SetUpdateState(Updating: Boolean);
  480. begin
  481. FPONotifyObservers(Self,ooChange,Nil);
  482. end;
  483. destructor TSTrings.Destroy;
  484. begin
  485. inherited destroy;
  486. end;
  487. Function TStrings.Add(const S: string): Integer;
  488. begin
  489. Result:=Count;
  490. Insert (Count,S);
  491. end;
  492. Function TStrings.AddObject(const S: string; AObject: TObject): Integer;
  493. begin
  494. Result:=Add(S);
  495. Objects[result]:=AObject;
  496. end;
  497. Procedure TStrings.Append(const S: string);
  498. begin
  499. Add (S);
  500. end;
  501. Procedure TStrings.AddStrings(TheStrings: TStrings);
  502. Var Runner : longint;
  503. begin
  504. try
  505. beginupdate;
  506. For Runner:=0 to TheStrings.Count-1 do
  507. self.AddObject (Thestrings[Runner],TheStrings.Objects[Runner]);
  508. finally
  509. EndUpdate;
  510. end;
  511. end;
  512. Procedure TStrings.AddStrings(const TheStrings: array of string);
  513. Var Runner : longint;
  514. begin
  515. try
  516. beginupdate;
  517. if Count + High(TheStrings)+1 > Capacity then
  518. Capacity := Count + High(TheStrings)+1;
  519. For Runner:=Low(TheStrings) to High(TheStrings) do
  520. self.Add(Thestrings[Runner]);
  521. finally
  522. EndUpdate;
  523. end;
  524. end;
  525. Procedure TStrings.Assign(Source: TPersistent);
  526. Var
  527. S : TStrings;
  528. begin
  529. If Source is TStrings then
  530. begin
  531. S:=TStrings(Source);
  532. BeginUpdate;
  533. Try
  534. clear;
  535. FSpecialCharsInited:=S.FSpecialCharsInited;
  536. FQuoteChar:=S.FQuoteChar;
  537. FDelimiter:=S.FDelimiter;
  538. FNameValueSeparator:=S.FNameValueSeparator;
  539. FLBS:=S.FLBS;
  540. AddStrings(S);
  541. finally
  542. EndUpdate;
  543. end;
  544. end
  545. else
  546. Inherited Assign(Source);
  547. end;
  548. Procedure TStrings.BeginUpdate;
  549. begin
  550. if FUpdateCount = 0 then SetUpdateState(true);
  551. inc(FUpdateCount);
  552. end;
  553. Procedure TStrings.EndUpdate;
  554. begin
  555. If FUpdateCount>0 then
  556. Dec(FUpdateCount);
  557. if FUpdateCount=0 then
  558. SetUpdateState(False);
  559. end;
  560. Function TStrings.Equals(Obj: TObject): Boolean;
  561. begin
  562. if Obj is TStrings then
  563. Result := Equals(TStrings(Obj))
  564. else
  565. Result := inherited Equals(Obj);
  566. end;
  567. Function TStrings.Equals(TheStrings: TStrings): Boolean;
  568. Var Runner,Nr : Longint;
  569. begin
  570. Result:=False;
  571. Nr:=Self.Count;
  572. if Nr<>TheStrings.Count then exit;
  573. For Runner:=0 to Nr-1 do
  574. If Strings[Runner]<>TheStrings[Runner] then exit;
  575. Result:=True;
  576. end;
  577. Procedure TStrings.Exchange(Index1, Index2: Integer);
  578. Var
  579. Obj : TObject;
  580. Str : String;
  581. begin
  582. Try
  583. beginUpdate;
  584. Obj:=Objects[Index1];
  585. Str:=Strings[Index1];
  586. Objects[Index1]:=Objects[Index2];
  587. Strings[Index1]:=Strings[Index2];
  588. Objects[Index2]:=Obj;
  589. Strings[Index2]:=Str;
  590. finally
  591. EndUpdate;
  592. end;
  593. end;
  594. function TStrings.GetEnumerator: TStringsEnumerator;
  595. begin
  596. Result:=TStringsEnumerator.Create(Self);
  597. end;
  598. Function TStrings.GetText: PChar;
  599. begin
  600. Result:=StrNew(Pchar(Self.Text));
  601. end;
  602. Function TStrings.DoCompareText(const s1,s2 : string) : PtrInt;
  603. begin
  604. result:=CompareText(s1,s2);
  605. end;
  606. Function TStrings.IndexOf(const S: string): Integer;
  607. begin
  608. Result:=0;
  609. While (Result<Count) and (DoCompareText(Strings[Result],S)<>0) do Result:=Result+1;
  610. if Result=Count then Result:=-1;
  611. end;
  612. Function TStrings.IndexOfName(const Name: string): Integer;
  613. Var
  614. len : longint;
  615. S : String;
  616. begin
  617. CheckSpecialChars;
  618. Result:=0;
  619. while (Result<Count) do
  620. begin
  621. S:=Strings[Result];
  622. len:=pos(FNameValueSeparator,S)-1;
  623. if (len>=0) and (DoCompareText(Name,Copy(S,1,Len))=0) then
  624. exit;
  625. inc(result);
  626. end;
  627. result:=-1;
  628. end;
  629. Function TStrings.IndexOfObject(AObject: TObject): Integer;
  630. begin
  631. Result:=0;
  632. While (Result<count) and (Objects[Result]<>AObject) do Result:=Result+1;
  633. If Result=Count then Result:=-1;
  634. end;
  635. Procedure TStrings.InsertObject(Index: Integer; const S: string;
  636. AObject: TObject);
  637. begin
  638. Insert (Index,S);
  639. Objects[Index]:=AObject;
  640. end;
  641. Procedure TStrings.LoadFromFile(const FileName: string);
  642. Var
  643. TheStream : TFileStream;
  644. begin
  645. TheStream:=TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite);
  646. try
  647. LoadFromStream(TheStream);
  648. finally
  649. TheStream.Free;
  650. end;
  651. end;
  652. Procedure TStrings.LoadFromStream(Stream: TStream);
  653. {
  654. Borlands method is no good, since a pipe for
  655. instance doesn't have a size.
  656. So we must do it the hard way.
  657. }
  658. Const
  659. BufSize = 1024;
  660. MaxGrow = 1 shl 29;
  661. Var
  662. Buffer : AnsiString;
  663. BytesRead,
  664. BufLen,
  665. I,BufDelta : Longint;
  666. begin
  667. // reread into a buffer
  668. try
  669. beginupdate;
  670. Buffer:='';
  671. BufLen:=0;
  672. I:=1;
  673. Repeat
  674. BufDelta:=BufSize*I;
  675. SetLength(Buffer,BufLen+BufDelta);
  676. BytesRead:=Stream.Read(Buffer[BufLen+1],BufDelta);
  677. inc(BufLen,BufDelta);
  678. If I<MaxGrow then
  679. I:=I shl 1;
  680. Until BytesRead<>BufDelta;
  681. SetLength(Buffer, BufLen-BufDelta+BytesRead);
  682. SetTextStr(Buffer);
  683. SetLength(Buffer,0);
  684. finally
  685. EndUpdate;
  686. end;
  687. end;
  688. Procedure TStrings.Move(CurIndex, NewIndex: Integer);
  689. Var
  690. Obj : TObject;
  691. Str : String;
  692. begin
  693. BeginUpdate;
  694. Obj:=Objects[CurIndex];
  695. Str:=Strings[CurIndex];
  696. Objects[CurIndex]:=Nil; // Prevent Delete from freeing.
  697. Delete(Curindex);
  698. InsertObject(NewIndex,Str,Obj);
  699. EndUpdate;
  700. end;
  701. Procedure TStrings.SaveToFile(const FileName: string);
  702. Var TheStream : TFileStream;
  703. begin
  704. TheStream:=TFileStream.Create(FileName,fmCreate);
  705. try
  706. SaveToStream(TheStream);
  707. finally
  708. TheStream.Free;
  709. end;
  710. end;
  711. Procedure TStrings.SaveToStream(Stream: TStream);
  712. Var
  713. S : String;
  714. begin
  715. S:=Text;
  716. if S = '' then Exit;
  717. Stream.WriteBuffer(Pointer(S)^,Length(S));
  718. end;
  719. Procedure TStrings.SetText(TheText: PChar);
  720. Var S : String;
  721. begin
  722. If TheText<>Nil then
  723. S:=StrPas(TheText)
  724. else
  725. S:='';
  726. SetTextStr(S);
  727. end;
  728. {****************************************************************************}
  729. {* TStringList *}
  730. {****************************************************************************}
  731. {$if defined(VER2_0) or not defined(FPC_TESTGENERICS)}
  732. Procedure TStringList.ExchangeItems(Index1, Index2: Integer);
  733. Var P1,P2 : Pointer;
  734. begin
  735. P1:=Pointer(Flist^[Index1].FString);
  736. P2:=Pointer(Flist^[Index1].FObject);
  737. Pointer(Flist^[Index1].Fstring):=Pointer(Flist^[Index2].Fstring);
  738. Pointer(Flist^[Index1].FObject):=Pointer(Flist^[Index2].FObject);
  739. Pointer(Flist^[Index2].Fstring):=P1;
  740. Pointer(Flist^[Index2].FObject):=P2;
  741. end;
  742. Procedure TStringList.Grow;
  743. Var
  744. NC : Integer;
  745. begin
  746. NC:=FCapacity;
  747. If NC>=256 then
  748. NC:=NC+(NC Div 4)
  749. else if NC=0 then
  750. NC:=4
  751. else
  752. NC:=NC*4;
  753. SetCapacity(NC);
  754. end;
  755. Procedure TStringList.InternalClear(FromIndex : Integer = 0; ClearOnly : Boolean = False);
  756. Var
  757. I: Integer;
  758. begin
  759. if FOwnsObjects then
  760. begin
  761. For I:=FromIndex to FCount-1 do
  762. begin
  763. Flist^[I].FString:='';
  764. freeandnil(Flist^[i].FObject);
  765. end;
  766. end
  767. else
  768. begin
  769. For I:=FromIndex to FCount-1 do
  770. Flist^[I].FString:='';
  771. end;
  772. FCount:=FromIndex;
  773. if Not ClearOnly then
  774. SetCapacity(0);
  775. end;
  776. Procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare);
  777. var
  778. Pivot, vL, vR: Integer;
  779. begin
  780. if R - L <= 1 then begin // a little bit of time saver
  781. if L < R then
  782. if CompareFn(Self, L, R) > 0 then
  783. ExchangeItems(L, R);
  784. Exit;
  785. end;
  786. vL := L;
  787. vR := R;
  788. Pivot := L + Random(R - L); // they say random is best
  789. while vL < vR do begin
  790. while (vL < Pivot) and (CompareFn(Self, vL, Pivot) <= 0) do
  791. Inc(vL);
  792. while (vR > Pivot) and (CompareFn(Self, vR, Pivot) > 0) do
  793. Dec(vR);
  794. ExchangeItems(vL, vR);
  795. if Pivot = vL then // swap pivot if we just hit it from one side
  796. Pivot := vR
  797. else if Pivot = vR then
  798. Pivot := vL;
  799. end;
  800. if Pivot - 1 >= L then
  801. QuickSort(L, Pivot - 1, CompareFn);
  802. if Pivot + 1 <= R then
  803. QuickSort(Pivot + 1, R, CompareFn);
  804. end;
  805. Procedure TStringList.InsertItem(Index: Integer; const S: string);
  806. begin
  807. InsertItem(Index, S, nil);
  808. end;
  809. Procedure TStringList.InsertItem(Index: Integer; const S: string; O: TObject);
  810. begin
  811. Changing;
  812. If FCount=Fcapacity then Grow;
  813. If Index<FCount then
  814. System.Move (FList^[Index],FList^[Index+1],
  815. (FCount-Index)*SizeOf(TStringItem));
  816. Pointer(Flist^[Index].Fstring):=Nil; // Needed to initialize...
  817. Flist^[Index].FString:=S;
  818. Flist^[Index].FObject:=O;
  819. Inc(FCount);
  820. Changed;
  821. end;
  822. Procedure TStringList.SetSorted(Value: Boolean);
  823. begin
  824. If FSorted<>Value then
  825. begin
  826. If Value then sort;
  827. FSorted:=VAlue
  828. end;
  829. end;
  830. Procedure TStringList.Changed;
  831. begin
  832. If (FUpdateCount=0) Then
  833. begin
  834. If Assigned(FOnChange) then
  835. FOnchange(Self);
  836. FPONotifyObservers(Self,ooChange,Nil);
  837. end;
  838. end;
  839. Procedure TStringList.Changing;
  840. begin
  841. If FUpdateCount=0 then
  842. if Assigned(FOnChanging) then
  843. FOnchanging(Self);
  844. end;
  845. Function TStringList.Get(Index: Integer): string;
  846. begin
  847. If (Index<0) or (INdex>=Fcount) then
  848. Error (SListIndexError,Index);
  849. Result:=Flist^[Index].FString;
  850. end;
  851. Function TStringList.GetCapacity: Integer;
  852. begin
  853. Result:=FCapacity;
  854. end;
  855. Function TStringList.GetCount: Integer;
  856. begin
  857. Result:=FCount;
  858. end;
  859. Function TStringList.GetObject(Index: Integer): TObject;
  860. begin
  861. If (Index<0) or (INdex>=Fcount) then
  862. Error (SListIndexError,Index);
  863. Result:=Flist^[Index].FObject;
  864. end;
  865. Procedure TStringList.Put(Index: Integer; const S: string);
  866. begin
  867. If Sorted then
  868. Error(SSortedListError,0);
  869. If (Index<0) or (INdex>=Fcount) then
  870. Error (SListIndexError,Index);
  871. Changing;
  872. Flist^[Index].FString:=S;
  873. Changed;
  874. end;
  875. Procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  876. begin
  877. If (Index<0) or (INdex>=Fcount) then
  878. Error (SListIndexError,Index);
  879. Changing;
  880. Flist^[Index].FObject:=AObject;
  881. Changed;
  882. end;
  883. Procedure TStringList.SetCapacity(NewCapacity: Integer);
  884. Var NewList : Pointer;
  885. I,MSize : Longint;
  886. begin
  887. If (NewCapacity<0) then
  888. Error (SListCapacityError,NewCapacity);
  889. If NewCapacity>FCapacity then
  890. begin
  891. GetMem (NewList,NewCapacity*SizeOf(TStringItem));
  892. If NewList=Nil then
  893. Error (SListCapacityError,NewCapacity);
  894. If Assigned(FList) then
  895. begin
  896. MSize:=FCapacity*Sizeof(TStringItem);
  897. System.Move (FList^,NewList^,MSize);
  898. FillWord (Pchar(NewList)[MSize],(NewCapacity-FCapacity)*WordRatio, 0);
  899. FreeMem (Flist,MSize);
  900. end;
  901. Flist:=NewList;
  902. FCapacity:=NewCapacity;
  903. end
  904. else if NewCapacity<FCapacity then
  905. begin
  906. if NewCapacity = 0 then
  907. begin
  908. FreeMem(FList);
  909. FList := nil;
  910. end else
  911. begin
  912. InternalClear(NewCapacity,True);
  913. GetMem(NewList, NewCapacity * SizeOf(TStringItem));
  914. System.Move(FList^, NewList^, NewCapacity * SizeOf(TStringItem));
  915. FreeMem(FList);
  916. FList := NewList;
  917. end;
  918. FCapacity:=NewCapacity;
  919. end;
  920. end;
  921. Procedure TStringList.SetUpdateState(Updating: Boolean);
  922. begin
  923. If Updating then
  924. Changing
  925. else
  926. Changed
  927. end;
  928. destructor TStringList.Destroy;
  929. begin
  930. InternalClear;
  931. Inherited destroy;
  932. end;
  933. Function TStringList.Add(const S: string): Integer;
  934. begin
  935. If Not Sorted then
  936. Result:=FCount
  937. else
  938. If Find (S,Result) then
  939. Case DUplicates of
  940. DupIgnore : Exit;
  941. DupError : Error(SDuplicateString,0)
  942. end;
  943. InsertItem (Result,S);
  944. end;
  945. Procedure TStringList.Clear;
  946. begin
  947. if FCount = 0 then Exit;
  948. Changing;
  949. InternalClear;
  950. Changed;
  951. end;
  952. Procedure TStringList.Delete(Index: Integer);
  953. begin
  954. If (Index<0) or (Index>=FCount) then
  955. Error(SlistINdexError,Index);
  956. Changing;
  957. Flist^[Index].FString:='';
  958. if FOwnsObjects then
  959. FreeAndNil(Flist^[Index].FObject);
  960. Dec(FCount);
  961. If Index<FCount then
  962. System.Move(Flist^[Index+1],
  963. Flist^[Index],
  964. (Fcount-Index)*SizeOf(TStringItem));
  965. Changed;
  966. end;
  967. Procedure TStringList.Exchange(Index1, Index2: Integer);
  968. begin
  969. If (Index1<0) or (Index1>=FCount) then
  970. Error(SListIndexError,Index1);
  971. If (Index2<0) or (Index2>=FCount) then
  972. Error(SListIndexError,Index2);
  973. Changing;
  974. ExchangeItems(Index1,Index2);
  975. changed;
  976. end;
  977. procedure TStringList.SetCaseSensitive(b : boolean);
  978. begin
  979. if b=FCaseSensitive then
  980. Exit;
  981. FCaseSensitive:=b;
  982. if FSorted then
  983. begin
  984. FForceSort:=True;
  985. sort;
  986. FForceSort:=False;
  987. end;
  988. end;
  989. Function TStringList.DoCompareText(const s1,s2 : string) : PtrInt;
  990. begin
  991. if FCaseSensitive then
  992. result:=AnsiCompareStr(s1,s2)
  993. else
  994. result:=AnsiCompareText(s1,s2);
  995. end;
  996. function TStringList.CompareStrings(const s1,s2 : string) : Integer;
  997. begin
  998. Result := DoCompareText(s1, s2);
  999. end;
  1000. Function TStringList.Find(const S: string; Out Index: Integer): Boolean;
  1001. var
  1002. L, R, I: Integer;
  1003. CompareRes: PtrInt;
  1004. begin
  1005. Result := false;
  1006. // Use binary search.
  1007. L := 0;
  1008. R := Count - 1;
  1009. while (L<=R) do
  1010. begin
  1011. I := L + (R - L) div 2;
  1012. CompareRes := DoCompareText(S, Flist^[I].FString);
  1013. if (CompareRes>0) then
  1014. L := I+1
  1015. else begin
  1016. R := I-1;
  1017. if (CompareRes=0) then begin
  1018. Result := true;
  1019. if (Duplicates<>dupAccept) then
  1020. L := I; // forces end of while loop
  1021. end;
  1022. end;
  1023. end;
  1024. Index := L;
  1025. end;
  1026. Function TStringList.IndexOf(const S: string): Integer;
  1027. begin
  1028. If Not Sorted then
  1029. Result:=Inherited indexOf(S)
  1030. else
  1031. // faster using binary search...
  1032. If Not Find (S,Result) then
  1033. Result:=-1;
  1034. end;
  1035. Procedure TStringList.Insert(Index: Integer; const S: string);
  1036. begin
  1037. If Sorted then
  1038. Error (SSortedListError,0)
  1039. else
  1040. If (Index<0) or (Index>FCount) then
  1041. Error (SListIndexError,Index)
  1042. else
  1043. InsertItem (Index,S);
  1044. end;
  1045. Procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1046. begin
  1047. If (FForceSort or (Not Sorted)) and (FCount>1) then
  1048. begin
  1049. Changing;
  1050. QuickSort(0,FCount-1, CompareFn);
  1051. Changed;
  1052. end;
  1053. end;
  1054. function StringListAnsiCompare(List: TStringList; Index1, Index: Integer): Integer;
  1055. begin
  1056. Result := List.DoCompareText(List.FList^[Index1].FString,
  1057. List.FList^[Index].FString);
  1058. end;
  1059. Procedure TStringList.Sort;
  1060. begin
  1061. CustomSort(@StringListAnsiCompare);
  1062. end;
  1063. {$else}
  1064. { generics based implementation of TStringList follows }
  1065. function StringListAnsiCompare(List: TStringList; Index1, Index2: Integer): Integer;
  1066. begin
  1067. Result := List.DoCompareText(List.Strings[Index1], List.Strings[Index2]);
  1068. end;
  1069. constructor TStringList.Create;
  1070. begin
  1071. inherited;
  1072. FOwnsObjects:=false;
  1073. FMap := TFPStrObjMap.Create;
  1074. FMap.OnPtrCompare := @MapPtrCompare;
  1075. FOnCompareText := @DefaultCompareText;
  1076. NameValueSeparator:='=';
  1077. CheckSpecialChars;
  1078. end;
  1079. destructor TStringList.Destroy;
  1080. begin
  1081. FMap.Free;
  1082. inherited;
  1083. end;
  1084. function TStringList.GetDuplicates: TDuplicates;
  1085. begin
  1086. Result := FMap.Duplicates;
  1087. end;
  1088. function TStringList.GetSorted: boolean;
  1089. begin
  1090. Result := FMap.Sorted;
  1091. end;
  1092. procedure TStringList.SetDuplicates(NewDuplicates: TDuplicates);
  1093. begin
  1094. FMap.Duplicates := NewDuplicates;
  1095. end;
  1096. procedure TStringList.SetSorted(NewSorted: Boolean);
  1097. begin
  1098. FMap.Sorted := NewSorted;
  1099. end;
  1100. procedure TStringList.Changed;
  1101. begin
  1102. if FUpdateCount = 0 then
  1103. if Assigned(FOnChange) then
  1104. FOnChange(Self);
  1105. end;
  1106. procedure TStringList.Changing;
  1107. begin
  1108. if FUpdateCount = 0 then
  1109. if Assigned(FOnChanging) then
  1110. FOnChanging(Self);
  1111. end;
  1112. function TStringList.Get(Index: Integer): string;
  1113. begin
  1114. Result := FMap.Keys[Index];
  1115. end;
  1116. function TStringList.GetCapacity: Integer;
  1117. begin
  1118. Result := FMap.Capacity;
  1119. end;
  1120. function TStringList.GetCount: Integer;
  1121. begin
  1122. Result := FMap.Count;
  1123. end;
  1124. function TStringList.GetObject(Index: Integer): TObject;
  1125. begin
  1126. Result := FMap.Data[Index];
  1127. end;
  1128. procedure TStringList.Put(Index: Integer; const S: string);
  1129. begin
  1130. Changing;
  1131. FMap.Keys[Index] := S;
  1132. Changed;
  1133. end;
  1134. procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  1135. begin
  1136. Changing;
  1137. FMap.Data[Index] := AObject;
  1138. Changed;
  1139. end;
  1140. procedure TStringList.SetCapacity(NewCapacity: Integer);
  1141. begin
  1142. FMap.Capacity := NewCapacity;
  1143. end;
  1144. procedure TStringList.SetUpdateState(Updating: Boolean);
  1145. begin
  1146. if Updating then
  1147. Changing
  1148. else
  1149. Changed
  1150. end;
  1151. function TStringList.Add(const S: string): Integer;
  1152. begin
  1153. Result := FMap.Add(S);
  1154. end;
  1155. procedure TStringList.Clear;
  1156. begin
  1157. if FMap.Count = 0 then exit;
  1158. Changing;
  1159. FMap.Clear;
  1160. Changed;
  1161. end;
  1162. procedure TStringList.Delete(Index: Integer);
  1163. begin
  1164. if (Index < 0) or (Index >= FMap.Count) then
  1165. Error(SListIndexError, Index);
  1166. Changing;
  1167. FMap.Delete(Index);
  1168. Changed;
  1169. end;
  1170. procedure TStringList.Exchange(Index1, Index2: Integer);
  1171. begin
  1172. if (Index1 < 0) or (Index1 >= FMap.Count) then
  1173. Error(SListIndexError, Index1);
  1174. if (Index2 < 0) or (Index2 >= FMap.Count) then
  1175. Error(SListIndexError, Index2);
  1176. Changing;
  1177. FMap.InternalExchange(Index1, Index2);
  1178. Changed;
  1179. end;
  1180. procedure TStringList.SetCaseSensitive(NewSensitive: Boolean);
  1181. begin
  1182. if NewSensitive <> FCaseSensitive then
  1183. begin
  1184. FCaseSensitive := NewSensitive;
  1185. if Sorted then
  1186. Sort;
  1187. end;
  1188. end;
  1189. function TStringList.MapPtrCompare(Key1, Key2: Pointer): Integer;
  1190. begin
  1191. Result := FOnCompareText(string(Key1^), string(Key2^));
  1192. end;
  1193. function TStringList.DefaultCompareText(const s1, s2: string): PtrInt;
  1194. begin
  1195. if FCaseSensitive then
  1196. Result := AnsiCompareStr(s1, s2)
  1197. else
  1198. Result := AnsiCompareText(s1, s2);
  1199. end;
  1200. function TStringList.DoCompareText(const s1, s2: string): PtrInt;
  1201. begin
  1202. Result := FOnCompareText(s1, s2);
  1203. end;
  1204. function TStringList.Find(const S: string; var Index: Integer): Boolean;
  1205. begin
  1206. Result := FMap.Find(S, Index);
  1207. end;
  1208. function TStringList.IndexOf(const S: string): Integer;
  1209. begin
  1210. Result := FMap.IndexOf(S);
  1211. end;
  1212. procedure TStringList.Insert(Index: Integer; const S: string);
  1213. begin
  1214. if not Sorted and (0 <= Index) and (Index < FMap.Count) then
  1215. Changing;
  1216. FMap.InsertKey(Index, S);
  1217. Changed;
  1218. end;
  1219. procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare);
  1220. var
  1221. I, J, Pivot: Integer;
  1222. begin
  1223. repeat
  1224. I := L;
  1225. J := R;
  1226. Pivot := (L + R) div 2;
  1227. repeat
  1228. while CompareFn(Self, I, Pivot) < 0 do Inc(I);
  1229. while CompareFn(Self, J, Pivot) > 0 do Dec(J);
  1230. if I <= J then
  1231. begin
  1232. FMap.InternalExchange(I, J); // No check, indices are correct.
  1233. if Pivot = I then
  1234. Pivot := J
  1235. else if Pivot = J then
  1236. Pivot := I;
  1237. Inc(I);
  1238. Dec(j);
  1239. end;
  1240. until I > J;
  1241. if L < J then
  1242. QuickSort(L,J, CompareFn);
  1243. L := I;
  1244. until I >= R;
  1245. end;
  1246. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1247. begin
  1248. if not Sorted and (FMap.Count > 1) then
  1249. begin
  1250. Changing;
  1251. QuickSort(0, FMap.Count-1, CompareFn);
  1252. Changed;
  1253. end;
  1254. end;
  1255. procedure TStringList.Sort;
  1256. begin
  1257. if not Sorted and (FMap.Count > 1) then
  1258. begin
  1259. Changing;
  1260. FMap.Sort;
  1261. Changed;
  1262. end;
  1263. end;
  1264. {$endif}