stringl.inc 46 KB

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