stringl.inc 40 KB

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