stringl.inc 29 KB

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