!       ABEL_PARSE.TPU
!
!       Table of Contents as of 27-Mar-1988
!
!       Procedure name              Page    Description
!       --------------              ----    -----------
!
!       eve$index_over_whitespace      2    Move over whitespace
!       eve$get_token                  3    Returns the next token in command
!       eve$add_final_string           4    Parse rest of command line as string
!       eve$parse                      5    Main command line parsing routine
!       abl$init_qualifiers            6    Initialize qualifier variables
!       abl$process_qualifier          7    Process command line qualifier
!       abl$show_qualifiers            8    Display qualifiers for command
!       eve$prompt_string              9    Eve's string prompting routine
!       abl$prompt_word                10    Abel's word prompting routine
!       eve$process_command            11    Process command in cmd line editor

!                                                                       Page 1


! Modifications to Eve's parsing mechanism:

! The developers' philosophy on Eve's command interface was that it should
! be as simple as possible.  This means that slight variations in the function
! of a command need to be separate commands.  I personally don't like this for
! several reasons:  people that would be using TPU in the first place are most
! likely familiar with DCL and the nuances of command execute you can get
! using qualifiers, I don't want to have to write a separate TPU routine for
! each variation of a command, I have enough routines to worry about as it is,
! and I don't want the "list command" to take any longer than it has to.

! Perhaps a description of how an Eve command is defined to Eve is in order...
! When you type a command at the Eve "Command:" prompt (ie, use the eve_do
! function, you are actually building a call to an Eve routine written in TPU.
! Eve_do parses "tokens" off the command line, prefixing the first token with
! "eve_", then looking for an Eve routine that matches.  Through a bit of magic
! and lots of confusing code, multiple word commands are also understood
! (multiple spaces are reduced to 1 and changed to "_"'s).
! Therefore, any TPU routine whose procedure name begins with
! "eve_" is an Eve command; the name of the routine for the Eve "line" function
! is eve_line.  Some "eve_" TPU routines may have arguments to them, and Eve
! needs to know about these to put together the routine call.  This is done by
! setting a TPU variable; the variable name is the function name prefixed with
! "eve$arg1_" for the first argument, "eve$arg2_" for the second, and so on.
! The value of this variable can be either "string" or "integer".
! Since eve_line takes an integer argument (the line number to go to),
! there is a variable eve$arg1_line whose value is "integer".  When you use the
! line command "LINE 12", Eve generates the TPU routine call "eve_line(12)".

! Abel provides an extension to this command parsing system.  The user may also
! specify qualifiers on the command line to better control how a command gets
! executed.  For example, using the Eve "remove" command always clears the
! paste buffer before removing text, but what if I wanted to append what I've
! currently selected to the end of the paste buffer?  I would normally have to
! write a separate command to do this, but the code would be exactly the same
! as the code for eve_remove with the exception of one executable line
! that says to erase the buffer.  Using qualifiers, the Eve command becomes
! something like "remove /append" and the same code can be used for both
! variations.

! To make qualifiers work, they must first be defined in a similar manner as
! the arguments.  If a command has qualifiers, there should be a variable
! prefixed with "abl$qd_" (actually, whatever abl$x_qual_def_prefix is) that is
! the qualifier definition string.  The Eve "remove" command would have a
! qualifier definition variable named abl$qd_remove.  The symbol used for
! introducing qualifiers is defined by abl$x_qualifier_character and is
! currently "/" (without "'s) to maintain some consistency with DCL (if you
! change this, be prepared to change all the qualifier defintions, too).
! The contents of the qualifier definition string should be
!   qualifiers  =   qualifier[qualifier...]
!   qualifier   =   name [ ~ | $ [string] | % [integer] ]
!   name        =   abl$x_qualifier_character + alphanum
!   alphanum    =   alphanumerics
!   string      =   any alphanumeric letters
!   integer     =   an integer number
!   Note:  spaces are not permitted in the definition string.

! A qualifier name followed by a "$" is a string, and following the $ is the
! initial value for string (if any).  Likewise for the "%".  If a qualifier
! name is followed by "~" or nothing, then it is a boolean qualifier; the "~"
! denotes a false initial value, and nothing denotes a true initial value.

! Using the value of the qualifier definition string, the command parser
! initializes TPU variables to represent the default qualifier values.  The
! variables initialized are handled similar to the qualifier definition string
! variables, except their prefix is defined by abl$x_qualifier_prefix
! (currently "abl$q_").  These variable initializations happen just before any
! user-supplied qualifiers are processed, so the last value of the variable is
! the user's definition if any, and the same qualifiers can be used by
! different commands.

! To continue the Eve "remove" example, abl$qd_remove might equal "/append~",
! which means that by default abl$q_append will be false, but if the user
! specifies "remove /append" on the Eve command line, then abl$q_append will
! be true.  Look at the values of the abl$q_ variables for more examples of
! qualifier definition strings.

! When qualifiers are used multiple times on the Eve command line, only the
! last usage will be effective (just like DCL).

! Some global variables make qualifier processing easier/faster/better.  These
! should be initialized at the beginning of the Abel session.  All of these are
! new Abel variables, except for the modification of Eve's
! eve$x_token_separators.
!
!       abl$x_is_qualifier := 0;            !token type
!       abl$x_qualifier_character := "/";   !qualifier introducer
!       abl$x_qualifier_characters := "_0123456789abcdefghijklmnopqustuvwxyz" +
!           "ABCDEFGHIJKLMNOPQRSTUVWXYZ";   !valid qualifier name characters
!       abl$x_qual_def_prefix := "abl$qd_"; !prefix for qualifier definitions
!       abl$x_qualifier_prefix := "abl$q_"; !variable prefix for qualifiers
!       eve$x_token_separators := eve$x_token_separators +
!           abl$x_qualifier_character;      !makes "/hello/there" 2 tokens

! The changes to Eve's TPU code to provide qualifier support are well
! documented and pretty limited.  Hopefully this won't break too much, later.


!                                                                       Page 2


procedure eve$index_over_whitespace     ! Move over whitespace

! Move eve$x_command_index over whitespace.  Stay put if not on whitespace
! when starting.
!
! Use the whitespace variable (eve$x_whitespace) instead of the token
! separators variable (eve$x_token_separators); the token sep's variable
! includes the qualifier symbol as a valid token separator, but it
! should not be considered whitespace.
!
! Source:
!   Eve

local c;            ! Current character in command line

loop
    exitif eve$x_command_index > eve$x_command_length;
    c := substr (eve$x_command_line, eve$x_command_index, 1);
    exitif index (eve$x_whitespace, c) = 0;
    eve$x_command_index := eve$x_command_index + 1;
endloop;

endprocedure;


!                                                                       Page 3


procedure eve$get_token                 ! Returns the next token in command

! Eve$get_token returns the next token in the command line.
! Returns a null string if no more tokens.
!
! Tokens include symbols, quoted strings, and punctuation, and strings
! that are "none of the above."  A quoted string at the end of a line
! does not have to have a final close quote.
!
! Source:
!   Eve
!
! Edit History:
!   [1]     28-Mar-87   Jef     Change so eve$x_is_number is not left to
!                               default.
!
!   [2]     28-Mar-87   Jef     Add support for qualifiers as a valid token.

local original_index,           ! Original index into command line
      quote_char,               ! Quote character being used for quoted string
      c,                        ! Current character in command line
      closed_quote,             ! True if quote_char ends quoted string
      work_token;               ! Temporary string for building current token

quote_char := eve$kt_null;
c := eve$kt_null;
closed_quote := 0;
work_token := eve$kt_null;
eve$x_is_symbol := 0;
eve$x_is_quoted_string := 0;
eve$x_is_punctuation := 0;
eve$x_is_number := 0;           ! [1] change from 1 to zero
abl$x_is_qualifier := 0;        ! [2] add line

eve$index_over_whitespace;
original_index := eve$x_command_index;
if eve$x_command_index > eve$x_command_length then
!    eve$x_is_number := 0;      ! [1] line not necessary
    eve$get_token := eve$kt_null;
    return;
endif;

c := substr (eve$x_command_line, eve$x_command_index, 1);

! [2] +
if c = abl$x_qualifier_character then
    abl$x_is_qualifier := 1;
    loop
        eve$x_command_index := eve$x_command_index + 1;
        exitif eve$x_command_index > eve$x_command_length;
        c := substr(eve$x_command_line, eve$x_command_index,1);
        exitif index(eve$x_token_separators, c) > 0;
    endloop;
    eve$get_token := substr (eve$x_command_line, original_index,
                             eve$x_command_index - original_index);
    return;
endif;
! [2] -
if index (eve$x_symbol_characters, c) > 0 then
    eve$x_is_symbol := 1;
    eve$x_is_number := 1;       ! [1] add line
    loop
        eve$x_is_number := eve$x_is_number and
                           (index (eve$x_digit_characters, c) > 0);
        eve$x_is_symbol := eve$x_is_symbol and
                           (index (eve$x_symbol_characters, c) > 0);
        eve$x_command_index := eve$x_command_index + 1;
        exitif eve$x_command_index > eve$x_command_length;
        c := substr (eve$x_command_line, eve$x_command_index, 1);
        exitif index (eve$x_token_separators, c) > 0;
    endloop;
    eve$get_token := substr (eve$x_command_line, original_index,
                             eve$x_command_index - original_index);
    return;
endif;

!eve$x_is_number := 0;          ! [1] eliminate line
if (c = "'") or (c = '"') then
    eve$x_is_quoted_string := 1;
    quote_char := c;
    loop
        eve$x_command_index := eve$x_command_index + 1;
        exitif eve$x_command_index > eve$x_command_length;
        c := substr (eve$x_command_line, eve$x_command_index, 1);
        if c = quote_char then
            ! Check for doubled quotes
            eve$x_command_index := eve$x_command_index + 1;
            if eve$x_command_index > eve$x_command_length then
                closed_quote := 1;
                exitif 1;
            endif;
            c := substr (eve$x_command_line, eve$x_command_index, 1);
            if c <> quote_char then
                closed_quote := 1;
                exitif 1;
            endif;
        endif;
    endloop;
    work_token := substr (eve$x_command_line, original_index,
                          eve$x_command_index - original_index);
    ! Add close quote if there wasn't one due to end of line.
    if closed_quote then
        eve$get_token := work_token;
    else
        eve$get_token := work_token + quote_char;
    endif;
    return;
endif;

! Not a symbol, not a quoted string, so just return the single character and
! set eve$x_punctuation.

eve$x_punctuation := 1;
eve$x_command_index := eve$x_command_index + 1;
eve$get_token := substr (eve$x_command_line, original_index, 1);

endprocedure;


!                                                                       Page 4


procedure eve$add_final_string          ! Parse rest of command line as string
    (result_so_far, current_token)

! Procedure for handling the last string argument in a command line
! when it is not a quoted string.  Returns result of parse after
! handling the string.
!
! Look for additional qualifiers in the rest of the line.  If found, move
! the eve$x_command_index as necessary else move it to the end of the string.
!
! Parameters:
!   result_so_far   string      containing parse to date - input
!   current_token   string      containing current token - input
!
! Globals:
!   eve$x_command_length    integer     length of eve$x_command_line
!   eve$x_command_index     integer     pointer into eve$x_command_line
!
! Source:
!   Eve

local
    qualifier_location, ! Found a qualifier
    parse_result,       ! Result of parsing complete command line
    rest_of_line,       ! Remainer of command line including this token
    quote_mark;         ! Quote mark to be used in parse_result

parse_result := result_so_far;
rest_of_line := current_token + substr (eve$x_command_line, eve$x_command_index,
    (eve$x_command_length - eve$x_command_index) + 1);
!
! See if there's a qualifier later on this line
!
qualifier_location := index(rest_of_line,"/");
if qualifier_location <> 0 then
    !
    ! Found a qualifier, cut rest of line short and adjust eve$x_command_index
    !
    rest_of_line :=substr(rest_of_line,1,qualifier_location -1);
    eve$x_command_index := eve$x_command_index - length(current_token) +
        length(rest_of_line);
    edit(rest_of_line,trim,off);
else
    !
    ! Didn't find a qualifier, so move index to end of line
    !
    eve$x_command_index := eve$x_command_length;
endif;

!message("command_line   """ + eve$x_command_line + """");
!message("command_length " + str(eve$x_command_length));
!message("command_index  " + str(eve$x_command_index));
!message("qualifier_loc  " + str(qualifier_location));
!message("rest_of_line   """ + rest_of_line + """");
!message("parse_result   """ + parse_result + """");
!abort;

if index (rest_of_line, '"') = 0 then
    quote_mark := '"';
else
    if index (rest_of_line, "'") = 0 then
        quote_mark := "'";
    else        ! double the quote marks in string
        quote_mark := '"';
        rest_of_line := eve$double_quotes (rest_of_line);
    endif;
endif;
!
parse_result := parse_result + quote_mark + rest_of_line + quote_mark +")";
return (parse_result);

endprocedure;


!                                                                       Page 5


procedure eve$parse (line_to_parse)     ! Main command line parsing routine

! The main command line parsing procedure.  Returns a VAXTPU command string.
! Parses Eve commands, which call procedures whose names start with "eve_".
! Commands can be typed with or without parentheses and quotation marks.
! Multi-word commands like fill_paragraph may be typed with a space instead
! of an underscore.  Defaults are provided so that procedures will prompt
! for missing arguments (prompting varies too much for effective
! centralization).
!
! Expand_name currently will not find matches past the 32nd char, so
! all command names must be less than 23 (not including "eve_") since
! "eve$argx_" takes up 9 characters
!
! Using substr on the command token assures that we look for matches
! in the first 32 characters only; commands that are not unique in the
! first 23 characters will cause a problem
!
! Parameters:
!   line_to_parse               string          Eve command
!
! Globals:
!   eve$x_command_line      string      line-to-parse
!   eve$x_command_length    integer     length of eve$x_command_line
!   eve$x_command_index     integer     pointer into eve$x_command_line
!
! Source:
!   Eve
!
! Edit History:
!   [1]     29-Mar-87   Jef
!       Add support for qualifiers.  Note that commands that accept a string
!       as the last argument must quote the string if it contains the "/"
!       character, otherwise the parser will try to interpret it as a qualifier.

local parse_result,             ! String containing VAXTPU command to execute
      current_token,            ! String currently being processed
      uppercase_token,          ! Uppercase version of current_token
      expanded_token,           ! String with all candidate commands
      choice_token,             ! Current item from expanded_token
      command_token,            ! Eve command name, with underscores
      command_name,             ! Eve command name, without underscores
      choices,                  ! Subset of expanded_token that match
      how_many_choices,         ! How many items in choices
      possible_completion,      ! String containing first n words of command
      completion,               ! First possible_completion
      ambiguous_completion,     ! True if possible_completion is not unique
      no_more_words,            ! True when all words in command name parsed
      reusing_token,            ! True if lookahead found an argument instead
                                ! of part of a command name
      this_buffer,              ! Current buffer
      arguments,                ! Number of arguments expected for this command
      which_argument;           ! Number of argument currently being processed

on_error
! Trap messages with tpu$_nonames and tpu$_multiplenames
endon_error;

eve$x_command_line := line_to_parse;
eve$x_command_index := 1;
eve$x_command_length := length (eve$x_command_line);
eve$x_ambiguous_parse := 0;
eve$x_argument_type := eve$kt_null;
parse_result := eve$kt_null;
command_token := eve$kt_null;
command_name := eve$kt_null;
expanded_token := eve$kt_null;
uppercase_token := eve$kt_null;
erase (eve$choice_buffer);

! Get command name - since commands may have spaces, this can
! involve parsing several tokens

! Handle first token separately, outside the loop, for easier diagnostics
! and handling eve_ prefix

current_token := eve$get_token;
if current_token = eve$kt_null then
    message ("No command given");
    return (eve$kt_null);
endif;
if not eve$x_is_symbol then
    message (fao ("Unrecognized command: !AS", current_token));
    return (eve$kt_null);
endif;
uppercase_token := current_token;
change_case (uppercase_token, upper);
if substr (uppercase_token, 1, 4) <> "EVE_" then
    uppercase_token := "EVE_" + uppercase_token;
endif;
this_buffer := current_buffer;

! Loop for parsing command token

loop
    expanded_token := expand_name (uppercase_token, procedures);
    if expanded_token = eve$kt_null then
        if parse_result <> eve$kt_null then
            reusing_token := 1;
            exitif 1;
        else
            ! Usually will get leading space due to "Command: " prompt
            message
                (fao ("Don't understand command: !AS",
                      substr (eve$x_command_line, 1, eve$x_command_index - 1)));
            return (eve$kt_null);
        endif;
    endif;
    how_many_choices := 0;
    ambiguous_completion := 0;
    completion := eve$kt_null;
    choices := eve$kt_null;

    ! Move to choice buffer and loop through the choices.

    eve$expand_to_choices (expanded_token);
    loop
        exitif mark (none) = end_of (eve$choice_buffer);
        choice_token := current_line;
        how_many_choices := how_many_choices + 1;
        if uppercase_token = choice_token then          ! found an exact match
            parse_result := choice_token;
        endif;
        possible_completion := eve$complete (uppercase_token, choice_token);
        if possible_completion = choice_token then
            no_more_words := 1;
        endif;
        if how_many_choices = 1 then
            completion := possible_completion;
        else
            if completion <> possible_completion then
                ambiguous_completion := 1;
            endif;
        endif;
        move_vertical (1);
    endloop;
    eve$strip_choices (4);
    position (this_buffer);

    translate (eve$choice_buffer, " ", "_");
    if parse_result = uppercase_token then
        reusing_token := 0;
        exitif 1;
    else
        if how_many_choices = 1 then
            parse_result := expanded_token;
        else
            if ambiguous_completion then
                eve$display_choices
                    (fao ("Ambiguous command name: !AS",
                          substr (eve$x_command_line, 1,
                                  eve$x_command_index - 1)));
                return (eve$kt_null);
            else
                if no_more_words then
                    parse_result := completion;
                    reusing_token := 0;
                    exitif 1;
                endif;
            endif;
        endif;
    endif;

    ! Get next token and try to build command

    current_token := eve$get_token;
    if current_token = eve$kt_null then
        if parse_result <> eve$kt_null then
            reusing_token := 1;
            exitif 1;
        else
            eve$display_choices (fao ("Ambiguous command name: !AS",
                                      substr (eve$x_command_line, 1,
                                              eve$x_command_index - 1)));
            return (eve$kt_null);
        endif;
    else
        uppercase_token := completion + "_" + current_token;
        change_case (uppercase_token, upper);
    endif;

endloop;

command_token := substr (parse_result, 5, 1);
change_case (parse_result, lower);
command_token := command_token +
                 substr (parse_result, 6, length (parse_result) - 5);
command_name := command_token;
translate (command_name, " ", "_");

! Check for arguments that this command expects

arguments := 1;
loop
    !
    ! Expand_name currently will not find matches past the 32nd char, so
    ! all command names must be less than 23 (not including "eve_") since
    ! "eve$argx_" takes up 9 characters
    !
    ! Using substr on the command token assures that we look for matches
    ! in the first 32 characters only; commands that are not unique in the
    ! first 23 characters will cause a problem
    !
    exitif expand_name ("eve$arg" + str (arguments) + "_" +
         substr(command_token,1,23), variables) = eve$kt_null;
    arguments := arguments + 1;
endloop;
arguments := arguments - 1;

! Since execute can only take 132 character strings, Eve command
! names can only be (132 - 30) = 102 characters long.
! If arguments = 0 or eve$arg* is uninitialized, eve$x_argument_type
! has already been initialized to the null string.

if arguments > 0 then
    execute ("eve$x_argument_type:=eve$arg1_" + command_token);
endif;

! Get second token - if it's an open paren, get the next token.
! Parsing command name may have stopped on real 2nd token, so check.

if not reusing_token then
    current_token := eve$get_token;
endif;

which_argument := 0;
if arguments > 0 then
    parse_result := parse_result + "(";
endif;

! Loop to handle arguments, in 4 steps:
!       1) If last argument, check for closing punctuation and return
!       2) If last token, handle defaults for remaining arguments
!       3) Handle the argument
!       4) Get the next token

! [1] +
! Logic changed to support qualifiers for commands:
!       0) If token is qualifier, process it
!       1) If last argument, check for closing punctuation and return
!       2) If last token, handle defaults for remaining arguments
!       3) Handle the argument
!       4) Get the next token
!
! Step 2 has been modified to support qualifiers imbedded in unquoted strings.
!
! Initialize qualifier definition string and qualifiers if necessary
!
if expand_name(abl$x_qual_def_prefix + command_token, variables) <> "" then
    execute("abl$qualifier_definition := " + abl$x_qual_def_prefix +
        command_token);
    abl$init_qualifiers(abl$qualifier_definition);
else
    abl$qualifier_definition := eve$kt_null;
endif;
! [1] -

loop
    ! [1] +

    !message("command_line   """ + eve$x_command_line + """");
    !message("command_length " + str(eve$x_command_length));
    !message("command_index  " + str(eve$x_command_index));
    !message("current_token  """ + current_token + """");
    !message("which_argument " + str(which_argument));
    !message("arguments      " + str(arguments));
    !message("parse_result   """ + parse_result + """");

    if abl$x_is_qualifier then
        if substr(current_token,1,2) = abl$x_qualifier_character + "?" then
            execute("abl$show_qualifiers('" + command_token +
                "', abl$qualifier_definition)");
            return eve$kt_null;
        endif;
        if not abl$process_qualifier(abl$qualifier_definition,current_token)
            then return eve$kt_null;
        endif;
        !
        ! Get the next token here without adding anything to parse_result
        !
        current_token := eve$get_token;
    else
    ! [1] -
        ! If last argument, handle closing punctuation and return

        if which_argument = arguments then
            if (current_token = "\") or (current_token = "|") then
                current_token := eve$get_token;
            endif;

            if current_token = eve$kt_null then
                ! [1] +
                ! If this command had arguments it should end with ")", but
                ! if qualifiers followed the last argument, then add_final pu
                ! the ")" on for us, so make sure we don't do that twice
                !
                if (arguments > 0) and
                    (substr(parse_result,length(parse_result),1) <> ")") then
                    parse_result := parse_result + ")";
                endif;
                ! [1]-
                return (parse_result);
            else
                if arguments = 0 then
                    message (fao ("!AS does not take any arguments", command_name));
                else
                    message (fao ("!AS takes only !SL argument!%S",
                                  command_name, arguments));
                endif;
                return (eve$kt_null);
            endif;
        endif;

        ! If there are no more tokens, handle default arguments.
        ! All Eve functions will prompt appropriately when they get the
        ! null string or negative numbers as arguments

        if current_token = eve$kt_null then
            loop
                exitif which_argument = arguments;
                which_argument := which_argument + 1;
                execute ("eve$x_argument_type:=eve$arg" + str (which_argument) +
                         "_" + command_token);
                change_case (eve$x_argument_type, lower);
                if eve$x_argument_type = "string" then
                    parse_result := parse_result + '""';
                else
                    if eve$x_argument_type = "integer" then
                        parse_result := parse_result + 'eve$k_no_arg+0';
                    else
                        message
                            (fao ("Argument type !AS must be integer or string",
                                  eve$x_argument_type));
                        return (eve$kt_null);
                    endif;
                endif;
                if which_argument < arguments then
                    parse_result := parse_result + ",";
                endif;
            endloop;

            parse_result := parse_result + ")";
            return (parse_result);
        endif;

        ! Handle current argument

        which_argument := which_argument + 1;
        execute ("eve$x_argument_type:=eve$arg" + str (which_argument) +
                 "_" + command_token);

        change_case (eve$x_argument_type, lower);
        if eve$x_argument_type = "string" then
            if eve$x_is_quoted_string then
                parse_result := parse_result + current_token;
            else
                if which_argument = arguments then
                    parse_result :=
                        eve$add_final_string (parse_result, current_token);
                    ! [1] +
                    ! Add_final_string advanced command_index to either the
                    ! command_length (done processing comand) or to a qualifier;
                    ! if we're done, then return, otherwise let eve$parse do
                    ! qualifier(s)
                    !
                    if eve$x_command_index = eve$x_command_length then
                        return (parse_result);
                    endif;
                    ! [1] -
                else
                    loop
                        exitif eve$x_command_index > eve$x_command_length;
                        exitif
                            index (eve$x_token_separators,
                                   substr (eve$x_command_line,
                                           eve$x_command_index, 1)) > 0;
                        current_token := current_token + eve$get_token;
                    endloop;
                    current_token := eve$double_quotes (current_token);
                    parse_result := parse_result + '"' + current_token + '"';
                endif;
            endif;
        else
            if eve$x_argument_type = "integer" then
                if eve$x_is_number then
                    translate (current_token, "1", "l");
                    parse_result := parse_result + current_token;
                else
                    message (fao ("!AS expects a number for argument !SL",
                                  command_name, which_argument));
                    return (eve$kt_null);
                endif;
            else
                message (fao ("Argument type !AS must be integer or string",
                              eve$x_argument_type));
                return (eve$kt_null);
            endif;
        endif;

        ! Get next token

        current_token := eve$get_token;
        if current_token = "," then
            current_token := eve$get_token;
            parse_result := parse_result + ",";
        else
            if which_argument < arguments then
                parse_result := parse_result + ",";
            endif;
        endif;
    endif;
endloop;

endprocedure;


!                                                                       Page 6


procedure abl$init_qualifiers           ! Initialize qualifier variables
    ($qualifiers)

! Accepts a qualifier definition string and initializes the proper qualifier
! variables.  This is not a modified Eve routine.  Called by the command
! parser.
!
! Parameters:
!   $qualifiers     string      qualifier definition string
!
! Source:
!   Abel

local
    eoq,                                ! end of qualifier in "qualifiers"
    boqn,                               ! beginning of name in "qualifier"
    eoqn,                               ! end of qualifier name in "qualifier"
    tpu_cmd,                            ! tpu command to execute
    qualifier,                          ! current qualifier in process
    qualifier_name,                     ! name of qualifier
    qualifier_type,                     ! { "$" | "%" | "~" | " "}
    qualifier_value,                    ! value of qualifier
    qualifiers;                         ! qualifiers string
qualifiers:=$qualifiers;
edit(qualifiers,trim);
boqn:=2;                                ! skip leading abl$x_qualifier_character
loop
    exitif qualifiers="";
    tpu_cmd:="";
    eoq:=index(substr(qualifiers,2,length(qualifiers)),
        abl$x_qualifier_character);
    if eoq=0 then eoq:=length(qualifiers) endif;
    qualifier:=substr(qualifiers,1,eoq);
    qualifiers:=substr(qualifiers,eoq+1,length(qualifiers));
    if substr(qualifier,1,1)<>abl$x_qualifier_character then
        message("Illegal qualifier string");
        return 0;
    endif;
!   message("qualifier:  "+qualifier);
    eoqn:=boqn;                                     !find last alpha char
    loop
        exitif index(abl$x_qualifier_characters, substr(qualifier,eoqn,1))=0;
        eoqn:=eoqn+1;
    endloop;
    qualifier_name:=substr(qualifier,boqn,eoqn-boqn);
    if length(qualifier_name)=0 then
        message('No qualifier name in "'+qualifier+'"');
        return 0;
    endif;
    qualifier_name:=abl$x_qualifier_prefix + qualifier_name;
!   message("qualifier name:  '"+qualifier_name+"'");

    qualifier_type:=substr(qualifier,eoqn,1);
    if qualifier_type="" then qualifier_type:=" " endif;
    case qualifier_type from " " to "~"
        [" "]:  tpu_cmd:=tpu_cmd+qualifier_name+":="+str(true)+";";
        ["~"]:  tpu_cmd:=tpu_cmd+qualifier_name+":="+str(false)+";";
        ["$"]:  qualifier_value:=substr(qualifier,eoqn+1,length(qualifier));
                tpu_cmd:=tpu_cmd+qualifier_name+":='"+qualifier_value+"';";
        ["%"]:  qualifier_value:=substr(qualifier,eoqn+1,length(qualifier));
                if qualifier_value="" then qualifier_value:="0" endif;
                tpu_cmd:=tpu_cmd+qualifier_name+":="+qualifier_value+";";
        [inrange,outrange]:
                message('Unrecognized qualifier qualifier "'+qualifier_type+
                    '" in "'+qualifier+'"');
                return 0;
    endcase;
!   message(tpu_cmd);
    execute(tpu_cmd);
endloop;
return true;
endprocedure


!                                                                       Page 7


procedure abl$process_qualifier         ! Process command line qualifier
    ($qual_def,$qualifier)

! Sets the value of qualifier variables based on the qualifier.  Called by the
! command line parser.
!
! Parameters:
!   $qual_def       string      qualifier definition string
!   $qualifier      string      user's qualifier
!
! Source:
!   Abel

local
    boqn,                       !beg of qualifier name in qualifier
    bov,                        !beg of qualifier value in qualifier
    eoq,                        !eoq of qualifier in qualifier
    eoqn,                       !eoq of qualifier name in qualifier
    command_string,             !command string produced
    qc,                         !same as abl$x_qualifier_character
    qualifier,                  !qualifier string
    qualifier_name,             !qualifier name string
    qualifier_negated,          !qualifier preceded with "no" boolean
    qualifier_value,            !qualifier value string
    qualifiers,                 !qualifiers to parse string
    qual_def,                   !local copy of qualifier definition
    tmp$boq,                    !beg of qualifier in qual_def
    tmp$boqn,                   !beg of qualifier name in qual_def
    tmp$eoqn,                   !end of qualifier name in qual_def
    tmp$loq,                    !length of qualifier in qual_def
    tmp$qualifier,              !qualifier string from qual_def
    tmp$qualifier_name,         !qualifier name from qual_def
    tmp$qualifier_type;         !qualifier type qualifier string

!
! Massage input
!
qual_def := $qual_def;
edit(qual_def,trim,lower);
qualifier:=$qualifier;
edit(qualifier,trim,lower);
qc:=abl$x_qualifier_character;                          !to save typing
command_string := "";
eoq:=length(qualifier);
!
! If user is asking about qualifiers, display them and quit
!
if index(qualifier, qc + "?") <> 0 then
    abl$show_qualifiers(eve$kt_null,qual_def);
    return 0;
endif;
!
! Get user's qualifier name
!
boqn:=2;
eoqn:=boqn;
loop
    exitif index(abl$x_qualifier_characters, substr(qualifier,eoqn,1))=0;
    eoqn:=eoqn+1;
endloop;
eoqn:=eoqn-1;   !is last char of name in qualifier
qualifier_name:=substr(qualifier,boqn,eoqn-boqn+1);

if length(qualifier_name)=0 then                        !really a qual name?
    message('No qualifier name in "'+qualifier+'"');    ! no...err & ret
    return 0;
endif;
!
! Try to find qualifier name in the qualifier definition string
! If can't find it, see if the qualifier is negated...if it is, strip off "no",
! remember that the qualifier was negated, and try to find it in the qualifier
! definition string again
!
qualifier_negated:=false;
tmp$boq:=index(qual_def,"/"+qualifier_name);            !find qual in def'n str
if tmp$boq=0 then                                       !find it?
    if substr(qualifier_name,1,2)<>"no" then            ! no...qual neg?
        message("Unrecognized qualifier:  /"+qualifier_name); !  no...err & ret
        return 0;
    endif;

    qualifier_name:=                                    !strip off "no"
        substr(qualifier_name,3,length(qualifier_name));

    if length(qualifier_name)=0 then                    !really a qual name?
        message('No qualifier name in "'+qualifier+'"');! no...err & ret
        return 0;
    endif;

    tmp$boq:=index(qual_def,"/"+qualifier_name);        !find qual in tmp
    if tmp$boq=0 then                                   !find it?
        message("Unrecognized qualifier:  /"+qualifier_name); ! no...err & ret
        return 0;
    endif;
    qualifier_negated:=true;                            !remember negated
endif;
!
! We found a match in the qualifier definition string...make sure the match
! is the only one
!
if index(substr(qual_def,tmp$boq+1,length(qual_def)),"/"+qualifier_name)<>0
then
    message('"'+qualifier+'"'+" is an ambiguous qualifier");
    return 0;
endif;
!
! From now on, use the qualifier from the qualifier definition string
! (in case the user abbreviated his qualifier)
!
tmp$loq:=index(substr(qual_def,tmp$boq+1,length(qual_def)),"/");
if tmp$loq=0 then tmp$loq:=length(qual_def) endif;
tmp$qualifier:=substr(qual_def,tmp$boq,tmp$loq);
tmp$boqn:=2;                                            !get qualifier name
tmp$eoqn:=tmp$boqn;
loop
    exitif index(abl$x_qualifier_characters,
        substr(tmp$qualifier,tmp$eoqn,1)) = 0;
    tmp$eoqn:=tmp$eoqn+1;
endloop;
tmp$qualifier_name:=substr(tmp$qualifier,tmp$boqn,tmp$eoqn-tmp$boqn);
!
! Determine the qualifier type, and dispatch
!
tmp$qualifier_type:=substr(tmp$qualifier,tmp$eoqn,1);
if tmp$qualifier_type="" then tmp$qualifier_type:=" " endif;
case tmp$qualifier_type from " " to "~"
    !
    ! Boolean
    !
    [" ","~"]:
            edit(qualifier,trim);
            if length(qualifier) then
                message('Junk at end of qualifier:  "'+qualifier+'"');
                return 0;
            endif;
            command_string := command_string + abl$x_qualifier_prefix +
                tmp$qualifier_name + ":=";
            if qualifier_negated then command_string:=command_string+
                str(false)+";"
            else command_string:=command_string+str(true)+";"
            endif;
    !
    ! String or integer
    !
    ["$","%"]:
            if qualifier_negated then
                message("Qualifier may not be negated: "+tmp$qualifier_name);
                return 0;
            endif;
            bov:=eoqn+1;
            loop
                exitif bov>eoq;
                exitif substr(qualifier,bov,1) = "=";
                bov:=bov+1;
            endloop;
            if bov>eoq then
                message('"=" expected on qualifier: '+tmp$qualifier_name);
                return 0;
            endif;
            qualifier_value:=substr(qualifier,bov+1,length(qualifier));
            edit(qualifier_value,trim);
            if tmp$qualifier_type="$" then
                command_string := command_string + abl$x_qualifier_prefix +
                    tmp$qualifier_name + ':="' + qualifier_value + '";';
            else
                command_string := command_string + abl$x_qualifier_prefix +
                    tmp$qualifier_name + ":=" + str(int(qualifier_value)) + ";";
            endif;
    [inrange,outrange]:
            message('Unrecognized qualifier type "' + tmp$qualifier_type +
                '" in "' + tmp$qualifier + '"');
            return 0;
endcase;

!message(command_string);
if command_string<>"" then execute(command_string) endif;

return true;

endprocedure


!                                                                       Page 8


procedure abl$show_qualifiers           ! Display qualifiers for command
    ($command,$qualifiers)

! Abel routine to show qualifier definition in readable fashion
!
! Parameters:
!   $command        string      command that qualifiers are for
!   $qualifiers     string      qualifier definition
!
! Source:
!   Abel

local
    starting_position,              ! where the user started
    had_to_map,                     ! set if had to map show_buf to info_win
    dummy_input,                    ! wait for user's return key to continue
    eoq,                            ! pointer to end of qualifier definition
    boqn,                           ! pointer to beginning of qualifier name
    eoqn,                           ! pointer to end of qualifier name
    qualifier,                      ! single qualifier definition (name and val)
    qualifier_name,                 ! single qualifier name
    qualifier_type,                 ! single qualifier's type char (~,$,%," ")
    qualifier_value,                ! single qualifier's default value
    qualifiers;                     ! local copy of qualifier definition string

on_error
endon_error

!
! Initializations
!
starting_position := mark(none);
qualifiers := $qualifiers;
edit(qualifiers,trim,lower);
if qualifiers = eve$kt_null then
    if $command <> eve$kt_null then
        message("No qualifiers for " + $command + " command");
    else
        message("No qualifiers");
    endif;
    return 0;
endif;
!
! If the show_buffer isn't mapped on the screen, map it to the info_window
!
if get_info(show_buffer,"map_count")=0 then
    had_to_map := true;
    map(info_window,show_buffer);
else
    had_to_map := false;
endif;
!
! Write some header stuff to the show buffer
!
erase(show_buffer);
position(show_buffer);
split_line;
copy_text("Qualifier Listing");
if $command <> eve$kt_null then
    copy_text(" for " + $command);
endif;
split_line;
split_line;
copy_text("        Qualifier           Type                Default Value");
split_line;
copy_text("        -----------         ----                -------------");
split_line;
!
! For each qualifier in the definition string
!
loop
    exitif qualifiers = "";
    !
    ! Strip one qualifier off of qualifiers
    !
    eoq := index(substr(qualifiers,2,length(qualifiers)),
        abl$x_qualifier_character);
    if eoq = 0 then eoq := length(qualifiers) endif;
    qualifier := substr(qualifiers,1,eoq);
    qualifiers := substr(qualifiers,eoq+1,length(qualifiers));
    !
    ! Check the 1st char to make sure we have a real qualifier here
    !
    if substr(qualifier,1,1) <> abl$x_qualifier_character then
        message("Illegal qualifier definition string");
        exitif 1;
    endif;
    !
    ! Get the qualifier's name
    !
    boqn := 2;
    eoqn := boqn;                                     !find last alpha char
    loop
        exitif index(abl$x_qualifier_characters,substr(qualifier,eoqn,1)) = 0;
        eoqn := eoqn+1;
    endloop;
    qualifier_name := substr(qualifier,boqn,eoqn-boqn);
    if length(qualifier_name) = 0 then
        message('No qualifier name in "'+qualifier+'"');
        return 0;
    endif;
    qualifier_name := abl$x_qualifier_character + qualifier_name;
    copy_text("        ");
    copy_text(qualifier_name+substr(eve$x_spaces,1,20-length(qualifier_name)));
    !
    ! Determine qualifier's type
    !
    qualifier_type := substr(qualifier,eoqn,1);
    if qualifier_type = "" then qualifier_type:=" " endif;
    !
    ! Write out qualifier's type and default value
    !
    case qualifier_type from " " to "~"
        !
        ! Boolean qualifier
        !
        [" ","~"]:
            copy_text("boolean             ");
            if qualifier_type = " " then copy_text("true");
            else copy_text("false");
            endif;
            split_line;
        !
        ! String
        !
        ["$"]:
            qualifier_value := substr(qualifier,eoqn+1,length(qualifier));
            copy_text('string              "'+qualifier_value+'"');
            split_line;
        !
        ! Integer
        !
        ["%"]:
            qualifier_value := substr(qualifier,eoqn+1,length(qualifier));
            copy_text("integer             "+str(int(qualifier_value)));
            split_line;
        !
        ! Unknown types
        !
        [inrange,outrange]:
                message('Unrecognized qualifier qualifier "'+qualifier_type+
                    '" in "'+qualifier+'"');
                return 0;
    endcase;
endloop;
!
! If we had to map, wait for user then put his screen back
!
position(beginning_of(show_buffer));
eve$update_status_lines;
if had_to_map then
    update(info_window);
    dummy_input := read_line("Press return to continue");
    unmap(info_window);
endif;
position(starting_position);
endprocedure

!End of command parser enhancements/modifications


!                                                                       Page 9


procedure eve$prompt_string             ! Eve's string prompting routine
    (old_string, new_string, prompt_string, no_value_message)

! Prompt for input to new_string if old_string is empty; allow old_string and
! new_string to be same variable in call to eve$prompt_string
!
! Parameters:
!   old_string      string      current value of string
!   new_string      string      returned string
!   prompt_string   string      prompt
!   no_value_me...  string      displayed if nothing entered
!
! Source:
!   Eve

local
    read_line_string,
    temp;                       ! String read after prompt

temp:=old_string;               !Save value of old_string
                                ! in case old_string and
                                ! new_string are same variable
if old_string = eve$x_null then
    new_string := read_line (prompt_string);
    if new_string = eve$x_null then
        message (no_value_message);
        return (0);
    else
        return (1);
    endif;
else
    new_string := temp;
    return (1);
endif;
endprocedure;


!                                                                       Page 10


procedure abl$prompt_word               ! Abel's word prompting routine
    ($word_list, $old_word, $new_word, $prompt_string, $no_value_message)

! Prompts the user for one of the words in the word list, based loosely on
! Eve's eve$prompt_string routine.  The word_list is the list of words, each
! prefaced by "/" (since it is already a reserved character in Abel).  No
! checking is done on the word_list parameter, so it must be correct.  An
! example word_list might be "/yes/no".  Case is not important in the word_list
! (it is always raised) and spaces are eliminated from the string.  The
! returned $new_word is always in upper case, and contains the full word; in
! the above example, a user could enter "n" but "NO" would be returned.
!
! Parameters:
!   $word_list      string      list of possible options
!   $old_word       string      current value of word
!   $new_word       string      returned word
!   $prompt_string  string      prompt
!   $no_value_me... string      displayed if nothing entered
!
! Source:
!   Abel

local
    had_to_complain,                    ! set if had to tell user the options
    options,                    ! text if bad word entered
    word_list,                  ! local copy of $word_list
    next_word_ptr,              ! pointer in word_list to start of next word
    new_word_ptr,               ! pointer in word_list to user's choice
    ambiguous_word,                             ! pointer in word_list to another user's choice
    new_word,                   ! local copy of $new_word
    old_word;                   ! local copy of $old_word

old_word := $old_word;
edit(old_word,lower,trim);
had_to_complain := false;
!
! Massage the word_list for the message to display for words not found
!
word_list := substr($word_list,2,1000);         !drop the first "/"
edit(word_list,lower,collapse);
options := "Valid options are:  ";
loop
    !
    ! keep moving a word and ", " from the word list to the options
    ! until the word_list is exhausted
    !
    exitif word_list = "";
    next_word_ptr := index(word_list,"/");
    if next_word_ptr = 0 then next_word_ptr := 1000 endif;
    options := options + substr(word_list, 1, next_word_ptr - 1) + ", ";
    word_list := substr(word_list, next_word_ptr + 1, 1000);
endloop;
!
! remove trailing ", " from choice message
!
options := substr(options,1,length(options)-2);

word_list := $word_list;
edit(word_list,lower,collapse);
!
! loop until valid word entered or aborted with a return
!
loop
    !
    ! get a value into old_word
    !
    if old_word = eve$kt_null then
        old_word := read_line ($prompt_string);
        edit(old_word,lower,trim,collapse);
        if old_word = eve$kt_null then
            message ($no_value_message);
            $new_word := eve$kt_null;
            return (0);
        endif;
    endif;
    !
    ! if the word is in our list then
    !   make sure word matches our list only once (non-ambiguous)
    !   get value in new_word and leave
        ! else reprompt
    !
    new_word_ptr := index(word_list, "/" + old_word);
    if new_word_ptr <> 0 then
        new_word := substr(word_list, new_word_ptr + 1, 1000);
                ambiguous_word :=
                        index(substr(word_list,new_word_ptr + 1,1000), "/" + old_word);
                if ambiguous_word = 0 then
                next_word_ptr := index(new_word,"/");
                if next_word_ptr = 0 then next_word_ptr := 1000; endif;
                new_word := substr(new_word, 1, next_word_ptr - 1);
                exitif 1;
                endif;
    endif;
    !
    ! Word not found...tell user correct options
    !
    message(options);
    had_to_complain := true;
        old_word := eve$kt_null;                ! so that we reparse
endloop;

if had_to_complain then
    message(eve$kt_null);
endif;
$new_word := new_word;
return 1;

endprocedure;


!                                                                       Page 11


procedure eve$process_command           ! Process command in cmd line editor
    (new_do_line)

! Process command selected in command line editor
!
! Parameters:
!   new_do_line     string      containing Eve command
!
! Source:
!   Eve
!
! Edit History:
!   Jef     Just to be consistent with the bolded no_write buffer stuff in the
!           eveplus routines, bold the choice buffer status line
!
!   Jef     Re-init qualifiers when re-doing a command

local
      repetitions,      ! Number of times to execute this command
      this_position,    ! Marker for current cursor position
      this_window,      ! Current window
      command_token,    ! Token necessary to init qualifier variables
      choice_msg;       ! [1] Message text for choice buffer status line

on_error
endon_error;

! Make sure we do not repeat the current repeat command

repetitions := eve$x_repeat_count;
eve$x_repeat_count := 1;

if new_do_line <> eve$x_null then
    eve$x_do_line := new_do_line;
    eve$parsed_do_line := eve$parse (eve$x_do_line);
    if eve$parsed_do_line = eve$x_null then ! message sent during parse error
        eve$x_do_line := eve$x_null;
    else
        ! Unmap choice window before executing the command
        if get_info (eve$choice_window, "buffer") <> 0 then
            unmap (eve$choice_window);
        endif;
        loop
            exitif repetitions = 0;
            execute (eve$parsed_do_line);
            repetitions := repetitions - 1;
        endloop;
    endif;
else
    eve$x_ambiguous_parse := 0; ! need this since eve$parse is not called here
    if (eve$x_start_do_key = "do") and (eve$x_stop_do_key = "do") then
        if eve$x_do_line = eve$x_null then
            message ("No previous command given");
        else
            message (fao ("Doing previous command: !AS", eve$x_do_line));
            !
            ! Initialize qualifiers for this command, if any
            !
            command_token := substr(eve$parsed_do_line,5,255);
            if index(command_token,"(") <> 0 then
                command_token := substr(command_token,1,
                    index(command_token,"(")-1);
            endif;
            if expand_name (abl$x_qual_def_prefix + command_token, variables)
                <> eve$kt_null then
                execute ("abl$init_qualifiers(" + abl$x_qual_def_prefix
                    + command_token + ")");
            endif;
            !
            ! Perform the command
            !
            loop
                exitif repetitions = 0;
                execute (eve$parsed_do_line);
                repetitions := repetitions - 1;
            endloop;
        endif;
    else
        message ("No command given");
    endif;
endif;

if eve$x_ambiguous_parse then
    if get_info (eve$choice_window, "buffer") = 0 then
        map (eve$choice_window, eve$choice_buffer);
    endif;
    if get_info (eve$choice_buffer, "record_count") >=
        eve$x_choice_window_length then
        choice_msg:=                ![1] and next 7 or so lines
" Choices           Press Next Screen or Prev Screen to see other choices";
    else
        choice_msg:=" Choices";
    endif;
    set(status_line,eve$choice_window,reverse,"X");
    set(status_line,eve$choice_window,bold,choice_msg);
    update (eve$choice_window);
    position (eve$command_window);
    position (end_of (eve$command_buffer));
    move_horizontal (-1);
else
    ! Unmap choice window if not done previously
    if get_info (eve$choice_window, "buffer") <> 0 then
        unmap (eve$choice_window);
    endif;
    this_position := mark (none);
    this_window := current_window;
    position (eve$command_window);
    position (end_of (eve$command_buffer));
    if new_do_line = eve$x_null then
        move_vertical (-1);
        erase_line;
    endif;
    update (eve$command_window);
    position (this_window);
    position (this_position);
endif;

endprocedure;
