stringl.inc 39 KB

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