File: wraps.adb

package info (click to toggle)
spark 2012.0.deb-9
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 29,260 kB
  • ctags: 3,098
  • sloc: ada: 186,243; cpp: 13,497; makefile: 685; yacc: 440; lex: 176; ansic: 119; sh: 16
file content (531 lines) | stat: -rw-r--r-- 20,949 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
-------------------------------------------------------------------------------
-- (C) Altran Praxis Limited
-------------------------------------------------------------------------------
--
-- The SPARK toolset is free software; you can redistribute it and/or modify it
-- under terms of the GNU General Public License as published by the Free
-- Software Foundation; either version 3, or (at your option) any later
-- version. The SPARK toolset is distributed in the hope that it will be
-- useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-- Public License for more details. You should have received a copy of the GNU
-- General Public License distributed with the SPARK toolset; see file
-- COPYING3. If not, go to http://www.gnu.org/licenses for a complete copy of
-- the license.
--
--=============================================================================

with Ada.Command_Line, GNAT.OS_Lib, GNAT.IO_Aux, SPARK_IO;
use type SPARK_IO.File_Type;
use type SPARK_IO.File_Status;

--  Key layout concerns are summarised below:
-- --------------------------------------------------------------------
--  Column number:
--  123456789...
--
--                        The column beoynd which wrapping is desired>M
--                                                                    M
--                             V--split Characters-V----------V       M
--  SSSSSSSSSSSXXXXXXXXXXXXXXXXCXXXXXXXXXXXXXXXXXXXCXXXXXXXXXXXCYYYYYYYZZZZZZZZ...
--  ^Possible  ^Text for this line                             ^      M ^
--   start                           Text cut off for next line|      M Text
--   offset                                                           M beyond
--
-- --------------------
--  Note that, wrapped lines are prefixed with an indent. This is achieved by
--  modifying the text to include non-breaking space Characters.
-- --------------------------------------------------------------------
--
--  Key layout rules are summarised below:
--
--  The line fits:
--  Condition: start+line lies within the wrapping point.
--  Action: write out the line and finish. (Never insert a newline here.
--          Newlines are only added for wrapping purposes.)
--
--  The line does not fit:
--  Condition: Above condition is false.
--  Action: Compare start point and wrapping column.
--
--  Condition: The start point is beyond the wrapping column.
--  Action: Scan remaining text forwards until a split Character (that is not
--          the last Character!) is encountered.  Write out line up to and
--          including the split Character, and a newline. Collect remaining
--          line, and start again. An indent is now requested.
--          If a split Character is never found (or it's only found at the
--          last Character), write out the whole line and finish.
--
--  Condition: The start point is within the wrapping column.
--  Action: Scan backwards from the wrapping column. If a split Character is
--          encountered (that is not the first Character!), then write out the line
--          up to and including the split Character, and a newline.  Collect
--          remaining line, and start again. An indent is now requested.  If a split
--          Character is never found (or it's only found at the first Character),
--          write out the whole line, set offset to just after the wrapper column,
--          and start again.
--
-- Note: If the last line terminates with a new line, then the above rules hold.
-- If the last line does not terminate with a new line, then the above rules hold,
-- with an additional new line being added just before the end of the file.

package body WRAPS is

   -- CONSTANTS --
   maxlinelength    : constant Integer := 262143; -- 2**18 - 1 or 256k
   MaxCols          : constant Integer := 80;   -- Maximum no. of output columns normally permitted
   Indentation      : constant Integer := 10;   -- Columns to indent for 2nd. & subsequent wraps
   SpaceChar        : constant Integer := 32;   -- ' ' {assumption: ASCII, so space is chr(32)}
   OpenParenthesis  : constant Integer := 40;   -- '('
   CloseParenthesis : constant Integer := 41;   -- ')'
   OpenBracket      : constant Integer := 91;   -- '['
   CloseBracket     : constant Integer := 93;   -- ']'

   -- TYPES --
   type linelength is range 0 .. maxlinelength;
   subtype linearraypos is linelength range 1 .. linelength'Last;
   subtype charcode is Integer range 0 .. 255;   --assumption: ASCII range 0..255
   type linearray is array (linearraypos) of charcode;
   type line is record
      contents : linearray;
      length   : linelength;
   end record;  -- line

   procedure WRAP is
      infile, outfile : SPARK_IO.File_Type;
      STATUS          : SPARK_IO.File_Status;

      procedure DoRead (CH : out charcode)
      --# global in     infile;
      --#        in out SPARK_IO.File_Sys;
      --# derives CH,
      --#         SPARK_IO.File_Sys from infile,
      --#                                SPARK_IO.File_Sys;
      is
         C : Character;
      begin
         SPARK_IO.Get_Char (infile, C);
         CH := Character'Pos (C);
      end DoRead;

      procedure DoWrite (CH : in charcode)
      --# global in     outfile;
      --#        in out SPARK_IO.File_Sys;
      --# derives SPARK_IO.File_Sys from *,
      --#                                CH,
      --#                                outfile;
      is
      begin
         SPARK_IO.Put_Char (outfile, Character'Val (CH));
      end DoWrite;

      procedure DoReadln
      --# global in     infile;
      --#        in out SPARK_IO.File_Sys;
      --# derives SPARK_IO.File_Sys from *,
      --#                                infile;
      is
      begin
         SPARK_IO.Skip_Line (infile, 1);
      end DoReadln;

      procedure DoWriteln
      --# global in     outfile;
      --#        in out SPARK_IO.File_Sys;
      --# derives SPARK_IO.File_Sys from *,
      --#                                outfile;
      is
      begin
         SPARK_IO.New_Line (outfile, 1);
      end DoWriteln;

      function CheckFileExists (Name : String) return Boolean
      -- checks that we're not trying to open a non-existent file
      is
         --# hide CheckFileExists;
      begin
         return GNAT.IO_Aux.File_Exists (Name);
      end CheckFileExists;

      -- True if we were called with a filename argument
      function New_Wrap return Boolean is
         --# hide New_Wrap;
      begin
         return Ada.Command_Line.Argument_Count > 0;
      end New_Wrap;

      -- Return the input file.
      -- This will be either the first argument, or "WRAPS.IN" if there is no
      -- argument.
      function OpenInFile return  SPARK_IO.File_Type is
         --# hide OpenInFile;
         filename   : GNAT.OS_Lib.String_Access := new String'("WRAP.INP");
         filehandle : SPARK_IO.File_Type;
         STATUS     : SPARK_IO.File_Status;
      begin
         if New_Wrap then
            filename := new String'(Ada.Command_Line.Argument (1));
         end if;
         if CheckFileExists (filename.all) then
            SPARK_IO.Open (filehandle, SPARK_IO.In_File, filename'Length, filename.all, "", STATUS);

            if STATUS /= SPARK_IO.Ok then
               SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "Open input failed with: " & SPARK_IO.File_Status'Image (STATUS), 0);
            end if;
         else
            filehandle := SPARK_IO.Null_File;
         end if;
         return filehandle;
      end OpenInFile;

      -- Return the output file.
      -- This will be either the first argument and ".tmp", or "WRAPS.OUT" if there is no
      -- argument.
      function OpenOutFile return  SPARK_IO.File_Type is
         --# hide OpenOutFile;
         filename   : GNAT.OS_Lib.String_Access := new String'("WRAP.OUT");
         filehandle : SPARK_IO.File_Type;
         STATUS     : SPARK_IO.File_Status;
      begin
         if New_Wrap then
            filename := new String'(Ada.Command_Line.Argument (1) & ".tmp");
         end if;

         SPARK_IO.Create (filehandle, filename'Length, filename.all, "", STATUS);
         if STATUS /= SPARK_IO.Ok then
            SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "Create output failed with: " & SPARK_IO.File_Status'Image (STATUS), 0);
         end if;
         if filehandle = SPARK_IO.Null_File then
            SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "Create output return Null_File", 0);
         end if;
         return filehandle;
      end OpenOutFile;

      procedure Initialise
      --# global out infile;
      --#        out outfile;
      --# derives infile,
      --#         outfile from ;
      is
      begin
         infile := OpenInFile;
         --# accept Flow, 22, "OpenInFile is hidden. Expression may actually change.";
         if infile = SPARK_IO.Null_File then
            --# end accept;
            outfile := SPARK_IO.Null_File;
         else
            outfile := OpenOutFile;
         end if;
      end Initialise;

      -- Delete the original file and rename the temporary file.
      procedure CleanUp
      --# derives ;
      is
         --# hide CleanUp;
         SUCCESS      : Boolean := False;
         infile_name  : GNAT.OS_Lib.String_Access;
         outfile_name : GNAT.OS_Lib.String_Access;
         newfile_name : GNAT.OS_Lib.String_Access;
      begin
         if New_Wrap then -- We have arguments

            infile_name  := new String'(Ada.Command_Line.Argument (1));
            outfile_name := new String'(Ada.Command_Line.Argument (1) & ".tmp");

            if Ada.Command_Line.Argument_Count = 2 then -- We have a destination filename
               newfile_name := new String'(Ada.Command_Line.Argument (2));
            else -- Replace the original file
               newfile_name := new String'(Ada.Command_Line.Argument (1));
            end if;

            -- Delete the original input file.
            GNAT.OS_Lib.Delete_File (infile_name.all, SUCCESS);

            if not SUCCESS then
               SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);
               SPARK_IO.Put_Line
                 (SPARK_IO.Standard_Output,
                  "*** ERROR - wrap_utility failed to delete input file failed with Errno=" & Integer'Image (GNAT.OS_Lib.Errno),
                  0);
               SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);
            end if;

            -- Move the output file to the destination name.
            GNAT.OS_Lib.Rename_File (outfile_name.all, newfile_name.all, SUCCESS);

            if not SUCCESS then
               SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);
               SPARK_IO.Put_Line (SPARK_IO.Standard_Output, "*** ERROR - wrap_utility failed to rename output file", 0);
               SPARK_IO.New_Line (SPARK_IO.Standard_Output, 1);
            end if;
         else
            null; -- We are behaving as the old wrap util, so do nothing.
         end if; -- New_Wrap
      end CleanUp;

      --------------------------------------------------------------------------------

      procedure ReadLine (L : out line)
      --# global in     infile;
      --#        in out SPARK_IO.File_Sys;
      --# derives L,
      --#         SPARK_IO.File_Sys from infile,
      --#                                SPARK_IO.File_Sys;
      --This routine reads a line of Character-codes into the line-buffer L.
      is
         CH       : charcode;
         lensofar : linelength;
      begin
         lensofar := 0;
         while not SPARK_IO.End_Of_Line (infile) and lensofar < linelength (maxlinelength) loop
            lensofar := lensofar + 1;
            DoRead (CH);
            --# accept Flow, 23, L.contents, "Array elements are initialised before use.";
            L.contents (lensofar) := CH;
            --# end accept;
         end loop;
         L.length := lensofar;
         DoReadln;

         --# accept Flow, 602, L, L.contents, "Array elements are initialised before use.";
      end ReadLine;

      procedure OutPartOfLine (L              : in line;
                               FromCol, ToCol : in linelength)
      --# global in     outfile;
      --#        in out SPARK_IO.File_Sys;
      --# derives SPARK_IO.File_Sys from *,
      --#                                FromCol,
      --#                                L,
      --#                                outfile,
      --#                                ToCol;
      --Writes out line-buffer L unchanged, without adding any single quotes.
      is
         index : linelength;
      begin
         index := FromCol;
         while index <= ToCol loop
            DoWrite (L.contents (index));
            index := index + 1;
         end loop;
      end OutPartOfLine;

      procedure OutUnchangedLine (L : in line)
      --# global in     outfile;
      --#        in out SPARK_IO.File_Sys;
      --# derives SPARK_IO.File_Sys from *,
      --#                                L,
      --#                                outfile;
      --Writes out line-buffer L unchanged, without any line-wrapping.
      is
      begin
         OutPartOfLine (L, 1, L.length);
         DoWriteln;
      end OutUnchangedLine;

      --Copies a line, wrapping if it appears to be necessary.
      procedure CopyAndMaybeWrapLine
      --# global in     infile;
      --#        in     outfile;
      --#        in out SPARK_IO.File_Sys;
      --# derives SPARK_IO.File_Sys from *,
      --#                                infile,
      --#                                outfile;
      is
         currentline : line;

         function IsALongLine (L : in line) return Boolean
         --True if we need to wrap the line around - i.e. more than MaxCols.
         is
         begin
            return L.length > linelength (MaxCols);
         end IsALongLine;

         --Write out line-buffer L, wrapping where necessary.
         procedure OutWrappedLine (L : in line)
         --# global in     outfile;
         --#        in out SPARK_IO.File_Sys;
         --# derives SPARK_IO.File_Sys from *,
         --#                                L,
         --#                                outfile;
         is
            OnCol, Width : linelength;

            procedure OutNextPartOfLine (L       : in     line;
                                         FromCol : in out linelength;
                                         InWidth : in     linelength)
            --# global in     outfile;
            --#        in out SPARK_IO.File_Sys;
            --# derives FromCol           from *,
            --#                                InWidth,
            --#                                L &
            --#         SPARK_IO.File_Sys from *,
            --#                                FromCol,
            --#                                InWidth,
            --#                                L,
            --#                                outfile;
            -- pre FromCol + InWidth <= L.length and InWidth > 1;
            is
               ToCol : linelength;

               function OKSplitChar (C : in charcode) return Boolean
               --Returns true if C is a space, parenthesis or bracket.
               is
               begin
                  return (C = SpaceChar)
                    or else (C = OpenParenthesis)
                    or else (C = CloseParenthesis)
                    or else (C = OpenBracket)
                    or else (C = CloseBracket);
               end OKSplitChar;

            begin --OutNextPartOfLine

               -- L:       The line under consideration.
               -- FromCol: Index the first portion of L that not has already been displayed.
               -- InWidth: The remaining width of the screen before MaxCols is reached.

               -- ToCol: Index the last portion of L to be displayed on this
               -- line. The correct value is calculated below.

               -- Assume that the line fits perfectly.
               ToCol := (FromCol + InWidth) - 1;

               if not OKSplitChar (L.contents (ToCol)) then
                  -- Cannot split at the ideal position.

                  -- Search backwards to find the next-best split point.
                  loop
                     ToCol := ToCol - 1;
                     exit when OKSplitChar (L.contents (ToCol)) or else (ToCol = FromCol);
                  end loop;

                  if ToCol = FromCol then
                     -- Could not find any split point looking backwards. The
                     -- line will need to wrap beyond MaxCols.

                     -- Search forwards to find the least-worst split point.
                     ToCol := (FromCol + InWidth) - 1;
                     loop
                        exit when (ToCol >= L.length) or else OKSplitChar (L.contents (ToCol));
                        ToCol := ToCol + 1;
                     end loop;
                  end if;
               end if;

               -- Display the selected portion of the line.
               OutPartOfLine (L, FromCol, ToCol);

               -- The index to first portion of L that not has already been displayed
               -- is adjusted accordingly.
               FromCol := ToCol + 1;
            end OutNextPartOfLine;

            procedure Indent (NoOfCols : in linelength)
            --# global in     outfile;
            --#        in out SPARK_IO.File_Sys;
            --# derives SPARK_IO.File_Sys from *,
            --#                                NoOfCols,
            --#                                outfile;
            -- pre NoOfCols >= 0;
            is
            begin
               for index in linelength range 1 .. NoOfCols loop
                  DoWrite (SpaceChar);
               end loop;
            end Indent;

         begin--OutWrappedLine

            --Start at column 1.
            OnCol := 1;

            -- To start with, have the width of the screen in which to place content.
            Width := linelength (MaxCols);

            while OnCol + Width <= L.length loop

               OutNextPartOfLine (L, OnCol, Width);

               -- Wrap and indent, only if there is more text to display for this line.
               if OnCol <= L.length then
                  DoWriteln;
                  Indent (linelength (Indentation));
               end if;

               -- Now, have the width of the screen (less indent) in which to place content.
               Width := linelength (MaxCols - Indentation);
            end loop;

            -- Display any residue.
            if OnCol <= L.length then
               OutPartOfLine (L, OnCol, L.length);
            end if;

            -- All done. Add final newline.
            DoWriteln;
         end OutWrappedLine;

      begin--CopyAndMaybeWrapLine
         ReadLine (currentline);
         if IsALongLine (currentline) then
            OutWrappedLine (currentline);
         else
            OutUnchangedLine (currentline);
         end if;
      end CopyAndMaybeWrapLine;

      procedure DisplayErrorMessage (Leader :    String;
                                     STATUS : in SPARK_IO.File_Status)
      --# global in out SPARK_IO.File_Sys;
      --# derives SPARK_IO.File_Sys from *,
      --#                                Leader,
      --#                                STATUS;
      is
         --# hide DisplayErrorMessage ;
      begin
         SPARK_IO.Put_Line (SPARK_IO.Standard_Output, (Leader & SPARK_IO.File_Status'Image (STATUS)), 0);
      end DisplayErrorMessage;

   begin -- WRAP

      --  GetFileNames;
      Initialise;

      --# accept Flow, 22, "Initialise hides behaviour. Expression may actually change.";
      if infile /= SPARK_IO.Null_File then
         --# end accept;

         -- Please note that End_Of_File (infile) will report true where:
         -- The next Character is: EOF
         -- The next Character sequence is: NL, EOF

         while not SPARK_IO.End_Of_File (infile) loop
            CopyAndMaybeWrapLine;
         end loop;
         -- This call to DoWriteln would appear to be totally unnecessary. However, it would
         -- correctly restore a NL if End_Of_File has really detected: NL, EOF. (Of course,
         -- it also adds an additional NL if End_Of_File actually detected: EOF.)
         DoWriteln;
         --# accept Flow, 10, infile, "May actually affect infile.";
         SPARK_IO.Close (infile, STATUS);
         --# end accept;

         if STATUS /= SPARK_IO.Ok then
            DisplayErrorMessage ("Close  In_File: ", STATUS);
         end if;

         --# accept Flow, 10, outfile, "May actually affect outfile.";
         SPARK_IO.Close (outfile, STATUS);
         --# end accept;

         if STATUS /= SPARK_IO.Ok then
            DisplayErrorMessage ("Close Out_File: ", STATUS);
         end if;

         CleanUp;
      end if;
   end WRAP;

end WRAPS;