OSDN Git Service

2003-10-21 Arnaud Charlet <charlet@act-europe.fr>
[pf3gnuchains/gcc-fork.git] / gcc / ada / a-strfix.adb
1 ------------------------------------------------------------------------------
2 --                                                                          --
3 --                         GNAT RUNTIME COMPONENTS                          --
4 --                                                                          --
5 --                    A D A . S T R I N G S . F I X E D                     --
6 --                                                                          --
7 --                                 B o d y                                  --
8 --                                                                          --
9 --          Copyright (C) 1992-2002 Free Software Foundation, Inc.          --
10 --                                                                          --
11 -- GNAT is free software;  you can  redistribute it  and/or modify it under --
12 -- terms of the  GNU General Public License as published  by the Free Soft- --
13 -- ware  Foundation;  either version 2,  or (at your option) any later ver- --
14 -- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
17 -- for  more details.  You should have  received  a copy of the GNU General --
18 -- Public License  distributed with GNAT;  see file COPYING.  If not, write --
19 -- to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, --
20 -- MA 02111-1307, USA.                                                      --
21 --                                                                          --
22 -- As a special exception,  if other files  instantiate  generics from this --
23 -- unit, or you link  this unit with other files  to produce an executable, --
24 -- this  unit  does not  by itself cause  the resulting  executable  to  be --
25 -- covered  by the  GNU  General  Public  License.  This exception does not --
26 -- however invalidate  any other reasons why  the executable file  might be --
27 -- covered by the  GNU Public License.                                      --
28 --                                                                          --
29 -- GNAT was originally developed  by the GNAT team at  New York University. --
30 -- Extensive contributions were provided by Ada Core Technologies Inc.      --
31 --                                                                          --
32 ------------------------------------------------------------------------------
33
34 --  Note: This code is derived from the ADAR.CSH public domain Ada 83
35 --  versions of the Appendix C string handling packages. One change is
36 --  to avoid the use of Is_In, so that we are not dependent on inlining.
37 --  Note that the search function implementations are to be found in the
38 --  auxiliary package Ada.Strings.Search. Also the Move procedure is
39 --  directly incorporated (ADAR used a subunit for this procedure). A
40 --  number of errors having to do with bounds of function return results
41 --  were also fixed, and use of & removed for efficiency reasons.
42
43 with Ada.Strings.Maps; use Ada.Strings.Maps;
44 with Ada.Strings.Search;
45
46 package body Ada.Strings.Fixed is
47
48    ------------------------
49    -- Search Subprograms --
50    ------------------------
51
52    function Index
53      (Source   : in String;
54       Pattern  : in String;
55       Going    : in Direction := Forward;
56       Mapping  : in Maps.Character_Mapping := Maps.Identity)
57       return     Natural
58    renames Ada.Strings.Search.Index;
59
60    function Index
61      (Source   : in String;
62       Pattern  : in String;
63       Going    : in Direction := Forward;
64       Mapping  : in Maps.Character_Mapping_Function)
65       return     Natural
66    renames Ada.Strings.Search.Index;
67
68    function Index
69      (Source : in String;
70       Set    : in Maps.Character_Set;
71       Test   : in Membership := Inside;
72       Going  : in Direction  := Forward)
73       return   Natural
74    renames Ada.Strings.Search.Index;
75
76    function Index_Non_Blank
77      (Source : in String;
78       Going  : in Direction := Forward)
79       return   Natural
80    renames Ada.Strings.Search.Index_Non_Blank;
81
82    function Count
83      (Source   : in String;
84       Pattern  : in String;
85       Mapping  : in Maps.Character_Mapping := Maps.Identity)
86       return     Natural
87    renames Ada.Strings.Search.Count;
88
89    function Count
90      (Source   : in String;
91       Pattern  : in String;
92       Mapping  : in Maps.Character_Mapping_Function)
93       return     Natural
94    renames Ada.Strings.Search.Count;
95
96    function Count
97      (Source   : in String;
98       Set      : in Maps.Character_Set)
99       return     Natural
100    renames Ada.Strings.Search.Count;
101
102    procedure Find_Token
103      (Source : in String;
104       Set    : in Maps.Character_Set;
105       Test   : in Membership;
106       First  : out Positive;
107       Last   : out Natural)
108    renames Ada.Strings.Search.Find_Token;
109
110    ---------
111    -- "*" --
112    ---------
113
114    function "*"
115      (Left  : in Natural;
116       Right : in Character)
117       return  String
118    is
119       Result : String (1 .. Left);
120
121    begin
122       for J in Result'Range loop
123          Result (J) := Right;
124       end loop;
125
126       return Result;
127    end "*";
128
129    function "*"
130      (Left  : in Natural;
131       Right : in String)
132       return  String
133    is
134       Result : String (1 .. Left * Right'Length);
135       Ptr    : Integer := 1;
136
137    begin
138       for J in 1 .. Left loop
139          Result (Ptr .. Ptr + Right'Length - 1) := Right;
140          Ptr := Ptr + Right'Length;
141       end loop;
142
143       return Result;
144    end "*";
145
146    ------------
147    -- Delete --
148    ------------
149
150    function Delete
151      (Source  : in String;
152       From    : in Positive;
153       Through : in Natural)
154       return    String
155    is
156    begin
157       if From > Through then
158          declare
159             subtype Result_Type is String (1 .. Source'Length);
160
161          begin
162             return Result_Type (Source);
163          end;
164
165       elsif From not in Source'Range
166         or else Through > Source'Last
167       then
168          raise Index_Error;
169
170       else
171          declare
172             Front  : constant Integer := From - Source'First;
173             Result : String (1 .. Source'Length - (Through - From + 1));
174
175          begin
176             Result (1 .. Front) :=
177               Source (Source'First .. From - 1);
178             Result (Front + 1 .. Result'Last) :=
179               Source (Through + 1 .. Source'Last);
180
181             return Result;
182          end;
183       end if;
184    end Delete;
185
186    procedure Delete
187      (Source  : in out String;
188       From    : in Positive;
189       Through : in Natural;
190       Justify : in Alignment := Left;
191       Pad     : in Character := Space)
192    is
193    begin
194       Move (Source  => Delete (Source, From, Through),
195             Target  => Source,
196             Justify => Justify,
197             Pad     => Pad);
198    end Delete;
199
200    ----------
201    -- Head --
202    ----------
203
204    function Head
205      (Source : in String;
206       Count  : in Natural;
207       Pad    : in Character := Space)
208       return   String
209    is
210       subtype Result_Type is String (1 .. Count);
211
212    begin
213       if Count < Source'Length then
214          return
215            Result_Type (Source (Source'First .. Source'First + Count - 1));
216
217       else
218          declare
219             Result : Result_Type;
220
221          begin
222             Result (1 .. Source'Length) := Source;
223
224             for J in Source'Length + 1 .. Count loop
225                Result (J) := Pad;
226             end loop;
227
228             return Result;
229          end;
230       end if;
231    end Head;
232
233    procedure Head
234      (Source  : in out String;
235       Count   : in Natural;
236       Justify : in Alignment := Left;
237       Pad     : in Character := Space)
238    is
239    begin
240       Move (Source  => Head (Source, Count, Pad),
241             Target  => Source,
242             Drop    => Error,
243             Justify => Justify,
244             Pad     => Pad);
245    end Head;
246
247    ------------
248    -- Insert --
249    ------------
250
251    function Insert
252      (Source   : in String;
253       Before   : in Positive;
254       New_Item : in String)
255       return     String
256    is
257       Result : String (1 .. Source'Length + New_Item'Length);
258       Front  : constant Integer := Before - Source'First;
259
260    begin
261       if Before not in Source'First .. Source'Last + 1 then
262          raise Index_Error;
263       end if;
264
265       Result (1 .. Front) :=
266         Source (Source'First .. Before - 1);
267       Result (Front + 1 .. Front + New_Item'Length) :=
268         New_Item;
269       Result (Front + New_Item'Length + 1 .. Result'Last) :=
270         Source (Before .. Source'Last);
271
272       return Result;
273    end Insert;
274
275    procedure Insert
276      (Source   : in out String;
277       Before   : in Positive;
278       New_Item : in String;
279       Drop     : in Truncation := Error)
280    is
281    begin
282       Move (Source => Insert (Source, Before, New_Item),
283             Target => Source,
284             Drop   => Drop);
285    end Insert;
286
287    ----------
288    -- Move --
289    ----------
290
291    procedure Move
292      (Source  : in  String;
293       Target  : out String;
294       Drop    : in  Truncation := Error;
295       Justify : in  Alignment  := Left;
296       Pad     : in  Character  := Space)
297    is
298       Sfirst  : constant Integer := Source'First;
299       Slast   : constant Integer := Source'Last;
300       Slength : constant Integer := Source'Length;
301
302       Tfirst  : constant Integer := Target'First;
303       Tlast   : constant Integer := Target'Last;
304       Tlength : constant Integer := Target'Length;
305
306       function Is_Padding (Item : String) return Boolean;
307       --  Check if Item is all Pad characters, return True if so, False if not
308
309       function Is_Padding (Item : String) return Boolean is
310       begin
311          for J in Item'Range loop
312             if Item (J) /= Pad then
313                return False;
314             end if;
315          end loop;
316
317          return True;
318       end Is_Padding;
319
320    --  Start of processing for Move
321
322    begin
323       if Slength = Tlength then
324          Target := Source;
325
326       elsif Slength > Tlength then
327
328          case Drop is
329             when Left =>
330                Target := Source (Slast - Tlength + 1 .. Slast);
331
332             when Right =>
333                Target := Source (Sfirst .. Sfirst + Tlength - 1);
334
335             when Error =>
336                case Justify is
337                   when Left =>
338                      if Is_Padding (Source (Sfirst + Tlength .. Slast)) then
339                         Target :=
340                           Source (Sfirst .. Sfirst + Target'Length - 1);
341                      else
342                         raise Length_Error;
343                      end if;
344
345                   when Right =>
346                      if Is_Padding (Source (Sfirst .. Slast - Tlength)) then
347                         Target := Source (Slast - Tlength + 1 .. Slast);
348                      else
349                         raise Length_Error;
350                      end if;
351
352                   when Center =>
353                      raise Length_Error;
354                end case;
355
356          end case;
357
358       --  Source'Length < Target'Length
359
360       else
361          case Justify is
362             when Left =>
363                Target (Tfirst .. Tfirst + Slength - 1) := Source;
364
365                for I in Tfirst + Slength .. Tlast loop
366                   Target (I) := Pad;
367                end loop;
368
369             when Right =>
370                for I in Tfirst .. Tlast - Slength loop
371                   Target (I) := Pad;
372                end loop;
373
374                Target (Tlast - Slength + 1 .. Tlast) := Source;
375
376             when Center =>
377                declare
378                   Front_Pad   : constant Integer := (Tlength - Slength) / 2;
379                   Tfirst_Fpad : constant Integer := Tfirst + Front_Pad;
380
381                begin
382                   for I in Tfirst .. Tfirst_Fpad - 1 loop
383                      Target (I) := Pad;
384                   end loop;
385
386                   Target (Tfirst_Fpad .. Tfirst_Fpad + Slength - 1) := Source;
387
388                   for I in Tfirst_Fpad + Slength .. Tlast loop
389                      Target (I) := Pad;
390                   end loop;
391                end;
392          end case;
393       end if;
394    end Move;
395
396    ---------------
397    -- Overwrite --
398    ---------------
399
400    function Overwrite
401      (Source   : in String;
402       Position : in Positive;
403       New_Item : in String)
404       return     String
405    is
406    begin
407       if Position not in Source'First .. Source'Last + 1 then
408          raise Index_Error;
409       end if;
410
411       declare
412          Result_Length : constant Natural :=
413                            Integer'Max
414                              (Source'Length,
415                               Position - Source'First + New_Item'Length);
416
417          Result : String (1 .. Result_Length);
418          Front  : constant Integer := Position - Source'First;
419
420       begin
421          Result (1 .. Front) :=
422            Source (Source'First .. Position - 1);
423          Result (Front + 1 .. Front + New_Item'Length) :=
424            New_Item;
425          Result (Front + New_Item'Length + 1 .. Result'Length) :=
426            Source (Position + New_Item'Length .. Source'Last);
427          return Result;
428       end;
429    end Overwrite;
430
431    procedure Overwrite
432      (Source   : in out String;
433       Position : in Positive;
434       New_Item : in String;
435       Drop     : in Truncation := Right)
436    is
437    begin
438       Move (Source => Overwrite (Source, Position, New_Item),
439             Target => Source,
440             Drop   => Drop);
441    end Overwrite;
442
443    -------------------
444    -- Replace_Slice --
445    -------------------
446
447    function Replace_Slice
448      (Source   : in String;
449       Low      : in Positive;
450       High     : in Natural;
451       By       : in String)
452       return     String
453    is
454    begin
455       if Low > Source'Last + 1 or High < Source'First - 1 then
456          raise Index_Error;
457       end if;
458
459       if High >= Low then
460          declare
461             Front_Len : constant Integer :=
462                           Integer'Max (0, Low - Source'First);
463             --  Length of prefix of Source copied to result
464
465             Back_Len  : constant Integer :=
466                           Integer'Max (0, Source'Last - High);
467             --  Length of suffix of Source copied to result
468
469             Result_Length : constant Integer :=
470                               Front_Len + By'Length + Back_Len;
471             --  Length of result
472
473             Result : String (1 .. Result_Length);
474
475          begin
476             Result (1 .. Front_Len) :=
477               Source (Source'First .. Low - 1);
478             Result (Front_Len + 1 .. Front_Len + By'Length) :=
479               By;
480             Result (Front_Len + By'Length + 1 .. Result'Length) :=
481               Source (High + 1 .. Source'Last);
482
483             return Result;
484          end;
485
486       else
487          return Insert (Source, Before => Low, New_Item => By);
488       end if;
489    end Replace_Slice;
490
491    procedure Replace_Slice
492      (Source   : in out String;
493       Low      : in Positive;
494       High     : in Natural;
495       By       : in String;
496       Drop     : in Truncation := Error;
497       Justify  : in Alignment  := Left;
498       Pad      : in Character  := Space)
499    is
500    begin
501       Move (Replace_Slice (Source, Low, High, By), Source, Drop, Justify, Pad);
502    end Replace_Slice;
503
504    ----------
505    -- Tail --
506    ----------
507
508    function Tail
509      (Source : in String;
510       Count  : in Natural;
511       Pad    : in Character := Space)
512       return   String
513    is
514       subtype Result_Type is String (1 .. Count);
515
516    begin
517       if Count < Source'Length then
518          return Result_Type (Source (Source'Last - Count + 1 .. Source'Last));
519
520       --  Pad on left
521
522       else
523          declare
524             Result : Result_Type;
525
526          begin
527             for J in 1 .. Count - Source'Length loop
528                Result (J) := Pad;
529             end loop;
530
531             Result (Count - Source'Length + 1 .. Count) := Source;
532             return Result;
533          end;
534       end if;
535    end Tail;
536
537    procedure Tail
538      (Source  : in out String;
539       Count   : in Natural;
540       Justify : in Alignment := Left;
541       Pad     : in Character := Space)
542    is
543    begin
544       Move (Source  => Tail (Source, Count, Pad),
545             Target  => Source,
546             Drop    => Error,
547             Justify => Justify,
548             Pad     => Pad);
549    end Tail;
550
551    ---------------
552    -- Translate --
553    ---------------
554
555    function Translate
556      (Source  : in String;
557       Mapping : in Maps.Character_Mapping)
558       return    String
559    is
560       Result : String (1 .. Source'Length);
561
562    begin
563       for J in Source'Range loop
564          Result (J - (Source'First - 1)) := Value (Mapping, Source (J));
565       end loop;
566
567       return Result;
568    end Translate;
569
570    procedure Translate
571      (Source  : in out String;
572       Mapping : in Maps.Character_Mapping)
573    is
574    begin
575       for J in Source'Range loop
576          Source (J) := Value (Mapping, Source (J));
577       end loop;
578    end Translate;
579
580    function Translate
581      (Source  : in String;
582       Mapping : in Maps.Character_Mapping_Function)
583       return    String
584    is
585       Result : String (1 .. Source'Length);
586       pragma Unsuppress (Access_Check);
587
588    begin
589       for J in Source'Range loop
590          Result (J - (Source'First - 1)) := Mapping.all (Source (J));
591       end loop;
592
593       return Result;
594    end Translate;
595
596    procedure Translate
597      (Source  : in out String;
598       Mapping : in Maps.Character_Mapping_Function)
599    is
600       pragma Unsuppress (Access_Check);
601    begin
602       for J in Source'Range loop
603          Source (J) := Mapping.all (Source (J));
604       end loop;
605    end Translate;
606
607    ----------
608    -- Trim --
609    ----------
610
611    function Trim
612      (Source : in String;
613       Side   : in Trim_End)
614       return   String
615    is
616       Low, High : Integer;
617
618    begin
619       Low := Index_Non_Blank (Source, Forward);
620
621       --  All blanks case
622
623       if Low = 0 then
624          return "";
625
626       --  At least one non-blank
627
628       else
629          High := Index_Non_Blank (Source, Backward);
630
631          case Side is
632             when Strings.Left =>
633                declare
634                   subtype Result_Type is String (1 .. Source'Last - Low + 1);
635
636                begin
637                   return Result_Type (Source (Low .. Source'Last));
638                end;
639
640             when Strings.Right =>
641                declare
642                   subtype Result_Type is String (1 .. High - Source'First + 1);
643
644                begin
645                   return Result_Type (Source (Source'First .. High));
646                end;
647
648             when Strings.Both =>
649                declare
650                   subtype Result_Type is String (1 .. High - Low + 1);
651
652                begin
653                   return Result_Type (Source (Low .. High));
654                end;
655          end case;
656       end if;
657    end Trim;
658
659    procedure Trim
660      (Source  : in out String;
661       Side    : in Trim_End;
662       Justify : in Alignment := Left;
663       Pad     : in Character := Space)
664    is
665    begin
666       Move (Trim (Source, Side),
667             Source,
668             Justify => Justify,
669             Pad => Pad);
670    end Trim;
671
672    function Trim
673      (Source : in String;
674       Left   : in Maps.Character_Set;
675       Right  : in Maps.Character_Set)
676       return   String
677    is
678       High, Low : Integer;
679
680    begin
681       Low := Index (Source, Set => Left, Test  => Outside, Going => Forward);
682
683       --  Case where source comprises only characters in Left
684
685       if Low = 0 then
686          return "";
687       end if;
688
689       High :=
690         Index (Source, Set => Right, Test  => Outside, Going => Backward);
691
692       --  Case where source comprises only characters in Right
693
694       if High = 0 then
695          return "";
696       end if;
697
698       declare
699          subtype Result_Type is String (1 .. High - Low + 1);
700
701       begin
702          return Result_Type (Source (Low .. High));
703       end;
704    end Trim;
705
706    procedure Trim
707      (Source  : in out String;
708       Left    : in Maps.Character_Set;
709       Right   : in Maps.Character_Set;
710       Justify : in Alignment := Strings.Left;
711       Pad     : in Character := Space)
712    is
713    begin
714       Move (Source  => Trim (Source, Left, Right),
715             Target  => Source,
716             Justify => Justify,
717             Pad     => Pad);
718    end Trim;
719
720 end Ada.Strings.Fixed;