stringl.inc 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591
  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), get_caller_frame(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), get_caller_frame(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. Objects[CurIndex]:=Nil; // Prevent Delete from freeing.
  688. Delete(Curindex);
  689. InsertObject(NewIndex,Str,Obj);
  690. EndUpdate;
  691. end;
  692. Procedure TStrings.SaveToFile(const FileName: string);
  693. Var TheStream : TFileStream;
  694. begin
  695. TheStream:=TFileStream.Create(FileName,fmCreate);
  696. try
  697. SaveToStream(TheStream);
  698. finally
  699. TheStream.Free;
  700. end;
  701. end;
  702. Procedure TStrings.SaveToStream(Stream: TStream);
  703. Var
  704. S : String;
  705. begin
  706. S:=Text;
  707. if S = '' then Exit;
  708. Stream.WriteBuffer(Pointer(S)^,Length(S));
  709. end;
  710. Procedure TStrings.SetText(TheText: PChar);
  711. Var S : String;
  712. begin
  713. If TheText<>Nil then
  714. S:=StrPas(TheText)
  715. else
  716. S:='';
  717. SetTextStr(S);
  718. end;
  719. {****************************************************************************}
  720. {* TStringList *}
  721. {****************************************************************************}
  722. {$if defined(VER2_0) or not defined(FPC_TESTGENERICS)}
  723. Procedure TStringList.ExchangeItems(Index1, Index2: Integer);
  724. Var P1,P2 : Pointer;
  725. begin
  726. P1:=Pointer(Flist^[Index1].FString);
  727. P2:=Pointer(Flist^[Index1].FObject);
  728. Pointer(Flist^[Index1].Fstring):=Pointer(Flist^[Index2].Fstring);
  729. Pointer(Flist^[Index1].FObject):=Pointer(Flist^[Index2].FObject);
  730. Pointer(Flist^[Index2].Fstring):=P1;
  731. Pointer(Flist^[Index2].FObject):=P2;
  732. end;
  733. Procedure TStringList.Grow;
  734. Var
  735. NC : Integer;
  736. begin
  737. NC:=FCapacity;
  738. If NC>=256 then
  739. NC:=NC+(NC Div 4)
  740. else if NC=0 then
  741. NC:=4
  742. else
  743. NC:=NC*4;
  744. SetCapacity(NC);
  745. end;
  746. Procedure TStringList.InternalClear;
  747. Var
  748. I: Integer;
  749. begin
  750. if FOwnsObjects then
  751. begin
  752. For I:=0 to FCount-1 do
  753. begin
  754. Flist^[I].FString:='';
  755. freeandnil(Flist^[i].FObject);
  756. end;
  757. end
  758. else
  759. begin
  760. For I:=0 to FCount-1 do
  761. Flist^[I].FString:='';
  762. end;
  763. FCount:=0;
  764. SetCapacity(0);
  765. end;
  766. Procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare);
  767. var
  768. Pivot, vL, vR: Integer;
  769. begin
  770. if R - L <= 1 then begin // a little bit of time saver
  771. if L < R then
  772. if CompareFn(Self, L, R) > 0 then
  773. ExchangeItems(L, R);
  774. Exit;
  775. end;
  776. vL := L;
  777. vR := R;
  778. Pivot := L + Random(R - L); // they say random is best
  779. while vL < vR do begin
  780. while (vL < Pivot) and (CompareFn(Self, vL, Pivot) <= 0) do
  781. Inc(vL);
  782. while (vR > Pivot) and (CompareFn(Self, vR, Pivot) > 0) do
  783. Dec(vR);
  784. ExchangeItems(vL, vR);
  785. if Pivot = vL then // swap pivot if we just hit it from one side
  786. Pivot := vR
  787. else if Pivot = vR then
  788. Pivot := vL;
  789. end;
  790. if Pivot - 1 >= L then
  791. QuickSort(L, Pivot - 1, CompareFn);
  792. if Pivot + 1 <= R then
  793. QuickSort(Pivot + 1, R, CompareFn);
  794. end;
  795. Procedure TStringList.InsertItem(Index: Integer; const S: string);
  796. begin
  797. InsertItem(Index, S, nil);
  798. end;
  799. Procedure TStringList.InsertItem(Index: Integer; const S: string; O: TObject);
  800. begin
  801. Changing;
  802. If FCount=Fcapacity then Grow;
  803. If Index<FCount then
  804. System.Move (FList^[Index],FList^[Index+1],
  805. (FCount-Index)*SizeOf(TStringItem));
  806. Pointer(Flist^[Index].Fstring):=Nil; // Needed to initialize...
  807. Flist^[Index].FString:=S;
  808. Flist^[Index].FObject:=O;
  809. Inc(FCount);
  810. Changed;
  811. end;
  812. Procedure TStringList.SetSorted(Value: Boolean);
  813. begin
  814. If FSorted<>Value then
  815. begin
  816. If Value then sort;
  817. FSorted:=VAlue
  818. end;
  819. end;
  820. Procedure TStringList.Changed;
  821. begin
  822. If (FUpdateCount=0) Then
  823. begin
  824. If Assigned(FOnChange) then
  825. FOnchange(Self);
  826. FPONotifyObservers(Self,ooChange,Nil);
  827. end;
  828. end;
  829. Procedure TStringList.Changing;
  830. begin
  831. If FUpdateCount=0 then
  832. if Assigned(FOnChanging) then
  833. FOnchanging(Self);
  834. end;
  835. Function TStringList.Get(Index: Integer): string;
  836. begin
  837. If (Index<0) or (INdex>=Fcount) then
  838. Error (SListIndexError,Index);
  839. Result:=Flist^[Index].FString;
  840. end;
  841. Function TStringList.GetCapacity: Integer;
  842. begin
  843. Result:=FCapacity;
  844. end;
  845. Function TStringList.GetCount: Integer;
  846. begin
  847. Result:=FCount;
  848. end;
  849. Function TStringList.GetObject(Index: Integer): TObject;
  850. begin
  851. If (Index<0) or (INdex>=Fcount) then
  852. Error (SListIndexError,Index);
  853. Result:=Flist^[Index].FObject;
  854. end;
  855. Procedure TStringList.Put(Index: Integer; const S: string);
  856. begin
  857. If Sorted then
  858. Error(SSortedListError,0);
  859. If (Index<0) or (INdex>=Fcount) then
  860. Error (SListIndexError,Index);
  861. Changing;
  862. Flist^[Index].FString:=S;
  863. Changed;
  864. end;
  865. Procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  866. begin
  867. If (Index<0) or (INdex>=Fcount) then
  868. Error (SListIndexError,Index);
  869. Changing;
  870. Flist^[Index].FObject:=AObject;
  871. Changed;
  872. end;
  873. Procedure TStringList.SetCapacity(NewCapacity: Integer);
  874. Var NewList : Pointer;
  875. MSize : Longint;
  876. begin
  877. If (NewCapacity<0) then
  878. Error (SListCapacityError,NewCapacity);
  879. If NewCapacity>FCapacity then
  880. begin
  881. GetMem (NewList,NewCapacity*SizeOf(TStringItem));
  882. If NewList=Nil then
  883. Error (SListCapacityError,NewCapacity);
  884. If Assigned(FList) then
  885. begin
  886. MSize:=FCapacity*Sizeof(TStringItem);
  887. System.Move (FList^,NewList^,MSize);
  888. FillWord (Pchar(NewList)[MSize],(NewCapacity-FCapacity)*WordRatio, 0);
  889. FreeMem (Flist,MSize);
  890. end;
  891. Flist:=NewList;
  892. FCapacity:=NewCapacity;
  893. end
  894. else if NewCapacity<FCapacity then
  895. begin
  896. if NewCapacity = 0 then
  897. begin
  898. FreeMem(FList);
  899. FList := nil;
  900. end else
  901. begin
  902. GetMem(NewList, NewCapacity * SizeOf(TStringItem));
  903. System.Move(FList^, NewList^, NewCapacity * SizeOf(TStringItem));
  904. FreeMem(FList);
  905. FList := NewList;
  906. end;
  907. FCapacity:=NewCapacity;
  908. end;
  909. end;
  910. Procedure TStringList.SetUpdateState(Updating: Boolean);
  911. begin
  912. If Updating then
  913. Changing
  914. else
  915. Changed
  916. end;
  917. destructor TStringList.Destroy;
  918. begin
  919. InternalClear;
  920. Inherited destroy;
  921. end;
  922. Function TStringList.Add(const S: string): Integer;
  923. begin
  924. If Not Sorted then
  925. Result:=FCount
  926. else
  927. If Find (S,Result) then
  928. Case DUplicates of
  929. DupIgnore : Exit;
  930. DupError : Error(SDuplicateString,0)
  931. end;
  932. InsertItem (Result,S);
  933. end;
  934. Procedure TStringList.Clear;
  935. begin
  936. if FCount = 0 then Exit;
  937. Changing;
  938. InternalClear;
  939. Changed;
  940. end;
  941. Procedure TStringList.Delete(Index: Integer);
  942. begin
  943. If (Index<0) or (Index>=FCount) then
  944. Error(SlistINdexError,Index);
  945. Changing;
  946. Flist^[Index].FString:='';
  947. if FOwnsObjects then
  948. FreeAndNil(Flist^[Index].FObject);
  949. Dec(FCount);
  950. If Index<FCount then
  951. System.Move(Flist^[Index+1],
  952. Flist^[Index],
  953. (Fcount-Index)*SizeOf(TStringItem));
  954. Changed;
  955. end;
  956. Procedure TStringList.Exchange(Index1, Index2: Integer);
  957. begin
  958. If (Index1<0) or (Index1>=FCount) then
  959. Error(SListIndexError,Index1);
  960. If (Index2<0) or (Index2>=FCount) then
  961. Error(SListIndexError,Index2);
  962. Changing;
  963. ExchangeItems(Index1,Index2);
  964. changed;
  965. end;
  966. procedure TStringList.SetCaseSensitive(b : boolean);
  967. begin
  968. if b=FCaseSensitive then
  969. Exit;
  970. FCaseSensitive:=b;
  971. if FSorted then
  972. begin
  973. FForceSort:=True;
  974. sort;
  975. FForceSort:=False;
  976. end;
  977. end;
  978. Function TStringList.DoCompareText(const s1,s2 : string) : PtrInt;
  979. begin
  980. if FCaseSensitive then
  981. result:=AnsiCompareStr(s1,s2)
  982. else
  983. result:=AnsiCompareText(s1,s2);
  984. end;
  985. Function TStringList.Find(const S: string; Out Index: Integer): Boolean;
  986. var
  987. L, R, I: Integer;
  988. CompareRes: PtrInt;
  989. begin
  990. Result := false;
  991. // Use binary search.
  992. L := 0;
  993. R := Count - 1;
  994. while (L<=R) do
  995. begin
  996. I := L + (R - L) div 2;
  997. CompareRes := DoCompareText(S, Flist^[I].FString);
  998. if (CompareRes>0) then
  999. L := I+1
  1000. else begin
  1001. R := I-1;
  1002. if (CompareRes=0) then begin
  1003. Result := true;
  1004. if (Duplicates<>dupAccept) then
  1005. L := I; // forces end of while loop
  1006. end;
  1007. end;
  1008. end;
  1009. Index := L;
  1010. end;
  1011. Function TStringList.IndexOf(const S: string): Integer;
  1012. begin
  1013. If Not Sorted then
  1014. Result:=Inherited indexOf(S)
  1015. else
  1016. // faster using binary search...
  1017. If Not Find (S,Result) then
  1018. Result:=-1;
  1019. end;
  1020. Procedure TStringList.Insert(Index: Integer; const S: string);
  1021. begin
  1022. If Sorted then
  1023. Error (SSortedListError,0)
  1024. else
  1025. If (Index<0) or (Index>FCount) then
  1026. Error (SListIndexError,Index)
  1027. else
  1028. InsertItem (Index,S);
  1029. end;
  1030. Procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1031. begin
  1032. If (FForceSort or (Not Sorted)) and (FCount>1) then
  1033. begin
  1034. Changing;
  1035. QuickSort(0,FCount-1, CompareFn);
  1036. Changed;
  1037. end;
  1038. end;
  1039. function StringListAnsiCompare(List: TStringList; Index1, Index: Integer): Integer;
  1040. begin
  1041. Result := List.DoCompareText(List.FList^[Index1].FString,
  1042. List.FList^[Index].FString);
  1043. end;
  1044. Procedure TStringList.Sort;
  1045. begin
  1046. CustomSort(@StringListAnsiCompare);
  1047. end;
  1048. {$else}
  1049. { generics based implementation of TStringList follows }
  1050. function StringListAnsiCompare(List: TStringList; Index1, Index2: Integer): Integer;
  1051. begin
  1052. Result := List.DoCompareText(List.Strings[Index1], List.Strings[Index2]);
  1053. end;
  1054. constructor TStringList.Create;
  1055. begin
  1056. inherited;
  1057. FOwnsObjects:=false;
  1058. FMap := TFPStrObjMap.Create;
  1059. FMap.OnPtrCompare := @MapPtrCompare;
  1060. FOnCompareText := @DefaultCompareText;
  1061. NameValueSeparator:='=';
  1062. CheckSpecialChars;
  1063. end;
  1064. destructor TStringList.Destroy;
  1065. begin
  1066. FMap.Free;
  1067. inherited;
  1068. end;
  1069. function TStringList.GetDuplicates: TDuplicates;
  1070. begin
  1071. Result := FMap.Duplicates;
  1072. end;
  1073. function TStringList.GetSorted: boolean;
  1074. begin
  1075. Result := FMap.Sorted;
  1076. end;
  1077. procedure TStringList.SetDuplicates(NewDuplicates: TDuplicates);
  1078. begin
  1079. FMap.Duplicates := NewDuplicates;
  1080. end;
  1081. procedure TStringList.SetSorted(NewSorted: Boolean);
  1082. begin
  1083. FMap.Sorted := NewSorted;
  1084. end;
  1085. procedure TStringList.Changed;
  1086. begin
  1087. if FUpdateCount = 0 then
  1088. if Assigned(FOnChange) then
  1089. FOnChange(Self);
  1090. end;
  1091. procedure TStringList.Changing;
  1092. begin
  1093. if FUpdateCount = 0 then
  1094. if Assigned(FOnChanging) then
  1095. FOnChanging(Self);
  1096. end;
  1097. function TStringList.Get(Index: Integer): string;
  1098. begin
  1099. Result := FMap.Keys[Index];
  1100. end;
  1101. function TStringList.GetCapacity: Integer;
  1102. begin
  1103. Result := FMap.Capacity;
  1104. end;
  1105. function TStringList.GetCount: Integer;
  1106. begin
  1107. Result := FMap.Count;
  1108. end;
  1109. function TStringList.GetObject(Index: Integer): TObject;
  1110. begin
  1111. Result := FMap.Data[Index];
  1112. end;
  1113. procedure TStringList.Put(Index: Integer; const S: string);
  1114. begin
  1115. Changing;
  1116. FMap.Keys[Index] := S;
  1117. Changed;
  1118. end;
  1119. procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  1120. begin
  1121. Changing;
  1122. FMap.Data[Index] := AObject;
  1123. Changed;
  1124. end;
  1125. procedure TStringList.SetCapacity(NewCapacity: Integer);
  1126. begin
  1127. FMap.Capacity := NewCapacity;
  1128. end;
  1129. procedure TStringList.SetUpdateState(Updating: Boolean);
  1130. begin
  1131. if Updating then
  1132. Changing
  1133. else
  1134. Changed
  1135. end;
  1136. function TStringList.Add(const S: string): Integer;
  1137. begin
  1138. Result := FMap.Add(S);
  1139. end;
  1140. procedure TStringList.Clear;
  1141. begin
  1142. if FMap.Count = 0 then exit;
  1143. Changing;
  1144. FMap.Clear;
  1145. Changed;
  1146. end;
  1147. procedure TStringList.Delete(Index: Integer);
  1148. begin
  1149. if (Index < 0) or (Index >= FMap.Count) then
  1150. Error(SListIndexError, Index);
  1151. Changing;
  1152. FMap.Delete(Index);
  1153. Changed;
  1154. end;
  1155. procedure TStringList.Exchange(Index1, Index2: Integer);
  1156. begin
  1157. if (Index1 < 0) or (Index1 >= FMap.Count) then
  1158. Error(SListIndexError, Index1);
  1159. if (Index2 < 0) or (Index2 >= FMap.Count) then
  1160. Error(SListIndexError, Index2);
  1161. Changing;
  1162. FMap.InternalExchange(Index1, Index2);
  1163. Changed;
  1164. end;
  1165. procedure TStringList.SetCaseSensitive(NewSensitive: Boolean);
  1166. begin
  1167. if NewSensitive <> FCaseSensitive then
  1168. begin
  1169. FCaseSensitive := NewSensitive;
  1170. if Sorted then
  1171. Sort;
  1172. end;
  1173. end;
  1174. function TStringList.MapPtrCompare(Key1, Key2: Pointer): Integer;
  1175. begin
  1176. Result := FOnCompareText(string(Key1^), string(Key2^));
  1177. end;
  1178. function TStringList.DefaultCompareText(const s1, s2: string): PtrInt;
  1179. begin
  1180. if FCaseSensitive then
  1181. Result := AnsiCompareStr(s1, s2)
  1182. else
  1183. Result := AnsiCompareText(s1, s2);
  1184. end;
  1185. function TStringList.DoCompareText(const s1, s2: string): PtrInt;
  1186. begin
  1187. Result := FOnCompareText(s1, s2);
  1188. end;
  1189. function TStringList.Find(const S: string; var Index: Integer): Boolean;
  1190. begin
  1191. Result := FMap.Find(S, Index);
  1192. end;
  1193. function TStringList.IndexOf(const S: string): Integer;
  1194. begin
  1195. Result := FMap.IndexOf(S);
  1196. end;
  1197. procedure TStringList.Insert(Index: Integer; const S: string);
  1198. begin
  1199. if not Sorted and (0 <= Index) and (Index < FMap.Count) then
  1200. Changing;
  1201. FMap.InsertKey(Index, S);
  1202. Changed;
  1203. end;
  1204. procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare);
  1205. var
  1206. I, J, Pivot: Integer;
  1207. begin
  1208. repeat
  1209. I := L;
  1210. J := R;
  1211. Pivot := (L + R) div 2;
  1212. repeat
  1213. while CompareFn(Self, I, Pivot) < 0 do Inc(I);
  1214. while CompareFn(Self, J, Pivot) > 0 do Dec(J);
  1215. if I <= J then
  1216. begin
  1217. FMap.InternalExchange(I, J); // No check, indices are correct.
  1218. if Pivot = I then
  1219. Pivot := J
  1220. else if Pivot = J then
  1221. Pivot := I;
  1222. Inc(I);
  1223. Dec(j);
  1224. end;
  1225. until I > J;
  1226. if L < J then
  1227. QuickSort(L,J, CompareFn);
  1228. L := I;
  1229. until I >= R;
  1230. end;
  1231. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1232. begin
  1233. if not Sorted and (FMap.Count > 1) then
  1234. begin
  1235. Changing;
  1236. QuickSort(0, FMap.Count-1, CompareFn);
  1237. Changed;
  1238. end;
  1239. end;
  1240. procedure TStringList.Sort;
  1241. begin
  1242. if not Sorted and (FMap.Count > 1) then
  1243. begin
  1244. Changing;
  1245. FMap.Sort;
  1246. Changed;
  1247. end;
  1248. end;
  1249. {$endif}