comp.lang.ada
 help / color / mirror / Atom feed
From: mfb@mbunix.mitre.org (Michael F Brenner)
Subject: Re: Image processing File I/O
Date: 1998/09/24
Date: 1998-09-24T00:00:00+00:00	[thread overview]
Message-ID: <6udfdv$cl3@top.mitre.org> (raw)
In-Reply-To: dale-2409982201430001@dale.ppp.cs.rmit.edu.au


In addition to Sequential IO on an 8-bit NUMERICAL byte type in 
the old Ada 83, you could use Stream IO in Ada. Here is a package
that permits you to read and write binary data using Stream IO
in DOS, Windows, and Linux (you need to change the one line
that is marked for the directory separator, forward or backwards
slash).

The package HAND below makes stream IO streams look like ordinary
files by giving them handle numbers you can open, close, read from,
and right to. The two features used in Ada that were not present
in the old Ada 83 are the streams themselves, and 8-bit characters.

Of course, Ada still permits you to do sequential IO on files of
numerical 8-bit bytes, but timing tests on Solaris, DOS, and 
Windows NT (using the DOS compiler) all show that, in these
environments, stream IO is now quicker. (Earlier versions of 
gnat had sequential IO quicker).

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

package string_control is
  pragma pure (string_control);

 -- Ordinary String control

 function  caselessly_equal (left, right: character)
                             return boolean;
 function  caselessly_equal (object: string; start, length: natural;
                             pattern: string)
                             return boolean;
 procedure concat           (message:  character;
                             onto: in out string;
                             length:   in out natural);
 procedure concat           (message:  string;
                             onto: in out string;
                             length:   in out natural);

 -- First-missing and first-occurring return 0 when not found.

 function  first_missing    (message: string;
                             pattern: character := ' ')
                             return integer;
 function  first_occurring  (message: string;
                             pattern: character := ' ')
                             return integer;
 function  first_occurring  (message: string;
                             pattern: string)
                             return integer;
 function  image            (message: string)
                             return string;
 procedure in_place_insert  (message: in out string;
                             message_length: in out natural;
                             insertion: string;
                             after: natural);
 function  indent           (message: string)
                             return string;

 function  length           (message: string)
                             return natural;

 -- Last-missing and last-occuring return 0 when not found.

 function  last_missing     (message: string;
                             pattern: character := ' ')
                             return integer;
 function  last_occurring   (message: string;
                             pattern: character := ' ')
                             return integer;
 function  left_trim        (message: string;
                             pattern: character := ' ')
                             return string;
 procedure lower_case       (message: in out string;
                             start: natural;
                             finish: natural);
 function  lower_case       (c: character)
                             return character;
 function  lower_case       (message: string)
                             return string;
 function  right_trim       (message: string;
                             pattern: character := ' ')
                             return string;
 function  quotes_are_good  (message: string;
                             quote:   character := '"')
                             return boolean;
 procedure quotes_inserted  (message: string;
                             result:  in out string; -- 2+2*message'length
                             length:  out natural;
                             quote:   character := '"');
 procedure quotes_removed   (message: string;
                             result:  in out string; -- message'length
                             length:  out natural;
                             quote:   character := '"');
 function  same             (left, right: string)
                             return boolean;
 function  substitute       (base_string: string;
                             search_string: string;
                             replace_string: string)
                             return string;
 procedure upper_case       (message: in out string;
                             start: natural;
                             finish: natural);
 function  upper_case       (c: character)
                             return character;
 function  upper_case       (message: string)
                             return string;

  -- http://email:port/disk:file1/file2/.../filen#anchor?arg1?arg2?...?argk
  --
  -- @ Separators (://, :, /, #, ?)
  -- @ Words (protocol, email, port, disk, file, anchor, arg)
  -- @ Nonquoted words may contain alphanumerics, ~, _, !, @, -, and periods.
  -- @ Quoted words may contain anything, doubling the quotes

  procedure url_break_apart (url: string;
                             protocol_start:  out natural;
                             protocol_finish: out natural;
                             internet_start:  out natural;
                             internet_finish: out natural;
                             port_start:      out natural;
                             port_finish:     out natural;
                             disk_start:      out natural;
                             disk_finish:     out natural;
                             file_start:      out natural;
                             file_finish:     out natural;
                             anchor_start:    out natural;
                             anchor_finish:   out natural;
                             args_start:      out natural;
                             args_finish:     out natural);

  -- Lexical classes are sets of characters, here represented as bits.

  type classes is array(character) of boolean;
  pragma pack (classes);

  small_letter_class: constant classes :=
    (character'first .. character'val (96) => false,
     'a' .. 'z' => true,
     '{' .. character'last => false);

  capital_letter_class: constant classes :=
    (character'first .. '@' => false,
     'A' .. 'Z' => true,
     '[' .. character'last => false);

  alphabetic_class: constant classes :=
    (character'first .. '@' => false,
     'A' .. 'Z' => true,
     '[' .. character'val (96) => false,
     'a' .. 'z' => true,
     '{' .. character'last => false);

  digit_class: constant classes :=
    (character'first .. '/' => false,
     '0'..'9' => true,
     ':'..character'last => false);

  sign_class: constant classes :=
    (character'first .. character'pred ('+') => false,
     '+' => true,
     character'succ('+')..character'pred('-') => false,
     '-' => true,
     character'succ('-').. character'last => false);

  digit_or_sign_class: constant classes :=
    (character'first .. character'pred ('+') => false,
     '+' => true,
     character'succ('+')..character'pred('-') => false,
     '-' => true,
     character'succ('-')..character'pred('0') => false,
     '0'..'9' => true,
     character'succ('9')..character'last => false);



  underline_class: constant classes :=
    (character'first .. '^' => false,
     '_' => true,
     character'val (96) ..character'last => false);

  alphanumeric_class: constant classes :=
    (character'first .. '@' => false,
     'A' .. 'Z' => true,
     '[' .. '^' => false,
     '_' => true,
     character'val (96) => false,
     'a' .. 'z' => true,
     '{' .. character'last => false);

  null_class: constant classes := (others => false);

  -- The MATCH method searches for a character class in the MESSAGE,
  -- returning the column in which the pattern was found,
  -- otherwise returning zero.

  type recognitions is record
    RSTART, RLENGTH: natural;
  end record;

  function match (message: string; class: classes) return recognitions;

end string_control;


package body string_control is

 function  caselessly_equal (left, right: character) return boolean is
 begin
   return
     left=right or else
     (left in 'A'..'Z' and then right=character'val(character'pos(left)+32))
     or else
     (left in 'a'..'z' and then right=character'val(character'pos(left)-32));
 end caselessly_equal;

 function  caselessly_equal (object: string; start, length: natural;
                             pattern: string) return boolean is
 begin
   if length /= pattern'length then return FALSE;
   else
     for i in 1..length loop
       if not caselessly_equal (object  (start         + i - 1),
                                pattern (pattern'first + i - 1)) then
          return FALSE;
       end if;
     end loop;
   end if;
   return TRUE;
 end caselessly_equal;

 procedure concat (message:  character;
                   onto: in out string;
                   length:   in out natural) is
   concat_too_long: exception;
 begin
   if length + 1 > onto'length then
     raise concat_too_long;
   end if;
   onto (length + 1) := message;
   length := length + 1;
 end concat;

 procedure concat (message:  string;
                   onto: in out string;
                   length:   in out natural) is
   concat_too_long: exception;
 begin
   if length + message'length > onto'length then
     raise concat_too_long;
   end if;
   onto (length + 1 .. length + message'length) := message;
   length := length + message'length;
 end concat;

 function first_missing (message: string;
                         pattern: character := ' ') return integer is
 begin
   for i in message'range loop
     if message(i) /= pattern then
       return i;
     end if;                -- message (message'first..i) are patterns.
   end loop;
   return 0;                -- Returns the first column without PATTERN.
 end first_missing;

 function first_occurring (message: string;
                           pattern: character := ' ') return integer is
 begin
   for i in message'range loop
     if message(i)  = pattern then
       return i;            -- message contains the pattern
     end if;                -- message (message'first..i) contains no pattern
   end loop;
   return 0;                -- message contains no pattern
 end first_occurring;

 function first_occurring (message: string; pattern: string)
                           return integer is
   upto: natural := message'first;
   last: integer := message'last + 1 - pattern'length;
   in_first_occuring_the_pattern_is_null: exception;
   RSTART:  natural := 0;
 begin
   RSTART := 0;

   if pattern="" then
      raise in_first_occuring_the_pattern_is_null;
   end if;

   loop
     exit when upto>last;

     if message(upto..upto+pattern'length-1)=pattern then
       RSTART := upto;
       return RSTART;
     end if;

     upto := upto + 1;
   end loop;
   return 0;
 end first_occurring;

 function image (message: string) return string is
   hold: string (1..message'length) := message;
 begin
   return hold;
 end image;

 procedure in_place_insert (message: in out string;
                            message_length: in out natural;
                            insertion: string;
                            after: natural) is
   string_control_in_place_insert_ml_plus_il_gt_ml: exception;
 begin
   if message_length + insertion'length > message'length then
     raise string_control_in_place_insert_ml_plus_il_gt_ml;
   end if;
   message(insertion'length + 1 + after .. message_length+insertion'length) :=
     message(after+1..message_length);
   message(after+1..after+insertion'length) := insertion;
   message_length := message_length + insertion'length;
 end in_place_insert;

 function indent(message: string) return string is
 begin
   return (1..message'length + 1 => ' ');
 end indent;

 function last_missing (message: string;
                        pattern: character := ' ') return integer is
 begin
   for i in reverse message'range loop
     if message(i) /= pattern then
       return i;
     end if;                -- message (i..message'last) are patterns.
   end loop;
   return 0;                -- Returns the last column without PATTERN.
 end last_missing;

 function last_occurring (message: string;
                          pattern: character := ' ') return integer is
 begin
   for i in reverse message'range loop
     if message (i) = pattern then
       return i;
     end if;                -- message (i..message'last) contains no pattern
   end loop;
   return 0;                -- Returns the last column with PATTERN.
 end last_occurring;

 function left_trim (message: string;
                     pattern: character := ' ') return string is
   left_trim_last: constant natural := first_missing (message, pattern);
 begin
   if left_trim_last = 0 then
     return ""; -- blank not found at all means null string
   else
     return message (left_trim_last .. message'last);
   end if;
 end left_trim;

 function length (message: string) return natural is
 begin
   return message'length;
 end length;

 function lower_case (c: character) return character is
 begin
   if c in 'A'..'Z' then
     return character'val (character'pos (c) + 32);
   end if;
   return c;
 end lower_case;

 procedure lower_case (message: in out string;
                       start: natural; finish: natural) is
 begin
   for i in start..finish loop
     if message (i) in 'A'..'Z' then
       message (i) := character'val (character'pos (message (i)) + 32);
     end if;
   end loop;
 end lower_case;

 function lower_case (message: string) return string is
   hold_message: string (1..message'length) := message;
   ch:           character;
   offset: constant := character'pos ('a') - character'pos ('A');
 begin
   for c in hold_message'range loop
     ch := hold_message (c);
     if ch in 'A'..'Z' then
       hold_message (c) := character'val (character'pos (ch) + offset);
     end if;            -- MESSAGE (FIRST..C) is in lower_case.
   end loop;
   return hold_message; -- MESSAGE is in lower_case.
 end lower_case;

 function match (message: string; class: classes) return recognitions is
   recognition: recognitions;
   RSTART:  natural renames recognition.RSTART;
   RLENGTH: natural renames recognition.RLENGTH;
   upto: natural := message'first;
 begin
   RSTART := 0;
   loop
      exit when upto > message'last;
      exit when class (message (upto)) ;
      upto := upto + 1;
   end loop;
   if upto > message'last then
     return (0, 0);
   else
     RSTART := upto;
   end if;
   loop
     exit when upto > message'last;
     exit when not class (message (upto));
     upto := upto + 1;
   end loop;
   RLENGTH := upto - RSTART;
   return recognition;
 end match;

 function  quotes_are_good  (message: string;
                             quote:   character := '"')
                             return boolean is
   input: natural := message'first + 1;
 begin
   if message'length<2 or else
      message(message'first)/=quote or else
      message(message'last) /=quote then
     return false;
   end if;
   loop
     exit when input >= message'length;
     if message(input)/='"' then
       input:=input+1;
     elsif message(input+1)/='"' then
       return false;
     elsif input=message'length-1 then
       return false;
     else
       input:=input+2;
     end if;
   end loop;
   return true;
 end quotes_are_good;

 procedure quotes_inserted  (message: string;
                             result:  in out string; -- 2+2*message'length
                             length:  out natural;
                             quote:   character := '"') is
   output: natural := 0;
 begin
   concat (quote, result, output);
   for input in message'range loop
     if message(input) = quote then
       concat (quote, result, output);
       concat (quote, result, output);
     else
       concat (message(input), result, output);
     end if;
   end loop;
   concat (quote, result, output);
   length := output;
 end quotes_inserted;

 procedure quotes_removed   (message: string;
                             result:  in out string; -- message'length
                             length:  out natural;
                             quote:   character := '"') is
   output: natural := result'first - 1;
   input:  natural := message'first+1;
   quotation_is_not_proper1: exception;
   quotation_is_not_proper2: exception;
 begin
   if    message'length < 2 or else
         message(message'first) /= quote or else
         message(message'last)  /= quote then
     output := message'length;
     result(result'first-1+1..result'first-1+message'length) := message;
   else
     loop
       exit when input>=message'last; -- Since message(message'last)=quote.
       if    message(input)/=quote then
         concat(message(input), result, output);
       elsif message(input+1) /= quote then
         raise quotation_is_not_proper2;
       else -- Two quotes in a row
         concat(message(input), result, output);
         input:=input+1;
       end if;
       input:=input+1;
     end loop;
   end if;
   length := output;
 end quotes_removed;

 function right_trim (message: string;
                      pattern: character := ' ') return string is
   right_trim_last: constant natural := last_missing (message, pattern);
 begin
   return message (message'first .. right_trim_last);
 end right_trim;

 function same (left, right: string)  return boolean is

   -- String Comparison: Semantically, left=right.

 begin
   return left'length = right'length and then
          left = right;
 end same;

 function substitute (base_string: string;
                      search_string: string;
                      replace_string: string)
                      return string is

   location: constant natural := first_occurring (base_string, search_string);
 begin
   if location = 0 then
     return base_string;
   elsif location = 1 then -- this requires the base_string to start at 1
     return replace_string &
            base_string (location+
                         search_string'length..
                         base_string'last);
   elsif location = base_string'last + 1 - search_string'length then
     return base_string (base_string'first..
                         base_string'last-search_string'length) &
            replace_string;
   else
     declare
       left_part: constant string :=
         base_string (base_string'first..location - 1);
     begin
       return left_part &
              replace_string &
              base_string (location + search_string'length ..
                           base_string'last);
     end;
   end if;
 end substitute;

 function upper_case (c: character) return character is
 begin
   if c in 'a'..'z' then
     return character'val (character'pos (c) - 32);
   end if;
   return c;
 end upper_case;

 procedure upper_case (message: in out string;
                       start: natural; finish: natural) is
 begin
   for i in start..finish loop
     if message (i) in 'a'..'z' then
       message (i) := character'val (character'pos (message (i)) - 32);
     end if;
   end loop;
 end upper_case;

 function upper_case (message: string) return string is
   hold_message:          string (1..message'length) := message;
   ch:                    character;
   offset: constant := character'pos ('a') - character'pos ('A');
 begin
   for c in hold_message'range loop
     ch := hold_message (c);
     if ch in 'a'..'z' then
       hold_message (c) := character'val (character'pos (ch) - offset);
     end if;            -- MESSAGE (FIRST..C) is in upper_case.
   end loop;
   return hold_message; -- MESSAGE is in upper case.
 end upper_case;

 procedure get_url_word (message: string;
                         message_start:    natural;
                         message_finish:   natural;
                         word_start:       out natural;
                         word_finish:      out natural;
                         separator_start:  out natural;
                         separator_finish: out natural;
                         separators: string := ":/#?") is

   -- Example 1: ~_.!a.7.@
   -- Example 2: "http://email.address/cgi/bin#query?""what """" quote"""

   c:          character;
   hold_quote: character := ascii.nul;
   upto:       natural   := message_start;
   something_cannot_arise_from_nothing: exception;
   unclosed_quote:                      exception;
   single_quote: constant character    := ''';
   double_quote: constant character    := '"';
   quotes:       constant string(1..2) := single_quote & double_quote;
 begin
   word_start := message_start;
   if message_start  > message_finish or
      message_finish < message'first  or
      message_start  > message'last then
     raise something_cannot_arise_from_nothing;
   end if;

   -- The range message_start..message_finish in contained in MESSAGE.

   if first_occurring (quotes, message (message_start)) /= 0 then
       -- Quote at start.
     hold_quote := message (upto);
     upto := upto + 1;
     loop
       exit when upto > message_finish;
       c := message (upto);
       if c = hold_quote then
         if upto < message_finish and then
            message (upto+1) = hold_quote then
           upto := upto + 1;
         else
           word_finish := upto;
           separator_start := upto+1;
           separator_finish := upto+1;
           return;
         end if;
       end if;
       upto := upto + 1;
     end loop;
     raise unclosed_quote;

   else -- No quote at start.
     loop
       exit when upto > message_finish;
       c := message (upto);
       if first_occurring (separators, c) /= 0 then
         word_finish := upto-1;
         separator_start := upto;
         loop
           exit when upto > message_finish;
           c := message (upto);
           exit when first_occurring (separators, c) = 0;
           upto := upto + 1;
         end loop;
         separator_finish := upto - 1;
         return;
       end if;
       upto := upto + 1;
     end loop;
     word_finish := upto - 1;
     separator_start := upto;
     separator_finish := 0;
   end if;
 end get_url_word;

 procedure url_break_apart (url: string;
                            protocol_start:  out natural;
                            protocol_finish: out natural;
                            internet_start:  out natural;
                            internet_finish: out natural;
                            port_start:      out natural;
                            port_finish:     out natural;
                            disk_start:      out natural;
                            disk_finish:     out natural;
                            file_start:      out natural;
                            file_finish:     out natural;
                            anchor_start:    out natural;
                            anchor_finish:   out natural;
                            args_start:      out natural;
                            args_finish:     out natural) is

   -- Under NT 4.0 the following rules apply:
   --   @ the file part goes up to 255 characters long
   --   @ permit as many periods anywhere in the file
   --   @ forbid characters: question mark and asterisk in a file
   --   @ permit as many blanks in the middle of the file
   --   @ strip off all blanks at the beginning and end of the file's name
   --
   -- This code currently compiles under the first two rules, and
   -- does not enforce the other 3 rules.

   ws, wf, ss, sf: natural;
   sl: integer;
   type separators is (error, colon_slash_slash, colon, other, none);
   separator: separators := error;
   separator_c: character;
   not_a_url: exception;

   procedure ts_get_url_word is -- for unit test only
     type expected_results is array (1..4) of natural;
     ws, wf, ss, sf: natural; -- set by get_word
     message1: constant string (2..19) := "~_.!a.7.@://aaa#b?";
     message2: constant string (1..15) := """what """" quote""";
     ts_get_url_word_failed: exception;
   begin
     get_url_word (message1, 2, 12, ws, wf, ss, sf);
     if    ws /=  2 then raise ts_get_url_word_failed;
     elsif wf /= 10 then raise ts_get_url_word_failed;
     elsif ss /= 11 then raise ts_get_url_word_failed;
     elsif sf /= 12 then raise ts_get_url_word_failed;
     end if;

     get_url_word (message1, 15, 19, ws, wf, ss, sf);
     if ws /= 15 or wf /=16 or ss /= 17 or sf /= 17 then
       raise ts_get_url_word_failed;
     end if;

     get_url_word (message2, 1, 15, ws, wf, ss, sf);
     if    ws /=  1 then raise ts_get_url_word_failed;
     elsif wf /= 15 then raise ts_get_url_word_failed;
     elsif ss /= 16 then raise ts_get_url_word_failed;
     elsif sf /= 16 then raise ts_get_url_word_failed;
     end if;
   end ts_get_url_word;
 begin
   ts_get_url_word;
   protocol_start := 0;
   protocol_finish := 0;
   internet_start := 0;
   internet_finish := 0;
   port_start := 0;
   port_finish := 0;
   disk_start := 0;
   disk_finish := 0;
   file_start := 0;
   file_finish := 0;
   anchor_start := 0;
   anchor_finish := 0;
   args_start := 0;
   args_finish := 0;

   get_url_word (url, url'first, url'last, ws, wf, ss, sf);

   sl:=sf+1-ss;
   if    ss not in url'first..url'last then
     separator := none;
   elsif sl=3 and then url (ss..sf) = "://" then -- it is a full URL
     separator := colon_slash_slash;
   elsif sl=1 and then url (ss) = ':' then -- it is a disk drive
     separator := colon;
   else
     separator := other;
   end if;

   case separator is
     when error => raise not_a_url;
     when colon_slash_slash =>
       protocol_start := ws;
       protocol_finish := wf;
       get_url_word (url, sf+1, url'last, ws, wf, ss, sf);
       internet_start := ws;
       internet_finish := wf;
       if ss in url'range and then ss=sf and then url (ss) = ':' then -- port
         get_url_word (url, sf+1, url'last, ws, wf, ss, sf);
         port_start := ws;
         port_finish := wf;
       end if;
       if ss not in url'range or else ss/=sf or else url (ss) /= '/' then
         return;
       end if;
       get_url_word (url, sf+1, url'last, ws, wf, ss, sf);
     when colon => null;
     when none  => null;
     when other => null;
   end case;

   if ss=sf and ss in url'range then
     separator_c := url (ss);
   else
     separator_c := ascii.lf;
   end if;

   if separator_c = ':' then
     disk_start := ws;
     disk_finish := wf;
     get_url_word (url, sf+1, url'last, ws, wf, ss, sf);
   end if;

   if ss=sf and ss in url'range then
     separator_c := url (ss);
   else
     separator_c := ascii.lf;
   end if;

   file_start := ws;
   loop
     file_finish := wf;
     exit when separator_c /= '/';
     get_url_word (url, sf+1, url'last, ws, wf, ss, sf);
     if ss=sf and ss in url'range then
       separator_c := url (ss);
     else
       separator_c := ascii.lf;
     end if;
   end loop;

   if separator_c = '#' then -- that is, the PRIOR separator
     get_url_word (url, sf+1, url'last, ws, wf, ss, sf);
     if ss=sf and ss in url'range then
       separator_c := url (ss);
     else
       separator_c := ascii.lf;
     end if;
     anchor_start := ws;
     anchor_finish := wf;
   end if;

   if separator_c = '?' then -- that is, the PRIOR separator
     get_url_word (url, sf+1, url'last, ws, wf, ss, sf);
     args_start := ws;
     loop
       get_url_word (url, sf+1, url'last, ws, wf, ss, sf);
       exit when ss /= sf or else ss not in url'range or else url (ss) /= '?';
     end loop;
     args_finish := wf;
   end if;
 end url_break_apart;

end string_control;


package variable_string_control is
   pragma pure (variable_string_control);

-- Variable strings are each allocated to any maximum length, and vary
-- from 0 to that maximum in length. Compiler enforces specifying that
-- maximum length when each variable is declared.

   type variable_strings (size: positive) is limited private;

-- The concatenation of variable strings is an unconstrained string.

  function "&" (left: variable_strings;
                right: variable_strings)
                return string;
  function "&" (left: string;
                right: variable_strings)
                return string;
  function "&" (left: variable_strings;
                right: string)
                return string;

-- When strings agree up to the minimum of their lengths,
-- the shorter string is considered less than the longer string.

  function "<"  (left, right: variable_strings) return boolean;
  function ">"  (left, right: variable_strings) return boolean;
  function "<=" (left, right: variable_strings) return boolean;
  function ">=" (left, right: variable_strings) return boolean;

-- COPY is used to change variable strings.

  procedure copy (source: variable_strings; target: in out variable_strings);
  procedure copy (source: string; target: in out variable_strings);

-- The concatenation method is used to append onto the end of a v string.

  procedure concat(appendage: string;
                   onto: in out variable_strings);
  procedure concat(appendage: variable_strings;
                   onto: in out variable_strings);

-- The ELEMENT method returns a column of a variable string just like
-- array references get columns from ordinary_strings.

  function element (message: variable_strings;
                    col: natural) return character;

-- The IMAGE method returns the string image of a variable string.

  function image (message: variable_strings) return string;

-- The in_place_lower_case function converts
-- alphabetic character from upper to lower
-- case, ignoring all other characters.

  procedure in_place_lower_case (message: in out variable_strings);

-- The in_place_truncate method is a procedure instead of a function,
-- for efficiency.

  procedure in_place_truncate (message: in out variable_strings;
                               ch: character := ' ');

-- The in_place_upper_case function converts
-- alphabetic character from lower to upper
-- case, ignoring all other characters.

  procedure in_place_upper_case (message: in out variable_strings);

-- The LENGTH method returns the length of the variablestring.

  function length (message: variable_strings) return natural;

-- The length_allocated returns the maximum length of the v string.

  function length_allocated (message: variable_strings) return natural;

-- The SAME method is the equality operator for variable strings. To
-- be equal, the two strings must be of the same length, and agree
-- for each of their characters.

  function same (left, right: variable_strings) return boolean;
  function same (left: variable_strings; right: string) return boolean;

-- The set_element method sets a character in a single column
-- of a variable string like array references do for character
-- strings.

  procedure set_element (message: in out variable_strings;
                         col: natural;
                         value:  character);

-- The set_length method truncates a string to a given length or else
-- extends its current length with blanks.

  procedure set_length (message: in out variable_strings;
                        new_length: natural);

-- The SLICE method emulates array slices for variable strings. The
-- SUBSTR method is similar, but based on length instead of final column.
-- The two-argument SUBSTR gets all characters on or after the given column.
-- All of these permit the START argument to take on a value equal to the
-- length of the variable string plus 1, returning the null ordinary_string.

  function slice  (message: variable_strings;
                   start, finish: natural) return string;
  function substr (message: variable_strings;
                   start, length: natural) return string;
  function substr (message: variable_strings;
                   start: natural) return string;
private
  type variable_strings (size: positive) is record
    length: integer := -1; -- Causes arrays of strings to fully Allocate.
    list:   string (1..size);
  end record;
end variable_string_control;                                             


ith string_control;
package body variable_string_control is

 procedure is_okay (message: variable_strings) is
   varstring_never_init: exception;
 begin
   if message.length < 0 then
     raise varstring_never_init;
   end if;
 end is_okay;

 function "&" (left: variable_strings;
               right: variable_strings)
               return string is
   hold: variable_strings (left.length + right.length);
 begin
   is_okay (left);
   is_okay (right);
   copy (left, hold);
   concat (right.list(1..right.length), onto=>hold);
   return hold.list(1..hold.length);
 end "&";

 function "&" (left: string; right: variable_strings) return string is
   hold: variable_strings (left'length + right.length);
 begin
   is_okay (right);
   copy (left, hold);
   concat (right.list(1..right.length), onto=>hold);
   return hold.list(1..hold.length);
 end "&";

 function "&" (left: variable_strings; right: string) return string is
   hold: variable_strings (left.length + right'length);
 begin
   is_okay (left);
   copy (left, hold);
   concat (right, onto=>hold);
   return hold.list(1..hold.length);
 end "&";

 function "<" (left, right: variable_strings) return boolean is
 begin
   is_okay(left);
   is_okay(right);
   if    left.length < right.length then
     return left.list(1..left.length)  <= right.list(1..left.length);
   elsif left.length = right.length then
     return left.list(1..left.length)  <  right.list(1..left.length);
   else -- left.length > right.length
     return left.list(1..right.length) <  right.list(1..right.length);
   end if;
 end "<";

 function ">" (left, right: variable_strings) return boolean is
 begin
   is_okay(left);
   is_okay(right);
   if    left.length < right.length then
     return left.list(1..left.length)  >  right.list(1..left.length);
   elsif left.length = right.length then
     return left.list(1..left.length)  >  right.list(1..left.length);
   else -- left.length > right.length
     return left.list(1..right.length) >= right.list(1..right.length);
   end if;
 end ">";

 function "<=" (left, right: variable_strings) return boolean is
 begin
   return left < right or else left = right;
 end "<=";

 function ">=" (left, right: variable_strings) return boolean is
 begin
   return left > right or else left = right;
 end ">=";

 procedure concat(appendage: string; onto: in out variable_strings) is
   new_length: constant integer := onto.length + appendage'length;
   concat_string_onto_varstring_too_long: exception;
 begin
   is_okay (onto);
   if new_length > onto.size then
     raise concat_string_onto_varstring_too_long;
   end if;
   onto.list(onto.length+1..new_length) := appendage;
   onto.length := new_length;
 end concat;

 procedure concat(appendage: variable_strings;
                  onto: in out variable_strings) is
   new_length: constant integer := onto.length + appendage.length;
   concat_varstring_onto_varstring_too_long: exception;
 begin
   is_okay (onto);
   if new_length > onto.size then
     raise concat_varstring_onto_varstring_too_long;
   end if;
   onto.list(onto.length+1..new_length) :=
     appendage.list(1..appendage.length);
   onto.length := new_length;
 end concat;

 procedure copy (source: variable_strings; target: in out variable_strings) is
   copy_varstring_to_varstring_too_long: exception;
 begin
   is_okay (source);
   if source.length > target.size then
     raise copy_varstring_to_varstring_too_long;
   end if;
   target.length := source.length;
   target.list (1..source.length) := source.list (1..source.length);
 end copy;

 procedure copy (source: string; target: in out variable_strings) is
   copy_string_to_varstring_too_long: exception;
 begin
   if source'length > target.size then
     raise copy_string_to_varstring_too_long;
   end if;
   target.length := source'length;
   if source'length>0 then
     target.list (1..source'length) := source;
   end if;
 end copy;

 function element (message: variable_strings;
                   col: natural) return character is
   varstring_element_out_of_range: exception;
 begin
   is_okay (message);
   if    col not in 1..message.length then
     raise varstring_element_out_of_range;
   end if;
   return message.list (col);
 end element;

 function image (message: variable_strings) return string is
 begin
   is_okay (message);
   if message.length=0 then return "";
   else                     return message.list (1..message.length);
   end if;
 end image;


 procedure in_place_lower_case (message: in out variable_strings) is
 begin
   is_okay (message);
   for i in 1..message.length loop
     message.list(i) := string_control.lower_case (message.list(i));
   end loop;
 end in_place_lower_case;

 procedure in_place_truncate (message: in out variable_strings;
                              ch: character := ' ') is
 begin
   is_okay (message);
   loop
     exit when message.length = 0;
     exit when message.list (message.length) /= ch;
     message.length := message.length - 1;
   end loop;
 end in_place_truncate;

 procedure in_place_upper_case (message: in out variable_strings) is
 begin
   is_okay (message);
   for i in 1..message.length loop
     message.list(i) := string_control.upper_case (message.list(i));
   end loop;
 end in_place_upper_case;

 function length (message: variable_strings) return natural is
 begin
   is_okay (message);
   return message.length;
 end length;

 function length_allocated (message: variable_strings) return natural is
 begin
   return message.size;
 end length_allocated;

 function same (left, right: variable_strings) return boolean is
 begin
   is_okay (left);
   is_okay(right);
   return left.length=right.length and then
          left.list(1..left.length) = right.list(1..left.length);
 end same;

 function same   (left: variable_strings; right: string) return boolean is
 begin
   is_okay (left);
   return left.length=right'length and then
          left.list(1..left.length) = right;
 end same;

 procedure set_element (message: in out variable_strings;
                        col: natural;
                        value: character) is
   varstring_set_element_index_out_of_range: exception;
   varstring_set_element_index_way_out_of_range: exception;
 begin
   is_okay (message);
   if    col > message.size then
     raise varstring_set_element_index_way_out_of_range;
   elsif col = message.length+1 then
     message.length     := message.length + 1;
     message.list (col) := value;
   elsif col > message.length then
     raise varstring_set_element_index_out_of_range;
   else
     message.list (col) := value;
   end if;
 end set_element;

 procedure set_length (message: in out variable_strings;
                       new_length: natural) is
   hold_length: integer := message.length;
   varstring_set_length_too_long: exception;
 begin
   if hold_length > message.size then
     raise varstring_set_length_too_long;
   end if;
   message.length := new_length;
   if hold_length < 0 then
     for i in 1..message.size loop
       message.list (i) := ' ';
     end loop;
   end if;
   if    hold_length > 0 then
     for i in hold_length+1 .. new_length loop
       message.list (i) := ' ';
     end loop;
   end if;
 end set_length;

 function slice (message: variable_strings;
                 start, finish: natural) return string is
   varstring_slice_start_out_of_range: exception;
   varstring_slice_finish_out_of_range: exception;
 begin
   is_okay (message);
   if    start not in 1..message.length then
     raise varstring_slice_start_out_of_range;
   elsif finish not in 0..message.length then
     raise varstring_slice_finish_out_of_range;
   end if;
   if finish < start then
     return "";
   else
     declare
       hold: constant string (1..finish+1-start) :=
                        message.list(start..finish);
     begin
       return hold;
     end;
   end if;
 end slice;

 function substr (message: variable_strings;
                  start, length: natural) return string is
 begin
   is_okay (message);
   return slice (message, start, start+length-1);
 end substr;

 function substr (message: variable_strings;
                  start: natural)
                  return string is
 begin
   is_okay (message);
   return slice (message, start, message.length);
 end substr;

end variable_string_control;
with system;
package hand is
  pragma elaborate_body;

  --  Purpose:
  --
  --    Provide OS binding to file open, read, write, position, close
  --
  --  Warning: To compute the system.address do this: message(1)'address
  --
  --
  --  Architecture:
  --
  --     ADO (Abstract Data Object)
  --     SIS (Semantically Independent Specification)

  type handles is range -1..32;
  for handles'size use 16;

  null_handle:     constant handles := 0;
  broken_handle:   constant handles := -1; -- File does not exist.
  standard_input:  constant handles := 0;
  standard_output: constant handles := 1;
  standard_error:  constant handles := 2;

  type    open_methods   is (read_open, write_open, append_open);
  type    seek_methods   is (from_BOF, from_current, from_EOF);
  type    file_positions is range -2147483648 .. 2147483647;
  subtype string_8       is string (1..8);
  subtype string_12      is string (1..12);

  already_internally_open:      exception;
  could_not_check:              exception;
  could_not_create:             exception;
  could_not_open:               exception;
  could_not_read:               exception;
  could_not_reposition:         exception;
  could_not_seek:               exception;
  missing_slash_at_end_of_path: exception;

  procedure close         (handle: handles);
  procedure commit        (handle: handles);
  function  create        (name:   string)
                           return handles;
  procedure create_unique (path: string;
                           name:   out string;
                           handle: out handles);
  function  image         (handle: handles)
                           return string;
  function  is_standard_input (handle: handles)
                           return boolean;
  function  is_standard_output (handle: handles)
                           return boolean;
  function  open          (name: string;
                           open_method: open_methods)
                           return handles;
  function  position      (handle: handles)
                           return file_positions;
  procedure read          (whereto: system.address;
                           length: in out file_positions;
                           handle: handles);
  function  seek          (handle: handles;
                           position: file_positions;
                           seek_method: seek_methods)
                           return file_positions;
  function  size          (handle: handles)
                           return file_positions;
  procedure write         (whereto: system.address;
                           length:  in out file_positions;
                           handle:  handles);
end hand;


with Ada.IO_exceptions;
with Ada.streams.stream_io;
with Ada.text_io.text_streams;
with variable_string_control;
package body hand is
  -- compiler_this_package_is_dependent_on: constant string := "any Ada 95";
  use Ada.streams.stream_io;

  directory_separator: constant character := '\'; -- change to '/' for Linux

  type unique_counts is mod 4;
  unique_count: unique_counts := 0;

  -- Streams

  type selfs is record
    soul:       file_type;
    incarnated: boolean:=false;
  end record;

  subtype usable_handles is handles range 3 .. handles'last;
  type universes is array (usable_handles) of selfs;
  self: universes;
  handle_is_not_incarnated: exception;

  procedure check (handle: handles; message: string) is
  begin
    if handle in usable_handles then
      if not self (handle).incarnated then
        raise handle_is_not_incarnated;
      end if;
    end if;
  end check;

  function stream (handle: handles) return stream_access is
    broken_handle_detected: exception;
  begin
    case handle is
      when standard_input=>
        return Ada.streams.stream_io.stream_access(
          Ada.text_io.text_streams.stream(Ada.text_io.standard_input));
      when standard_output=>
        return Ada.streams.stream_io.stream_access(
          Ada.text_io.text_streams.stream(Ada.text_io.standard_output));
      when standard_error =>
        return Ada.streams.stream_io.stream_access(
          Ada.text_io.text_streams.stream(Ada.text_io.standard_error));
      when broken_handle=>
        raise broken_handle_detected;
      when usable_handles=>
        return stream (self (handle).soul);
    end case;
  end stream;

  function available return handles is
  begin
    for handle in usable_handles loop
      if not self (handle).incarnated then
        self (handle).incarnated := true;
        return handle;
      end if;
    end loop;
    raise could_not_create;
  end available;

  procedure commit (handle: handles) is
  begin
    check (handle, "commit");
    if handle in usable_handles then
      flush (self (handle).soul);
    else
      null;
    end if;
  end commit;

  procedure close (handle: handles) is
  begin
    if handle in usable_handles then
        -- Handle is not checked, so failed-open handles can be closed.
      if self (handle).incarnated then
        close (self (handle).soul);
        self (handle).incarnated := false;
      end if;
    end if;
  exception
    when Ada.IO_exceptions.status_error =>
      self (handle).incarnated := false;
  end close;

  function image (handle: handles) return string is
  begin
    return handles'image (handle);
  end image;

  function is_standard_input (handle: handles) return boolean is
  begin
    return handle=standard_input;
  end is_standard_input;

  function is_standard_output (handle: handles) return boolean is
  begin
    return handle=standard_output;
  end is_standard_output;

  function create (name: string) return handles is
    handle: handles;
    could_neither_create_it_nor_open_it_for_output: exception;
    use variable_string_control;
  begin
    if name'length=0 then
      handle := standard_output;
    else
      handle := available;
      create (self (handle).soul, out_file, name);
    end if;
    return handle;
  exception
    when Ada.IO_exceptions.name_error =>
      open (self (handle).soul, out_file, name);
      return handle;
    when Ada.IO_exceptions.use_error =>
      begin
        open (self (handle).soul, out_file, name);
        return handle;
      exception
        when Ada.IO_exceptions.use_error =>
          raise could_neither_create_it_nor_open_it_for_output;
          return broken_handle;
      end;
  end create;

  procedure create_unique (path: string;
                           name: out string;
                           handle: out handles) is

    unique_image: string:=unique_counts'image(unique_count);
    random_string: constant string := "tempxxx" &
        unique_image(unique_image'first + 1) & ".tmp";  -- 9 only ?
    -- random_string: constant string := "tempxxxx.tmp";
    full_path: string := path & random_string;

  begin
    unique_count := unique_count + 1;
    if path'length /= 0 and then
       path (path'last) /= directory_separator then
      raise missing_slash_at_end_of_path;
    end if;
    handle := create (full_path);
    name   := full_path (path'length+1 .. path'length+random_string'length);
  end create_unique;

  function open (name: string;
                 open_method: open_methods)
                 return handles is

    handle:        handles;
    file_position: file_positions := 0;
    use variable_string_control;
  begin
    if name'length=0 then
      case open_method is
        when read_open =>
          handle := standard_input;
        when write_open =>
          handle := standard_output;
        when append_open=>
          handle := standard_output;
      end case;
    else
      handle := available;
      case open_method is
        when read_open =>
          open (self (handle).soul, in_file, name);
        when write_open =>
          open (self (handle).soul, out_file, name);
        when append_open=>
          open (self (handle).soul, append_file, name);
          file_position := seek (handle, 0, from_EOF);
          -- redundant because of bug in gnat 3.10. Needed in gnat3.11?
      end case;
    end if;
    return handle;
  exception
    when Ada.IO_exceptions.name_error =>
      self (handle).incarnated := false;
      return broken_handle;
  end open;

  function position (handle: handles) return file_positions is
    standard_handles_cannot_be_positioned: exception;
  begin
    check (handle, "position");
    if handle not in usable_handles then
      raise standard_handles_cannot_be_positioned;
    end if;
    return file_positions (index (self(handle).soul)) - 1;
  end position;

  procedure read (whereto: system.address;
                  length: in out file_positions;
                  handle: handles) is

    use Ada.streams;
    subtype buffers is stream_element_array(1..stream_element_offset(length));
    buffer: buffers;
    for buffer use at whereto;
    seo: stream_element_offset:=stream_element_offset(length);
  begin
    check (handle, "read");
    read(stream (handle).all, buffer, seo);
    length:=file_positions(seo);
  end read;

  function is_keyboard_device (handle: handles) return boolean is
  begin
    return false; --?
  end is_keyboard_device;

  function seek (handle: handles;
                 position: file_positions;
                 seek_method: seek_methods)
                 return file_positions is

    cannot_seek_standard_handles: exception;
    p: constant file_positions := position + 1;
    here: constant file_positions := hand.position (handle);
  begin
    check (handle, "seek");
    if handle in usable_handles then
      declare
        file: file_type renames self(handle).soul;
      begin
        case seek_method is
          when from_BOF=>
            set_index (file, positive_count (p));
          when from_EOF=>
            set_index (file, positive_count (file_positions(size(file)) + p));
          when from_current=>
            set_index (file, positive_count (here + P));
        end case;
        return file_positions(index (file))-1;
      end;
    else
      raise cannot_seek_standard_handles;
    end if;
  end seek;

  function size (handle: handles) return file_positions is
    my_size: count;
    signed_size: file_positions;
  begin
    check (handle, "size");
    if handle in usable_handles then
      my_size := size (self (handle).soul);
      signed_size := file_positions(my_size);
      return signed_size;
    else
      return 0;
    end if;
  end size;

  procedure write (whereto: system.address;
                   length:  in out file_positions;
                   handle:  handles) is
    use Ada.streams;
    subtype buffers is stream_element_array(1..stream_element_offset(length));
    buffer: buffers;
    for buffer use at whereto;
  begin
    check (handle, "write");
    write (stream (handle).all, buffer);
  end write;

end hand;

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





  reply	other threads:[~1998-09-24  0:00 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
1998-09-24  0:00 Image processing File I/O Jeong, Laehyung
1998-09-24  0:00 ` dewarr
1998-09-24  0:00 ` Dale Stanbrough
1998-09-24  0:00   ` Michael F Brenner [this message]
  -- strict thread matches above, loose matches on Subject: below --
1998-09-24  0:00 tmoran
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox