{ NOTICE OF COPYRIGHT AND OWNERSHIP OF SOFTWARE:

  Copyright 1977, 1978, 1979, 1980, 1981, 1982, 1983  by Oregon Software, Inc.
  All Rights Reserved.

  This computer program is the property of Oregon Software, Inc.
  of Portland, Oregon, U.S.A., and may be used
  and copied only as specifically permitted under written
  license agreement signed by Oregon Software, Inc.

  Whether this program is copied in whole or in part and whether this
  program is copied in original or in modified form, ALL COPIES OF THIS
  PROGRAM MUST DISPLAY THIS NOTICE OF COPYRIGHT AND OWNERSHIP IN FULL.

   Conformant array string routines.
   Release version: 2.1C Level: 3  Date: 17-Aug-1984 11:26:07
   Processor: ALL  System: All
}

{

Declare string variables for this package with type declarations of the
form "packed array [0..n] of char", where n >= 1.  The length of the string
is stored in element 0, elements 1 to n are the characters of the string.
The routines will also accept parameters of type "packed array [1..n] of
char", in which case "n" is considered to be the length of the string.  In
particular, quoted strings in Pascal are considered to be of this type, and
may be intermixed with string variables when calling routines which don't
require "var" params.

The capabilities provided are:

Len(S) - a function giving the current length of string S;

Clear(S) - initializes string S to empty;

ReadString(F,S) - reads a value for string S from the text
  file F.  The string is terminated by Eoln(F) and a
  Readln(F) is performed.  String overflow (input data contains
  more characters than the target array can hold) results in truncation.

WriteString(F,S) - writes the string S to the text file F.

Concatenate(T,S) - appends string S to the target string T.
  The resulting value is string T.  Overflow results in
  truncation to StringMax characters.

Search(S,T,Start) - searches string S for the first
  occurrence of string T to the right of position Start
  (characters are numbered beginning with one).  The
  function Search() returns the position of the first
  character in the matching substring, or the value zero if
  the string T does not appear.

Insert(T,S,Start) - inserts the string S into the target
  string T at position Start.  Characters are shifted to the
  right as necessary.  Overflow produces a truncated target
  string;  a Start position which would produce a string
  which was not contiguous has no effect.

Assign(T,S) - Assign string S to the target string T.  Especially
useful for assigning a literal string to a variable string.

AssChar(T,C) - Assign character C to the target string T.
Especially useful for assigning a single character to a
variable string.

Equal(T,S) - Function Equal returns TRUE when T=S, returns
FALSE otherwise.

The Start and Span parameters in the following procedures
define a substring beginning at position Start (between
characters Start-1 and Start) with a length of Abs(Span).
If Span is positive, the substring is to the right of Start;
if negative, the substring is to the left.

DelString(S,Start,Span) - deletes the substring defined by
  Start, Span from the string S.

Substring(T,S,Start,Span) - the substring of string S
  defined by Start, Span is assigned to the target string T.
}
{[b+]}


procedure exitst(error: integer);

{ exit with status, forces abort of pascal program.
}

  external; {in the pascal library}


procedure abort(s: packed array [low..high: integer] of char);

{ abort program if an illegal combination of arguments has been
  detected.
}

  forward;


procedure writestring(var f: text; {an output file, we hope}
                      var s: packed array [slow..shigh: integer] of char);

 { Write a string variable to file "f". }

  var
    i: integer;


  begin {writestring}
    if (slow = 0) {funny string} then
      for i := 1 to ord(s[0]) do write(f, s[i])
    else if (slow = 1) {string literal} then
      for i := 1 to shigh do write(f, s[i])
    else abort('writestring')
  end {writestring} ;


procedure abort;


  begin {abort}
    write('Illegal string argument in "');
    writestring(output, s);
    writeln('"');
    exitst(4);
  end {abort} ;


function len(s: packed array [slow..shigh: integer] of char): integer;


  begin {len}
    if slow = 0 then len := ord(s[0])
    else if slow = 1 then len := shigh
    else abort('len');
  end {len} ;


procedure clear(var s: packed array [low..high: integer] of char);


  begin {clear}
    if low <> 0 then abort('clear')
    else s[0] := chr(0);
  end {clear} ;


procedure concatenate(var t: packed array [tlow..thigh: integer] of char;
                      s: packed array [slow..shigh: integer] of char);

  var
    i, slen, tlen: integer;


  begin {concatenate}
    if (tlow <> 0) or (slow <> 1) and (slow <> 0) then abort('concatenate')
    else
      begin
      if slow = 1 then slen := shigh
      else slen := ord(s[0]);
      tlen := ord(t[0]);
      if slen + tlen > thigh then slen := thigh - tlen;
      t[0] := chr(slen + tlen);
      for i := 1 to slen do t[i + tlen] := s[i];
      end;
  end {concatenate} ;


function search(s: packed array [slow..shigh: integer] of char;
                t: packed array [tlow..thigh: integer] of char;
                start: integer): integer;

  var
    i, j, tlen, slen: integer;
    uneq: boolean;


  begin {search}
    if (start < 1) or (slow <> 0) and (slow <> 1) or (tlow <> 0) and
       (tlow <> 1) then
      abort('search')
    else
      begin
      search := 0;
      if slow = 0 then slen := ord(s[0])
      else slen := shigh;
      if tlow = 0 then tlen := ord(t[0])
      else tlen := thigh;
      if (start + tlen <= slen + 1) and (tlen <> 0) then
        begin
        i := start - 1;
        repeat
          i := i + 1;
          j := 0;
          repeat
            j := j + 1;
            uneq := t[j] <> s[i + j - 1];
          until uneq or (j = tlen);
        until (not uneq) or (i = slen - tlen + 1);
        if uneq then search := 0
        else search := i;
        end;
      end;
  end {search} ;


procedure readstring(var f: text; { an input file }
                     var s: packed array [slow..shigh: integer] of char);

  var
    slen: integer {used to hold accumulated length of s, quicker access than
                   s[0]} ;


  begin {readstring}
    if slow <> 0 then abort('readstring')
    else
      begin
      slen := 0;
      while (not eoln(f)) and (slen < shigh) do
        begin
        slen := slen + 1;
        read(f, s[slen]);
        end;
      s[0] := chr(slen);
      readln(f);
      end;
  end {readstring} ;


procedure substring(var t: packed array [tlow..thigh: integer] of char;
                    s: packed array [slow..shigh: integer] of char;
                    start, span: integer);

  var
    i, slen: integer;


  begin {substring}
    if (tlow <> 0) or (slow <> 0) and (slow <> 1) then abort('substring')
    else
      begin
      if slow = 0 then slen := ord(s[0])
      else slen := shigh;
      if span < 0 then
        begin
        span := - span;
        start := start - span
        end;
      if start < 1 then
        begin
        span := span + start - 1;
        start := 1
        end;
      if start + span > slen + 1 then span := slen - start + 1;
      if thigh < span then abort('substring')
      else if span <= 0 then t[0] := chr(0)
      else
        begin
        for i := 1 to span do t[i] := s[start + i - 1];
        t[0] := chr(span);
        end;
      end;
  end {substring} ;


procedure delstring(var t: packed array [tlow..thigh: integer] of char;
                       start, span: integer);

  var
    i, limit, tlen: integer;


  begin {deletestring}
    if tlow <> 0 then abort('deletestring')
    else
      begin
      tlen := ord(t[0]);
      if span < 0 then
        begin
        span := - span;
        start := start - span
        end;
      limit := start + span;
      if start < 1 then start := 1;
      if limit > tlen + 1 then limit := tlen + 1;
      span := limit - start;
      if span > 0 then
        begin
        for i := 0 to tlen - limit do t[start + i] := t[limit + i];
        t[0] := chr(ord(t[0]) - span);
        end;
      end;
  end {deletestring} ;


procedure insert(var t: packed array [tlow..thigh: integer] of char;
                 s: packed array [slow..shigh: integer] of char;
                 p: integer);

  var
    i, j, tlen, slen: integer;


  begin {insert}
    if (tlow <> 0) or (slow <> 0) and (slow <> 1) then abort('insert')
    else
      begin
      tlen := ord(t[0]);
      if slow = 0 then slen := ord(s[0])
      else slen := shigh;
      if slen > 0 then
        if (p > 0) and (p <= tlen + 1) then
          begin
          if slen + tlen >= thigh then tlen := thigh else
          tlen := slen + tlen;
          for i := tlen downto p + slen do t[i] := t[i - slen];
          if tlen < p + slen then j := tlen
          else j := p + slen - 1;
          for i := p to j do t[i] := s[i - p + 1];
          t[0] := chr(tlen);
          end
        else abort('insert') { error: non-contiguous string } ;
      end;
  end {insert} ;


procedure assign(var t: packed array [tlow..thigh: integer] of char;
                 s: packed array [slow..shigh: integer] of char);

  var
    slen: integer;
    i: integer;


  begin {assign}
    if (tlow <> 0) and (tlow <> 1) or (slow <> 0) and (slow <> 1) then
      abort('assign - bad arguments');
    if slow = 0 then slen := ord(s[0])

   {User option here - the following code removes all trailing blanks
    from a literal string when inserting it into a variable string...
    This may be annoying to some users as you may wish the actually assign
    blanks to the end of a string in this manner.  To change the code
    comment out the following :

        else begin
         slen := shigh;
         while (s.....
         end;

    and replace the "else begin" with

       else slen := shigh;

   }
       else begin
      slen := shigh;
      while (s[slen] = ' ') and (slen > 1) do slen := slen - 1;
      end;
    if slen > thigh then abort('assign - destination string too short');
    for i := 1 to slen do t[i] := s[i];
    if tlow = 0 then t[0] := chr(slen)
    else if tlow = 1 then for i := slen + 1 to thigh do t[i] := ' ';
  end {assign} ;


procedure asschar(var t: packed array [tlow..thigh: integer] of char;
                     c: char);


  begin {assignchar}
    if tlow <> 0 then abort('assignchar');
    t[0] := chr(1);
    t[1] := c;
  end {assignchar} ;


function equal(s1: packed array [s1low..s1high: integer] of char;
               s2: packed array [s2low..s2high: integer] of char): boolean;

  var
    s1len, s2len: integer;
    eq: boolean;
    i: integer;


  begin {equal}
    if (s1low <> 0) and (s1low <> 1) or (s2low <> 0) and (s2low <> 1) then
      abort('equal - bad arguments')
    else
      begin
      if (s1low = 0) then s1len := ord(s1[0])
      else
        begin
        s1len := s1high;
        while (s1[s1len] = ' ') and (s1len > 1) do s1len := s1len - 1;
        end;
      if (s2low = 0) then s2len := ord(s2[0])
      else
        begin
        s2len := s2high;
        while (s2[s2len] = ' ') and (s2len > 1) do s2len := s2len - 1;
        end;
      if s1len <> s2len then equal := false
      else
        begin
        eq := true;
        for i := 1 to s1len do eq := eq and (s1[i] = s2[i]);
        equal := eq;
        end;
      end;
  end; {equal}
                                                                                                                                                                                                                                                                                                                                                                                                                  