1------------------------------------------------------------------------------
2--                                                                          --
3--                        GNAT RUN-TIME COMPONENTS                          --
4--                                                                          --
5--        A D A . W I D E _ W I D E _ T E X T _ I O . E D I T I N G         --
6--                                                                          --
7--                                 B o d y                                  --
8--                                                                          --
9--          Copyright (C) 1992-2012, 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 3,  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.                                     --
17--                                                                          --
18-- As a special exception under Section 7 of GPL version 3, you are granted --
19-- additional permissions described in the GCC Runtime Library Exception,   --
20-- version 3.1, as published by the Free Software Foundation.               --
21--                                                                          --
22-- You should have received a copy of the GNU General Public License and    --
23-- a copy of the GCC Runtime Library Exception along with this program;     --
24-- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
25-- <http://www.gnu.org/licenses/>.                                          --
26--                                                                          --
27-- GNAT was originally developed  by the GNAT team at  New York University. --
28-- Extensive contributions were provided by Ada Core Technologies Inc.      --
29--                                                                          --
30------------------------------------------------------------------------------
31
32with Ada.Strings.Fixed;
33with Ada.Strings.Wide_Wide_Fixed;
34
35package body Ada.Wide_Wide_Text_IO.Editing is
36
37   package Strings            renames Ada.Strings;
38   package Strings_Fixed      renames Ada.Strings.Fixed;
39   package Strings_Wide_Wide_Fixed renames Ada.Strings.Wide_Wide_Fixed;
40   package Wide_Wide_Text_IO       renames Ada.Wide_Wide_Text_IO;
41
42   -----------------------
43   -- Local_Subprograms --
44   -----------------------
45
46   function To_Wide (C : Character) return Wide_Wide_Character;
47   pragma Inline (To_Wide);
48   --  Convert Character to corresponding Wide_Wide_Character
49
50   ---------------------
51   -- Blank_When_Zero --
52   ---------------------
53
54   function Blank_When_Zero (Pic : Picture) return Boolean is
55   begin
56      return Pic.Contents.Original_BWZ;
57   end Blank_When_Zero;
58
59   --------------------
60   -- Decimal_Output --
61   --------------------
62
63   package body Decimal_Output is
64
65      -----------
66      -- Image --
67      -----------
68
69      function Image
70        (Item       : Num;
71         Pic        : Picture;
72         Currency   : Wide_Wide_String    := Default_Currency;
73         Fill       : Wide_Wide_Character := Default_Fill;
74         Separator  : Wide_Wide_Character := Default_Separator;
75         Radix_Mark : Wide_Wide_Character := Default_Radix_Mark)
76         return Wide_Wide_String
77      is
78      begin
79         return Format_Number
80            (Pic.Contents, Num'Image (Item),
81             Currency, Fill, Separator, Radix_Mark);
82      end Image;
83
84      ------------
85      -- Length --
86      ------------
87
88      function Length
89        (Pic      : Picture;
90         Currency : Wide_Wide_String := Default_Currency) return Natural
91      is
92         Picstr     : constant String := Pic_String (Pic);
93         V_Adjust   : Integer := 0;
94         Cur_Adjust : Integer := 0;
95
96      begin
97         --  Check if Picstr has 'V' or '$'
98
99         --  If 'V', then length is 1 less than otherwise
100
101         --  If '$', then length is Currency'Length-1 more than otherwise
102
103         --  This should use the string handling package ???
104
105         for J in Picstr'Range loop
106            if Picstr (J) = 'V' then
107               V_Adjust := -1;
108
109            elsif Picstr (J) = '$' then
110               Cur_Adjust := Currency'Length - 1;
111            end if;
112         end loop;
113
114         return Picstr'Length - V_Adjust + Cur_Adjust;
115      end Length;
116
117      ---------
118      -- Put --
119      ---------
120
121      procedure Put
122        (File       : Wide_Wide_Text_IO.File_Type;
123         Item       : Num;
124         Pic        : Picture;
125         Currency   : Wide_Wide_String    := Default_Currency;
126         Fill       : Wide_Wide_Character := Default_Fill;
127         Separator  : Wide_Wide_Character := Default_Separator;
128         Radix_Mark : Wide_Wide_Character := Default_Radix_Mark)
129      is
130      begin
131         Wide_Wide_Text_IO.Put (File, Image (Item, Pic,
132                                   Currency, Fill, Separator, Radix_Mark));
133      end Put;
134
135      procedure Put
136        (Item       : Num;
137         Pic        : Picture;
138         Currency   : Wide_Wide_String    := Default_Currency;
139         Fill       : Wide_Wide_Character := Default_Fill;
140         Separator  : Wide_Wide_Character := Default_Separator;
141         Radix_Mark : Wide_Wide_Character := Default_Radix_Mark)
142      is
143      begin
144         Wide_Wide_Text_IO.Put (Image (Item, Pic,
145                             Currency, Fill, Separator, Radix_Mark));
146      end Put;
147
148      procedure Put
149        (To         : out Wide_Wide_String;
150         Item       : Num;
151         Pic        : Picture;
152         Currency   : Wide_Wide_String    := Default_Currency;
153         Fill       : Wide_Wide_Character := Default_Fill;
154         Separator  : Wide_Wide_Character := Default_Separator;
155         Radix_Mark : Wide_Wide_Character := Default_Radix_Mark)
156      is
157         Result : constant Wide_Wide_String :=
158           Image (Item, Pic, Currency, Fill, Separator, Radix_Mark);
159
160      begin
161         if Result'Length > To'Length then
162            raise Wide_Wide_Text_IO.Layout_Error;
163         else
164            Strings_Wide_Wide_Fixed.Move (Source => Result, Target => To,
165                                     Justify => Strings.Right);
166         end if;
167      end Put;
168
169      -----------
170      -- Valid --
171      -----------
172
173      function Valid
174        (Item     : Num;
175         Pic      : Picture;
176         Currency : Wide_Wide_String := Default_Currency) return Boolean
177      is
178      begin
179         declare
180            Temp : constant Wide_Wide_String := Image (Item, Pic, Currency);
181            pragma Warnings (Off, Temp);
182         begin
183            return True;
184         end;
185
186      exception
187         when Layout_Error => return False;
188
189      end Valid;
190   end Decimal_Output;
191
192   ------------
193   -- Expand --
194   ------------
195
196   function Expand (Picture : String) return String is
197      Result        : String (1 .. MAX_PICSIZE);
198      Picture_Index : Integer := Picture'First;
199      Result_Index  : Integer := Result'First;
200      Count         : Natural;
201      Last          : Integer;
202
203   begin
204      if Picture'Length < 1 then
205         raise Picture_Error;
206      end if;
207
208      if Picture (Picture'First) = '(' then
209         raise Picture_Error;
210      end if;
211
212      loop
213         case Picture (Picture_Index) is
214
215            when '(' =>
216
217               --  We now need to scan out the count after a left paren. In
218               --  the non-wide version we used Integer_IO.Get, but that is
219               --  not convenient here, since we don't want to drag in normal
220               --  Text_IO just for this purpose. So we do the scan ourselves,
221               --  with the normal validity checks.
222
223               Last := Picture_Index + 1;
224               Count := 0;
225
226               if Picture (Last) not in '0' .. '9' then
227                  raise Picture_Error;
228               end if;
229
230               Count := Character'Pos (Picture (Last)) - Character'Pos ('0');
231               Last := Last + 1;
232
233               loop
234                  if Last > Picture'Last then
235                     raise Picture_Error;
236                  end if;
237
238                  if Picture (Last) = '_' then
239                     if Picture (Last - 1) = '_' then
240                        raise Picture_Error;
241                     end if;
242
243                  elsif Picture (Last) = ')' then
244                     exit;
245
246                  elsif Picture (Last) not in '0' .. '9' then
247                     raise Picture_Error;
248
249                  else
250                     Count := Count * 10
251                                +  Character'Pos (Picture (Last)) -
252                                   Character'Pos ('0');
253                  end if;
254
255                  Last := Last + 1;
256               end loop;
257
258               --  In what follows note that one copy of the repeated
259               --  character has already been made, so a count of one is
260               --  no-op, and a count of zero erases a character.
261
262               for J in 2 .. Count loop
263                  Result (Result_Index + J - 2) := Picture (Picture_Index - 1);
264               end loop;
265
266               Result_Index := Result_Index + Count - 1;
267
268               --  Last was a ')' throw it away too
269
270               Picture_Index := Last + 1;
271
272            when ')' =>
273               raise Picture_Error;
274
275            when others =>
276               Result (Result_Index) := Picture (Picture_Index);
277               Picture_Index := Picture_Index + 1;
278               Result_Index := Result_Index + 1;
279
280         end case;
281
282         exit when Picture_Index > Picture'Last;
283      end loop;
284
285      return Result (1 .. Result_Index - 1);
286
287   exception
288      when others =>
289         raise Picture_Error;
290   end Expand;
291
292   -------------------
293   -- Format_Number --
294   -------------------
295
296   function Format_Number
297     (Pic                 : Format_Record;
298      Number              : String;
299      Currency_Symbol     : Wide_Wide_String;
300      Fill_Character      : Wide_Wide_Character;
301      Separator_Character : Wide_Wide_Character;
302      Radix_Point         : Wide_Wide_Character) return Wide_Wide_String
303   is
304      Attrs    : Number_Attributes := Parse_Number_String (Number);
305      Position : Integer;
306      Rounded  : String := Number;
307
308      Sign_Position : Integer := Pic.Sign_Position; --  may float.
309
310      Answer       : Wide_Wide_String (1 .. Pic.Picture.Length);
311      Last         : Integer;
312      Currency_Pos : Integer := Pic.Start_Currency;
313
314      Dollar : Boolean := False;
315      --  Overridden immediately if necessary
316
317      Zero : Boolean := True;
318      --  Set to False when a non-zero digit is output
319
320   begin
321
322      --  If the picture has fewer decimal places than the number, the image
323      --  must be rounded according to the usual rules.
324
325      if Attrs.Has_Fraction then
326         declare
327            R : constant Integer :=
328              (Attrs.End_Of_Fraction - Attrs.Start_Of_Fraction + 1)
329                - Pic.Max_Trailing_Digits;
330            R_Pos : Integer;
331
332         begin
333            if R > 0 then
334               R_Pos := Rounded'Length - R;
335
336               if Rounded (R_Pos + 1) > '4' then
337
338                  if Rounded (R_Pos) = '.' then
339                     R_Pos := R_Pos - 1;
340                  end if;
341
342                  if Rounded (R_Pos) /= '9' then
343                     Rounded (R_Pos) := Character'Succ (Rounded (R_Pos));
344                  else
345                     Rounded (R_Pos) := '0';
346                     R_Pos := R_Pos - 1;
347
348                     while R_Pos > 1 loop
349                        if Rounded (R_Pos) = '.' then
350                           R_Pos := R_Pos - 1;
351                        end if;
352
353                        if Rounded (R_Pos) /= '9' then
354                           Rounded (R_Pos) := Character'Succ (Rounded (R_Pos));
355                           exit;
356                        else
357                           Rounded (R_Pos) := '0';
358                           R_Pos := R_Pos - 1;
359                        end if;
360                     end loop;
361
362                     --  The rounding may add a digit in front. Either the
363                     --  leading blank or the sign (already captured) can be
364                     --  overwritten.
365
366                     if R_Pos = 1 then
367                        Rounded (R_Pos) := '1';
368                        Attrs.Start_Of_Int := Attrs.Start_Of_Int - 1;
369                     end if;
370                  end if;
371               end if;
372            end if;
373         end;
374      end if;
375
376      for J in Answer'Range loop
377         Answer (J) := To_Wide (Pic.Picture.Expanded (J));
378      end loop;
379
380      if Pic.Start_Currency /= Invalid_Position then
381         Dollar := Answer (Pic.Start_Currency) = '$';
382      end if;
383
384      --  Fix up "direct inserts" outside the playing field. Set up as one
385      --  loop to do the beginning, one (reverse) loop to do the end.
386
387      Last := 1;
388      loop
389         exit when Last = Pic.Start_Float;
390         exit when Last = Pic.Radix_Position;
391         exit when Answer (Last) = '9';
392
393         case Answer (Last) is
394
395            when '_' =>
396               Answer (Last) := Separator_Character;
397
398            when 'b' =>
399               Answer (Last) := ' ';
400
401            when others =>
402               null;
403
404         end case;
405
406         exit when Last = Answer'Last;
407
408         Last := Last + 1;
409      end loop;
410
411      --  Now for the end...
412
413      for J in reverse Last .. Answer'Last loop
414         exit when J = Pic.Radix_Position;
415
416         --  Do this test First, Separator_Character can equal Pic.Floater
417
418         if Answer (J) = Pic.Floater then
419            exit;
420         end if;
421
422         case Answer (J) is
423
424            when '_' =>
425               Answer (J) := Separator_Character;
426
427            when 'b' =>
428               Answer (J) := ' ';
429
430            when '9' =>
431               exit;
432
433            when others =>
434               null;
435
436         end case;
437      end loop;
438
439      --  Non-floating sign
440
441      if Pic.Start_Currency /= -1
442        and then Answer (Pic.Start_Currency) = '#'
443        and then Pic.Floater /= '#'
444      then
445         if Currency_Symbol'Length >
446            Pic.End_Currency - Pic.Start_Currency + 1
447         then
448            raise Picture_Error;
449
450         elsif Currency_Symbol'Length =
451            Pic.End_Currency - Pic.Start_Currency + 1
452         then
453            Answer (Pic.Start_Currency .. Pic.End_Currency) :=
454              Currency_Symbol;
455
456         elsif Pic.Radix_Position = Invalid_Position
457           or else Pic.Start_Currency < Pic.Radix_Position
458         then
459            Answer (Pic.Start_Currency .. Pic.End_Currency) :=
460                                                        (others => ' ');
461            Answer (Pic.End_Currency - Currency_Symbol'Length + 1 ..
462                    Pic.End_Currency) := Currency_Symbol;
463
464         else
465            Answer (Pic.Start_Currency .. Pic.End_Currency) :=
466                                                        (others => ' ');
467            Answer (Pic.Start_Currency ..
468                    Pic.Start_Currency + Currency_Symbol'Length - 1) :=
469                                                        Currency_Symbol;
470         end if;
471      end if;
472
473      --  Fill in leading digits
474
475      if Attrs.End_Of_Int - Attrs.Start_Of_Int + 1 >
476                                                Pic.Max_Leading_Digits
477      then
478         raise Layout_Error;
479      end if;
480
481      Position :=
482        (if Pic.Radix_Position = Invalid_Position then Answer'Last
483         else Pic.Radix_Position - 1);
484
485      for J in reverse Attrs.Start_Of_Int .. Attrs.End_Of_Int loop
486         while Answer (Position) /= '9'
487                 and then
488               Answer (Position) /= Pic.Floater
489         loop
490            if Answer (Position) = '_' then
491               Answer (Position) := Separator_Character;
492            elsif Answer (Position) = 'b' then
493               Answer (Position) := ' ';
494            end if;
495
496            Position := Position - 1;
497         end loop;
498
499         Answer (Position) := To_Wide (Rounded (J));
500
501         if Rounded (J) /= '0' then
502            Zero := False;
503         end if;
504
505         Position := Position - 1;
506      end loop;
507
508      --  Do lead float
509
510      if Pic.Start_Float = Invalid_Position then
511
512         --  No leading floats, but need to change '9' to '0', '_' to
513         --  Separator_Character and 'b' to ' '.
514
515         for J in Last .. Position loop
516
517            --  Last set when fixing the "uninteresting" leaders above.
518            --  Don't duplicate the work.
519
520            if Answer (J) = '9' then
521               Answer (J) := '0';
522
523            elsif Answer (J) = '_' then
524               Answer (J) := Separator_Character;
525
526            elsif Answer (J) = 'b' then
527               Answer (J) := ' ';
528
529            end if;
530
531         end loop;
532
533      elsif Pic.Floater = '<'
534              or else
535            Pic.Floater = '+'
536              or else
537            Pic.Floater = '-'
538      then
539         for J in Pic.End_Float .. Position loop --  May be null range
540            if Answer (J) = '9' then
541               Answer (J) := '0';
542
543            elsif Answer (J) = '_' then
544               Answer (J) := Separator_Character;
545
546            elsif Answer (J) = 'b' then
547               Answer (J) := ' ';
548
549            end if;
550         end loop;
551
552         if Position > Pic.End_Float then
553            Position := Pic.End_Float;
554         end if;
555
556         for J in Pic.Start_Float .. Position - 1 loop
557            Answer (J) := ' ';
558         end loop;
559
560         Answer (Position) := Pic.Floater;
561         Sign_Position     := Position;
562
563      elsif Pic.Floater = '$' then
564
565         for J in Pic.End_Float .. Position loop --  May be null range
566            if Answer (J) = '9' then
567               Answer (J) := '0';
568
569            elsif Answer (J) = '_' then
570               Answer (J) := ' ';   --  no separator before leftmost digit
571
572            elsif Answer (J) = 'b' then
573               Answer (J) := ' ';
574            end if;
575         end loop;
576
577         if Position > Pic.End_Float then
578            Position := Pic.End_Float;
579         end if;
580
581         for J in Pic.Start_Float .. Position - 1 loop
582            Answer (J) := ' ';
583         end loop;
584
585         Answer (Position) := Pic.Floater;
586         Currency_Pos      := Position;
587
588      elsif Pic.Floater = '*' then
589
590         for J in Pic.End_Float .. Position loop --  May be null range
591            if Answer (J) = '9' then
592               Answer (J) := '0';
593
594            elsif Answer (J) = '_' then
595               Answer (J) := Separator_Character;
596
597            elsif Answer (J) = 'b' then
598               Answer (J) := '*';
599            end if;
600         end loop;
601
602         if Position > Pic.End_Float then
603            Position := Pic.End_Float;
604         end if;
605
606         for J in Pic.Start_Float .. Position loop
607            Answer (J) := '*';
608         end loop;
609
610      else
611         if Pic.Floater = '#' then
612            Currency_Pos := Currency_Symbol'Length;
613         end if;
614
615         for J in reverse Pic.Start_Float .. Position loop
616            case Answer (J) is
617
618               when '*' =>
619                  Answer (J) := Fill_Character;
620
621               when 'Z' | 'b' | '/' | '0' =>
622                  Answer (J) := ' ';
623
624               when '9' =>
625                  Answer (J) := '0';
626
627               when '.' | 'V' | 'v' | '<' | '$' | '+' | '-' =>
628                  null;
629
630               when '#' =>
631                  if Currency_Pos = 0 then
632                     Answer (J) := ' ';
633                  else
634                     Answer (J)   := Currency_Symbol (Currency_Pos);
635                     Currency_Pos := Currency_Pos - 1;
636                  end if;
637
638               when '_' =>
639
640                  case Pic.Floater is
641
642                     when '*' =>
643                        Answer (J) := Fill_Character;
644
645                     when 'Z' | 'b' =>
646                        Answer (J) := ' ';
647
648                     when '#' =>
649                        if Currency_Pos = 0 then
650                           Answer (J) := ' ';
651
652                        else
653                           Answer (J)   := Currency_Symbol (Currency_Pos);
654                           Currency_Pos := Currency_Pos - 1;
655                        end if;
656
657                     when others =>
658                        null;
659
660                  end case;
661
662               when others =>
663                  null;
664
665            end case;
666         end loop;
667
668         if Pic.Floater = '#' and then Currency_Pos /= 0 then
669            raise Layout_Error;
670         end if;
671      end if;
672
673      --  Do sign
674
675      if Sign_Position = Invalid_Position then
676         if Attrs.Negative then
677            raise Layout_Error;
678         end if;
679
680      else
681         if Attrs.Negative then
682            case Answer (Sign_Position) is
683               when 'C' | 'D' | '-' =>
684                  null;
685
686               when '+' =>
687                  Answer (Sign_Position) := '-';
688
689               when '<' =>
690                  Answer (Sign_Position)   := '(';
691                  Answer (Pic.Second_Sign) := ')';
692
693               when others =>
694                  raise Picture_Error;
695
696            end case;
697
698         else --  positive
699
700            case Answer (Sign_Position) is
701
702               when '-' =>
703                  Answer (Sign_Position) := ' ';
704
705               when '<' | 'C' | 'D' =>
706                  Answer (Sign_Position)   := ' ';
707                  Answer (Pic.Second_Sign) := ' ';
708
709               when '+' =>
710                  null;
711
712               when others =>
713                  raise Picture_Error;
714
715            end case;
716         end if;
717      end if;
718
719      --  Fill in trailing digits
720
721      if Pic.Max_Trailing_Digits > 0 then
722
723         if Attrs.Has_Fraction then
724            Position := Attrs.Start_Of_Fraction;
725            Last     := Pic.Radix_Position + 1;
726
727            for J in Last .. Answer'Last loop
728
729               if Answer (J) = '9' or else Answer (J) = Pic.Floater then
730                  Answer (J) := To_Wide (Rounded (Position));
731
732                  if Rounded (Position) /= '0' then
733                     Zero := False;
734                  end if;
735
736                  Position := Position + 1;
737                  Last     := J + 1;
738
739                  --  Used up fraction but remember place in Answer
740
741                  exit when Position > Attrs.End_Of_Fraction;
742
743               elsif Answer (J) = 'b' then
744                  Answer (J) := ' ';
745
746               elsif Answer (J) = '_' then
747                  Answer (J) := Separator_Character;
748
749               end if;
750
751               Last := J + 1;
752            end loop;
753
754            Position := Last;
755
756         else
757            Position := Pic.Radix_Position + 1;
758         end if;
759
760         --  Now fill remaining 9's with zeros and _ with separators
761
762         Last := Answer'Last;
763
764         for J in Position .. Last loop
765            if Answer (J) = '9' then
766               Answer (J) := '0';
767
768            elsif Answer (J) = Pic.Floater then
769               Answer (J) := '0';
770
771            elsif Answer (J) = '_' then
772               Answer (J) := Separator_Character;
773
774            elsif Answer (J) = 'b' then
775               Answer (J) := ' ';
776
777            end if;
778         end loop;
779
780         Position := Last + 1;
781
782      else
783         if Pic.Floater = '#' and then Currency_Pos /= 0 then
784            raise Layout_Error;
785         end if;
786
787         --  No trailing digits, but now J may need to stick in a currency
788         --  symbol or sign.
789
790         Position :=
791           (if Pic.Start_Currency = Invalid_Position then Answer'Last + 1
792            else Pic.Start_Currency);
793      end if;
794
795      for J in Position .. Answer'Last loop
796         if Pic.Start_Currency /= Invalid_Position and then
797            Answer (Pic.Start_Currency) = '#' then
798            Currency_Pos := 1;
799         end if;
800
801         --  Note: There are some weird cases J can imagine with 'b' or '#'
802         --  in currency strings where the following code will cause
803         --  glitches. The trick is to tell when the character in the
804         --  answer should be checked, and when to look at the original
805         --  string. Some other time. RIE 11/26/96 ???
806
807         case Answer (J) is
808            when '*' =>
809               Answer (J) := Fill_Character;
810
811            when 'b' =>
812               Answer (J) := ' ';
813
814            when '#' =>
815               if Currency_Pos > Currency_Symbol'Length then
816                  Answer (J) := ' ';
817
818               else
819                  Answer (J)   := Currency_Symbol (Currency_Pos);
820                  Currency_Pos := Currency_Pos + 1;
821               end if;
822
823            when '_' =>
824
825               case Pic.Floater is
826
827                  when '*' =>
828                     Answer (J) := Fill_Character;
829
830                  when 'Z' | 'z' =>
831                     Answer (J) := ' ';
832
833                  when '#' =>
834                     if Currency_Pos > Currency_Symbol'Length then
835                        Answer (J) := ' ';
836                     else
837                        Answer (J)   := Currency_Symbol (Currency_Pos);
838                        Currency_Pos := Currency_Pos + 1;
839                     end if;
840
841                  when others =>
842                     null;
843
844               end case;
845
846            when others =>
847               exit;
848
849         end case;
850      end loop;
851
852      --  Now get rid of Blank_when_Zero and complete Star fill
853
854      if Zero and then Pic.Blank_When_Zero then
855
856         --  Value is zero, and blank it
857
858         Last := Answer'Last;
859
860         if Dollar then
861            Last := Last - 1 + Currency_Symbol'Length;
862         end if;
863
864         if Pic.Radix_Position /= Invalid_Position and then
865            Answer (Pic.Radix_Position) = 'V' then
866            Last := Last - 1;
867         end if;
868
869         return Wide_Wide_String'(1 .. Last => ' ');
870
871      elsif Zero and then Pic.Star_Fill then
872         Last := Answer'Last;
873
874         if Dollar then
875            Last := Last - 1 + Currency_Symbol'Length;
876         end if;
877
878         if Pic.Radix_Position /= Invalid_Position then
879
880            if Answer (Pic.Radix_Position) = 'V' then
881               Last := Last - 1;
882
883            elsif Dollar then
884               if Pic.Radix_Position > Pic.Start_Currency then
885                  return
886                     Wide_Wide_String'(1 .. Pic.Radix_Position - 1 => '*') &
887                     Radix_Point &
888                     Wide_Wide_String'(Pic.Radix_Position + 1 .. Last => '*');
889
890               else
891                  return
892                     Wide_Wide_String'
893                     (1 ..
894                      Pic.Radix_Position + Currency_Symbol'Length - 2
895                                             => '*') &
896                     Radix_Point &
897                     Wide_Wide_String'
898                       (Pic.Radix_Position + Currency_Symbol'Length .. Last
899                                             => '*');
900               end if;
901
902            else
903               return
904                 Wide_Wide_String'(1 .. Pic.Radix_Position - 1 => '*') &
905                 Radix_Point &
906                 Wide_Wide_String'(Pic.Radix_Position + 1 .. Last => '*');
907            end if;
908         end if;
909
910         return Wide_Wide_String'(1 .. Last => '*');
911      end if;
912
913      --  This was once a simple return statement, now there are nine
914      --  different return cases.  Not to mention the five above to deal
915      --  with zeros.  Why not split things out?
916
917      --  Processing the radix and sign expansion separately
918      --  would require lots of copying--the string and some of its
919      --  indicies--without really simplifying the logic.  The cases are:
920
921      --  1) Expand $, replace '.' with Radix_Point
922      --  2) No currency expansion, replace '.' with Radix_Point
923      --  3) Expand $, radix blanked
924      --  4) No currency expansion, radix blanked
925      --  5) Elide V
926      --  6) Expand $, Elide V
927      --  7) Elide V, Expand $ (Two cases depending on order.)
928      --  8) No radix, expand $
929      --  9) No radix, no currency expansion
930
931      if Pic.Radix_Position /= Invalid_Position then
932
933         if Answer (Pic.Radix_Position) = '.' then
934            Answer (Pic.Radix_Position) := Radix_Point;
935
936            if Dollar then
937
938               --  1) Expand $, replace '.' with Radix_Point
939
940               return Answer (1 .. Currency_Pos - 1) & Currency_Symbol &
941                  Answer (Currency_Pos + 1 .. Answer'Last);
942
943            else
944               --  2) No currency expansion, replace '.' with Radix_Point
945
946               return Answer;
947            end if;
948
949         elsif Answer (Pic.Radix_Position) = ' ' then --  blanked radix.
950            if Dollar then
951
952               --  3) Expand $, radix blanked
953
954               return Answer (1 .. Currency_Pos - 1) & Currency_Symbol &
955                 Answer (Currency_Pos + 1 .. Answer'Last);
956
957            else
958               --  4) No expansion, radix blanked
959
960               return Answer;
961            end if;
962
963         --  V cases
964
965         else
966            if not Dollar then
967
968               --  5) Elide V
969
970               return Answer (1 .. Pic.Radix_Position - 1) &
971                  Answer (Pic.Radix_Position + 1 .. Answer'Last);
972
973            elsif Currency_Pos < Pic.Radix_Position then
974
975               --  6) Expand $, Elide V
976
977               return Answer (1 .. Currency_Pos - 1) & Currency_Symbol &
978                  Answer (Currency_Pos + 1 .. Pic.Radix_Position - 1) &
979                  Answer (Pic.Radix_Position + 1 .. Answer'Last);
980
981            else
982               --  7) Elide V, Expand $
983
984               return Answer (1 .. Pic.Radix_Position - 1) &
985                  Answer (Pic.Radix_Position + 1 .. Currency_Pos - 1) &
986                  Currency_Symbol &
987                  Answer (Currency_Pos + 1 .. Answer'Last);
988            end if;
989         end if;
990
991      elsif Dollar then
992
993         --  8) No radix, expand $
994
995         return Answer (1 .. Currency_Pos - 1) & Currency_Symbol &
996            Answer (Currency_Pos + 1 .. Answer'Last);
997
998      else
999         --  9) No radix, no currency expansion
1000
1001         return Answer;
1002      end if;
1003   end Format_Number;
1004
1005   -------------------------
1006   -- Parse_Number_String --
1007   -------------------------
1008
1009   function Parse_Number_String (Str : String) return Number_Attributes is
1010      Answer : Number_Attributes;
1011
1012   begin
1013      for J in Str'Range loop
1014         case Str (J) is
1015
1016            when ' ' =>
1017               null; --  ignore
1018
1019            when '1' .. '9' =>
1020
1021               --  Decide if this is the start of a number.
1022               --  If so, figure out which one...
1023
1024               if Answer.Has_Fraction then
1025                  Answer.End_Of_Fraction := J;
1026               else
1027                  if Answer.Start_Of_Int = Invalid_Position then
1028                     --  start integer
1029                     Answer.Start_Of_Int := J;
1030                  end if;
1031                  Answer.End_Of_Int := J;
1032               end if;
1033
1034            when '0' =>
1035
1036               --  Only count a zero before the decimal point if it follows a
1037               --  non-zero digit.  After the decimal point, zeros will be
1038               --  counted if followed by a non-zero digit.
1039
1040               if not Answer.Has_Fraction then
1041                  if Answer.Start_Of_Int /= Invalid_Position then
1042                     Answer.End_Of_Int := J;
1043                  end if;
1044               end if;
1045
1046            when '-' =>
1047
1048               --  Set negative
1049
1050               Answer.Negative := True;
1051
1052            when '.' =>
1053
1054               --  Close integer, start fraction
1055
1056               if Answer.Has_Fraction then
1057                  raise Picture_Error;
1058               end if;
1059
1060               --  Two decimal points is a no-no
1061
1062               Answer.Has_Fraction    := True;
1063               Answer.End_Of_Fraction := J;
1064
1065               --  Could leave this at Invalid_Position, but this seems the
1066               --  right way to indicate a null range...
1067
1068               Answer.Start_Of_Fraction := J + 1;
1069               Answer.End_Of_Int        := J - 1;
1070
1071            when others =>
1072               raise Picture_Error; -- can this happen? probably not!
1073         end case;
1074      end loop;
1075
1076      if Answer.Start_Of_Int = Invalid_Position then
1077         Answer.Start_Of_Int := Answer.End_Of_Int + 1;
1078      end if;
1079
1080      --  No significant (intger) digits needs a null range
1081
1082      return Answer;
1083   end Parse_Number_String;
1084
1085   ----------------
1086   -- Pic_String --
1087   ----------------
1088
1089   --  The following ensures that we return B and not b being careful not
1090   --  to break things which expect lower case b for blank. See CXF3A02.
1091
1092   function Pic_String (Pic : Picture) return String is
1093      Temp : String (1 .. Pic.Contents.Picture.Length) :=
1094        Pic.Contents.Picture.Expanded;
1095   begin
1096      for J in Temp'Range loop
1097         if Temp (J) = 'b' then
1098            Temp (J) := 'B';
1099         end if;
1100      end loop;
1101
1102      return Temp;
1103   end Pic_String;
1104
1105   ------------------
1106   -- Precalculate --
1107   ------------------
1108
1109   procedure Precalculate  (Pic : in out Format_Record) is
1110
1111      Computed_BWZ : Boolean := True;
1112
1113      type Legality is  (Okay, Reject);
1114      State : Legality := Reject;
1115      --  Start in reject, which will reject null strings
1116
1117      Index : Pic_Index := Pic.Picture.Expanded'First;
1118
1119      function At_End return Boolean;
1120      pragma Inline (At_End);
1121
1122      procedure Set_State (L : Legality);
1123      pragma Inline (Set_State);
1124
1125      function Look return Character;
1126      pragma Inline (Look);
1127
1128      function Is_Insert return Boolean;
1129      pragma Inline (Is_Insert);
1130
1131      procedure Skip;
1132      pragma Inline (Skip);
1133
1134      procedure Trailing_Currency;
1135      procedure Trailing_Bracket;
1136      procedure Number_Fraction;
1137      procedure Number_Completion;
1138      procedure Number_Fraction_Or_Bracket;
1139      procedure Number_Fraction_Or_Z_Fill;
1140      procedure Zero_Suppression;
1141      procedure Floating_Bracket;
1142      procedure Number_Fraction_Or_Star_Fill;
1143      procedure Star_Suppression;
1144      procedure Number_Fraction_Or_Dollar;
1145      procedure Leading_Dollar;
1146      procedure Number_Fraction_Or_Pound;
1147      procedure Leading_Pound;
1148      procedure Picture;
1149      procedure Floating_Plus;
1150      procedure Floating_Minus;
1151      procedure Picture_Plus;
1152      procedure Picture_Minus;
1153      procedure Picture_Bracket;
1154      procedure Number;
1155      procedure Optional_RHS_Sign;
1156      procedure Picture_String;
1157
1158      ------------
1159      -- At_End --
1160      ------------
1161
1162      function At_End return Boolean is
1163      begin
1164         return Index > Pic.Picture.Length;
1165      end At_End;
1166
1167      ----------------------
1168      -- Floating_Bracket --
1169      ----------------------
1170
1171      --  Note that Floating_Bracket is only called with an acceptable
1172      --  prefix. But we don't set Okay, because we must end with a '>'.
1173
1174      procedure Floating_Bracket is
1175      begin
1176         Pic.Floater := '<';
1177         Pic.End_Float := Index;
1178         Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
1179
1180         --  First bracket wasn't counted...
1181
1182         Skip; --  known '<'
1183
1184         loop
1185            if At_End then
1186               return;
1187            end if;
1188
1189            case Look is
1190
1191               when '_' | '0' | '/' =>
1192                  Pic.End_Float := Index;
1193                  Skip;
1194
1195               when 'B' | 'b'  =>
1196                  Pic.End_Float := Index;
1197                  Pic.Picture.Expanded (Index) := 'b';
1198                  Skip;
1199
1200               when '<' =>
1201                  Pic.End_Float := Index;
1202                  Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
1203                  Skip;
1204
1205               when '9' =>
1206                  Number_Completion;
1207
1208               when '$' =>
1209                  Leading_Dollar;
1210
1211               when '#' =>
1212                  Leading_Pound;
1213
1214               when 'V' | 'v' | '.' =>
1215                  Pic.Radix_Position := Index;
1216                  Skip;
1217                  Number_Fraction_Or_Bracket;
1218                  return;
1219
1220               when others =>
1221               return;
1222            end case;
1223         end loop;
1224      end Floating_Bracket;
1225
1226      --------------------
1227      -- Floating_Minus --
1228      --------------------
1229
1230      procedure Floating_Minus is
1231      begin
1232         loop
1233            if At_End then
1234               return;
1235            end if;
1236
1237            case Look is
1238               when '_' | '0' | '/' =>
1239                  Pic.End_Float := Index;
1240                  Skip;
1241
1242               when 'B' | 'b'  =>
1243                  Pic.End_Float := Index;
1244                  Pic.Picture.Expanded (Index) := 'b';
1245                  Skip;
1246
1247               when '-' =>
1248                  Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
1249                  Pic.End_Float := Index;
1250                  Skip;
1251
1252               when '9' =>
1253                  Number_Completion;
1254                  return;
1255
1256               when '.' | 'V' | 'v' =>
1257                  Pic.Radix_Position := Index;
1258                  Skip; --  Radix
1259
1260                  while Is_Insert loop
1261                     Skip;
1262                  end loop;
1263
1264                  if At_End then
1265                     return;
1266                  end if;
1267
1268                  if Look = '-' then
1269                     loop
1270                        if At_End then
1271                           return;
1272                        end if;
1273
1274                        case Look is
1275
1276                           when '-' =>
1277                              Pic.Max_Trailing_Digits :=
1278                                Pic.Max_Trailing_Digits + 1;
1279                              Pic.End_Float := Index;
1280                              Skip;
1281
1282                           when '_' | '0' | '/' =>
1283                              Skip;
1284
1285                           when 'B' | 'b'  =>
1286                              Pic.Picture.Expanded (Index) := 'b';
1287                              Skip;
1288
1289                           when others =>
1290                              return;
1291
1292                        end case;
1293                     end loop;
1294
1295                  else
1296                     Number_Completion;
1297                  end if;
1298
1299                  return;
1300
1301               when others =>
1302                  return;
1303            end case;
1304         end loop;
1305      end Floating_Minus;
1306
1307      -------------------
1308      -- Floating_Plus --
1309      -------------------
1310
1311      procedure Floating_Plus is
1312      begin
1313         loop
1314            if At_End then
1315               return;
1316            end if;
1317
1318            case Look is
1319               when '_' | '0' | '/' =>
1320                  Pic.End_Float := Index;
1321                  Skip;
1322
1323               when 'B' | 'b'  =>
1324                  Pic.End_Float := Index;
1325                  Pic.Picture.Expanded (Index) := 'b';
1326                  Skip;
1327
1328               when '+' =>
1329                  Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
1330                  Pic.End_Float := Index;
1331                  Skip;
1332
1333               when '9' =>
1334                  Number_Completion;
1335                  return;
1336
1337               when '.' | 'V' | 'v' =>
1338                  Pic.Radix_Position := Index;
1339                  Skip; --  Radix
1340
1341                  while Is_Insert loop
1342                     Skip;
1343                  end loop;
1344
1345                  if At_End then
1346                     return;
1347                  end if;
1348
1349                  if Look = '+' then
1350                     loop
1351                        if At_End then
1352                           return;
1353                        end if;
1354
1355                        case Look is
1356
1357                           when '+' =>
1358                              Pic.Max_Trailing_Digits :=
1359                                Pic.Max_Trailing_Digits + 1;
1360                              Pic.End_Float := Index;
1361                              Skip;
1362
1363                           when '_' | '0' | '/' =>
1364                              Skip;
1365
1366                           when 'B' | 'b'  =>
1367                              Pic.Picture.Expanded (Index) := 'b';
1368                              Skip;
1369
1370                           when others =>
1371                              return;
1372
1373                        end case;
1374                     end loop;
1375
1376                  else
1377                     Number_Completion;
1378                  end if;
1379
1380                  return;
1381
1382               when others =>
1383                  return;
1384
1385            end case;
1386         end loop;
1387      end Floating_Plus;
1388
1389      ---------------
1390      -- Is_Insert --
1391      ---------------
1392
1393      function Is_Insert return Boolean is
1394      begin
1395         if At_End then
1396            return False;
1397         end if;
1398
1399         case Pic.Picture.Expanded (Index) is
1400
1401            when '_' | '0' | '/' => return True;
1402
1403            when 'B' | 'b' =>
1404               Pic.Picture.Expanded (Index) := 'b'; --  canonical
1405               return True;
1406
1407            when others => return False;
1408         end case;
1409      end Is_Insert;
1410
1411      --------------------
1412      -- Leading_Dollar --
1413      --------------------
1414
1415      --  Note that Leading_Dollar can be called in either State. It will set
1416      --  state to Okay only if a 9 or (second) is encountered.
1417
1418      --  Also notice the tricky bit with State and Zero_Suppression.
1419      --  Zero_Suppression is Picture_Error if a '$' or a '9' has been
1420      --  encountered, exactly the cases where State has been set.
1421
1422      procedure Leading_Dollar is
1423      begin
1424         --  Treat as a floating dollar, and unwind otherwise
1425
1426         Pic.Floater := '$';
1427         Pic.Start_Currency := Index;
1428         Pic.End_Currency := Index;
1429         Pic.Start_Float := Index;
1430         Pic.End_Float := Index;
1431
1432         --  Don't increment Pic.Max_Leading_Digits, we need one "real"
1433         --  currency place.
1434
1435         Skip; --  known '$'
1436
1437         loop
1438            if At_End then
1439               return;
1440            end if;
1441
1442            case Look is
1443
1444               when '_' | '0' | '/' =>
1445                  Pic.End_Float := Index;
1446                  Skip;
1447
1448                  --  A trailing insertion character is not part of the
1449                  --  floating currency, so need to look ahead.
1450
1451                  if Look /= '$' then
1452                     Pic.End_Float := Pic.End_Float - 1;
1453                  end if;
1454
1455               when 'B' | 'b'  =>
1456                  Pic.End_Float := Index;
1457                  Pic.Picture.Expanded (Index) := 'b';
1458                  Skip;
1459
1460               when 'Z' | 'z' =>
1461                  Pic.Picture.Expanded (Index) := 'Z'; -- consistency
1462
1463                  if State = Okay then
1464                     raise Picture_Error;
1465                  else
1466                     --  Will overwrite Floater and Start_Float
1467
1468                     Zero_Suppression;
1469                  end if;
1470
1471               when '*' =>
1472                  if State = Okay then
1473                     raise Picture_Error;
1474                  else
1475                     --  Will overwrite Floater and Start_Float
1476
1477                     Star_Suppression;
1478                  end if;
1479
1480               when '$' =>
1481                  Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
1482                  Pic.End_Float := Index;
1483                  Pic.End_Currency := Index;
1484                  Set_State (Okay); Skip;
1485
1486               when '9' =>
1487                  if State /= Okay then
1488                     Pic.Floater := '!';
1489                     Pic.Start_Float := Invalid_Position;
1490                     Pic.End_Float := Invalid_Position;
1491                  end if;
1492
1493                  --  A single dollar does not a floating make
1494
1495                  Number_Completion;
1496                  return;
1497
1498               when 'V' | 'v' | '.' =>
1499                  if State /= Okay then
1500                     Pic.Floater := '!';
1501                     Pic.Start_Float := Invalid_Position;
1502                     Pic.End_Float := Invalid_Position;
1503                  end if;
1504
1505                  --  Only one dollar before the sign is okay, but doesn't
1506                  --  float.
1507
1508                  Pic.Radix_Position := Index;
1509                  Skip;
1510                  Number_Fraction_Or_Dollar;
1511                  return;
1512
1513               when others =>
1514                  return;
1515
1516            end case;
1517         end loop;
1518      end Leading_Dollar;
1519
1520      -------------------
1521      -- Leading_Pound --
1522      -------------------
1523
1524      --  This one is complex!  A Leading_Pound can be fixed or floating,
1525      --  but in some cases the decision has to be deferred until we leave
1526      --  this procedure.  Also note that Leading_Pound can be called in
1527      --  either State.
1528
1529      --  It will set state to Okay only if a 9 or (second) # is encountered
1530
1531      --  One Last note:  In ambiguous cases, the currency is treated as
1532      --  floating unless there is only one '#'.
1533
1534      procedure Leading_Pound is
1535
1536         Inserts : Boolean := False;
1537         --  Set to True if a '_', '0', '/', 'B', or 'b' is encountered
1538
1539         Must_Float : Boolean := False;
1540         --  Set to true if a '#' occurs after an insert
1541
1542      begin
1543         --  Treat as a floating currency. If it isn't, this will be
1544         --  overwritten later.
1545
1546         Pic.Floater := '#';
1547
1548         Pic.Start_Currency := Index;
1549         Pic.End_Currency := Index;
1550         Pic.Start_Float := Index;
1551         Pic.End_Float := Index;
1552
1553         --  Don't increment Pic.Max_Leading_Digits, we need one "real"
1554         --  currency place.
1555
1556         Pic.Max_Currency_Digits := 1; --  we've seen one.
1557
1558         Skip; --  known '#'
1559
1560         loop
1561            if At_End then
1562               return;
1563            end if;
1564
1565            case Look is
1566
1567               when '_' | '0' | '/' =>
1568                  Pic.End_Float := Index;
1569                  Inserts := True;
1570                  Skip;
1571
1572               when 'B' | 'b'  =>
1573                  Pic.Picture.Expanded (Index) := 'b';
1574                  Pic.End_Float := Index;
1575                  Inserts := True;
1576                  Skip;
1577
1578               when 'Z' | 'z' =>
1579                  Pic.Picture.Expanded (Index) := 'Z'; -- consistency
1580
1581                  if Must_Float then
1582                     raise Picture_Error;
1583                  else
1584                     Pic.Max_Leading_Digits := 0;
1585
1586                     --  Will overwrite Floater and Start_Float
1587
1588                     Zero_Suppression;
1589                  end if;
1590
1591               when '*' =>
1592                  if Must_Float then
1593                     raise Picture_Error;
1594                  else
1595                     Pic.Max_Leading_Digits := 0;
1596
1597                     --  Will overwrite Floater and Start_Float
1598
1599                     Star_Suppression;
1600                  end if;
1601
1602               when '#' =>
1603                  if Inserts then
1604                     Must_Float := True;
1605                  end if;
1606
1607                  Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
1608                  Pic.End_Float := Index;
1609                  Pic.End_Currency := Index;
1610                  Set_State (Okay);
1611                  Skip;
1612
1613               when '9' =>
1614                  if State /= Okay then
1615
1616                     --  A single '#' doesn't float
1617
1618                     Pic.Floater := '!';
1619                     Pic.Start_Float := Invalid_Position;
1620                     Pic.End_Float := Invalid_Position;
1621                  end if;
1622
1623                  Number_Completion;
1624                  return;
1625
1626               when 'V' | 'v' | '.' =>
1627                  if State /= Okay then
1628                     Pic.Floater := '!';
1629                     Pic.Start_Float := Invalid_Position;
1630                     Pic.End_Float := Invalid_Position;
1631                  end if;
1632
1633                  --  Only one pound before the sign is okay, but doesn't
1634                  --  float.
1635
1636                  Pic.Radix_Position := Index;
1637                  Skip;
1638                  Number_Fraction_Or_Pound;
1639                  return;
1640
1641               when others =>
1642                  return;
1643            end case;
1644         end loop;
1645      end Leading_Pound;
1646
1647      ----------
1648      -- Look --
1649      ----------
1650
1651      function Look return Character is
1652      begin
1653         if At_End then
1654            raise Picture_Error;
1655         end if;
1656
1657         return Pic.Picture.Expanded (Index);
1658      end Look;
1659
1660      ------------
1661      -- Number --
1662      ------------
1663
1664      procedure Number is
1665      begin
1666         loop
1667
1668            case Look is
1669               when '_' | '0' | '/' =>
1670                  Skip;
1671
1672               when 'B' | 'b'  =>
1673                  Pic.Picture.Expanded (Index) := 'b';
1674                  Skip;
1675
1676               when '9' =>
1677                  Computed_BWZ := False;
1678                  Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
1679                  Set_State (Okay);
1680                  Skip;
1681
1682               when '.' | 'V' | 'v' =>
1683                  Pic.Radix_Position := Index;
1684                  Skip;
1685                  Number_Fraction;
1686                  return;
1687
1688               when others =>
1689                  return;
1690
1691            end case;
1692
1693            if At_End then
1694               return;
1695            end if;
1696
1697            --  Will return in Okay state if a '9' was seen
1698
1699         end loop;
1700      end Number;
1701
1702      -----------------------
1703      -- Number_Completion --
1704      -----------------------
1705
1706      procedure Number_Completion is
1707      begin
1708         while not At_End loop
1709            case Look is
1710
1711               when '_' | '0' | '/' =>
1712                  Skip;
1713
1714               when 'B' | 'b'  =>
1715                  Pic.Picture.Expanded (Index) := 'b';
1716                  Skip;
1717
1718               when '9' =>
1719                  Computed_BWZ := False;
1720                  Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
1721                  Set_State (Okay);
1722                  Skip;
1723
1724               when 'V' | 'v' | '.' =>
1725                  Pic.Radix_Position := Index;
1726                  Skip;
1727                  Number_Fraction;
1728                  return;
1729
1730               when others =>
1731                  return;
1732            end case;
1733         end loop;
1734      end Number_Completion;
1735
1736      ---------------------
1737      -- Number_Fraction --
1738      ---------------------
1739
1740      procedure Number_Fraction is
1741      begin
1742         --  Note that number fraction can be called in either State.
1743         --  It will set state to Valid only if a 9 is encountered.
1744
1745         loop
1746            if At_End then
1747               return;
1748            end if;
1749
1750            case Look is
1751               when '_' | '0' | '/' =>
1752                  Skip;
1753
1754               when 'B' | 'b'  =>
1755                  Pic.Picture.Expanded (Index) := 'b';
1756                  Skip;
1757
1758               when '9' =>
1759                  Computed_BWZ := False;
1760                  Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1;
1761                  Set_State (Okay); Skip;
1762
1763               when others =>
1764                  return;
1765            end case;
1766         end loop;
1767      end Number_Fraction;
1768
1769      --------------------------------
1770      -- Number_Fraction_Or_Bracket --
1771      --------------------------------
1772
1773      procedure Number_Fraction_Or_Bracket is
1774      begin
1775         loop
1776            if At_End then
1777               return;
1778            end if;
1779
1780            case Look is
1781
1782               when '_' | '0' | '/' => Skip;
1783
1784               when 'B' | 'b'  =>
1785                  Pic.Picture.Expanded (Index) := 'b';
1786                  Skip;
1787
1788               when '<' =>
1789                  Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1;
1790                  Pic.End_Float := Index;
1791                  Skip;
1792
1793                  loop
1794                     if At_End then
1795                        return;
1796                     end if;
1797
1798                     case Look is
1799                        when '_' | '0' | '/' =>
1800                           Skip;
1801
1802                        when 'B' | 'b'  =>
1803                           Pic.Picture.Expanded (Index) := 'b';
1804                           Skip;
1805
1806                        when '<' =>
1807                           Pic.Max_Trailing_Digits :=
1808                             Pic.Max_Trailing_Digits + 1;
1809                           Pic.End_Float := Index;
1810                           Skip;
1811
1812                        when others =>
1813                           return;
1814                     end case;
1815                  end loop;
1816
1817               when others =>
1818                  Number_Fraction;
1819                  return;
1820            end case;
1821         end loop;
1822      end Number_Fraction_Or_Bracket;
1823
1824      -------------------------------
1825      -- Number_Fraction_Or_Dollar --
1826      -------------------------------
1827
1828      procedure Number_Fraction_Or_Dollar is
1829      begin
1830         loop
1831            if At_End then
1832               return;
1833            end if;
1834
1835            case Look is
1836               when '_' | '0' | '/' =>
1837                  Skip;
1838
1839               when 'B' | 'b'  =>
1840                  Pic.Picture.Expanded (Index) := 'b';
1841                  Skip;
1842
1843               when '$' =>
1844                  Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1;
1845                  Pic.End_Float := Index;
1846                  Skip;
1847
1848                  loop
1849                     if At_End then
1850                        return;
1851                     end if;
1852
1853                     case Look is
1854                        when '_' | '0' | '/' =>
1855                           Skip;
1856
1857                        when 'B' | 'b'  =>
1858                           Pic.Picture.Expanded (Index) := 'b';
1859                           Skip;
1860
1861                        when '$' =>
1862                           Pic.Max_Trailing_Digits :=
1863                             Pic.Max_Trailing_Digits + 1;
1864                           Pic.End_Float := Index;
1865                           Skip;
1866
1867                        when others =>
1868                           return;
1869                     end case;
1870                  end loop;
1871
1872               when others =>
1873                  Number_Fraction;
1874                  return;
1875            end case;
1876         end loop;
1877      end Number_Fraction_Or_Dollar;
1878
1879      ------------------------------
1880      -- Number_Fraction_Or_Pound --
1881      ------------------------------
1882
1883      procedure Number_Fraction_Or_Pound is
1884      begin
1885         loop
1886            if At_End then
1887               return;
1888            end if;
1889
1890            case Look is
1891
1892               when '_' | '0' | '/' =>
1893                  Skip;
1894
1895               when 'B' | 'b'  =>
1896                  Pic.Picture.Expanded (Index) := 'b';
1897                  Skip;
1898
1899               when '#' =>
1900                  Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1;
1901                  Pic.End_Float := Index;
1902                  Skip;
1903
1904                  loop
1905                     if At_End then
1906                        return;
1907                     end if;
1908
1909                     case Look is
1910
1911                        when '_' | '0' | '/' =>
1912                           Skip;
1913
1914                        when 'B' | 'b'  =>
1915                           Pic.Picture.Expanded (Index) := 'b';
1916                           Skip;
1917
1918                        when '#' =>
1919                           Pic.Max_Trailing_Digits :=
1920                             Pic.Max_Trailing_Digits + 1;
1921                           Pic.End_Float := Index;
1922                           Skip;
1923
1924                        when others =>
1925                           return;
1926
1927                     end case;
1928                  end loop;
1929
1930               when others =>
1931                  Number_Fraction;
1932                  return;
1933
1934            end case;
1935         end loop;
1936      end Number_Fraction_Or_Pound;
1937
1938      ----------------------------------
1939      -- Number_Fraction_Or_Star_Fill --
1940      ----------------------------------
1941
1942      procedure Number_Fraction_Or_Star_Fill is
1943      begin
1944         loop
1945            if At_End then
1946               return;
1947            end if;
1948
1949            case Look is
1950
1951               when '_' | '0' | '/' =>
1952                  Skip;
1953
1954               when 'B' | 'b'  =>
1955                  Pic.Picture.Expanded (Index) := 'b';
1956                  Skip;
1957
1958               when '*' =>
1959                  Pic.Star_Fill := True;
1960                  Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1;
1961                  Pic.End_Float := Index;
1962                  Skip;
1963
1964                  loop
1965                     if At_End then
1966                        return;
1967                     end if;
1968
1969                     case Look is
1970
1971                        when '_' | '0' | '/' =>
1972                           Skip;
1973
1974                        when 'B' | 'b'  =>
1975                           Pic.Picture.Expanded (Index) := 'b';
1976                           Skip;
1977
1978                        when '*' =>
1979                           Pic.Star_Fill := True;
1980                           Pic.Max_Trailing_Digits :=
1981                             Pic.Max_Trailing_Digits + 1;
1982                           Pic.End_Float := Index;
1983                           Skip;
1984
1985                        when others =>
1986                           return;
1987                     end case;
1988                  end loop;
1989
1990               when others =>
1991                  Number_Fraction;
1992                  return;
1993
1994            end case;
1995         end loop;
1996      end Number_Fraction_Or_Star_Fill;
1997
1998      -------------------------------
1999      -- Number_Fraction_Or_Z_Fill --
2000      -------------------------------
2001
2002      procedure Number_Fraction_Or_Z_Fill is
2003      begin
2004         loop
2005            if At_End then
2006               return;
2007            end if;
2008
2009            case Look is
2010
2011               when '_' | '0' | '/' =>
2012                  Skip;
2013
2014               when 'B' | 'b'  =>
2015                  Pic.Picture.Expanded (Index) := 'b';
2016                  Skip;
2017
2018               when 'Z' | 'z' =>
2019                  Pic.Max_Trailing_Digits := Pic.Max_Trailing_Digits + 1;
2020                  Pic.End_Float := Index;
2021                  Pic.Picture.Expanded (Index) := 'Z'; -- consistency
2022
2023                  Skip;
2024
2025                  loop
2026                     if At_End then
2027                        return;
2028                     end if;
2029
2030                     case Look is
2031
2032                        when '_' | '0' | '/' =>
2033                           Skip;
2034
2035                        when 'B' | 'b'  =>
2036                           Pic.Picture.Expanded (Index) := 'b';
2037                           Skip;
2038
2039                        when 'Z' | 'z' =>
2040                           Pic.Picture.Expanded (Index) := 'Z'; -- consistency
2041
2042                           Pic.Max_Trailing_Digits :=
2043                             Pic.Max_Trailing_Digits + 1;
2044                           Pic.End_Float := Index;
2045                           Skip;
2046
2047                        when others =>
2048                           return;
2049                     end case;
2050                  end loop;
2051
2052               when others =>
2053                  Number_Fraction;
2054                  return;
2055            end case;
2056         end loop;
2057      end Number_Fraction_Or_Z_Fill;
2058
2059      -----------------------
2060      -- Optional_RHS_Sign --
2061      -----------------------
2062
2063      procedure Optional_RHS_Sign is
2064      begin
2065         if At_End then
2066            return;
2067         end if;
2068
2069         case Look is
2070
2071            when '+' | '-' =>
2072               Pic.Sign_Position := Index;
2073               Skip;
2074               return;
2075
2076            when 'C' | 'c' =>
2077               Pic.Sign_Position := Index;
2078               Pic.Picture.Expanded (Index) := 'C';
2079               Skip;
2080
2081               if Look = 'R' or else Look = 'r' then
2082                  Pic.Second_Sign := Index;
2083                  Pic.Picture.Expanded (Index) := 'R';
2084                  Skip;
2085
2086               else
2087                  raise Picture_Error;
2088               end if;
2089
2090               return;
2091
2092            when 'D' | 'd' =>
2093               Pic.Sign_Position := Index;
2094               Pic.Picture.Expanded (Index) := 'D';
2095               Skip;
2096
2097               if Look = 'B' or else Look = 'b' then
2098                  Pic.Second_Sign := Index;
2099                  Pic.Picture.Expanded (Index) := 'B';
2100                  Skip;
2101
2102               else
2103                  raise Picture_Error;
2104               end if;
2105
2106               return;
2107
2108            when '>' =>
2109               if Pic.Picture.Expanded (Pic.Sign_Position) = '<' then
2110                  Pic.Second_Sign := Index;
2111                  Skip;
2112
2113               else
2114                  raise Picture_Error;
2115               end if;
2116
2117            when others =>
2118               return;
2119
2120         end case;
2121      end Optional_RHS_Sign;
2122
2123      -------------
2124      -- Picture --
2125      -------------
2126
2127      --  Note that Picture can be called in either State
2128
2129      --  It will set state to Valid only if a 9 is encountered or floating
2130      --  currency is called.
2131
2132      procedure Picture is
2133      begin
2134         loop
2135            if At_End then
2136               return;
2137            end if;
2138
2139            case Look is
2140
2141               when '_' | '0' | '/' =>
2142                  Skip;
2143
2144               when 'B' | 'b'  =>
2145                  Pic.Picture.Expanded (Index) := 'b';
2146                  Skip;
2147
2148               when '$' =>
2149                  Leading_Dollar;
2150                  return;
2151
2152               when '#' =>
2153                  Leading_Pound;
2154                  return;
2155
2156               when '9' =>
2157                  Computed_BWZ := False;
2158                  Set_State (Okay);
2159                  Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
2160                  Skip;
2161
2162               when 'V' | 'v' | '.' =>
2163                  Pic.Radix_Position := Index;
2164                  Skip;
2165                  Number_Fraction;
2166                  Trailing_Currency;
2167                  return;
2168
2169               when others =>
2170                  return;
2171
2172            end case;
2173         end loop;
2174      end Picture;
2175
2176      ---------------------
2177      -- Picture_Bracket --
2178      ---------------------
2179
2180      procedure Picture_Bracket is
2181      begin
2182         Pic.Sign_Position := Index;
2183         Pic.Sign_Position := Index;
2184
2185         --  Treat as a floating sign, and unwind otherwise
2186
2187         Pic.Floater := '<';
2188         Pic.Start_Float := Index;
2189         Pic.End_Float := Index;
2190
2191         --  Don't increment Pic.Max_Leading_Digits, we need one "real"
2192         --  sign place.
2193
2194         Skip; --  Known Bracket
2195
2196         loop
2197            case Look is
2198
2199               when '_' | '0' | '/' =>
2200                  Pic.End_Float := Index;
2201                  Skip;
2202
2203               when 'B' | 'b'  =>
2204                  Pic.End_Float := Index;
2205                  Pic.Picture.Expanded (Index) := 'b';
2206                  Skip;
2207
2208               when '<' =>
2209                  Set_State (Okay);  --  "<<>" is enough.
2210                  Floating_Bracket;
2211                  Trailing_Currency;
2212                  Trailing_Bracket;
2213                  return;
2214
2215               when '$' | '#' | '9' | '*' =>
2216                  if State /= Okay then
2217                     Pic.Floater := '!';
2218                     Pic.Start_Float := Invalid_Position;
2219                     Pic.End_Float := Invalid_Position;
2220                  end if;
2221
2222                  Picture;
2223                  Trailing_Bracket;
2224                  Set_State (Okay);
2225                  return;
2226
2227               when '.' | 'V' | 'v' =>
2228                  if State /= Okay then
2229                     Pic.Floater := '!';
2230                     Pic.Start_Float := Invalid_Position;
2231                     Pic.End_Float := Invalid_Position;
2232                  end if;
2233
2234                  --  Don't assume that state is okay, haven't seen a digit
2235
2236                  Picture;
2237                  Trailing_Bracket;
2238                  return;
2239
2240               when others =>
2241                  raise Picture_Error;
2242
2243            end case;
2244         end loop;
2245      end Picture_Bracket;
2246
2247      -------------------
2248      -- Picture_Minus --
2249      -------------------
2250
2251      procedure Picture_Minus is
2252      begin
2253         Pic.Sign_Position := Index;
2254
2255         --  Treat as a floating sign, and unwind otherwise
2256
2257         Pic.Floater := '-';
2258         Pic.Start_Float := Index;
2259         Pic.End_Float := Index;
2260
2261         --  Don't increment Pic.Max_Leading_Digits, we need one "real"
2262         --  sign place.
2263
2264         Skip; --  Known Minus
2265
2266         loop
2267            case Look is
2268
2269               when '_' | '0' | '/' =>
2270                  Pic.End_Float := Index;
2271                  Skip;
2272
2273               when 'B' | 'b'  =>
2274                  Pic.End_Float := Index;
2275                  Pic.Picture.Expanded (Index) := 'b';
2276                  Skip;
2277
2278               when '-' =>
2279                  Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
2280                  Pic.End_Float := Index;
2281                  Skip;
2282                  Set_State (Okay);  --  "-- " is enough.
2283                  Floating_Minus;
2284                  Trailing_Currency;
2285                  return;
2286
2287               when '$' | '#' | '9' | '*' =>
2288                  if State /= Okay then
2289                     Pic.Floater := '!';
2290                     Pic.Start_Float := Invalid_Position;
2291                     Pic.End_Float := Invalid_Position;
2292                  end if;
2293
2294                  Picture;
2295                  Set_State (Okay);
2296                  return;
2297
2298               when 'Z' | 'z' =>
2299
2300                  --  Can't have Z and a floating sign
2301
2302                  if State = Okay then
2303                     Set_State (Reject);
2304                  end if;
2305
2306                  Pic.Picture.Expanded (Index) := 'Z'; -- consistency
2307                  Zero_Suppression;
2308                  Trailing_Currency;
2309                  Optional_RHS_Sign;
2310                  return;
2311
2312               when '.' | 'V' | 'v' =>
2313                  if State /= Okay then
2314                     Pic.Floater := '!';
2315                     Pic.Start_Float := Invalid_Position;
2316                     Pic.End_Float := Invalid_Position;
2317                  end if;
2318
2319                  --  Don't assume that state is okay, haven't seen a digit
2320
2321                  Picture;
2322                  return;
2323
2324               when others =>
2325                  return;
2326
2327            end case;
2328         end loop;
2329      end Picture_Minus;
2330
2331      ------------------
2332      -- Picture_Plus --
2333      ------------------
2334
2335      procedure Picture_Plus is
2336      begin
2337         Pic.Sign_Position := Index;
2338
2339         --  Treat as a floating sign, and unwind otherwise
2340
2341         Pic.Floater := '+';
2342         Pic.Start_Float := Index;
2343         Pic.End_Float := Index;
2344
2345         --  Don't increment Pic.Max_Leading_Digits, we need one "real"
2346         --  sign place.
2347
2348         Skip; --  Known Plus
2349
2350         loop
2351            case Look is
2352
2353               when '_' | '0' | '/' =>
2354                  Pic.End_Float := Index;
2355                  Skip;
2356
2357               when 'B' | 'b'  =>
2358                  Pic.End_Float := Index;
2359                  Pic.Picture.Expanded (Index) := 'b';
2360                  Skip;
2361
2362               when '+' =>
2363                  Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
2364                  Pic.End_Float := Index;
2365                  Skip;
2366                  Set_State (Okay);  --  "++" is enough
2367                  Floating_Plus;
2368                  Trailing_Currency;
2369                  return;
2370
2371               when '$' | '#' | '9' | '*' =>
2372                  if State /= Okay then
2373                     Pic.Floater := '!';
2374                     Pic.Start_Float := Invalid_Position;
2375                     Pic.End_Float := Invalid_Position;
2376                  end if;
2377
2378                  Picture;
2379                  Set_State (Okay);
2380                  return;
2381
2382               when 'Z' | 'z' =>
2383                  if State = Okay then
2384                     Set_State (Reject);
2385                  end if;
2386
2387                  --  Can't have Z and a floating sign
2388
2389                  Pic.Picture.Expanded (Index) := 'Z'; -- consistency
2390
2391                  --  '+Z' is acceptable
2392
2393                  Set_State (Okay);
2394
2395                  Zero_Suppression;
2396                  Trailing_Currency;
2397                  Optional_RHS_Sign;
2398                  return;
2399
2400               when '.' | 'V' | 'v' =>
2401                  if State /= Okay then
2402                     Pic.Floater := '!';
2403                     Pic.Start_Float := Invalid_Position;
2404                     Pic.End_Float := Invalid_Position;
2405                  end if;
2406
2407                  --  Don't assume that state is okay, haven't seen a digit
2408
2409                  Picture;
2410                  return;
2411
2412               when others =>
2413                  return;
2414
2415            end case;
2416         end loop;
2417      end Picture_Plus;
2418
2419      --------------------
2420      -- Picture_String --
2421      --------------------
2422
2423      procedure Picture_String is
2424      begin
2425         while Is_Insert loop
2426            Skip;
2427         end loop;
2428
2429         case Look is
2430
2431            when '$' | '#' =>
2432               Picture;
2433               Optional_RHS_Sign;
2434
2435            when '+' =>
2436               Picture_Plus;
2437
2438            when '-' =>
2439               Picture_Minus;
2440
2441            when '<' =>
2442               Picture_Bracket;
2443
2444            when 'Z' | 'z' =>
2445               Pic.Picture.Expanded (Index) := 'Z'; -- consistency
2446               Zero_Suppression;
2447               Trailing_Currency;
2448               Optional_RHS_Sign;
2449
2450            when '*' =>
2451               Star_Suppression;
2452               Trailing_Currency;
2453               Optional_RHS_Sign;
2454
2455            when '9' | '.' | 'V' | 'v' =>
2456               Number;
2457               Trailing_Currency;
2458               Optional_RHS_Sign;
2459
2460            when others =>
2461               raise Picture_Error;
2462
2463         end case;
2464
2465         --  Blank when zero either if the PIC does not contain a '9' or if
2466         --  requested by the user and no '*'.
2467
2468         Pic.Blank_When_Zero :=
2469           (Computed_BWZ or else Pic.Blank_When_Zero)
2470             and then not Pic.Star_Fill;
2471
2472         --  Star fill if '*' and no '9'
2473
2474         Pic.Star_Fill := Pic.Star_Fill and then Computed_BWZ;
2475
2476         if not At_End then
2477            Set_State (Reject);
2478         end if;
2479
2480      end Picture_String;
2481
2482      ---------------
2483      -- Set_State --
2484      ---------------
2485
2486      procedure Set_State (L : Legality) is
2487      begin
2488         State := L;
2489      end Set_State;
2490
2491      ----------
2492      -- Skip --
2493      ----------
2494
2495      procedure Skip is
2496      begin
2497         Index := Index + 1;
2498      end Skip;
2499
2500      ----------------------
2501      -- Star_Suppression --
2502      ----------------------
2503
2504      procedure Star_Suppression is
2505      begin
2506         Pic.Floater := '*';
2507         Pic.Start_Float := Index;
2508         Pic.End_Float := Index;
2509         Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
2510         Set_State (Okay);
2511
2512         --  Even a single * is a valid picture
2513
2514         Pic.Star_Fill := True;
2515         Skip; --  Known *
2516
2517         loop
2518            if At_End then
2519               return;
2520            end if;
2521
2522            case Look is
2523
2524               when '_' | '0' | '/' =>
2525                  Pic.End_Float := Index;
2526                  Skip;
2527
2528               when 'B' | 'b'  =>
2529                  Pic.End_Float := Index;
2530                  Pic.Picture.Expanded (Index) := 'b';
2531                  Skip;
2532
2533               when '*' =>
2534                  Pic.End_Float := Index;
2535                  Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
2536                  Set_State (Okay); Skip;
2537
2538               when '9' =>
2539                  Set_State (Okay);
2540                  Number_Completion;
2541                  return;
2542
2543               when '.' | 'V' | 'v' =>
2544                  Pic.Radix_Position := Index;
2545                  Skip;
2546                  Number_Fraction_Or_Star_Fill;
2547                  return;
2548
2549               when '#' | '$' =>
2550                  Trailing_Currency;
2551                  Set_State (Okay);
2552                  return;
2553
2554               when others => raise Picture_Error;
2555            end case;
2556         end loop;
2557      end Star_Suppression;
2558
2559      ----------------------
2560      -- Trailing_Bracket --
2561      ----------------------
2562
2563      procedure Trailing_Bracket is
2564      begin
2565         if Look = '>' then
2566            Pic.Second_Sign := Index;
2567            Skip;
2568         else
2569            raise Picture_Error;
2570         end if;
2571      end Trailing_Bracket;
2572
2573      -----------------------
2574      -- Trailing_Currency --
2575      -----------------------
2576
2577      procedure Trailing_Currency is
2578      begin
2579         if At_End then
2580            return;
2581         end if;
2582
2583         if Look = '$' then
2584            Pic.Start_Currency := Index;
2585            Pic.End_Currency := Index;
2586            Skip;
2587
2588         else
2589            while not At_End and then Look = '#' loop
2590               if Pic.Start_Currency = Invalid_Position then
2591                  Pic.Start_Currency := Index;
2592               end if;
2593
2594               Pic.End_Currency := Index;
2595               Skip;
2596            end loop;
2597         end if;
2598
2599         loop
2600            if At_End then
2601               return;
2602            end if;
2603
2604            case Look is
2605               when '_' | '0' | '/' => Skip;
2606
2607               when 'B' | 'b'  =>
2608                  Pic.Picture.Expanded (Index) := 'b';
2609                  Skip;
2610
2611               when others => return;
2612            end case;
2613         end loop;
2614      end Trailing_Currency;
2615
2616      ----------------------
2617      -- Zero_Suppression --
2618      ----------------------
2619
2620      procedure Zero_Suppression is
2621      begin
2622         Pic.Floater := 'Z';
2623         Pic.Start_Float := Index;
2624         Pic.End_Float := Index;
2625         Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
2626         Pic.Picture.Expanded (Index) := 'Z'; -- consistency
2627
2628         Skip; --  Known Z
2629
2630         loop
2631            --  Even a single Z is a valid picture
2632
2633            if At_End then
2634               Set_State (Okay);
2635               return;
2636            end if;
2637
2638            case Look is
2639               when '_' | '0' | '/' =>
2640                  Pic.End_Float := Index;
2641                  Skip;
2642
2643               when 'B' | 'b'  =>
2644                  Pic.End_Float := Index;
2645                  Pic.Picture.Expanded (Index) := 'b';
2646                  Skip;
2647
2648               when 'Z' | 'z' =>
2649                  Pic.Picture.Expanded (Index) := 'Z'; -- consistency
2650
2651                  Pic.Max_Leading_Digits := Pic.Max_Leading_Digits + 1;
2652                  Pic.End_Float := Index;
2653                  Set_State (Okay);
2654                  Skip;
2655
2656               when '9' =>
2657                  Set_State (Okay);
2658                  Number_Completion;
2659                  return;
2660
2661               when '.' | 'V' | 'v' =>
2662                  Pic.Radix_Position := Index;
2663                  Skip;
2664                  Number_Fraction_Or_Z_Fill;
2665                  return;
2666
2667               when '#' | '$' =>
2668                  Trailing_Currency;
2669                  Set_State (Okay);
2670                  return;
2671
2672               when others =>
2673                  return;
2674            end case;
2675         end loop;
2676      end Zero_Suppression;
2677
2678   --  Start of processing for Precalculate
2679
2680   begin
2681      Picture_String;
2682
2683      if State = Reject then
2684         raise Picture_Error;
2685      end if;
2686
2687   exception
2688
2689      when Constraint_Error =>
2690
2691         --  To deal with special cases like null strings
2692
2693      raise Picture_Error;
2694
2695   end Precalculate;
2696
2697   ----------------
2698   -- To_Picture --
2699   ----------------
2700
2701   function To_Picture
2702     (Pic_String      : String;
2703      Blank_When_Zero : Boolean := False) return Picture
2704   is
2705      Result : Picture;
2706
2707   begin
2708      declare
2709         Item : constant String := Expand (Pic_String);
2710
2711      begin
2712         Result.Contents.Picture         := (Item'Length, Item);
2713         Result.Contents.Original_BWZ := Blank_When_Zero;
2714         Result.Contents.Blank_When_Zero := Blank_When_Zero;
2715         Precalculate (Result.Contents);
2716         return Result;
2717      end;
2718
2719   exception
2720      when others =>
2721         raise Picture_Error;
2722
2723   end To_Picture;
2724
2725   -------------
2726   -- To_Wide --
2727   -------------
2728
2729   function To_Wide (C : Character) return Wide_Wide_Character is
2730   begin
2731      return Wide_Wide_Character'Val (Character'Pos (C));
2732   end To_Wide;
2733
2734   -----------
2735   -- Valid --
2736   -----------
2737
2738   function Valid
2739     (Pic_String      : String;
2740      Blank_When_Zero : Boolean := False) return Boolean
2741   is
2742   begin
2743      declare
2744         Expanded_Pic : constant String := Expand (Pic_String);
2745         --  Raises Picture_Error if Item not well-formed
2746
2747         Format_Rec : Format_Record;
2748
2749      begin
2750         Format_Rec.Picture := (Expanded_Pic'Length, Expanded_Pic);
2751         Format_Rec.Blank_When_Zero := Blank_When_Zero;
2752         Format_Rec.Original_BWZ := Blank_When_Zero;
2753         Precalculate (Format_Rec);
2754
2755         --  False only if Blank_When_0 is True but the pic string has a '*'
2756
2757         return not Blank_When_Zero
2758           or else Strings_Fixed.Index (Expanded_Pic, "*") = 0;
2759      end;
2760
2761   exception
2762      when others => return False;
2763   end Valid;
2764
2765end Ada.Wide_Wide_Text_IO.Editing;
2766