!       ABEL_UTIL.TPU
!
!       Table of Contents as of 27-Mar-1988
!
!       Procedure name              Page    Description
!       --------------              ----    -----------
!
!       eve$what_line                  1    Internal "what line am I on"
!       eve$compress_whitespace        2    Delete whitespace around current char
!       eve$split_line                 3    Hook for auto-indent function
!       abl$abs                        5    Perform absolute-value function
!       abl$mod                        5    Perform modulus function
!       abl$notab_line                 6    Return current line without tabs
!       abl$extract_integer            7    Extract signed integer from string
!       abl$ascii                      8    Convert #'s to string of ascii
!       abl$undelimit                  9    Undelimits a delimited string
!       abl$notab_offset               10    Offset if tabs were spaces
!       abl$virtual_mark               11    Mark without a mark
!       abl$virtual_position           11    Go to mark that wasn't

!                                                                       Page 1


procedure eve$what_line                 ! Internal "what line am I on"

! Returns the line number of the current line in the current buffer.
!
! Source:
!   Eveplus

local
    high_line,
    low_line,
    start_of_buffer,
    this_line,
    this_position,
    total_lines;

this_position := mark (none);
start_of_buffer := beginning_of (current_buffer);
total_lines := get_info (current_buffer, "record_count") + 1;
high_line := total_lines;
if this_position = end_of (current_buffer) then
    low_line := total_lines;
else
    low_line := 1;
endif;

! Binary search

loop
    exitif high_line - low_line <= 1;
    this_line := low_line + ((high_line - low_line) / 2);
    position (start_of_buffer);
    move_vertical (this_line - 1);
    if mark (none) > this_position then
    high_line := this_line;
    else
        low_line := this_line;
    if mark (none) = this_position then
        high_line := this_line;
        endif;
    endif;
endloop;

position (this_position);
return low_line;
endprocedure


!                                                                       Page 2


procedure eve$compress_whitespace       ! Delete whitespace around current char

! Delete all whitespace surrounding the current character, except for
! the first whitespace character (delete that too if at start of line).
! Position cursor at beginning of next word.  No-op if current character
! is not whitespace.  Trim spaces if at end of line.
!
! Don't do anything if in overstrike mode.
!
! Source:
!   Eve

if get_info(current_buffer,"mode")=overstrike then
    return
endif;

if current_character = eve$kt_null then
    eve$trim_line;
    return;
endif;

if index (eve$x_whitespace, current_character) = 0 then
    return;
endif;

loop
    exitif current_offset = 0;
    move_horizontal (-1);
    if index (eve$x_whitespace, current_character) = 0 then
        move_horizontal(1);
        erase_character(1);
        copy_text(" ");         ! leave space (not first whitespace) if not at start of line
        exitif 1;
    endif;
endloop;

if index (eve$x_whitespace, current_character) <> 0 then
    erase (search (eve$pattern_whitespace, forward));
endif;

endprocedure;


!                                                                       Page 3


procedure eve$split_line                ! Hook for auto-indent function

! Split line at current cursor location, perform autoindent if on.
!
! Globals:
!   abl$autoindent  boolean     if 1 then perform autoindent
!
! Source:
!   Eva2

local
    starting_offset,                ! The offset where we started
    previous_whitespace,            ! Whitespace from previous line
    starting_position;              ! User's starting position

on_error
endon_error

!
! Only copy whitespace up to our current cursor position
!
starting_offset := current_offset;
if (abl$autoindent) and (starting_offset<>0) then
    starting_position:=mark(none);
    move_horizontal(-current_offset);
    previous_whitespace := search(anchor & span(eve$x_whitespace),forward);
    position(starting_position);
    split_line;
    if previous_whitespace <> 0 then
        copy_text(substr(previous_whitespace,1,starting_offset))
    endif;
else
    split_line;
endif;
endprocedure


!                                                                       Page 4


! General-purpose Abel routines

!   Results of calling these routines may be passed back to the caller in one
!   of two ways.  To properly use these routines, you must determine which
!   method the routine you want is using.  Note:  %'s and $'s are used to
!   prefix parameters in these descriptions; the % refers to an integer
!   parameter while the $ refers to a string.

!   The most general purpose method is parameter passing with a return-status.
!   Using this method, parameters from and to the caller are passed in the
!   parameter list of the call, and the routine itself is set to success (1) or
!   failure (0).  An example of this is the abl$mod(%1,%2,%3) = S/F call; the
!   first two parameters (%1 and %2) are the numerator and denominator for
!   the mod function, the last parameter (%3) is the result of the mod, and S/F
!   (success/failure) is the result of the call.  A typical use might be:
!       if not abl$mod(numerator,denominator,answer) then ...error routine...

!   The other method is result passing.  This method implies that there is only
!   one parameter to return to the caller, and the called routine cannot
!   error; the result of the call is the requested parameter.  An example of
!   this type of routine is the abl$abs(%1) = %2 call; %1 is the number to find
!   the absolute value of, and %2 is the answer; this routine cannot result in
!   an error.  A typical use of this call might be:
!       x := x + abl$abs(y);

!   Following is a description of each of the routines and their parameters.
!   When a routine equals S/F, the routine returns as its value success (1) or
!   failure (0).  Examine the individual routines for possible causes of
!   failures.

!   abl$abs(%1) = %2            returns the absolute value of %1 in %2
!   abl$mod(%1,%2,%3) = S/F     returns the mod of %1 / %2 in %3
!   abl$notab_line($1) = S/F    returns the current line with tabs removed in $1
!   abl$extract_integer($1,%2) = S/F
!                               returns the first signed integer from $1 in %2
!                               with number removed from $1
!   abl$ascii($1,$2) = S/F      converts a string of numbers from $1 to a
!                               string with those ascii characters in $2
!   abl$undelimit($1,$2,$3) = S/F
!                               undelimits string $1, returning first string
!                               in $2 and second string in $3
!   abl$notab_offset(%1) = S/F  returns what would be the current offset if
!                               tabs were replaced with spaces
!   abl$virtual_mark = S/F      remembers the current buffer, line, and offset
!   abl$virtual_position = S/F  moves to the position saved by abl$virtual_mark

!                                                                       Page 5


procedure abl$abs(number)               ! Perform absolute-value function

! Performs the ABS function.  Always returns a non-negative number.
!
! Parameters:
!   number          integer     number to return absolute value of
!
! Source:
!   Abel

if number < 0 then
    abl$abs := -number
else
    abl$abs := number
endif;
endprocedure


procedure abl$mod                       ! Perform modulus function
    (numerator,denominator,answer)

! Performs the MOD function.  If 0 denominator then return bad.
!
! Parameters:
!   numerator       integer     numerator of expression
!   denominator     integer     denominator of expression
!   answer          integer     result of MOD function
!
! Source:
!   Abel

if denominator = 0 then return 0 endif;
answer := numerator - ((numerator / denominator) * denominator);
return 1;
endprocedure


!                                                                       Page 6


procedure abl$notab_line($line)         ! Return current line without tabs

! Returns the current line without any tabs. Returns bad if tabs are uneven.
!
! Parameters:
!   $line           string      returned no-tab string
!
! Source:
!   Abel

local
    len,                                ! Length of current line
    line,                               ! Current line
    tab_offset,                         ! Offset of found tab in line
    tab_spaces,                         ! Number of spaces to use for tab
    tab_interval;                          ! Current tab settings

!
! Init's...currently can't sub uneven tabs
!
tab_interval := get_info(current_buffer,"tab_stops");
if get_info(tab_interval,"type") = string then return 0 endif;
line := current_line;
len := length(line);
!
! Find each tab in the line
!
loop
    !
    ! Find first tab, exit if none found
    !
    tab_offset := index(line,ascii(9)); !not a true offset
    exitif tab_offset = 0;
    tab_offset := tab_offset - 1;       !now it's an offset
    !
    ! Determine number of spaces to use in place of tab
    !
    if not abl$mod(tab_offset,tab_interval,tab_spaces) then
        return 0;
    endif;
    tab_spaces := tab_interval - tab_spaces;
    !
    ! Substitute tab with spaces
    !
    line := substr(line,1,tab_offset) +
        substr(eve$x_spaces,1,tab_spaces) +
        substr(line,tab_offset+2,len);
endloop;

$line := line;
return 1;

endprocedure


!                                                                       Page 7


procedure abl$extract_integer           ! Extract signed integer from string
    ($strng,$number)

! Extracts a space or comma delimited signed integer number from a string.
! Routine returns 1 if successful, 0 otherwise.
!
! Parameters:
!   $strng          string      string to find number in
!   $number         integer     found number
!
! Source:
!   Abel

local
    number_end,
    number_start,
    numerics,
    strng,
    strng_length,
    x;

! Initializations
strng:=$strng;
translate(strng," ",",");
edit(strng,trim,compress);
strng_length:=length(strng);
numerics:="0123456789";
number:="";
x:=1;

! Take care of leading sign, if any
if index("+-",substr(strng,1,1))<>0 then
    number := substr(strng,1,1);
    x:=2;
endif;

! Loop through strng
loop
    exitif x > strng_length;
    c:=substr(strng,x,1);
    if index(numerics,c) <> 0 then
        number := number + c;
    else
        exitif 1
    endif;
    x:=x+1;
endloop;
if number = "" then
    return 0
else
    $number := int(number);
    $strng := substr(strng,x,256);
    return 1;
endif;
endprocedure


!                                                                       Page 8


procedure abl$ascii                     ! Convert #'s to string of ascii
    ($number_strng,$strng)

! Converts a list of numbers in a string to a string with those ascii
! characters.  Returns bad if non-numerics in string, or number in string
! > 255 or < 0.
!
! Parameters:
!   $number_strng   string      list of numbers
!   $strng          string      string with above list of ascii characters
!
! Source:
!   Abel

local
    number_strng,
    strng,
    number;
number_strng := $number_strng;
strng := "";
loop
    exitif number_strng = "";
    if not abl$extract_integer(number_strng,number) then return 0 endif;
    if (number < 0) or (number > 255) then return 0 endif;
    strng := strng + ascii(number);
endloop;
$strng := strng;
return 1;
endprocedure


!                                                                       Page 9


procedure abl$undelimit                 ! Undelimits a delimited string
    ($delim_string,$string1,$string2)

! Breaks apart a two part delimited string, returning each string in separate
! variables.  Returns bad if given a bad delimited string.
!
! Parameters
!   $delim_string   delimited string of format "-string1-string-" where the
!                   "-" represents the delimiter; the delimiter may be any
!                   any character, but may appear only 3 times
!   $string1        the first string
!   $string2        the second string
!
! Source:
!   Abel

local
    delim_string,
    delimiter,
    d1,
    d2;
delim_string := $delim_string;
edit(delim_string,trim,off);
delimiter := substr(delim_string,1,1);
d1 := index(substr(delim_string,2,256),delimiter)+1;
d2 := index(substr(delim_string,d1+1,256),delimiter)+d1;
if d2 <> length(delim_string) then return 0 endif;
$string1 := substr(delim_string,2,d1-2);
$string2 := substr(delim_string,d1+1,d2-d1-1);
return 1;
endprocedure


!                                                                       Page 10


procedure abl$notab_offset($offset)     ! Offset if tabs were spaces

! Returns in $offset what the current character offset would be if tabs were
! really spaces.  Returns bad if tabs are uneven.
!
! Parameters:
!   $offset     integer     what current offset would be
!
! Source:
!   Abel

local i,line,col,interval;

interval:=get_info(current_buffer,"tab_stops");
if get_info(interval,"type")=string then
    return 0;
endif;
if mark(none) >= end_of(current_buffer) then
    $offset := 0
else
    line := current_line;
    if index(line,ascii(9)) = 0 then
        $offset := current_offset
    else
        i := 1;
        col := 0;
        loop
            exitif i > current_offset;
            if substr(line,i,1) = ascii(9) then
                col := ((col + interval)/interval)*interval
            else col := col + 1
            endif;
            i := i + 1
        endloop;
        $offset := col
    endif;
endif;
return 1;
endprocedure


!                                                                       Page 11


procedure abl$virtual_mark              ! Mark without a mark

! The virtual_mark routine remembers the current buffer, line and offset in
! global variables.  Companion routine to abl$virtual_position.  Returns bad
! if tabs are uneven.
!
! Globals:
!   abl$x_virtual_offset    integer     no-tab offset in current line
!   abl$x_virtual_line      integer     line number we're on
!   abl$x_virtual_buffer    buffer      current buffer
!
! Source:
!   Eva2

if not abl$notab_offset(abl$x_virtual_offset) then return 0 endif;
abl$x_virtual_line := eve$what_line;
abl$x_virtual_buffer := current_buffer;
return 1;
endprocedure


procedure abl$virtual_position          ! Go to mark that wasn't

! Virtual_position returns us to the buffer, line and offset saved by
! virtual_mark.  Companion procedure to abl$virtual_mark.  Returns bad if
! abl$virtual_mark hasn't been used.
!
! Globals:
!   abl$x_virtual_offset    integer     no-tab offset in current line
!   abl$x_virtual_line      integer     line number we're on
!   abl$x_virtual_buffer    buffer      current buffer
!
! Source:
!   Eva2

local
    starting_position,                  ! our starting position before we move
    line;                               ! text of the destination line

on_error
    position(starting_position);
    return 0;
endon_error

!
! Remember our starting position, go to destination buffer and line
!
if abl$x_virtual_buffer = 0 then return 0 endif;
starting_position := mark(none);
position(beginning_of(abl$x_virtual_buffer));
move_vertical(abl$x_virtual_line - 1);

!if not abl$notab_line(line) then
!    position(starting_position);
!    return 0;
!endif;
!if length(current_line) <> length(line) then !must have imbedded tabs
!    move_horizontal(-current_offset);
!    erase_character(length(current_line));
!    copy_text(line);
!else
!    position(search(line_end,forward));
!endif;

if length(current_line) < abl$x_virtual_offset then
    copy_text(substr(eve$x_spaces,1,abl$x_virtual_offset - length(line)));
    position(search(line_end,forward));
endif;
move_horizontal(-current_offset + abl$x_virtual_offset);
return 1;
endprocedure
