stringl.inc 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595
  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.SetTextStr(const Value: string);
  456. Var
  457. S : String;
  458. P : Integer;
  459. begin
  460. Try
  461. beginUpdate;
  462. Clear;
  463. P:=1;
  464. While GetNextLine (Value,S,P) do
  465. Add(S);
  466. finally
  467. EndUpdate;
  468. end;
  469. end;
  470. Procedure TStrings.SetUpdateState(Updating: Boolean);
  471. begin
  472. FPONotifyObservers(Self,ooChange,Nil);
  473. end;
  474. destructor TSTrings.Destroy;
  475. begin
  476. inherited destroy;
  477. end;
  478. Function TStrings.Add(const S: string): Integer;
  479. begin
  480. Result:=Count;
  481. Insert (Count,S);
  482. end;
  483. Function TStrings.AddObject(const S: string; AObject: TObject): Integer;
  484. begin
  485. Result:=Add(S);
  486. Objects[result]:=AObject;
  487. end;
  488. Procedure TStrings.Append(const S: string);
  489. begin
  490. Add (S);
  491. end;
  492. Procedure TStrings.AddStrings(TheStrings: TStrings);
  493. Var Runner : longint;
  494. begin
  495. try
  496. beginupdate;
  497. For Runner:=0 to TheStrings.Count-1 do
  498. self.AddObject (Thestrings[Runner],TheStrings.Objects[Runner]);
  499. finally
  500. EndUpdate;
  501. end;
  502. end;
  503. Procedure TStrings.AddStrings(const TheStrings: array of string);
  504. Var Runner : longint;
  505. begin
  506. try
  507. beginupdate;
  508. if Count + High(TheStrings)+1 > Capacity then
  509. Capacity := Count + High(TheStrings)+1;
  510. For Runner:=Low(TheStrings) to High(TheStrings) do
  511. self.Add(Thestrings[Runner]);
  512. finally
  513. EndUpdate;
  514. end;
  515. end;
  516. Procedure TStrings.Assign(Source: TPersistent);
  517. Var
  518. S : TStrings;
  519. begin
  520. If Source is TStrings then
  521. begin
  522. S:=TStrings(Source);
  523. BeginUpdate;
  524. Try
  525. clear;
  526. FSpecialCharsInited:=S.FSpecialCharsInited;
  527. FQuoteChar:=S.FQuoteChar;
  528. FDelimiter:=S.FDelimiter;
  529. FNameValueSeparator:=S.FNameValueSeparator;
  530. FLBS:=S.FLBS;
  531. AddStrings(S);
  532. finally
  533. EndUpdate;
  534. end;
  535. end
  536. else
  537. Inherited Assign(Source);
  538. end;
  539. Procedure TStrings.BeginUpdate;
  540. begin
  541. if FUpdateCount = 0 then SetUpdateState(true);
  542. inc(FUpdateCount);
  543. end;
  544. Procedure TStrings.EndUpdate;
  545. begin
  546. If FUpdateCount>0 then
  547. Dec(FUpdateCount);
  548. if FUpdateCount=0 then
  549. SetUpdateState(False);
  550. end;
  551. Function TStrings.Equals(Obj: TObject): Boolean;
  552. begin
  553. if Obj is TStrings then
  554. Result := Equals(TStrings(Obj))
  555. else
  556. Result := inherited Equals(Obj);
  557. end;
  558. Function TStrings.Equals(TheStrings: TStrings): Boolean;
  559. Var Runner,Nr : Longint;
  560. begin
  561. Result:=False;
  562. Nr:=Self.Count;
  563. if Nr<>TheStrings.Count then exit;
  564. For Runner:=0 to Nr-1 do
  565. If Strings[Runner]<>TheStrings[Runner] then exit;
  566. Result:=True;
  567. end;
  568. Procedure TStrings.Exchange(Index1, Index2: Integer);
  569. Var
  570. Obj : TObject;
  571. Str : String;
  572. begin
  573. Try
  574. beginUpdate;
  575. Obj:=Objects[Index1];
  576. Str:=Strings[Index1];
  577. Objects[Index1]:=Objects[Index2];
  578. Strings[Index1]:=Strings[Index2];
  579. Objects[Index2]:=Obj;
  580. Strings[Index2]:=Str;
  581. finally
  582. EndUpdate;
  583. end;
  584. end;
  585. function TStrings.GetEnumerator: TStringsEnumerator;
  586. begin
  587. Result:=TStringsEnumerator.Create(Self);
  588. end;
  589. Function TStrings.GetText: PChar;
  590. begin
  591. Result:=StrNew(Pchar(Self.Text));
  592. end;
  593. Function TStrings.DoCompareText(const s1,s2 : string) : PtrInt;
  594. begin
  595. result:=CompareText(s1,s2);
  596. end;
  597. Function TStrings.IndexOf(const S: string): Integer;
  598. begin
  599. Result:=0;
  600. While (Result<Count) and (DoCompareText(Strings[Result],S)<>0) do Result:=Result+1;
  601. if Result=Count then Result:=-1;
  602. end;
  603. Function TStrings.IndexOfName(const Name: string): Integer;
  604. Var
  605. len : longint;
  606. S : String;
  607. begin
  608. CheckSpecialChars;
  609. Result:=0;
  610. while (Result<Count) do
  611. begin
  612. S:=Strings[Result];
  613. len:=pos(FNameValueSeparator,S)-1;
  614. if (len>0) and (DoCompareText(Name,Copy(S,1,Len))=0) then
  615. exit;
  616. inc(result);
  617. end;
  618. result:=-1;
  619. end;
  620. Function TStrings.IndexOfObject(AObject: TObject): Integer;
  621. begin
  622. Result:=0;
  623. While (Result<count) and (Objects[Result]<>AObject) do Result:=Result+1;
  624. If Result=Count then Result:=-1;
  625. end;
  626. Procedure TStrings.InsertObject(Index: Integer; const S: string;
  627. AObject: TObject);
  628. begin
  629. Insert (Index,S);
  630. Objects[Index]:=AObject;
  631. end;
  632. Procedure TStrings.LoadFromFile(const FileName: string);
  633. Var
  634. TheStream : TFileStream;
  635. begin
  636. TheStream:=TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite);
  637. try
  638. LoadFromStream(TheStream);
  639. finally
  640. TheStream.Free;
  641. end;
  642. end;
  643. Procedure TStrings.LoadFromStream(Stream: TStream);
  644. {
  645. Borlands method is no good, since a pipe for
  646. instance doesn't have a size.
  647. So we must do it the hard way.
  648. }
  649. Const
  650. BufSize = 1024;
  651. MaxGrow = 1 shl 29;
  652. Var
  653. Buffer : AnsiString;
  654. BytesRead,
  655. BufLen,
  656. I,BufDelta : Longint;
  657. begin
  658. // reread into a buffer
  659. try
  660. beginupdate;
  661. Buffer:='';
  662. BufLen:=0;
  663. I:=1;
  664. Repeat
  665. BufDelta:=BufSize*I;
  666. SetLength(Buffer,BufLen+BufDelta);
  667. BytesRead:=Stream.Read(Buffer[BufLen+1],BufDelta);
  668. inc(BufLen,BufDelta);
  669. If I<MaxGrow then
  670. I:=I shl 1;
  671. Until BytesRead<>BufDelta;
  672. SetLength(Buffer, BufLen-BufDelta+BytesRead);
  673. SetTextStr(Buffer);
  674. SetLength(Buffer,0);
  675. finally
  676. EndUpdate;
  677. end;
  678. end;
  679. Procedure TStrings.Move(CurIndex, NewIndex: Integer);
  680. Var
  681. Obj : TObject;
  682. Str : String;
  683. begin
  684. BeginUpdate;
  685. Obj:=Objects[CurIndex];
  686. Str:=Strings[CurIndex];
  687. Delete(Curindex);
  688. InsertObject(NewIndex,Str,Obj);
  689. EndUpdate;
  690. end;
  691. Procedure TStrings.SaveToFile(const FileName: string);
  692. Var TheStream : TFileStream;
  693. begin
  694. TheStream:=TFileStream.Create(FileName,fmCreate);
  695. try
  696. SaveToStream(TheStream);
  697. finally
  698. TheStream.Free;
  699. end;
  700. end;
  701. Procedure TStrings.SaveToStream(Stream: TStream);
  702. Var
  703. S : String;
  704. begin
  705. S:=Text;
  706. if S = '' then Exit;
  707. Stream.WriteBuffer(Pointer(S)^,Length(S));
  708. end;
  709. Procedure TStrings.SetText(TheText: PChar);
  710. Var S : String;
  711. begin
  712. If TheText<>Nil then
  713. S:=StrPas(TheText)
  714. else
  715. S:='';
  716. SetTextStr(S);
  717. end;
  718. {****************************************************************************}
  719. {* TStringList *}
  720. {****************************************************************************}
  721. {$if defined(VER2_0) or not defined(FPC_TESTGENERICS)}
  722. Procedure TStringList.ExchangeItems(Index1, Index2: Integer);
  723. Var P1,P2 : Pointer;
  724. begin
  725. P1:=Pointer(Flist^[Index1].FString);
  726. P2:=Pointer(Flist^[Index1].FObject);
  727. Pointer(Flist^[Index1].Fstring):=Pointer(Flist^[Index2].Fstring);
  728. Pointer(Flist^[Index1].FObject):=Pointer(Flist^[Index2].FObject);
  729. Pointer(Flist^[Index2].Fstring):=P1;
  730. Pointer(Flist^[Index2].FObject):=P2;
  731. end;
  732. Procedure TStringList.Grow;
  733. Var
  734. NC : Integer;
  735. begin
  736. NC:=FCapacity;
  737. If NC>=256 then
  738. NC:=NC+(NC Div 4)
  739. else if NC=0 then
  740. NC:=4
  741. else
  742. NC:=NC*4;
  743. SetCapacity(NC);
  744. end;
  745. Procedure TStringList.InternalClear;
  746. Var
  747. I: Integer;
  748. begin
  749. if FOwnsObjects then
  750. begin
  751. For I:=0 to FCount-1 do
  752. begin
  753. Flist^[I].FString:='';
  754. freeandnil(Flist^[i].FObject);
  755. end;
  756. end
  757. else
  758. begin
  759. For I:=0 to FCount-1 do
  760. Flist^[I].FString:='';
  761. end;
  762. FCount:=0;
  763. SetCapacity(0);
  764. end;
  765. Procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare);
  766. var
  767. Pivot, vL, vR: Integer;
  768. begin
  769. if R - L <= 1 then begin // a little bit of time saver
  770. if L < R then
  771. if CompareFn(Self, L, R) > 0 then
  772. ExchangeItems(L, R);
  773. Exit;
  774. end;
  775. vL := L;
  776. vR := R;
  777. Pivot := L + Random(R - L); // they say random is best
  778. while vL < vR do begin
  779. while (vL < Pivot) and (CompareFn(Self, vL, Pivot) <= 0) do
  780. Inc(vL);
  781. while (vR > Pivot) and (CompareFn(Self, vR, Pivot) > 0) do
  782. Dec(vR);
  783. ExchangeItems(vL, vR);
  784. if Pivot = vL then // swap pivot if we just hit it from one side
  785. Pivot := vR
  786. else if Pivot = vR then
  787. Pivot := vL;
  788. end;
  789. if Pivot - 1 >= L then
  790. QuickSort(L, Pivot - 1, CompareFn);
  791. if Pivot + 1 <= R then
  792. QuickSort(Pivot + 1, R, CompareFn);
  793. end;
  794. Procedure TStringList.InsertItem(Index: Integer; const S: string);
  795. begin
  796. Changing;
  797. If FCount=Fcapacity then Grow;
  798. If Index<FCount then
  799. System.Move (FList^[Index],FList^[Index+1],
  800. (FCount-Index)*SizeOf(TStringItem));
  801. Pointer(Flist^[Index].Fstring):=Nil; // Needed to initialize...
  802. Flist^[Index].FString:=S;
  803. Flist^[Index].Fobject:=Nil;
  804. Inc(FCount);
  805. Changed;
  806. end;
  807. Procedure TStringList.InsertItem(Index: Integer; const S: string; O: TObject);
  808. begin
  809. Changing;
  810. If FCount=Fcapacity then Grow;
  811. If Index<FCount then
  812. System.Move (FList^[Index],FList^[Index+1],
  813. (FCount-Index)*SizeOf(TStringItem));
  814. Pointer(Flist^[Index].Fstring):=Nil; // Needed to initialize...
  815. Flist^[Index].FString:=S;
  816. Flist^[Index].FObject:=O;
  817. Inc(FCount);
  818. Changed;
  819. end;
  820. Procedure TStringList.SetSorted(Value: Boolean);
  821. begin
  822. If FSorted<>Value then
  823. begin
  824. If Value then sort;
  825. FSorted:=VAlue
  826. end;
  827. end;
  828. Procedure TStringList.Changed;
  829. begin
  830. If (FUpdateCount=0) Then
  831. begin
  832. If Assigned(FOnChange) then
  833. FOnchange(Self);
  834. FPONotifyObservers(Self,ooChange,Nil);
  835. end;
  836. end;
  837. Procedure TStringList.Changing;
  838. begin
  839. If FUpdateCount=0 then
  840. if Assigned(FOnChanging) then
  841. FOnchanging(Self);
  842. end;
  843. Function TStringList.Get(Index: Integer): string;
  844. begin
  845. If (Index<0) or (INdex>=Fcount) then
  846. Error (SListIndexError,Index);
  847. Result:=Flist^[Index].FString;
  848. end;
  849. Function TStringList.GetCapacity: Integer;
  850. begin
  851. Result:=FCapacity;
  852. end;
  853. Function TStringList.GetCount: Integer;
  854. begin
  855. Result:=FCount;
  856. end;
  857. Function TStringList.GetObject(Index: Integer): TObject;
  858. begin
  859. If (Index<0) or (INdex>=Fcount) then
  860. Error (SListIndexError,Index);
  861. Result:=Flist^[Index].FObject;
  862. end;
  863. Procedure TStringList.Put(Index: Integer; const S: string);
  864. begin
  865. If Sorted then
  866. Error(SSortedListError,0);
  867. If (Index<0) or (INdex>=Fcount) then
  868. Error (SListIndexError,Index);
  869. Changing;
  870. Flist^[Index].FString:=S;
  871. Changed;
  872. end;
  873. Procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  874. begin
  875. If (Index<0) or (INdex>=Fcount) then
  876. Error (SListIndexError,Index);
  877. Changing;
  878. Flist^[Index].FObject:=AObject;
  879. Changed;
  880. end;
  881. Procedure TStringList.SetCapacity(NewCapacity: Integer);
  882. Var NewList : Pointer;
  883. MSize : Longint;
  884. begin
  885. If (NewCapacity<0) then
  886. Error (SListCapacityError,NewCapacity);
  887. If NewCapacity>FCapacity then
  888. begin
  889. GetMem (NewList,NewCapacity*SizeOf(TStringItem));
  890. If NewList=Nil then
  891. Error (SListCapacityError,NewCapacity);
  892. If Assigned(FList) then
  893. begin
  894. MSize:=FCapacity*Sizeof(TStringItem);
  895. System.Move (FList^,NewList^,MSize);
  896. FillWord (Pchar(NewList)[MSize],(NewCapacity-FCapacity)*WordRatio, 0);
  897. FreeMem (Flist,MSize);
  898. end;
  899. Flist:=NewList;
  900. FCapacity:=NewCapacity;
  901. end
  902. else if NewCapacity<FCapacity then
  903. begin
  904. if NewCapacity = 0 then
  905. begin
  906. FreeMem(FList);
  907. FList := nil;
  908. end else
  909. begin
  910. GetMem(NewList, NewCapacity * SizeOf(TStringItem));
  911. System.Move(FList^, NewList^, NewCapacity * SizeOf(TStringItem));
  912. FreeMem(FList);
  913. FList := NewList;
  914. end;
  915. FCapacity:=NewCapacity;
  916. end;
  917. end;
  918. Procedure TStringList.SetUpdateState(Updating: Boolean);
  919. begin
  920. If Updating then
  921. Changing
  922. else
  923. Changed
  924. end;
  925. destructor TStringList.Destroy;
  926. begin
  927. InternalClear;
  928. Inherited destroy;
  929. end;
  930. Function TStringList.Add(const S: string): Integer;
  931. begin
  932. If Not Sorted then
  933. Result:=FCount
  934. else
  935. If Find (S,Result) then
  936. Case DUplicates of
  937. DupIgnore : Exit;
  938. DupError : Error(SDuplicateString,0)
  939. end;
  940. InsertItem (Result,S);
  941. end;
  942. Procedure TStringList.Clear;
  943. begin
  944. if FCount = 0 then Exit;
  945. Changing;
  946. InternalClear;
  947. Changed;
  948. end;
  949. Procedure TStringList.Delete(Index: Integer);
  950. begin
  951. If (Index<0) or (Index>=FCount) then
  952. Error(SlistINdexError,Index);
  953. Changing;
  954. Flist^[Index].FString:='';
  955. if FOwnsObjects then
  956. FreeAndNil(Flist^[Index].FObject);
  957. Dec(FCount);
  958. If Index<FCount then
  959. System.Move(Flist^[Index+1],
  960. Flist^[Index],
  961. (Fcount-Index)*SizeOf(TStringItem));
  962. Changed;
  963. end;
  964. Procedure TStringList.Exchange(Index1, Index2: Integer);
  965. begin
  966. If (Index1<0) or (Index1>=FCount) then
  967. Error(SListIndexError,Index1);
  968. If (Index2<0) or (Index2>=FCount) then
  969. Error(SListIndexError,Index2);
  970. Changing;
  971. ExchangeItems(Index1,Index2);
  972. changed;
  973. end;
  974. procedure TStringList.SetCaseSensitive(b : boolean);
  975. begin
  976. if b<>FCaseSensitive then
  977. begin
  978. FCaseSensitive:=b;
  979. if FSorted then
  980. sort;
  981. end;
  982. end;
  983. Function TStringList.DoCompareText(const s1,s2 : string) : PtrInt;
  984. begin
  985. if FCaseSensitive then
  986. result:=AnsiCompareStr(s1,s2)
  987. else
  988. result:=AnsiCompareText(s1,s2);
  989. end;
  990. Function TStringList.Find(const S: string; Out Index: Integer): Boolean;
  991. var
  992. L, R, I: Integer;
  993. CompareRes: PtrInt;
  994. begin
  995. Result := false;
  996. // Use binary search.
  997. L := 0;
  998. R := Count - 1;
  999. while (L<=R) do
  1000. begin
  1001. I := L + (R - L) div 2;
  1002. CompareRes := DoCompareText(S, Flist^[I].FString);
  1003. if (CompareRes>0) then
  1004. L := I+1
  1005. else begin
  1006. R := I-1;
  1007. if (CompareRes=0) then begin
  1008. Result := true;
  1009. if (Duplicates<>dupAccept) then
  1010. L := I; // forces end of while loop
  1011. end;
  1012. end;
  1013. end;
  1014. Index := L;
  1015. end;
  1016. Function TStringList.IndexOf(const S: string): Integer;
  1017. begin
  1018. If Not Sorted then
  1019. Result:=Inherited indexOf(S)
  1020. else
  1021. // faster using binary search...
  1022. If Not Find (S,Result) then
  1023. Result:=-1;
  1024. end;
  1025. Procedure TStringList.Insert(Index: Integer; const S: string);
  1026. begin
  1027. If Sorted then
  1028. Error (SSortedListError,0)
  1029. else
  1030. If (Index<0) or (Index>FCount) then
  1031. Error (SListIndexError,Index)
  1032. else
  1033. InsertItem (Index,S);
  1034. end;
  1035. Procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1036. begin
  1037. If Not Sorted and (FCount>1) then
  1038. begin
  1039. Changing;
  1040. QuickSort(0,FCount-1, CompareFn);
  1041. Changed;
  1042. end;
  1043. end;
  1044. function StringListAnsiCompare(List: TStringList; Index1, Index: Integer): Integer;
  1045. begin
  1046. Result := List.DoCompareText(List.FList^[Index1].FString,
  1047. List.FList^[Index].FString);
  1048. end;
  1049. Procedure TStringList.Sort;
  1050. begin
  1051. CustomSort(@StringListAnsiCompare);
  1052. end;
  1053. {$else}
  1054. { generics based implementation of TStringList follows }
  1055. function StringListAnsiCompare(List: TStringList; Index1, Index2: Integer): Integer;
  1056. begin
  1057. Result := List.DoCompareText(List.Strings[Index1], List.Strings[Index2]);
  1058. end;
  1059. constructor TStringList.Create;
  1060. begin
  1061. inherited;
  1062. FOwnsObjects:=false;
  1063. FMap := TFPStrObjMap.Create;
  1064. FMap.OnPtrCompare := @MapPtrCompare;
  1065. FOnCompareText := @DefaultCompareText;
  1066. CheckSpecialChars;
  1067. end;
  1068. destructor TStringList.Destroy;
  1069. begin
  1070. FMap.Free;
  1071. inherited;
  1072. end;
  1073. function TStringList.GetDuplicates: TDuplicates;
  1074. begin
  1075. Result := FMap.Duplicates;
  1076. end;
  1077. function TStringList.GetSorted: boolean;
  1078. begin
  1079. Result := FMap.Sorted;
  1080. end;
  1081. procedure TStringList.SetDuplicates(NewDuplicates: TDuplicates);
  1082. begin
  1083. FMap.Duplicates := NewDuplicates;
  1084. end;
  1085. procedure TStringList.SetSorted(NewSorted: Boolean);
  1086. begin
  1087. FMap.Sorted := NewSorted;
  1088. end;
  1089. procedure TStringList.Changed;
  1090. begin
  1091. if FUpdateCount = 0 then
  1092. if Assigned(FOnChange) then
  1093. FOnChange(Self);
  1094. end;
  1095. procedure TStringList.Changing;
  1096. begin
  1097. if FUpdateCount = 0 then
  1098. if Assigned(FOnChanging) then
  1099. FOnChanging(Self);
  1100. end;
  1101. function TStringList.Get(Index: Integer): string;
  1102. begin
  1103. Result := FMap.Keys[Index];
  1104. end;
  1105. function TStringList.GetCapacity: Integer;
  1106. begin
  1107. Result := FMap.Capacity;
  1108. end;
  1109. function TStringList.GetCount: Integer;
  1110. begin
  1111. Result := FMap.Count;
  1112. end;
  1113. function TStringList.GetObject(Index: Integer): TObject;
  1114. begin
  1115. Result := FMap.Data[Index];
  1116. end;
  1117. procedure TStringList.Put(Index: Integer; const S: string);
  1118. begin
  1119. Changing;
  1120. FMap.Keys[Index] := S;
  1121. Changed;
  1122. end;
  1123. procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  1124. begin
  1125. Changing;
  1126. FMap.Data[Index] := AObject;
  1127. Changed;
  1128. end;
  1129. procedure TStringList.SetCapacity(NewCapacity: Integer);
  1130. begin
  1131. FMap.Capacity := NewCapacity;
  1132. end;
  1133. procedure TStringList.SetUpdateState(Updating: Boolean);
  1134. begin
  1135. if Updating then
  1136. Changing
  1137. else
  1138. Changed
  1139. end;
  1140. function TStringList.Add(const S: string): Integer;
  1141. begin
  1142. Result := FMap.Add(S);
  1143. end;
  1144. procedure TStringList.Clear;
  1145. begin
  1146. if FMap.Count = 0 then exit;
  1147. Changing;
  1148. FMap.Clear;
  1149. Changed;
  1150. end;
  1151. procedure TStringList.Delete(Index: Integer);
  1152. begin
  1153. if (Index < 0) or (Index >= FMap.Count) then
  1154. Error(SListIndexError, Index);
  1155. Changing;
  1156. FMap.Delete(Index);
  1157. Changed;
  1158. end;
  1159. procedure TStringList.Exchange(Index1, Index2: Integer);
  1160. begin
  1161. if (Index1 < 0) or (Index1 >= FMap.Count) then
  1162. Error(SListIndexError, Index1);
  1163. if (Index2 < 0) or (Index2 >= FMap.Count) then
  1164. Error(SListIndexError, Index2);
  1165. Changing;
  1166. FMap.InternalExchange(Index1, Index2);
  1167. Changed;
  1168. end;
  1169. procedure TStringList.SetCaseSensitive(NewSensitive: Boolean);
  1170. begin
  1171. if NewSensitive <> FCaseSensitive then
  1172. begin
  1173. FCaseSensitive := NewSensitive;
  1174. if Sorted then
  1175. Sort;
  1176. end;
  1177. end;
  1178. function TStringList.MapPtrCompare(Key1, Key2: Pointer): Integer;
  1179. begin
  1180. Result := FOnCompareText(string(Key1^), string(Key2^));
  1181. end;
  1182. function TStringList.DefaultCompareText(const s1, s2: string): PtrInt;
  1183. begin
  1184. if FCaseSensitive then
  1185. Result := AnsiCompareStr(s1, s2)
  1186. else
  1187. Result := AnsiCompareText(s1, s2);
  1188. end;
  1189. function TStringList.DoCompareText(const s1, s2: string): PtrInt;
  1190. begin
  1191. Result := FOnCompareText(s1, s2);
  1192. end;
  1193. function TStringList.Find(const S: string; var Index: Integer): Boolean;
  1194. begin
  1195. Result := FMap.Find(S, Index);
  1196. end;
  1197. function TStringList.IndexOf(const S: string): Integer;
  1198. begin
  1199. Result := FMap.IndexOf(S);
  1200. end;
  1201. procedure TStringList.Insert(Index: Integer; const S: string);
  1202. begin
  1203. if not Sorted and (0 <= Index) and (Index < FMap.Count) then
  1204. Changing;
  1205. FMap.InsertKey(Index, S);
  1206. Changed;
  1207. end;
  1208. procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare);
  1209. var
  1210. I, J, Pivot: Integer;
  1211. begin
  1212. repeat
  1213. I := L;
  1214. J := R;
  1215. Pivot := (L + R) div 2;
  1216. repeat
  1217. while CompareFn(Self, I, Pivot) < 0 do Inc(I);
  1218. while CompareFn(Self, J, Pivot) > 0 do Dec(J);
  1219. if I <= J then
  1220. begin
  1221. FMap.InternalExchange(I, J); // No check, indices are correct.
  1222. if Pivot = I then
  1223. Pivot := J
  1224. else if Pivot = J then
  1225. Pivot := I;
  1226. Inc(I);
  1227. Dec(j);
  1228. end;
  1229. until I > J;
  1230. if L < J then
  1231. QuickSort(L,J, CompareFn);
  1232. L := I;
  1233. until I >= R;
  1234. end;
  1235. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1236. begin
  1237. if not Sorted and (FMap.Count > 1) then
  1238. begin
  1239. Changing;
  1240. QuickSort(0, FMap.Count-1, CompareFn);
  1241. Changed;
  1242. end;
  1243. end;
  1244. procedure TStringList.Sort;
  1245. begin
  1246. if not Sorted and (FMap.Count > 1) then
  1247. begin
  1248. Changing;
  1249. FMap.Sort;
  1250. Changed;
  1251. end;
  1252. end;
  1253. {$endif}