:::: ::: ::::: :::: :::: ::: ::: ::: ::: ::: ::: ::: ::: ::: ::: ::: ::: ::: ::: ::: ::: ::: ::: ::: ::::: ::::: ::: ::: ::: :::: :: :: :: ::: ::: ::: ::: :: :: :: ::: ::::::::::::::::::::: ::: ::: ::::::::::::: ::: :::: :::: ::: ::: ::: ::: ::: ::: ::: :: :: :: ::: :::: :::: ::: :: :: :: ::: ::::::: ::::::: :: ::::::: ::::::: ::::: ...Son of Eve Jef Kennedy PBR & Associates Abel is a VAX/VMS VAXTPU-based editor built on Digital's Eve. Abel provides the user with capabilities not previously found in Digital- provided editors. Abel is very sensitive to changes in Eve. Abel version 1.0 is built on Eve version 1.1-024; this is currently the only supported configuration. From talking with Jim Burrows (lead of Eve's development group), Abel will *not* work under Eve version 2 (VMS 5.x). It will need to go through some significant changes before it will work again (wanna help?). Please contact me if you have any questions about Abel: Jef Kennedy PBR & Associates 8500 Station Street, Suite 210 Mentor, Ohio 44060 216/946-6833 The Abel software is given to the public domain in hopes that it will provide others more advanced editing capabilities. You are invited to make changes. If you make significant improvements to Abel, please share them with others in the same spirit that you received Abel. The Abel software, manuals, online help, and training material are subject to change without notice and should not be construed as a commitment by it's authors, contributors, or employers of either. Overview This document provides a guide for discussion of some neat things done to make Abel work. This talk was originally meant to be more of an overview of the Abel editor and how to use it. Since Eve version 2 is internally different than Eve version 1 and Abel is so closely tied to the internals of Eve, Abel will break. I went through the 5 stages of grieving when I heard the news: denial, anger, bargaining, depression, and acceptance. Having reached the last step, I figured that we need to make the best of the situation. Instead of presenting Abel is a viable alternative to Eve, I want to discuss some of the neat things in Abel from a TPU programmer's perspective. Some of these ideas might spark ideas in others for forging new ground in VAXTPU editing...go for it! You do not necessarily have to know VAXTPU to participate in this session. If you are interested in running Abel or interested in VAXTPU programming, this session ought to be of use. The list of topics to discuss: Qualifier Support Block Functions Buffered Scrolling Use of Calluser Command Highlights ERASE and RESTORE RETURN (interactive DCL) SEARCH and SUBSTITUTE commands TSEARCH FX FILL PARAGRAPH with /AUTOLEFT WINDOWS Trimming/Elimming DEFAULT and SET Reserved Marks ABELINI and ABEL TPU utilities Encryption algorithm for SCRAMBLE and UNSCRAMBLE Qualifier Support Overview Qualifier support is probably the most significant improvement that Abel has to offer. It is the foundation of most all other improvements found in Abel. Command qualifiers allow you to control the behavior of commands, like fine-tuning. Qualifiers in Abel are treated very similarly to DCL and thus are familiar already. Qualifiers have several benefits: they keep the command list from growing and add functionality. In both Eve version 1 and 2, each nuance of command behavior must be expressed as a new command. For example, in Abel, the one REMOVE command has 3 boolean qualifiers. To duplicate the same functionality in Eve would require 9 commands; 1 for each possible combination of qualifiers. Let's look at how Abel deals internally with qualifiers...there are quite a few considerations. Qualifier definition variables: They define the qualifiers a command takes, or more specifically, define the qualifier variables that will be initialized when the command is processed by abl$init_qualifiers routine. Qualifier definition variables start with "ABL$QD_", with the suffix same as command (similar to EVE$ARG1_ for Eve). For example: ABL$QD_REMOVE := "/APPEND~/REMOVE/RESET"; Syntax for expressing qualifiers in definition: qualifiers = qualifier[qualifier...] qualifier = name [ ~ | $ [string] | % [integer] ] boolean, default true /QUALIFIER boolean, default false /QUALIFIER~ string, optional default /QUALIFIER$default_string integer, optional default /QUALIFIER$signed_integer ABL$INIT_QUALIFIERS initializes strings to "" and integers to 0 if no default is provided in the qualifier definition string. Qualifier Support (continued) Qualifier variables: These are variables used by ABL$INIT_QUALIFIERS, ABL$PROCESS_QUALIFIERS and the EVE_... command routines to pass values of the qualifiers. They start with "ABL$Q_", with the suffix the same as qualifier name. For example: ABL$Q_APPEND for /APPEND More than one command may use the same qualifier variables. For example the /RESET qualifier appears in the BLOCK REMOVE and the REMOVE commands, hence ABL$Q_RESET is used by both. You must include all qualifiers used in a command in the qualifier definition, otherwise (and this is from experience): the command routine will be using old values in the qualifier variables won't be allowed to use missing qualifiers on the command EVE$PARSE modifications: Once EVE$PARSE recognizes the Eve command, the parser initializes qualifiers for the command using ABL$INIT_QUALIFIERS, based on the qualifier definition string (ABL$QD_...), then processes each qualifier found on the command line with ABL$PROCESS_QUALIFIER. Qualifier Support (continued) Making EVE$PARSE recognize qualifiers: After having recognized a parser token as a qualifier, if the qualifier is "/?" then call ABL$SHOW_QUALIFIERS to give user formatted display of qualifiers for the entered command, or... Pass qualifier token to ABL$PROCESS_QUALIFIER: checks for validity against qualifier definition string assigns value to qualifier variable Had to be a little more sly about handling the last argument on the command line since there could be trailing qualifiers Restrictions imposed on argument expressions because of qualifiers: Must use "'s around "/" when not used as a qualifier introducer, for example: SEARCH "/HELLO" /HUNT will find "/HELLO". /HUNT is a qualifier. Cannot break up an argument with qualifier, example: SEARCH HELLO /HUNT THERE will give a parsing error since there are 2 arguments given and the SEARCH command expects only 1 Qualifier Support (continued) Keyboard definitions (pre-defined with VAXTPU): Cannot call EVE_... command routine without initializing qualfiers or else command routine will give unpredictable results. Needed a routine that could be bound to a key, initialize and modify the qualifier variables for a command, then execute the command. ABL$DO does just that. ABL$DO takes 2 parameters: string, VAXTPU call of EVE_... routine to execute string, VAXTPU line to execute after the qualifier variables have been set to their defaults for this command, and before the command gets executed Examples of ABL$DO in action: define_key("abl$do('eve_erase_word','abl$q_current := 1')", kp5, "erase_word", "abl$abel_keyboard"); define_key("abl$do('eve_unerase_word','')", key_name(kp5,shift_key), "restore_word", "abl$abel_keyboard"); DEFINE_KEY definitions (on-the-fly in Eve/Abel): Keys defined with DEFINE_KEY must also initialize qualifier variables before executing the command. In Eve, keys defined on-the-fly executed just as quickly when pressed as their pre-defined counterparts. The parser was called once during the DEFINE KEY command, and the parser's results were bound to the key. Since Abel needs to initialize and set qualifier variables, the overhead of the parser couldn't be avoided. Instead of calling the parser one time and assigning the result to the key, an EVE_DO call is bound to the key (it could by changed to an ABL$DO call to execute less code). Theoretically, pressing a key defined on-the-fly with DEFINE KEY requires the same amount of CPU processing as pressing the DO key and typing the command by hand. In practice, I have not seen any noticeable difference. Block Functions For commands that operate with a block on the screen, any corner of block is marked with BLOCK SELECT; the appropriate block command issued from the diagonally opposite corner. Some of these commands are BLOCK REMOVE, BOX, CUBE, SORT BLOCK. The other block commands each have their own way of working with blocks. The BLOCK INSERT HERE command gives a block of text at the current cursor location. With BLOCK TEXT, you must first mark a corner with the BLOCK SELECT command, then issue the BLOCK TEXT command with the cursor in the same column (different row). Most block commands are mode sensitive to give "intuitive" results. BOX and CUBE commands are derived from the Abel "graphics" subroutines and hence are not mode sensitive. The block commands have the side effect of replacing all tabs in affected area with appropriate number of spaces (can't use irregularly spaced tabs). This is so Abel can get the spacing right. Here are some examples of block functions: Performing a BLOCK SELECT on the character pointed to by the "v" and doing a BLOCK REMOVE while on the character pointed by the "^" in overstrike mode: v | Now is the time... | Now time... Now is the time... | Now time... Now is the time... gives Now time... Now is the time... | Now time... Now is the time... | Now time... ^ | The same example performed in insert mode: v | Now is the time... | Now time... Now is the time... | Now time... Now is the time... gives Now time... Now is the time... | Now time... Now is the time... | Now time... ^ | Buffered Scrolling This gives buffered scrolling ala EDT, where the cursor does not have to go to the extreme ends of the window before the window scrolls. Abel has a SET SCROLL FACTOR command to allow user-adjustment of the window scrolling. +-------------------------------+ | Buffer Area | | 20% | | - - - - - - - - - - - - - - - | Using a SET SCROLL FACTOR | | of 40, (40/2)% is used as | | buffer area at the top and | Cursor normally stays here | bottom of the window; the | 60% | remaining portion is used | | for free cursor motion. | | | - - - - - - - - - - - - - - - | | Buffer Area | | 20% | +-------------------------------+ The MOVE UP and MOVE DOWN command routine had to modified to understand buffered scrolling. The CURSOR_VERTICAL builtin used by Eve does not use the scroll setting of the window it is in. The alternative, MOVE_VERTICAL, does use the window's scroll setting but does not provide "free-cursor" motion like CURSOR_VERTICAL. The desired action was obtained using the following steps: CURSOR_VERTICAL provides "free-cursor" motion (cursor doesn't change screen columns) UPDATE(current_window) scrolls window if necessary based on scrolling information has an interesting quirk: the screen's cursor is left in same column as it was in before the UPDATE, but if the cursor is now beyond the end-of-line, the buffer's pointer (VAXTPU's internal cursor) is moved to the end-of-line; the result is that the screen's cursor does not reflect where your next character will go CURSOR_HORIZONTAL(0) takes care of the above quirk; the buffer's pointer is changed to point to the same place the screen's cursor is located Use of Calluser Abel's calluser routine provides several functions: Interface with VMS' sort faster than current VAXTPU sort for large number of records allows key to sort on and direction "Real" arithmetic calluser holds the "accumlator" call provides function and operand calluser returns current accumulator as string add, subtract, multiply, divide, square root, equals, clear Return error to force execution of an on_error routine in a procedure Echo reply back with string given could be used to make sure calluser is out there, or for version check The functions available in Abel's calluser program: abl$cu_echo := 0; abl$cu_begin_sort := 1; abl$cu_release_rec := 2; abl$cu_sort_merge := 3; abl$cu_return_rec := 4; abl$cu_end_sort := 5; abl$cu_clear := 6; abl$cu_add := 7; abl$cu_subtract := 8; abl$cu_multiply := 9; abl$cu_divide := 10; abl$cu_equals := 11; abl$cu_error := 12; abl$cu_sort_position := 13; abl$cu_sort_length := 14; abl$cu_sort_order := 15; abl$cu_init_sort := 16; abl$cu_sqrt := 17; Use of Calluser (continued) The SORT command is one user of the calluser routine...here's an exceprt of it to demonstrate calls to calluser: ! ! Set up for sort ! starting_position:=mark(none); position(beginning_of(sort_buffer)); dummy := call_user(abl$cu_init_sort,""); !inits all key info to zero ! if sort_order <> 0 then dummy := call_user(abl$cu_sort_order,str(sort_order)) endif; ... dummy := call_user(abl$cu_begin_sort,""); ! ! Pass records to sort ! loop ... dummy := call_user(abl$cu_release_rec,current_line); move_vertical(1); endloop; ! ! Sort ! dummy := call_user(abl$cu_sort_merge,""); erase(sort_buffer); ! ! Retrieve sorted records ! loop split_line; line := call_user(abl$cu_return_rec,""); copy_text(line); endloop; Command Highlights ERASE and RESTORE Separate buffers are kept for character, word, and line erasures. RETURN (interactive DCL) If RETURN is pressed inside the DCL buffer and DCL INTERACTIVE is on, the current line is sent to the DCL subprocess. RETURN outside the DCL buffer is processed as usual. SEARCH and SUBSTITUTE commands Very flexible target string expression and control over action. 19 qualifiers between the 2 commands. Qualifier Listing for Search Qualifier Type Default Value ----------- ---- ------------- /PROMPT boolean true /ASCII boolean false /CASE_SENSITIVE boolean false /HUNT boolean true /WILDCARD boolean true /PREVIOUS boolean false Qualifier Listing for Substitute Qualifier Type Default Value ----------- ---- ------------- /PREVIOUS boolean false /DELIMITED boolean false /PROMPT boolean true /OLD_ASCII boolean false /NEW_ASCII boolean false /WILDCARD boolean false /ITERATE boolean false /LOOP boolean false /MATCH boolean true /CASE_SENSITIVE boolean false /ASK boolean true /WHOLE boolean true /RESET boolean false TSEARCH Allows access to all VAXTPU pattern-matching primitives. Pattern is compiled on-the-fly, errors are trapped. Command Highlights (continued) FX Allows easy insertion of video attribute escape sequences into buffer. Shows all the characters in the editing window, show the video effect on the last line of the screen as you type. FILL PARAGRAPH with /AUTOLEFT Uses the first non-whitespace character column as the left margin for the fill. Beats having to change the left margin setting constantly to get an indented paragraph. WINDOWS Wanted to be able to manipulate windows in a similar fashion to buffers, but needed a mechanism to store their names. Window names are stored and referenced from their status lines. Windows can be adjusted, mapped and unmapped, created and deleted, split, etc... Qualifier Listing for Window Qualifier Type Default Value ----------- ---- ------------- /TOP integer 0 /BOTTOM integer 0 /LENGTH integer 0 /RTOP integer 0 /RBOTTOM integer 0 /DELETE boolean false /UNMAP boolean false /NEW boolean false /OLD boolean false /LIST boolean false /SPLIT boolean false /ORIGINAL boolean false Command Highlights (continued) Trimming/Elimming Several commands allow you to trim trailing spaces from your buffers and replace tabs with the appropriate number of spaces. Trimming: Elimming: WRITE /TRIM=Y WRITE /ELIM=Y EXIT with SET TRIM ON EXIT with SET ELIM ON TRIM ELIM Trimming is done only on spaces, not trailing tabs. Whenever trimming and elimming are to be done together, tab elimination is done first. Tab elimination is supported for regularly spaced tabs (SET TAB EVERY) of any size. DEFAULT and SET Abel allows you to control your editing environment with the following commands: DEFAULT TAB EVERY DEFAULT RIGHT MARGIN DEFAULT LEFT MARGIN SET DCL SET WORD WRAP SET TRIM SET ELIMINATE TABS SET SCROLL FACTOR SET WRITE SET TEXT SET AUTOINDENT SET SHIFT KEY SET WIDTH Eve's buffer environment commands are maintained, too: SET TABS EVERY SET TABS AT SET RIGHT MARGIN SET LEFT MARGIN Reserved Marks Two reserved marks can be used for GO TO'ing: SELECT position where last SELECT was performed BLOCK_SELECT position where last BLOCK SELECT was performed ABELINI and ABEL Abel lets you use a non-VAXTPU initialization file. The file is pointed to by logical ABELINI or looks for the file ABELINI.ABEL in the current directory. The file is read into the ABELINI buffer and executed with the ABEL command. ABEL is a command to execute the current buffer as a list of ABEL commands. Some interesting things are possible...editing sessions that edit and create themselves as they go, "script-file" driven editing... The TOP command can be used for getting back to the top of the executing buffer, BOTTOM can be used to exit; now all we need is a control structure... TPU utilities There are quite a few routines internal to Abel that could be useful. ABL$PROMPT_WORD Similar to Eve's EVE$PROMPT_STRING, but looks for a specific keywords that you can specify. ABL$PROMPT_WORD ($word_list, $old_word, $new_word, $prompt_string, $no_value_message) For example: ABL$PROMPT_WORD ("/yes/no","",answer,"Yes or no? ","Aborted") will issue the prompt and accept only "yes" or "no" (not case-sensitive), and return in answer the response (always in lowercase) and return as a result of the function call a 1 if answer given or 0 if not. If the user gives an invalid answer, he is given the options in an error message. ABL$VIRTUAL_MARK, ABL$VIRTUAL_POSITION Marks a place without using a mark; instead uses 3 global variables: ABL$X_VIRTUAL_OFFSET "no-tab" offset in current line ABL$X_VIRTUAL_LINE line number we're on ABL$X_VIRTUAL_BUFFER current buffer Gives an "unbound" mark for commands like the BLOCK commands where we want to return back to the same place (buffer, line, offset) regardless of what editing we did. ABL$GSR... "Graphic" subroutines, provide the following: movement in 4 directions draw 2 line types square (box) and cube (2 types each) Encryption algorithm Two commands provide "encrypting" and "decrypting" of buffers (files), SCRAMBLE and UNSCRAMBLE, based on a user-supplied key. The encryption algorithm developed was designed to be fast, taking advantage of the TRANSLATE primitive in VAXTPU. The key is used as the basis for mixing up the position of the characters in the ASCII character set, and a translate is performed on the buffer using the mixed character set. The encryption algorithm rotates the ASCII character against the first character of the key, then rearranges the character set for all remaining characters of the key by moving around substrings inside the ASCII character set. Here's the algorithm in more detail: ASCII is the untouched, normal ASCII character set CRYPTIC is the character set we're making start at first character in key get pointer to position of key character in ASCII two substrings of original ASCII character set are idenitified: head from beginning of string to pointer tail from pointer to end of string CYPTIC = tail of ASCII + head of ASCII loop for all remaining characters in key advance to next character in key get pointer to position of character in ASCII get pointer to position of character in CRYPTIC three substrings of CRYPTIC are identified: head from beginning of string to first pointer (whichever is lower) middle from first pointer to second pointer tail from second pointer to end of string CRYPTIC = tail of CRYPTIC + middle of CRYPTIC + head of CRYPTIC endloop If the key is "egg" (case-sensitive), and the ASCII character set consists of only the characters "abcdefghi", the character set is massaged as follows: Mixed set Key Gives abcdefghi e efghiabcd efghiabcd g bcdghiaef bcdghiaef g aefghibcd The buffer would be translated from "abcdefghi" to "aefghibcd", ie., all b's would become e's, all g's would become b's, etc... or vice versa depending on whether you're SCRAMBLE'ing or UNSCRAMBLE'ing. Encryption algorithm (continued) Here's another example using the key "fig": Mixed set Key Gives abcdefghi f fghiabcde fghiabcde i eiabcdefg eiabcdefg g gefeiabcd Some things to note about the algorithm. The key is case-sensitive and also sensitive to trailing spaces and tabs. The more characters you provide in the key, the more scrambled the character set gets (a one character key is *real* easy to figure out). Do NOT eliminate tabs or trim whitespace from a buffer that has been scrambled (use WRITE FILE /TRIM=NO /ELIM=NO)! No character set rotation scheme is unbreakable, but this should slow 'em down.