home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
PCTAG.ZIP
/
RETROMAC.COM
/
RETRO.SRC
< prev
Wrap
Text File
|
1989-10-01
|
45KB
|
1,117 lines
{*
* ---------------------------------------------------
* PC-TAGS(tm) RETRO Program for the Multi-Edit Editor
* ---------------------------------------------------
*
* Multi-Edit Macros to Search a PC-TAGS-Generated Tagfile and Retrieve a
* Function Definition
*
* Copyright (C) 1989 by Moderne Software. All rights reserved.
* Moderne Software, P.O. Box 3638, Santa Clara, CA 95055-3638
*
* Limited permission is given to registered PC-TAGS users to modify this
* file for their own personal use only. This file may not be used for any
* purpose other than in conjunction with the PC-TAGS software package.
*
* Entry Points
* ------------
* pctags_auto -- uses word under cursor for search
* pctags_prompt -- prompts for a word to use in search
*
* Support Macros
* --------------
* _init -- create and initialize global variables
* _delimiter -- determinse if a character is a word delimiter
* _dos_shell -- execute RETROEXEC environment value thru DOS
* _pctags_main -- search and retrieval workhorse code
* _get_tagfile -- retrieve the name of the next tagfile to search
* _parse_file_spec -- break multiple tagfile specs into individual
* directory/filename parts
* _convert_slashes -- convert all slashes in string to backslashes
* _lower -- convert all characters in string to lowercase
* _getcd -- get current drive and directory
* _pos_line_in_win -- position current line to third line in window
*
* History:
* --------
* 1 Oct 89 -- Version 1.00
*
*}
{***************************************}
{ Specify compiled file name }
$MACRO_FILE RETRO; { Compile to RETRO.MAC }
{***************************************
* Macro : _init
* Syntax: _init;
* Entry : None. Called at beginning of pctags_auto() and pctags_prompt()
* macros to initialize the global variables.
* Exit : All RETRO global variables are initialized.
* Note : This macro must be called every time pctags_auto() and
* pctags_prompt() are invoked.
*}
$MACRO _init;
{ Set this variable to CASE_INSENSITIVE if you want the search
for a matching tagname to ignore case.
}
SET_GLOBAL_STR( 'pctags_tagname_case', 'CASE_SENSITIVE' );
{ Initialize all global variables. Create if necessary. }
SET_GLOBAL_STR( 'pctags_env_tagfiles', '' );
SET_GLOBAL_STR( 'pctags_env_path', '' );
SET_GLOBAL_STR( 'pctags_env_retroexec', '' );
SET_GLOBAL_INT( 'pctags_first_call', FALSE );
END_MACRO; { _init }
{**************************************
* Macro : pctags_auto
* Syntax: pctags_auto;
* Entry : None. Callable from outside this module.
* Exit : Complete RETRO operation is performed. Tagname is extracted from
* under current cursor position.
* Returns TRUE if operation is a success, else FALSE.
* Note : This is one of the two entry points into this module.
*
* Use of Global Variables:
* pctags_function_name -- assign
*}
$MACRO pctags_auto;
DEF_INT( delimiter );
DEF_STR( tagname[ 80 ] );
{ Initialize global variables }
RUN_MACRO( '_init' );
{ Make sure cursor is currently under a valid word }
RUN_MACRO( '_delimiter ' + CUR_CHAR );
IF RETURN_INT THEN
{ Cursor not under a word }
MAKE_MESSAGE( 'Cursor is not under a valid word' );
BEEP;
RETURN_INT := TRUE; { Multi-Edit chokes on FALSE values }
GOTO END; { Abort macro }
END;
{ Save current column position }
MARK_POS;
{ Backtrack to start of word }
delimiter := FALSE;
WHILE delimiter = FALSE DO
{ Stop backing up if at start of line }
IF C_COL = 1 THEN
{ We're at the start of the word }
delimiter := TRUE;
ELSE
{ Move one character left }
LEFT;
{ Check this character for a delimiter }
RUN_MACRO( '_delimiter ' + CUR_CHAR );
delimiter := RETURN_INT;
{ Have to check for delimiter here so we can adjust
cursor.
}
IF delimiter THEN
{ Move cursor to forward to start of word }
RIGHT;
END;
END;
END;
{ Grab the characters that make up this word }
tagname := '';
delimiter := FALSE;
WHILE delimiter = FALSE DO
{ Append current character to tagname string }
tagname := tagname + CUR_CHAR;
{ Move cursor to next char }
RIGHT;
{ If we've reached the end of the line, we're done }
IF AT_EOL THEN
{ Stop grabbing characters }
delimiter := TRUE;
ELSE
{ Check if this character is the end of the word }
RUN_MACRO( '_delimiter ' + CUR_CHAR );
delimiter := RETURN_INT;
END;
END;
{ tagname now holds the tagname to retrieve }
{ Restore original cursor position }
GOTO_MARK;
{ Perform retrieval operation }
RUN_MACRO( '_pctags_main ' + tagname + ' ' );
END:
END_MACRO; { pctags_auto }
{***************************************
* Macro : pctags_prompt
* Syntax: pctags_prompt;
* Entry : None. Callable from outside this module.
* Exit : Complete RETRO operation is performed. Tagname is prompted for
* on the message line.
* Returns TRUE if operation is a success, else FALSE.
* Notes : This is one of two entry points into this module.
* The message line must be active or else ??? will happen.
*
* Use of Global Variables:
* pctags_function_name -- assign
*}
$MACRO pctags_prompt;
DEF_STR( tagname[ 80 ] );
{ Initialize global variables }
RUN_MACRO( '_init' );
{ Prompt for and get tagname }
IF STRING_IN( tagname, 'Tagname:',
71, MESSAGE_COL, MESSAGE_ROW, '' ) = FALSE THEN
{ User pressed <Esc>, return FALSE }
ABORT:
RETURN_INT := TRUE; { Multi-Edit chokes on FALSE values }
GOTO END; { Abort macro }
END;
{ Make sure empty string wasn't entered }
IF SVL( tagname ) = 0 THEN
GOTO ABORT;
END;
{ Perform retrieval operation }
RUN_MACRO( '_pctags_main ' + tagname + ' ' );
END:
END_MACRO; { pctags_prompt }
{***************************************
* Macro : _delimiter
* Syntax: _delimiter character
* Entry : Character is a char to test if it is a word delimiter.
* Exit : If character is an alphanumeric, underscore(_) or period(.) then it
* is NOT a delimiter. Returns RETURN_INT as FALSE. Else return TRUE.
*}
$MACRO _delimiter;
DEF_CHAR( ch );
{ Get argument }
ch := MPARM_STR;
{ Convert to uppercase for fewer comparisons }
ch := CAPS( ch );
{ Check if alphabetic }
IF ( ch >= 'A' ) AND ( ch <= 'Z' ) THEN
{ Not a delimiter }
RETURN_INT := FALSE;
GOTO END;
END;
{ Check if numeric }
IF ( ch >= '0' ) AND ( ch <= '9' ) THEN
{ Not a delimiter }
RETURN_INT := FALSE;
GOTO END;
END;
{ Check for underscore or period }
IF ( ch = '_' ) OR ( ch = '.' ) THEN
{ Not a delimiter }
RETURN_INT := FALSE;
GOTO END;
END;
{ Character IS a delimiter }
RETURN_INT := TRUE;
END:
END_MACRO; { _delimiter }
{***************************************
* Macro : _lower
* Syntax: _lower string
* Entry : string is a character string of any case.
* Exit : RETURN_STR = string with all lower case characters.
*}
$MACRO _lower;
DEF_STR( string );
DEF_CHAR( ch );
DEF_INT( str_length,
counter );
{ Get argument }
string := MPARM_STR;
{ Get length of string }
str_length := SVL( string );
{ Convert each uppercase character to lowercase }
counter := 1;
WHILE counter <= str_length DO
{ Get the character at the 'counter' position }
ch := COPY( string, counter, 1 );
{ Check if character is uppercase alpha }
IF ch >= 'A' THEN
IF ch <= 'Z' THEN
{ Convert to lowercase }
ch := CHAR( ASCII( ch ) + $20 );
{ Replace uppercase char in string with lowercase }
string := STR_DEL( string, counter, 1 );
string := STR_INS( ch, string, counter );
END;
END;
{ Increment counter }
counter := counter + 1;
END;
{ Return converted string }
RETURN_STR := string;
END_MACRO; { _lower }
{***************************************
* Macro : _convert_slashes
* Syntax: _convert_slashes string
* Entry : string = string which may contain slash characters.
* Exit : RETURN_STR = string with all slashes converted to backslashes
* Note : This conversion is necessary because the LOAD_FILE procedure does
* not support slashes in the file spec. (The FIND_FIRST/NEXT
* functions do.)
*}
$MACRO _convert_slashes;
DEF_INT( slash );
DEF_STR( string );
{ Get argument }
string := MPARM_STR;
slash := XPOS( '/', string, 1 );
WHILE slash <> 0 DO
{ Convert slash to backslash }
string := STR_DEL( string, slash, 1 ); { Remove slash }
string := STR_INS( '\', string, slash ); { Insert backslash }
{ Check for more slashes }
slash := XPOS( '/', string, slash + 1 );
END;
{ Return converted string }
RETURN_STR := string;
END_MACRO; { _convert_slashes }
{***************************************
* Macro : _getcd
* Syntax: _getcd
* Entry : None.
* Exit : RETURN_STR is set to the current directory, including drive.
*}
$MACRO _getcd;
DEF_INT( segment,
offset );
DEF_STR( dos_buffer[ 64 ],
curr_dir[ 66 ] );
{ This is going to be ugly... }
{ Initialize current directory string }
curr_dir := '';
{ Call DOS function $19 to get current drive }
R_AX := $1900;
INTR( $21 ); { AL = drive code (0=A, 1=B, etc.) }
R_AX := R_AX AND $00FF; { Clear AH }
{ Create drive spec with colon }
curr_dir := CHAR( R_AX + ASCII( 'A' ) ) + ':';
{ Call DOS function $47 to get current directory }
R_AX := $4700;
R_DX := $0000;
R_DS := SEG( dos_buffer );
R_SI := OFS( dos_buffer ) + 4; { Don't trash length and max-length fields }
INTR( $21 ); { DS:SI = ASCIIZ current directory }
{ Copy contents of dos_buffer into Multi-Edit string }
segment := SEG( dos_buffer );
offset := OFS( dos_buffer ) + 4;
WHILE PEEK( segment, offset ) <> $00 DO
{ Append char to Multi-Edit string }
curr_dir := curr_dir + CHAR( PEEK( segment, offset ) );
{ Advance to next character in dos_buffer }
offset := offset + 1;
END;
{ If dos_buffer is empty, current directory is the root.
Append single backslash to current directory.
}
IF offset = OFS( dos_buffer ) + 4 THEN
{ No chars found in DS:SI }
curr_dir := curr_dir + '\';
END;
{ Return constructed drive:current directory }
RETURN_STR := curr_dir;
END_MACRO; { _getcd }
{***************************************
* Macro : _dos_shell
* Syntax: _dos_shell file_spec
* Entry : file_spec is the name of the file the RETROEXEC environment variable
* command is supposed to make available.
* Global variable pctags_env_retroexec contains the command line to
* execute. It may contain special parameters documented below.
* Case of the special parameters is significant. It may also
* contain the DOS standard output redirection characters > or >>,
* but the command must then be bracketed with double quotes. The
* quotes will be removed before sending the command line to DOS.
* Exit : The command will have been executed. No return value is sent back.
* Special command_line parameters:
* %s - entire file specification of desired file (file_spec parm)
* %d - drive of desired file
* %p - path (no drive) of desired file (no terminating backslash)
* %f - filename of desired file
* %c - current directory (in drive:path format suitable for CD'ing)
* %u - user-defined substitution string
* %% - single %
*}
$MACRO _dos_shell;
DEF_INT( last_path_separator,
fname_start,
sub,
org_mem,
org_refresh );
DEF_STR( file_spec[ 80 ],
command_line[ 128 ],
drive[ 2 ],
path[ 64 ],
fname[ 12 ],
curr_dir[ 66 ],
sub_str[ 80 ] );
DEF_CHAR( parm );
{ Get parameter }
file_spec := MPARM_STR;
{ Move global variable into local storage }
command_line := GLOBAL_STR( 'pctags_env_retroexec' );
{ ...May not want to do this here...
RUN_MACRO( '_convert_slashes ' + command_line );
command_line := RETURN_STR;
}
{ If command line is bracketed by double quotes, remove them }
IF COPY( command_line, 1, 1 ) = '"' THEN
{ Check last character }
IF COPY( command_line, SVL( command_line ), 1 ) = '"' THEN
{ Remove double quotes }
command_line := COPY( command_line, 2,
SVL( command_line ) - 2 );
END;
END;
{ Create special parameter strings }
{ Get drive:path }
last_path_separator := XPOS( '\', file_spec, 1 );
WHILE XPOS( '\', file_spec, last_path_separator + 1 ) <> 0 DO
{ Save position of this backslash }
last_path_separator := XPOS( '\', file_spec, last_path_separator + 1 );
END;
{ Save start of filename }
fname_start := last_path_separator + 1;
{ If not in root, don't count last backslash }
IF last_path_separator <> 3 THEN
last_path_separator := last_path_separator - 1;
END;
{ Break drive:path into separate strings }
drive := COPY( file_spec, 1, 2 ); { %d special parm }
path := COPY( file_spec, 3, last_path_separator - 2 ); { %p special parm }
{ Get filename only } { %f special parm }
fname := COPY( file_spec, fname_start, SVL( file_spec ) - fname_start + 1 );
{ Get current directory }
RUN_MACRO( '_getcd' );
curr_dir := RETURN_STR; { %c special parm }
{ Parse command line, replacing special parameters }
sub := XPOS( '%', command_line, 1 );
WHILE sub <> 0 DO
{ Get character after '%' }
parm := COPY( command_line, sub + 1, 1 );
{ Interpret substitution character }
IF parm = 's' THEN
{ Entire file specification of desired file }
sub_str := file_spec;
GOTO REPLACE;
END;
IF parm = 'd' THEN
{ Drive of desired file }
sub_str := drive;
GOTO REPLACE;
END;
IF parm = 'p' THEN
{ Path (no drive) of desired file }
sub_str := path;
GOTO REPLACE;
END;
IF parm = 'f' THEN
{ Filename of desired file }
sub_str := fname;
GOTO REPLACE;
END;
IF parm = 'c' THEN
{ Current directory }
sub_str := curr_dir;
GOTO REPLACE;
END;
IF parm = 'u' THEN
{ User-defined substitution string }
sub_str := ''; { In case user hits <Esc> }
IF STRING_IN( sub_str, 'Enter substitution string:', 53,
MESSAGE_COL, MESSAGE_ROW, '' ) = 0 THEN
{ Make sure input string is clear }
sub_str := '';
END;
GOTO REPLACE;
END;
IF parm = '%' THEN
sub_str := '%';
GOTO REPLACE;
END;
{ Else unrecognized substitution parameter }
sub_str := '';
REPLACE:
{ Replace special parameter with substitution string }
command_line := COPY( command_line, 1, sub - 1 ) { Portion before '%' }
+
sub_str { Substitution string }
+ { Portion after '%' }
COPY( command_line, sub + 2, SVL( command_line ) - sub - 1 );
{ Look for any more '%' }
sub := XPOS( '%', command_line, sub + 1 );
END;
{ Let user know what's going on }
MAKE_MESSAGE( 'Executing RETROEXEC command' );
{ Save current memory usage and force maximum freeing of available
memory.
}
org_mem := REDUCE_MEM;
REDUCE_MEM := TRUE;
{ Send command line to secondary DOS command processor for execution }
{ SAVE_DOS_SCREEN; }
SHELL_TO_DOS( command_line, TRUE );
{ REST_DOS_SCREEN; }
{ Remove any output displayed onscreen during execution }
org_refresh := REFRESH;
REFRESH := TRUE;
NEW_SCREEN;
REFRESH := org_refresh;
{ Restore memory-usage context }
REDUCE_MEM := org_mem;
END_MACRO; { _dos_shell }
{***************************************
* Macro : _parse_file_spec
* Syntax: _parse_file_spec file_spec
* Entry : file_spec = file specification to parse. May contain wildcards.
* May also contain multiple specs, each separated by a semicolon.
* For example, "c:\*.tag;d:\me\tagfiles\*.tag;c:\pctags.tag".
* Both backslashes and slashes are recognized as path separators.
* Exit : Any drive:path in the first file spec is stored in the global
* variable pctags_env_path.
* Any subsequent file specs are stored in global var
* pctags_env_tagfiles.
* The filename portion of the spec is returned in RETURN_STR (may
* contain wildcards).
*
* Use of Global Variables:
* pctags_env_tagfiles -- assign
* pctags_env_path -- assign
*}
$MACRO _parse_file_spec;
DEF_INT( last_path_separator,
semicolon );
DEF_STR( file_spec[ 128 ] );
{ Get the parameter }
file_spec := MPARM_STR;
{ Does file_spec contain multiple specs? }
semicolon := XPOS( ';', file_spec, 1 );
IF semicolon = 0 THEN
{ No, file_spec contains one drive:path.
Clear pctags_env_tagfiles.
}
SET_GLOBAL_STR( 'pctags_env_tagfiles', '' );
ELSE
{ Yes, store subsequent specs in pctags_env_tagfiles }
SET_GLOBAL_STR( 'pctags_env_tagfiles',
COPY( file_spec,
semicolon + 1,
SVL( file_spec ) - semicolon ) );
{ Place first spec only in file_spec }
file_spec := COPY( file_spec, 1, semicolon - 1 );
END;
{ Does file_spec contain a drive or path spec? }
{ Find index to last path separator }
last_path_separator := XPOS( '\', file_spec, 1 );
WHILE XPOS( '\', file_spec, last_path_separator + 1 ) <> 0 DO
{ Save position of this backslash }
last_path_separator := XPOS( '\', file_spec, last_path_separator + 1 );
END;
{ If no path was specified, check for a drive }
IF last_path_separator = 0 THEN
last_path_separator := XPOS( ':', file_spec, 1 );
END;
{ If drive and/or path were specified, extract from file_spec and
store in pctags_env_path.
}
IF last_path_separator = 0 THEN
SET_GLOBAL_STR( 'pctags_env_path', '' );
ELSE
SET_GLOBAL_STR( 'pctags_env_path',
COPY( file_spec, 1, last_path_separator ) );
END;
{ Return file_spec (single spec with possible drive:path and wildcards) }
RETURN_STR := file_spec;
END_MACRO; { _parse_file_spec }
{***************************************
* Macro : _get_tagfile
* Syntax: _get_tagfile
* Entry : None.
* Exit : RETURN_STR is set to the retrieved tagfile name. It equals '' if
* no more tagfiles could be found.
*
* Use of Global Variables:
* pctags_first_call -- access and assign
* pctags_env_tagfiles -- access
* pctags_env_path -- access
*}
$MACRO _get_tagfile;
DEF_STR( file_spec,
single_tagfile );
{ If this is the first call to this macro during this RETRO
invocation, get the RETRO environment variable setting, if any.
}
IF GLOBAL_INT( 'pctags_first_call' ) = FALSE THEN
{ Get the next matching file name for file_spec }
IF NEXT_FILE = 0 THEN
{ Match found, get it }
single_tagfile := LAST_FILE_NAME;
ELSE
{ No match, set to empty string }
single_tagfile := '';
END;
ELSE
{ Go thru this code once per RETRO invocation }
SET_GLOBAL_INT( 'pctags_first_call', FALSE );
{ Get RETRO environment variable value }
file_spec := GET_ENVIRONMENT( 'RETRO' );
{ If no RETRO variable defined, use default }
IF file_spec = '' THEN
file_spec := '*.tags';
END;
{ Convert to all lowercase }
RUN_MACRO( '_lower ' + file_spec );
file_spec := RETURN_STR;
{ Convert any slashes to backslashes }
RUN_MACRO( '_convert_slashes ' + file_spec );
file_spec := RETURN_STR;
{ Parse any multiple file specs into single specs and
set the file pattern to the first parse file_spec (may
contain wildcards).
}
RUN_MACRO( '_parse_file_spec ' + file_spec );
file_spec := RETURN_STR;
{ Find first matching file }
IF FIRST_FILE( file_spec ) = 0 THEN
{ Match found, get it }
single_tagfile := LAST_FILE_NAME;
ELSE
{ No match, set to empty string }
single_tagfile := '';
END;
END;
{ Check if we got a tagfile name }
WHILE single_tagfile = '' DO
{ Are there more file specs in pctags_env_tagfiles? }
IF GLOBAL_STR( 'pctags_env_tagfiles' ) = '' THEN
{ Nope, this is the end of the road }
GOTO EXIT; { Out of while-loop }
END;
{ There are more tagfile specs. Process them. }
RUN_MACRO( '_parse_file_spec '
+
GLOBAL_STR( 'pctags_env_tagfiles' ) );
file_spec := RETURN_STR;
{ Get first matching file name for tagfile spec }
IF FIRST_FILE( file_spec ) = 0 THEN
{ Match found, get it }
single_tagfile := LAST_FILE_NAME;
ELSE
{ No match, set to empty string }
single_tagfile := '';
END;
END;
EXIT:
{ If filename found, convert to lowercase and insert any drive:path }
IF single_tagfile = '' THEN
{ Return empty filename }
RETURN_STR := '';
ELSE
{ Convert to lowercase }
RUN_MACRO( '_lower ' + single_tagfile );
single_tagfile := RETURN_STR;
{ Insert drive:path }
RETURN_STR := GLOBAL_STR( 'pctags_env_path' ) + single_tagfile;
END;
END_MACRO; { _get_tagfile }
{***************************************
* Macro : _pos_line_in_win
* Syntax: _pos_line_in_win
* Entry : None.
* Exit : The current line is positioned on the third line in the window.
* Note : The window is always a full-screen window.
*}
$MACRO _pos_line_in_win;
IF C_LINE > 2 THEN
UP; UP; { Move cursor up two lines }
DOWN; DOWN; { Then back down to original point }
END;
END_MACRO; { _pos_line_in_win }
{***************************************
* Macro : _pctags_main
* Syntax: _pctags_main function_name
* Entry : function_name contains the tagname we're searching for.
* Exit : TRUE is retrieval operation is successful, else FALSE.
* Description:
* RETRO operation is performed. This requires the following steps:
* -- Cycle through all required tagfiles. Default = *.tag or
* all tagfiles specified in RETRO environment variable
* which may contain multiple file specs, each with
* wildcards, separated by semicolons. For example,
* SET RETRO=c:\me\tagfiles\*.tag;c:\prog\*.tag.
* -- Search each tagfile for the pctags_function_name string.
* -- If not found in any tagfile, display message and quit.
* -- If found, get name of file and definition line from tagfile.
* -- Close tagfile.
* -- Load file for editing.
* -- If file cannot be found, tag info contains EXEC command and
* environment variable RETROEXEC is defined, execute the
* command-line value assigned to it. Try to load the file
* again.
* -- If file still cannot be found, display message and quit.
* -- Search file for definition line.
* -- Place definition line on third line in window (if possible).
* -- If any of these operations fail, display appropriate error
* message and quit.
*
* Use of Global Variables:
* pctags_first_call -- assign
* pctags_function_name -- access
*}
$MACRO _pctags_main;
DEF_INT( org_win_id,
org_case,
org_re,
org_refresh,
file_start,
decl_start,
exec_env,
line_number,
special_chars,
found_match,
continue_search );
DEF_STR( tagfile_name[ 80 ],
function_name[ 128 ],
file_spec[ 80 ],
decl_line[ 256 ],
env_var[ 128 ],
line[ 512 ] );
{ Get argument }
function_name := MPARM_STR;
{ Save current context }
org_win_id := WINDOW_ID; { In case we have to return to it }
org_case := IGNORE_CASE;
org_re := REG_EXP_STAT;
org_refresh := REFRESH;
{ Set up our context }
REG_EXP_STAT := FALSE; { Disable regular expression searches }
REFRESH := FALSE; { No flashing of windows }
{ Create hidden tagfile buffer/window }
CREATE_WINDOW;
SWITCH_WINDOW( WINDOW_COUNT );
WINDOW_ATTR := WINDOW_ATTR AND 3; { Make window completely hidden }
{ Initialize flags }
found_match := FALSE;
SET_GLOBAL_INT( 'pctags_first_call', TRUE ); { Global flag for _get_tagfile() }
tagfile_name := '';
NEXT_TAGFILE:
{ Cycle thru all RETRO (or default) tagfiles until done or match found }
WHILE found_match = FALSE DO
{ Get a tagfile }
RUN_MACRO( '_get_tagfile' );
tagfile_name := RETURN_STR;
{ If no more tagfiles to get, quit }
IF tagfile_name = '' THEN
{ Remove our temporary tagfile window }
DELETE_WINDOW;
{ Reset to original window }
IF SWITCH_WIN_ID( org_win_id ) THEN
{ This should always be true }
{ Macro compiler chokes unless return value is used }
END;
{ Display message }
{ Note: function_name ends with a single blank char }
MAKE_MESSAGE( function_name + 'not found.' );
BEEP;
{ Exit while-loop }
GOTO EXIT;
END;
{ Load tagfile into our hidden window }
LOAD_FILE( tagfile_name );
{ Set up a loop to search though the tagfile. Multiple
searches may be required because a match COULD be found on a
string in the tagfile other than the function-name portion of
the line.
}
{ Search tagfile for function name }
MAKE_MESSAGE( 'Searching ' + tagfile_name );
continue_search := TRUE;
TOF; { Force to start of file }
IF GLOBAL_STR( 'pctags_tagname_case' ) = 'CASE_SENSITIVE' THEN
IGNORE_CASE := FALSE;
ELSE
IGNORE_CASE := TRUE;
END;
WHILE continue_search DO
SEARCH_TAGFILE:
{ Search tagfile for function name }
IF SEARCH_FWD( function_name, 0 ) = 0 THEN
{ Did not find function name in this tagfile }
{ Stop searching this file, try another }
ERASE_WINDOW; { Guarantee empty buffer }
GOTO NEXT_TAGFILE; { Back to outer while-loop }
END;
{ Found a match }
{ The match MUST start in the first column. If it
doesn't then a match was found somewhere other than
in the function-name portion of the line.
}
IF C_COL <> 1 THEN
{ Not a valid match }
{ Advance buffer position so we don't
keep finding the same match.
}
RIGHT;
{ Continue searching tagfile }
GOTO SEARCH_TAGFILE;
END;
{ Found a good match! }
{ Stop searching this and all other tagfiles }
continue_search := FALSE; { This tagfile }
found_match := TRUE; { All tagfiles }
{ Read line with match, including newline }
line := GET_LINE;
{ Done with tagfile, close it }
{ Just clear the buffer, we'll use the same window
for the source file.
}
ERASE_WINDOW;
{ Format of line:
Column 1: Function name searching for (variable length)
Single space terminator
Optional RETRO "commands" (# and/or !)
Complete file specification of file containing
definition of function (variable length)
Single space terminator
Carot (^) character signifying the start of the
definition line
Line from file that defines the function, including
carriage return (variable length)
End of line is the last character in the definition
line, i.e. carriage return
As an example:
"function c:\dir\file.c ^int function( arg1, arg2 )\n"
}
{ Determine starting indices of file spec and definition
line.
}
file_start := XPOS( ' ', line, 1 );
file_start := file_start + 1; { Advance past space }
decl_start := XPOS( ' ', line, file_start );
decl_start := decl_start + 2; { Advance past space and carot }
{ Extract file spec and def line }
file_spec := COPY( line, file_start,
decl_start - file_start - 2 );
decl_line := COPY( line, decl_start,
SVL( line ) - decl_start + 1 );
{ Check file_spec for embedded-command characters }
exec_env := FALSE;
line_number := FALSE;
special_chars := 1; { Base-one strings }
NEXT_SPECIAL_CHAR:
IF COPY( file_spec, special_chars, 1 ) = '!' THEN
{ If file_spec not found, execute RETROEXEC
environment variable command line. This is
usually used to extract the file from a
source-control library.
}
exec_env := TRUE;
{ Increment string index }
special_chars := special_chars + 1;
{ Check for other special chars }
GOTO NEXT_SPECIAL_CHAR;
END;
IF COPY( file_spec, special_chars, 1 ) = '#' THEN
{ Line number of definition is stored in
tagfile instead of line contents
}
line_number := TRUE;
special_chars := special_chars + 1;
GOTO NEXT_SPECIAL_CHAR;
END;
{ Remove all special chars from file_spec }
IF special_chars <> 1 THEN
file_spec := COPY( file_spec, special_chars,
SVL( file_spec ) - special_chars + 1 );
END;
{ Verify that file_spec exists }
IF FILE_EXISTS( file_spec ) = 0 THEN
{ If exec_env active, get RETROEXEC value }
IF exec_env THEN
SET_GLOBAL_STR( 'pctags_env_retroexec',
GET_ENVIRONMENT( 'RETROEXEC' ) );
{ If variable found, execute it }
IF GLOBAL_STR( 'pctags_env_retroexec' ) <> '' THEN
RUN_MACRO( '_dos_shell '
+ file_spec );
END;
END;
{ Check for file's existence now }
IF FILE_EXISTS( file_spec ) = 0 THEN
{ Still not found }
MAKE_MESSAGE( file_spec
+ ' does not exist. Update tagfile.' );
BEEP;
{ Can't go any further }
DELETE_WINDOW;
IF SWITCH_WIN_ID( org_win_id ) THEN
{ Always true }
END;
GOTO EXIT;
END;
END;
{ Load file into our (hidden) window }
LOAD_FILE( file_spec );
TOF; { Guaranteed at top of file }
{ Searching for line contents or line number? }
IF line_number THEN
{ Goto specified line number in source file }
{ Convert string to int }
IF VAL( line_number, decl_line ) THEN
{ Someone's been messing with the
tagfile.
}
line_number := 1; { Leave cursor at top of file }
END;
GOTO_LINE( line_number );
{ Position line }
RUN_MACRO( '_pos_line_in_win' );
{ Display filename }
MAKE_MESSAGE( 'File: ' + file_spec );
{ Set important system variables }
FILE_NAME := CAPS( file_spec );
WINDOW_ATTR := 0;
FILE_CHANGED := FALSE;
ELSE
{ Search for definition line }
IGNORE_CASE := FALSE; { Force to case-sensitive search }
KEEP_SEARCHING:
IF SEARCH_FWD( decl_line, 0 ) THEN
{ Found a match }
{ Match must be at start of line to be
valid. If not at start, it could be
a "forward" declaration.
}
IF C_COL <> 1 THEN
{ Invalid match, keep searching }
{ Advance cursor position so we
won't keep finding the same
match.
}
RIGHT;
{ Continue searching source file }
GOTO KEEP_SEARCHING;
END;
{ Found function definition! }
{ Position the line in the window }
RUN_MACRO( '_pos_line_in_win' );
{ Display filename }
MAKE_MESSAGE( 'File: ' + file_spec );
{ Set important system variables }
FILE_NAME := CAPS( file_spec );
WINDOW_ATTR := 0;
FILE_CHANGED := FALSE;
ELSE
{ Definition file not found in file }
MAKE_MESSAGE( 'Update ' + tagfile_name +
' with ' + file_spec );
BEEP;
{ Recovery }
{ Dump this file/window }
DELETE_WINDOW;
IF SWITCH_WIN_ID( org_win_id ) THEN
{ Do this to satisfy macro compiler }
END;
END;
END;
END;
END;
EXIT:
{ Restore original context }
IGNORE_CASE := org_case;
REG_EXP_STAT := org_re;
REFRESH := org_refresh;
{ If match not found in any tagfile, that's not good... }
IF found_match = TRUE THEN
{ Congratulations! }
RETURN_INT := TRUE;
ELSE
{ Note: Would rather send FALSE here, but the Multi-Edit
"Run-Macro" menu chokes on it...
}
RETURN_INT := TRUE;
END;
END_MACRO; { _pctags_main }
{**************************************}