stringl.inc 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987
  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. Const
  870. LoadBufSize = 1024;
  871. LoadMaxGrow = MaxInt Div 2;
  872. Procedure TStrings.LoadFromStream(Stream: TStream; IgnoreEncoding : Boolean);
  873. {
  874. Borlands method is no good, since a pipe for
  875. instance doesn't have a size.
  876. So we must do it the hard way.
  877. }
  878. Var
  879. Buffer : AnsiString;
  880. BufLen : SizeInt;
  881. BytesRead, I, BufDelta : Longint;
  882. begin
  883. if not IgnoreEncoding then
  884. begin
  885. LoadFromStream(Stream,Nil);
  886. Exit;
  887. end;
  888. // reread into a buffer
  889. beginupdate;
  890. try
  891. Buffer:='';
  892. BufLen:=0;
  893. I:=1;
  894. Repeat
  895. BufDelta:=LoadBufSize*I;
  896. SetLength(Buffer,BufLen+BufDelta);
  897. BytesRead:=Stream.Read(Buffer[BufLen+1],BufDelta);
  898. inc(BufLen,BufDelta);
  899. If I<LoadMaxGrow then
  900. I:=I shl 1;
  901. Until BytesRead<>BufDelta;
  902. SetLength(Buffer, BufLen-BufDelta+BytesRead);
  903. SetTextStr(Buffer);
  904. SetLength(Buffer,0);
  905. finally
  906. EndUpdate;
  907. end;
  908. end;
  909. Procedure TStrings.LoadFromStream(Stream: TStream; AEncoding: TEncoding);
  910. {
  911. Borlands method is no good, since a pipe for
  912. instance doesn't have a size.
  913. So we must do it the hard way.
  914. }
  915. Var
  916. Buffer : TBytes;
  917. T : string;
  918. BufLen : SizeInt;
  919. BytesRead, I, BufDelta, PreambleLength : Longint;
  920. begin
  921. // reread into a buffer
  922. beginupdate;
  923. try
  924. SetLength(Buffer,0);
  925. BufLen:=0;
  926. I:=1;
  927. Repeat
  928. BufDelta:=LoadBufSize*I;
  929. SetLength(Buffer,BufLen+BufDelta);
  930. BytesRead:=Stream.Read(Buffer[BufLen],BufDelta);
  931. inc(BufLen,BufDelta);
  932. If I<LoadMaxGrow then
  933. I:=I shl 1;
  934. Until BytesRead<>BufDelta;
  935. SetLength(Buffer,BufLen-BufDelta+BytesRead);
  936. PreambleLength:=TEncoding.GetBufferEncoding(Buffer,AEncoding,FDefaultEncoding);
  937. T:=AEncoding.GetAnsiString(Buffer,PreambleLength,Length(Buffer)-PreambleLength);
  938. SetEncoding(AEncoding);
  939. SetLength(Buffer,0);
  940. SetTextStr(T);
  941. finally
  942. EndUpdate;
  943. end;
  944. end;
  945. Procedure TStrings.Move(CurIndex, NewIndex: Integer);
  946. Var
  947. Obj : TObject;
  948. Str : String;
  949. begin
  950. BeginUpdate;
  951. Try
  952. Obj:=Objects[CurIndex];
  953. Str:=Strings[CurIndex];
  954. Objects[CurIndex]:=Nil; // Prevent Delete from freeing.
  955. Delete(Curindex);
  956. InsertObject(NewIndex,Str,Obj);
  957. finally
  958. EndUpdate;
  959. end;
  960. end;
  961. Procedure TStrings.SaveToFile(const FileName: string);
  962. Var TheStream : TFileStream;
  963. begin
  964. TheStream:=TFileStream.Create(FileName,fmCreate);
  965. try
  966. SaveToStream(TheStream);
  967. finally
  968. TheStream.Free;
  969. end;
  970. end;
  971. Procedure TStrings.SaveToFile(const FileName: string; AEncoding: TEncoding);
  972. Var TheStream : TFileStream;
  973. begin
  974. TheStream:=TFileStream.Create(FileName,fmCreate);
  975. try
  976. SaveToStream(TheStream,AEncoding);
  977. finally
  978. TheStream.Free;
  979. end;
  980. end;
  981. Procedure TStrings.SaveToStream(Stream: TStream);
  982. Var
  983. S : String;
  984. begin
  985. if Encoding<>nil then
  986. SaveToStream(Stream,Encoding)
  987. else
  988. begin
  989. S:=Text;
  990. if S = '' then Exit;
  991. Stream.WriteBuffer(Pointer(S)^,Length(S));
  992. end;
  993. end;
  994. Procedure TStrings.SaveToStream(Stream: TStream; AEncoding: TEncoding);
  995. Var B : TBytes;
  996. begin
  997. if AEncoding=nil then
  998. AEncoding:=FDefaultEncoding;
  999. if FWriteBOM then
  1000. begin
  1001. B:=AEncoding.GetPreamble;
  1002. if Length(B)>0 then
  1003. Stream.WriteBuffer(B[0],Length(B));
  1004. end;
  1005. B:=AEncoding.GetAnsiBytes(Text);
  1006. if Length(B)>0 then
  1007. Stream.WriteBuffer(B[0],Length(B));
  1008. end;
  1009. Procedure TStrings.SetText(TheText: PChar);
  1010. Var S : String;
  1011. begin
  1012. If TheText<>Nil then
  1013. S:=StrPas(TheText)
  1014. else
  1015. S:='';
  1016. SetTextStr(S);
  1017. end;
  1018. {****************************************************************************}
  1019. {* TStringList *}
  1020. {****************************************************************************}
  1021. {$if not defined(FPC_TESTGENERICS)}
  1022. procedure TStringList.ExchangeItemsInt(Index1, Index2: Integer);
  1023. Var P1,P2 : Pointer;
  1024. begin
  1025. P1:=Pointer(Flist^[Index1].FString);
  1026. P2:=Pointer(Flist^[Index1].FObject);
  1027. Pointer(Flist^[Index1].Fstring):=Pointer(Flist^[Index2].Fstring);
  1028. Pointer(Flist^[Index1].FObject):=Pointer(Flist^[Index2].FObject);
  1029. Pointer(Flist^[Index2].Fstring):=P1;
  1030. Pointer(Flist^[Index2].FObject):=P2;
  1031. end;
  1032. function TStringList.GetSorted: Boolean;
  1033. begin
  1034. Result:=FSortStyle in [sslUser,sslAuto];
  1035. end;
  1036. procedure TStringList.ExchangeItems(Index1, Index2: Integer);
  1037. begin
  1038. ExchangeItemsInt(Index1, Index2);
  1039. end;
  1040. procedure TStringList.Grow;
  1041. Var
  1042. NC : Integer;
  1043. begin
  1044. NC:=FCapacity;
  1045. If NC>=256 then
  1046. NC:=NC+(NC Div 4)
  1047. else if NC=0 then
  1048. NC:=4
  1049. else
  1050. NC:=NC*4;
  1051. SetCapacity(NC);
  1052. end;
  1053. procedure TStringList.InternalClear(FromIndex: Integer; ClearOnly: Boolean);
  1054. Var
  1055. I: Integer;
  1056. begin
  1057. if FromIndex < FCount then
  1058. begin
  1059. if FOwnsObjects then
  1060. begin
  1061. For I:=FromIndex to FCount-1 do
  1062. begin
  1063. Flist^[I].FString:='';
  1064. freeandnil(Flist^[i].FObject);
  1065. end;
  1066. end
  1067. else
  1068. begin
  1069. For I:=FromIndex to FCount-1 do
  1070. Flist^[I].FString:='';
  1071. end;
  1072. FCount:=FromIndex;
  1073. end;
  1074. if Not ClearOnly then
  1075. SetCapacity(0);
  1076. end;
  1077. procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare
  1078. );
  1079. var
  1080. Pivot, vL, vR: Integer;
  1081. ExchangeProc: procedure(Left, Right: Integer) of object;
  1082. begin
  1083. //if ExchangeItems is override call that, else call (faster) ExchangeItemsInt
  1084. if TMethod(@Self.ExchangeItems).Code = CodePointer(@TStringList.ExchangeItems) then
  1085. ExchangeProc := @ExchangeItemsInt
  1086. else
  1087. ExchangeProc := @ExchangeItems;
  1088. if R - L <= 1 then begin // a little bit of time saver
  1089. if L < R then
  1090. if CompareFn(Self, L, R) > 0 then
  1091. ExchangeProc(L, R);
  1092. Exit;
  1093. end;
  1094. vL := L;
  1095. vR := R;
  1096. Pivot := L + Random(R - L); // they say random is best
  1097. while vL < vR do begin
  1098. while (vL < Pivot) and (CompareFn(Self, vL, Pivot) <= 0) do
  1099. Inc(vL);
  1100. while (vR > Pivot) and (CompareFn(Self, vR, Pivot) > 0) do
  1101. Dec(vR);
  1102. ExchangeProc(vL, vR);
  1103. if Pivot = vL then // swap pivot if we just hit it from one side
  1104. Pivot := vR
  1105. else if Pivot = vR then
  1106. Pivot := vL;
  1107. end;
  1108. if Pivot - 1 >= L then
  1109. QuickSort(L, Pivot - 1, CompareFn);
  1110. if Pivot + 1 <= R then
  1111. QuickSort(Pivot + 1, R, CompareFn);
  1112. end;
  1113. procedure TStringList.InsertItem(Index: Integer; const S: string);
  1114. begin
  1115. InsertItem(Index, S, nil);
  1116. end;
  1117. procedure TStringList.InsertItem(Index: Integer; const S: string; O: TObject);
  1118. begin
  1119. Changing;
  1120. If FCount=Fcapacity then Grow;
  1121. If Index<FCount then
  1122. System.Move (FList^[Index],FList^[Index+1],
  1123. (FCount-Index)*SizeOf(TStringItem));
  1124. Pointer(Flist^[Index].Fstring):=Nil; // Needed to initialize...
  1125. Flist^[Index].FString:=S;
  1126. Flist^[Index].FObject:=O;
  1127. Inc(FCount);
  1128. Changed;
  1129. end;
  1130. procedure TStringList.SetSorted(Value: Boolean);
  1131. begin
  1132. If Value then
  1133. SortStyle:=sslAuto
  1134. else
  1135. SortStyle:=sslNone
  1136. end;
  1137. procedure TStringList.Changed;
  1138. begin
  1139. If (FUpdateCount=0) Then
  1140. begin
  1141. If Assigned(FOnChange) then
  1142. FOnchange(Self);
  1143. FPONotifyObservers(Self,ooChange,Nil);
  1144. end;
  1145. end;
  1146. procedure TStringList.Changing;
  1147. begin
  1148. If FUpdateCount=0 then
  1149. if Assigned(FOnChanging) then
  1150. FOnchanging(Self);
  1151. end;
  1152. function TStringList.Get(Index: Integer): string;
  1153. begin
  1154. CheckIndex(Index);
  1155. Result:=Flist^[Index].FString;
  1156. end;
  1157. function TStringList.GetCapacity: Integer;
  1158. begin
  1159. Result:=FCapacity;
  1160. end;
  1161. function TStringList.GetCount: Integer;
  1162. begin
  1163. Result:=FCount;
  1164. end;
  1165. function TStringList.GetObject(Index: Integer): TObject;
  1166. begin
  1167. CheckIndex(Index);
  1168. Result:=Flist^[Index].FObject;
  1169. end;
  1170. procedure TStringList.Put(Index: Integer; const S: string);
  1171. begin
  1172. If Sorted then
  1173. Error(SSortedListError,0);
  1174. CheckIndex(Index);
  1175. Changing;
  1176. Flist^[Index].FString:=S;
  1177. Changed;
  1178. end;
  1179. procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  1180. begin
  1181. CheckIndex(Index);
  1182. Changing;
  1183. Flist^[Index].FObject:=AObject;
  1184. Changed;
  1185. end;
  1186. procedure TStringList.SetCapacity(NewCapacity: Integer);
  1187. Var NewList : Pointer;
  1188. MSize : Longint;
  1189. begin
  1190. If (NewCapacity<0) then
  1191. Error (SListCapacityError,NewCapacity);
  1192. If NewCapacity>FCapacity then
  1193. begin
  1194. GetMem (NewList,NewCapacity*SizeOf(TStringItem));
  1195. If NewList=Nil then
  1196. Error (SListCapacityError,NewCapacity);
  1197. If Assigned(FList) then
  1198. begin
  1199. MSize:=FCapacity*Sizeof(TStringItem);
  1200. System.Move (FList^,NewList^,MSize);
  1201. FillWord (Pchar(NewList)[MSize],(NewCapacity-FCapacity)*(SizeOf(TStringItem) div SizeOf(Word)), 0);
  1202. FreeMem (Flist,MSize);
  1203. end;
  1204. Flist:=NewList;
  1205. FCapacity:=NewCapacity;
  1206. end
  1207. else if NewCapacity<FCapacity then
  1208. begin
  1209. if NewCapacity = 0 then
  1210. begin
  1211. if FCount > 0 then
  1212. InternalClear(0,True);
  1213. FreeMem(FList);
  1214. FList := nil;
  1215. end else
  1216. begin
  1217. InternalClear(NewCapacity,True);
  1218. GetMem(NewList, NewCapacity * SizeOf(TStringItem));
  1219. System.Move(FList^, NewList^, NewCapacity * SizeOf(TStringItem));
  1220. FreeMem(FList);
  1221. FList := NewList;
  1222. end;
  1223. FCapacity:=NewCapacity;
  1224. end;
  1225. end;
  1226. procedure TStringList.SetUpdateState(Updating: Boolean);
  1227. begin
  1228. If Updating then
  1229. Changing
  1230. else
  1231. Changed
  1232. end;
  1233. destructor TStringList.Destroy;
  1234. begin
  1235. InternalClear;
  1236. Inherited destroy;
  1237. end;
  1238. function TStringList.Add(const S: string): Integer;
  1239. begin
  1240. If Not (SortStyle=sslAuto) then
  1241. Result:=FCount
  1242. else
  1243. If Find (S,Result) then
  1244. Case DUplicates of
  1245. DupIgnore : Exit;
  1246. DupError : Error(SDuplicateString,0)
  1247. end;
  1248. InsertItem (Result,S);
  1249. end;
  1250. procedure TStringList.Clear;
  1251. begin
  1252. if FCount = 0 then Exit;
  1253. Changing;
  1254. InternalClear;
  1255. Changed;
  1256. end;
  1257. procedure TStringList.Delete(Index: Integer);
  1258. begin
  1259. CheckIndex(Index);
  1260. Changing;
  1261. Flist^[Index].FString:='';
  1262. if FOwnsObjects then
  1263. FreeAndNil(Flist^[Index].FObject);
  1264. Dec(FCount);
  1265. If Index<FCount then
  1266. System.Move(Flist^[Index+1],
  1267. Flist^[Index],
  1268. (Fcount-Index)*SizeOf(TStringItem));
  1269. Changed;
  1270. end;
  1271. procedure TStringList.Exchange(Index1, Index2: Integer);
  1272. begin
  1273. CheckIndex(Index1);
  1274. CheckIndex(Index2);
  1275. Changing;
  1276. ExchangeItemsInt(Index1,Index2);
  1277. changed;
  1278. end;
  1279. procedure TStringList.SetCaseSensitive(b : boolean);
  1280. begin
  1281. if b=FCaseSensitive then
  1282. Exit;
  1283. FCaseSensitive:=b;
  1284. if FSortStyle=sslAuto then
  1285. begin
  1286. FForceSort:=True;
  1287. try
  1288. Sort;
  1289. finally
  1290. FForceSort:=False;
  1291. end;
  1292. end;
  1293. end;
  1294. procedure TStringList.SetSortStyle(AValue: TStringsSortStyle);
  1295. begin
  1296. if FSortStyle=AValue then Exit;
  1297. if (AValue=sslAuto) then
  1298. Sort;
  1299. FSortStyle:=AValue;
  1300. end;
  1301. procedure TStringList.CheckIndex(AIndex: Integer);
  1302. begin
  1303. If (AIndex<0) or (AIndex>=FCount) then
  1304. Error(SListIndexError,AIndex);
  1305. end;
  1306. function TStringList.DoCompareText(const s1, s2: string): PtrInt;
  1307. begin
  1308. if FCaseSensitive then
  1309. result:=AnsiCompareStr(s1,s2)
  1310. else
  1311. result:=AnsiCompareText(s1,s2);
  1312. end;
  1313. function TStringList.CompareStrings(const s1,s2 : string) : Integer;
  1314. begin
  1315. Result := DoCompareText(s1, s2);
  1316. end;
  1317. function TStringList.Find(const S: string; out Index: Integer): Boolean;
  1318. var
  1319. L, R, I: Integer;
  1320. CompareRes: PtrInt;
  1321. begin
  1322. Result := false;
  1323. Index:=-1;
  1324. if Not Sorted then
  1325. Raise EListError.Create(SErrFindNeedsSortedList);
  1326. // Use binary search.
  1327. L := 0;
  1328. R := Count - 1;
  1329. while (L<=R) do
  1330. begin
  1331. I := L + (R - L) div 2;
  1332. CompareRes := DoCompareText(S, Flist^[I].FString);
  1333. if (CompareRes>0) then
  1334. L := I+1
  1335. else begin
  1336. R := I-1;
  1337. if (CompareRes=0) then begin
  1338. Result := true;
  1339. if (Duplicates<>dupAccept) then
  1340. L := I; // forces end of while loop
  1341. end;
  1342. end;
  1343. end;
  1344. Index := L;
  1345. end;
  1346. function TStringList.IndexOf(const S: string): Integer;
  1347. begin
  1348. If Not Sorted then
  1349. Result:=Inherited indexOf(S)
  1350. else
  1351. // faster using binary search...
  1352. If Not Find (S,Result) then
  1353. Result:=-1;
  1354. end;
  1355. procedure TStringList.Insert(Index: Integer; const S: string);
  1356. begin
  1357. If SortStyle=sslAuto then
  1358. Error (SSortedListError,0)
  1359. else
  1360. begin
  1361. If (Index<0) or (Index>FCount) then
  1362. Error(SListIndexError,Index); // Cannot use CheckIndex, because there >= FCount...
  1363. InsertItem (Index,S);
  1364. end;
  1365. end;
  1366. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1367. begin
  1368. If (FForceSort or (Not (FSortStyle=sslAuto))) and (FCount>1) then
  1369. begin
  1370. Changing;
  1371. QuickSort(0,FCount-1, CompareFn);
  1372. Changed;
  1373. end;
  1374. end;
  1375. function StringListAnsiCompare(List: TStringList; Index1, Index: Integer): Integer;
  1376. begin
  1377. Result := List.DoCompareText(List.FList^[Index1].FString,
  1378. List.FList^[Index].FString);
  1379. end;
  1380. procedure TStringList.Sort;
  1381. begin
  1382. CustomSort(@StringListAnsiCompare);
  1383. end;
  1384. {$else}
  1385. { generics based implementation of TStringList follows }
  1386. function StringListAnsiCompare(List: TStringList; Index1, Index2: Integer): Integer;
  1387. begin
  1388. Result := List.DoCompareText(List.Strings[Index1], List.Strings[Index2]);
  1389. end;
  1390. constructor TStringList.Create;
  1391. begin
  1392. inherited;
  1393. FOwnsObjects:=false;
  1394. FMap := TFPStrObjMap.Create;
  1395. FMap.OnPtrCompare := @MapPtrCompare;
  1396. FOnCompareText := @DefaultCompareText;
  1397. NameValueSeparator:='=';
  1398. CheckSpecialChars;
  1399. end;
  1400. destructor TStringList.Destroy;
  1401. begin
  1402. FMap.Free;
  1403. inherited;
  1404. end;
  1405. function TStringList.GetDuplicates: TDuplicates;
  1406. begin
  1407. Result := FMap.Duplicates;
  1408. end;
  1409. function TStringList.GetSorted: boolean;
  1410. begin
  1411. Result := FMap.Sorted;
  1412. end;
  1413. procedure TStringList.SetDuplicates(NewDuplicates: TDuplicates);
  1414. begin
  1415. FMap.Duplicates := NewDuplicates;
  1416. end;
  1417. procedure TStringList.SetSorted(NewSorted: Boolean);
  1418. begin
  1419. FMap.Sorted := NewSorted;
  1420. end;
  1421. procedure TStringList.Changed;
  1422. begin
  1423. if FUpdateCount = 0 then
  1424. if Assigned(FOnChange) then
  1425. FOnChange(Self);
  1426. end;
  1427. procedure TStringList.Changing;
  1428. begin
  1429. if FUpdateCount = 0 then
  1430. if Assigned(FOnChanging) then
  1431. FOnChanging(Self);
  1432. end;
  1433. function TStringList.Get(Index: Integer): string;
  1434. begin
  1435. Result := FMap.Keys[Index];
  1436. end;
  1437. function TStringList.GetCapacity: Integer;
  1438. begin
  1439. Result := FMap.Capacity;
  1440. end;
  1441. function TStringList.GetCount: Integer;
  1442. begin
  1443. Result := FMap.Count;
  1444. end;
  1445. function TStringList.GetObject(Index: Integer): TObject;
  1446. begin
  1447. Result := FMap.Data[Index];
  1448. end;
  1449. procedure TStringList.Put(Index: Integer; const S: string);
  1450. begin
  1451. Changing;
  1452. FMap.Keys[Index] := S;
  1453. Changed;
  1454. end;
  1455. procedure TStringList.PutObject(Index: Integer; AObject: TObject);
  1456. begin
  1457. Changing;
  1458. FMap.Data[Index] := AObject;
  1459. Changed;
  1460. end;
  1461. procedure TStringList.SetCapacity(NewCapacity: Integer);
  1462. begin
  1463. FMap.Capacity := NewCapacity;
  1464. end;
  1465. procedure TStringList.SetUpdateState(Updating: Boolean);
  1466. begin
  1467. if Updating then
  1468. Changing
  1469. else
  1470. Changed
  1471. end;
  1472. function TStringList.Add(const S: string): Integer;
  1473. begin
  1474. Result := FMap.Add(S);
  1475. end;
  1476. procedure TStringList.Clear;
  1477. begin
  1478. if FMap.Count = 0 then exit;
  1479. Changing;
  1480. FMap.Clear;
  1481. Changed;
  1482. end;
  1483. procedure TStringList.Delete(Index: Integer);
  1484. begin
  1485. if (Index < 0) or (Index >= FMap.Count) then
  1486. Error(SListIndexError, Index);
  1487. Changing;
  1488. FMap.Delete(Index);
  1489. Changed;
  1490. end;
  1491. procedure TStringList.Exchange(Index1, Index2: Integer);
  1492. begin
  1493. if (Index1 < 0) or (Index1 >= FMap.Count) then
  1494. Error(SListIndexError, Index1);
  1495. if (Index2 < 0) or (Index2 >= FMap.Count) then
  1496. Error(SListIndexError, Index2);
  1497. Changing;
  1498. FMap.InternalExchange(Index1, Index2);
  1499. Changed;
  1500. end;
  1501. procedure TStringList.SetCaseSensitive(NewSensitive: Boolean);
  1502. begin
  1503. if NewSensitive <> FCaseSensitive then
  1504. begin
  1505. FCaseSensitive := NewSensitive;
  1506. if Sorted then
  1507. Sort;
  1508. end;
  1509. end;
  1510. function TStringList.MapPtrCompare(Key1, Key2: Pointer): Integer;
  1511. begin
  1512. Result := FOnCompareText(string(Key1^), string(Key2^));
  1513. end;
  1514. function TStringList.DefaultCompareText(const s1, s2: string): PtrInt;
  1515. begin
  1516. if FCaseSensitive then
  1517. Result := AnsiCompareStr(s1, s2)
  1518. else
  1519. Result := AnsiCompareText(s1, s2);
  1520. end;
  1521. function TStringList.DoCompareText(const s1, s2: string): PtrInt;
  1522. begin
  1523. Result := FOnCompareText(s1, s2);
  1524. end;
  1525. function TStringList.Find(const S: string; var Index: Integer): Boolean;
  1526. begin
  1527. Result := FMap.Find(S, Index);
  1528. end;
  1529. function TStringList.IndexOf(const S: string): Integer;
  1530. begin
  1531. Result := FMap.IndexOf(S);
  1532. end;
  1533. procedure TStringList.Insert(Index: Integer; const S: string);
  1534. begin
  1535. if not Sorted and (0 <= Index) and (Index < FMap.Count) then
  1536. Changing;
  1537. FMap.InsertKey(Index, S);
  1538. Changed;
  1539. end;
  1540. procedure TStringList.QuickSort(L, R: Integer; CompareFn: TStringListSortCompare);
  1541. var
  1542. I, J, Pivot: Integer;
  1543. begin
  1544. repeat
  1545. I := L;
  1546. J := R;
  1547. Pivot := (L + R) div 2;
  1548. repeat
  1549. while CompareFn(Self, I, Pivot) < 0 do Inc(I);
  1550. while CompareFn(Self, J, Pivot) > 0 do Dec(J);
  1551. if I <= J then
  1552. begin
  1553. FMap.InternalExchange(I, J); // No check, indices are correct.
  1554. if Pivot = I then
  1555. Pivot := J
  1556. else if Pivot = J then
  1557. Pivot := I;
  1558. Inc(I);
  1559. Dec(j);
  1560. end;
  1561. until I > J;
  1562. if L < J then
  1563. QuickSort(L,J, CompareFn);
  1564. L := I;
  1565. until I >= R;
  1566. end;
  1567. procedure TStringList.CustomSort(CompareFn: TStringListSortCompare);
  1568. begin
  1569. if not Sorted and (FMap.Count > 1) then
  1570. begin
  1571. Changing;
  1572. QuickSort(0, FMap.Count-1, CompareFn);
  1573. Changed;
  1574. end;
  1575. end;
  1576. procedure TStringList.Sort;
  1577. begin
  1578. if not Sorted and (FMap.Count > 1) then
  1579. begin
  1580. Changing;
  1581. FMap.Sort;
  1582. Changed;
  1583. end;
  1584. end;
  1585. {$endif}