home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
PCTAG.ZIP
/
RETROMAC.COM
/
RETRO.E
< prev
next >
Wrap
Text File
|
1989-10-01
|
49KB
|
1,224 lines
/*
* ------------------------------------------------
* PC-TAGS(tm) RETRO Program for the Epsilon Editor
* ------------------------------------------------
*
* EPSILON 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.
*
* This macro will run with Epsilon versions 3.2x thru 4.0x. By default, it
* will only work with version 4.0x. To compile and use with Epsilon
* version 3.2x, set the constant "V40x" to FALSE and "V32x" to TRUE. These
* constants are defined below.
*
* Entry Points
* ------------
* when_loading -- declare global variables
* pctags_auto -- uses word under cursor for search
* pctags_prompt -- prompts for a word to use in search
*
* Support macros
* --------------
* delimiter -- determines if character is a word delimiter
* atoi -- convert ASCII characters to integer
* strlwr -- convert an ASCIIZ string to lowercase
* dos_shell -- execute RETROEXEC environment value thru DOS
* pctags_main -- search and retrieval workhorse code
* get_tagfile -- retrieve the next tagfile to search
* parse_file_spec -- break multiple tagfile specifications into
* individual directory/filename parts
* position_line_within_window -- position current line to third line in
* current window, if possible
* file_loaded -- determine whether given file is currently loaded
* find_first -- DOS file-directory function
* find_next -- DOS file-directory function
* get_dta -- DOS retrieve disk transfer address (DTA)
* set_dta -- DOS set disk transfer address (DTA)
*
* History
* -------
* 1 Oct 89 -- Version 1.00
*
*/
/****************************************/
/* Global constants */
#include "eel.h" /* Epsilon standard definitions */
#include "lowlevel.h" /* Epsilon low-level data types */
#define TRUE 1
#define FALSE 0
/* Epsilon Version */
/* **************************** IMPORTANT!!! *******************************/
/* Only ONE of the version constants can be set to a value of "TRUE". All
* others must be set to "FALSE".
*
* If this macro will be used with Epsilon 4.00 or above, the "V40x" constant
* should be set to TRUE (this is the default).
* If using Epsilon 3.20-3.23, set the constant "V32x" to TRUE.
*
* BE SURE ONLY ONE OF THE VERSION CONSTANTS IS TRUE AND ALL OTHERS ARE FALSE!!!
*
*/
#define V40x TRUE /* Versions 4.00 and above */
#define V32x FALSE /* Versions 3.20-3.23 */
/* Return values */
#define SUCCESSFUL TRUE
#define UNSUCCESSFUL FALSE
#define CASE_SENSITIVE 0 /* String searches are case sensitive */
#define CASE_INSENSITIVE 1 /* Searches are case insensitive */
/****************************************/
/* Global data */
/* You can set the pctags_tagname_case variable to CASE_INSENSITIVE if
* you want the search for a matching tagname to ignore case.
*/
int pctags_tagname_case = CASE_SENSITIVE;
char pctags_function_name[ 80 ]; /* Function to retrieve */
char pctags_env_tagfiles[ 125 ]; /* RETRO env var value */
char pctags_env_path[ 80 ]; /* Drive:path of current tagfile */
int pctags_first_call; /* Flag used by get_tagfile() */
struct {
char ignored_data[ 30 ]; /* File info */
char filename[ 13 ]; /* ASCIIZ filename */
} pctags_file_info;
/* Global data that should be local to pctags_main(), but Epsilon does not
* support such large local data allocations.
*/
char pctags_decl_line[ 256 ];
char pctags_line[ 512 ];
char pctags_env_pctagsexec[ 129 ];
/***************************************
* Macro : when_loading
* Syntax: None, automatically executed when loaded, then discarded
* Entry : None.
* Exit : Any necessary RETRO initialization is performed.
* Notes : This macro is called by EPSILON automatically upon loading.
*/
when_loading()
{
/* Nothing so far... */
} /* when_loading() */
/***************************************
* Macro : pctags_auto
* Syntax: command int pctags_auto( void )
* Entry : None, will extract the word under the current cursor position.
* Exit : Non-zero if entire retrieval operation is successfully completed.
* Zero if operation is not successfully completed.
* Description :
* Function under cursor is extracted from line and used to search thru
* the tagfile(s). If a match is found, the file containing the
* function's definition is loaded and the cursor is placed at the
* start of the definition.
* Use of Global Variables:
* pctags_function_name -- assign
*/
command
int
pctags_auto()
{
char ch;
int start;
int end;
/* Make sure cursor is currently under a valid word */
if( delimiter( curchar() ) ){
/* Not under a word */
maybe_ding();
say( "Cursor is not under a valid word" );
return( UNSUCCESSFUL );
}
/* Advance to end of this word */
end = point;
while( (++end < size()) && (!delimiter( character( end ) )) )
;
/* Backtrack to start of word */
start = point;
while( (start != 0) && (!delimiter( character( --start ) )) )
;
/* Adjust start to first char in word */
if( (start != 0) || (delimiter( character( start ) )) ){
++start;
}
/* Copy word into our storage space */
grab( start, end, pctags_function_name );
/* Append a blank to end of word */
end = end - start; /* end = index immediately after last char */
pctags_function_name[ end ] = ' ';
pctags_function_name[ end + 1 ] = '\0';
/* Perform retrieval operation */
return( pctags_main( pctags_function_name ) );
} /* pctags_auto() */
/***************************************
* Macro : pctags_prompt
* Syntax: command int pctags_prompt( void )
* Entry : None, will prompt for function to retrieve.
* Exit : Non-zero if specified function is found, file containing function
* definition is loaded and cursor is placed at beginning of
* definition.
* Zero if specified function is not found.
* Description :
* User is prompted for the name of a function. The macro loads the file
* containing the function definition and places the cursor on the
* definition's line.
* Use of Global Variables:
* pctags_function_name -- assign
*/
command
int
pctags_prompt()
{
/* Prompt the user for a function name */
get_string( pctags_function_name, "Enter tagname: " );
/* If empty string was entered, abort */
if( strlen( pctags_function_name ) == 0 ){
/* Just return */
return( UNSUCCESSFUL );
}
/* Append a blank to the function name */
strcat( pctags_function_name, " " );
/* Perform retrieval operation */
return( pctags_main( pctags_function_name ) );
} /* pctags_prompt() */
/***************************************************************************/
/* The following functions are valid under MS-DOS only */
#ifdef MSDOS
/***************************************
* Macro : find_first
* Syntax: [static] char *find_first( char *file_spec )
* Entry : file_spec is a DOS file specification. It may contain a drive, path
* and/or wildcard characters.
* Exit : Returns the first matching file name to file_spec or empty string if
* no match was found.
* Use of Global Variables:
* pctags_file_info -- access and assign
*/
char *
find_first( file_spec )
char *file_spec;
{
#define NORMAL_FILES 0 /* File attributes */
int dta_segment, dta_offset; /* Global DTA */
EEL_PTR *fspec_ptr;
char *file_info_ptr;
/* Save current DTA address */
get_dta( &dta_segment, &dta_offset );
/* Point DTA to our data structure */
#if V40x
set_dta( get_pointer( &pctags_file_info, SEGMENT ),
get_pointer( &pctags_file_info, !SEGMENT ) );
#endif
#if V32x
file_info_ptr = (char *)&pctags_file_info;
fspec_ptr = (EEL_PTR *)&file_info_ptr; /* Convert long pointer to components */
set_dta( fspec_ptr->value.hiword, fspec_ptr->value.loword );
#endif
/* Use LOWLEVEL.H register variable to call DOS function */
m_regs.b.ah = 0x4E; /* Find-first function */
m_regs.w.cx = NORMAL_FILES; /* Match normal files, i.e. no dirs */
#if V40x
m_regs.w.ds = get_pointer( file_spec, SEGMENT );
m_regs.w.dx = get_pointer( file_spec, !SEGMENT );
#endif
#if V32x
fspec_ptr = (EEL_PTR *)&file_spec; /* Convert long pointer to components */
m_regs.w.ds = fspec_ptr->value.hiword; /* DS=pointer's segment */
m_regs.w.dx = fspec_ptr->value.loword; /* DS:DX->file spec */
#endif
do_interrupt( DOS_SERVICES, &m_regs ); /* Do it! */
/* Check carry flag for successful match */
if( m_regs.w.flags & CARRYFLAG ){
/* Check for bad file spec */
if( m_regs.w.ax == 0x12 ){
/* User should change this spec */
maybe_ding();
say( "Bad tagfile spec: %s", file_spec );
}
/* Set filename to empty string */
*(pctags_file_info.filename) = '\0';
}
/* Reset DTA */
set_dta( dta_segment, dta_offset );
/* Return pointer to filename match */
return( pctags_file_info.filename );
} /* find_first() */
/***************************************
* Macro : find_next
* Syntax: [static] char *find_next( void );
* Entry : pctags_file_info data structure must be filled from a previous call
* to find_first().
* Exit : Returns the next matching filename or empty string if no match was
* found.
* Use of Global Variables:
* pctags_file_info -- access and assign
*/
char *
find_next()
{
int dta_segment, dta_offset; /* Global DTA */
EEL_PTR *fspec_ptr;
char *file_info_ptr;
/* Get current DTA */
get_dta( &dta_segment, &dta_offset );
/* Point DTA to our data structure */
#if V40x
set_dta( get_pointer( &pctags_file_info, SEGMENT ),
get_pointer( &pctags_file_info, !SEGMENT ) );
#endif
#if V32x
file_info_ptr = (char *)&pctags_file_info;
fspec_ptr = (EEL_PTR *)&file_info_ptr; /* Convert long pointer to components */
set_dta( fspec_ptr->value.hiword, fspec_ptr->value.loword );
#endif
/* Use LOWLEVEL.H register variable to call DOS function */
m_regs.b.ah = 0x4F; /* Find-next function */
do_interrupt( DOS_SERVICES, &m_regs ); /* DTA is filled */
/* Check if a match was found */
if( m_regs.w.flags & CARRYFLAG ){
/* No match, clear filename string */
*(pctags_file_info.filename) = '\0';
}
/* Reset DTA */
set_dta( dta_segment, dta_offset );
/* Return pointer to matching filename */
return( pctags_file_info.filename );
} /* find_next() */
/***************************************
* Macro : get_dta
* Syntax: [static][void] get_dta( int *dta_segment, int *dta_offset )
* Entry : dta_segment is a pointer to storage for the current DTA segment
* address.
* dta_offset is a pointer to storage for the DTA offset address.
* Exit : dta_segment and dta_offset are appropriately set
*/
get_dta( dta_segment, dta_offset )
int *dta_segment;
int *dta_offset;
{
/* Use EEL.H register variable to call DOS function */
m_regs.b.ah = 0x2F; /* Get DTA address */
do_interrupt( DOS_SERVICES, &m_regs ); /* ES:BX->DTA */
/* Save DTA address in argument variables */
*dta_segment = m_regs.w.es;
*dta_offset = m_regs.w.bx;
} /* get_dta() */
/***************************************
* Macro : set_dta
* Syntax: [static][void] set_dta( int dta_segment, int dta_offset )
* Entry : dta_segment is the segment address at which to set the DTA.
* dta_offset is the offset address at which to set the DTA address.
* Exit : The DTA address is set accordingly.
*/
set_dta( dta_segment, dta_offset )
int dta_segment;
int dta_offset;
{
/* Use LOWLEVEL.H register variable to call DOS function */
m_regs.b.ah = 0x1A; /* Set DTA address */
m_regs.w.ds = dta_segment;
m_regs.w.dx = dta_offset; /* DS:DX->DTA address */
do_interrupt( DOS_SERVICES, &m_regs ); /* That's it */
} /* set_dta() */
/***************************************/
#endif /* MSDOS */
/*****************************************************************************/
/*
* Macro : delimiter
* Syntax: [static] int delimiter( char ch )
* Entry : ch = character to check as a delimiter
* Exit : Non-zero if character is a delimiter, else zero.
* Note : Character is considered a delimiter if it is not an alphanumeric
* or underscore or dot (for Turbo Pascal 5.5 only) character.
*/
int
delimiter( ch )
char ch;
{
/* Return values */
#define DELIMITER 1
#define NOT_DELIMITER 0
/* ASCII values */
#define DOT 0x2E
#define UNDERSCORE 0x5F
/* Check character as delimiter */
if( isalpha( ch ) || isdigit( ch ) || ch == UNDERSCORE || ch == DOT ){
/* Char is not a delimiter */
return( NOT_DELIMITER );
}
/* Char is a delimiter */
return( DELIMITER );
} /* delimiter() */
/***************************************
* Macro : atoi
* Syntax: [static] [int] atoi( char *string )
* Entry : string is an ASCIIZ character string
* Exit : All contiguous digits in string are converted to a positive, base-10
* integer.
*/
int
atoi( string )
char *string;
{
int num;
char *ch;
/* Cycle thru characters until a non-digit is found */
/* Note: No check is made for numeric overflow; it is assumed that
* source files will not exceed 32,767 lines in length.
*/
for( ch = string, num = 0; isdigit( *ch ); ++ch ){
/* Accumulate into converted value */
num = (num * 10) + (*ch - '0');
}
/* Return converted number */
return( num );
} /* atoi() */
/***************************************
* Macro : strlwr
* Syntax: [static] [void] strlwr( char *string )
* Entry : string is an ASCIIZ character string
* Exit : All uppercase characters in string are converted to lowercase.
*/
strlwr( string )
char *string;
{
/* Cycle through each character in string, converting if necessary */
while( *string ){
*string = tolower( *string );
++string;
}
} /* strlwr() */
/***************************************
* Macro : dos_shell
* Syntax: [static][void] dos_shell( char *command_line char *file_spec )
* Entry : Command_line is the DOS command line to have the shell 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 (back)slash)
* %f - filename of desired file
* %c - current directory (in drive:path format suitable for CD'ing)
* %u - user-defined substitution string
* %% - single %
*/
dos_shell( command_line, file_spec )
char *command_line;
char *file_spec; /* Absolute file spec, %s special parm */
{
int path_length;
char *cptr;
char drive[ 3 ];
char path[ 65 ];
char fname[ 13 ];
char curr_dir[ 80 ];
char sub_str[ 129 ];
char workspace[ 129 ];
/* If command_line is bracketed by double quotes, remove them */
if( *command_line == '"'
&&
command_line[ strlen( command_line ) - 1 ] == '"' ){
/* Remove double quotes */
command_line[ strlen( command_line ) - 1 ] = '\0';
strcpy( command_line, &command_line[ 1 ] );
}
/* Create special parameter strings */
/* Get drive:path */
cptr = rindex( file_spec, file_spec[ 2 ] ); /* cptr->last path separator */
path_length = cptr - file_spec - 1; /* Don't count drive spec(2) in length */
/* If not in root, don't count last "/" or "\" */
/* Note: PCTAGS.EXE will always write backslashes in path specs */
if( path_length > 1 ){
--path_length;
}
/* Break drive:path into separate strings */
strncpy( drive, file_spec, 2 ); /* %d special parm */
drive[ 2 ] = '\0';
strncpy( path, &file_spec[ 2 ], path_length ); /* %p special parm */
path[ path_length ] = '\0';
/* Get filename only */
strcpy( fname, ++cptr ); /* %f special parm */
/* Get current directory */
getcd( curr_dir ); /* %c special parm */
/* Parse command line, replacing special parameters */
cptr = command_line;
while( (cptr = index( cptr, '%' )) != NULL ){
/* Break line at '%' */
*cptr = '\0';
/* Interpret substitution character */
switch( *++cptr ){
case 's' : /* Entire file specification of desired file */
strcpy( sub_str, file_spec );
break;
case 'd' : /* Drive of desired file */
strcpy( sub_str, drive );
break;
case 'p' : /* Path (no drive) of desired file */
strcpy( sub_str, path );
break;
case 'f' : /* Filename of desired file */
strcpy( sub_str, fname );
break;
case 'c' : /* Current directory */
strcpy( sub_str, curr_dir );
break;
case 'u' : /* User-defined substitution string */
get_string( sub_str, "Enter substitution string: " );
break;
case '%' : /* Single percent sign */
strcpy( sub_str, "%" );
break;
default : /* Unrecognized, replace with empty string */
*sub_str = '\0';
break;
}
/* Replace special parameter with substitution string */
strcpy( workspace, command_line ); /* Portion before '%' */
strcat( workspace, sub_str ); /* Substitution string */
strcat( workspace, cptr + 1 ); /* Portion after "%x" */
strcpy( command_line, workspace ); /* Copy back into original */
}
/* Let user know what's going on */
say( "Executing RETROEXEC command" );
/* Send command line to secondary DOS command processor for execution */
#if V40x
shell( "", command_line, "" );
#endif
#if V32x
shell( "", command_line );
#endif
} /* dos_shell() */
/***************************************
* Macro : get_tagfile
* Syntax: [static][void] get_tagfile( char *file_spec )
* Entry : file_spec is a return parameter. Its value upon entry is not used.
* Exit : file_spec contains tagfile name or empty string if no tagfiles left to
* search.
*
* Use of Global Variables:
* pctags_first_call -- access and assign
* pctags_env_tagfiles -- access only
* pctags_env_path -- access only
*/
get_tagfile( file_spec )
char *file_spec;
{
char env_tagfiles[ 128 ];
char *single_tagfile;
/* If this is not the first call to this macro in this RETRO
* invocation, get the next matching filename.
*/
if( !pctags_first_call ){
/* Get next matching file name for file_spec */
single_tagfile = find_next();
}else{
/* Go thru this code once per pctags invocation */
pctags_first_call = FALSE;
/* Get RETRO environment variable */
if( (single_tagfile = getenv( "RETRO" )) == NULL ){
/* No environment variable defined, use default */
strcpy( env_tagfiles, "*.tag" );
}else{
/* Copy environment variable value into workspace */
strcpy( env_tagfiles, single_tagfile );
/* Convert to lowercase */
strlwr( env_tagfiles );
}
/* Parse any multiple file specs into single specs (file_spec
* may contain wildcards).
*/
parse_file_spec( env_tagfiles, file_spec );
/* Get first matching file name for file_spec */
single_tagfile = find_first( file_spec );
}
/* Check if we got a tagfile name */
while( *single_tagfile == '\0' ){
/* Are there more file specs in the
* pctags_env_tagfiles variable?
*/
if( *pctags_env_tagfiles == '\0' ){
/* Nope, this is the end of the road */
break; /* Out of the while-loop */
}
/* There are more tagfile specs. Process them. */
parse_file_spec( pctags_env_tagfiles, file_spec );
/* Get first matching file name for tagfile spec */
single_tagfile = find_first( file_spec );
}
/* If filename found, convert to lowercase and insert any drive:path */
if( *single_tagfile == '\0' ){
/* Return empty filename */
*file_spec = '\0';
}else{
/* Convert to lowercase */
strlwr( single_tagfile );
/* Insert drive:path */
strcpy( file_spec, pctags_env_path );
strcat( file_spec, single_tagfile );
/* Make sure tagfile is an absolute spec */
absolute( file_spec );
}
} /* get_tagfile() */
/***************************************
* Macro : parse_file_spec
* Syntax: [static] [void] parse_file_spec( char *fspec, char *file_spec )
* Entry : fspec = file specification to parse. May contain wildcards. May
* also contain multiple specs, each separated by a semicolon. For
* example, "c:\*.tag;d:\epsilon\tagfiles\*.tag;c:\pctags.tag".
* file_spec is an output parameter. A single parsed file spec will be
* stored in it.
* Exit : Any subsequent file specs are stored in pctags_env_tagfiles.
* The first file spec in the RETRO environment variable or
* pctags_env_tagfiles is returned in file_spec (may contain wildcards).
* Any drive:path in the first file spec is copied into pctags_env_path
* for later insertion before matched filename.
* Use of Global Variables:
* pctags_env_tagfiles -- assign
* pctags_env_path -- assign
*/
parse_file_spec( fspec, file_spec )
char *fspec; /* Input parameter */
char *file_spec; /* Output parameter */
{
char *semicolon;
char *last_slash;
char *last_path_separator;
/* Copy argument into return parameter for modification */
strcpy( file_spec, fspec );
/* Does file_spec contain multiple specs? */
if( (semicolon = index( file_spec, ';' )) == NULL ){
/* No, file_spec contains one drive:path.
* Clear pctags_env_tagfiles.
*/
*pctags_env_tagfiles = '\0';
}else{
/* Yes, separate single spec from subsequent specs */
*semicolon = '\0'; /* file_spec now is single spec */
/* Store subsequent specs in pctags_env_tagfiles */
strcpy( pctags_env_tagfiles, ++semicolon );
}
/* Does file_spec contain a drive or path spec? */
/* Find index to last path separator (support slashes and backslashes) */
last_path_separator = rindex( file_spec, '\\' );
last_slash = rindex( file_spec, '/' );
if( last_slash > last_path_separator ){
/* Point to last slash */
last_path_separator = last_slash;
}
/* If no path was specified, check for a drive */
if( last_path_separator == NULL ){
last_path_separator = rindex( file_spec, ':' );
}
/* If drive and/or path were specified, copy into pctags_env_path */
if( last_path_separator == NULL ){
/* No drive:path specified, clear pctags_env_path */
*pctags_env_path = '\0';
}else{
/* Copy drive:path */
strncpy( pctags_env_path, file_spec,
(last_path_separator - file_spec + 1) );
}
} /* parse_file_spec() */
/***************************************
* Macro : file_loaded
* Syntax: [static] int file_loaded( char *file_spec )
* Entry : file_spec is checked to see if it is already loaded into a buffer.
* Exit : TRUE if file_spec is already loaded, else FALSE.
*/
int
file_loaded( file_spec )
char *file_spec;
{
char buff_name[ FNAMELEN + 10 ];
char *buff_str;
char *org_buffname;
/* Save current buffer */
org_buffname = bufname;
/* Get first name in buffer list */
buff_str = buffer_list( 1 );
/* Cycle through buffer list searching for a matching filename */
do{
/* Set current buffer */
bufname = buff_str;
/* Compare filename associated with this buffer to our filename */
if( !strcmp( filename, file_spec ) ){
/* Found a match, file is already loaded */
/* Restore current buffer */
bufname = org_buffname;
return( TRUE );
}
}while( buff_str = buffer_list( 0 ) );
/* File is not currently loaded */
bufname = org_buffname; /* Restore current buffer */
return( FALSE );
} /* file_loaded() */
/***************************************
* Macro : position_line_within_window
* Syntax: [static] [void] position_line_within_window( void )
* Entry : None.
* Exit : If current window is at least five lines high, the current line
* is positioned on the third line in the window. If the window
* is less than five lines high, no change.
*/
position_line_within_window()
{
int saved_point;
/* If possible, position current line to third in window. Window
* must be at least five lines high to do so.
*/
if( window_size >= 5 ){
/* Save current position */
saved_point = point;
/* Back up two lines */
nl_reverse(); to_begin_line();
nl_reverse(); to_begin_line();
/* Set start of the window here */
window_start = point;
/* Restore point to definition line */
point = saved_point;
}
} /* position_line_within_window() */
/***************************************
* Macro : pctags_main
* Syntax: [static] int pctags_main( char *function_name )
* Entry : function_name = function name to locate and retrieve the file in
* which it is declared.
* Exit : Non-zero value if function definition is found, file containing the
* definition is loaded and cursor is placed on the definition's
* line.
* Else return zero.
* 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:\epsilon\tagfiles\*.tag;c:\prog\*.tag
* -- Search each tagfile for the 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 information contains an "EXEC"
* command and environment varible RETROEXEC is defined,
* execute the command line value assigned to it. Try to
* load the file again.
* -- 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_tagname_case -- access
*/
int
pctags_main( function_name )
char *function_name;
{
#define STRIP_CR 1 /* Remove CR of CR-LF pair when file_read() */
#define SEARCH_FORWARD 1
#define SEARCH_BACKWARD (-1)
int found_match;
int org_case_fold;
int error_code;
int continue_search;
int exec_env;
int line_number;
int saved_position;
int already_loaded;
char *org_buff_id;
char *buff_id;
char *file_start;
char *decl_start;
char *env_var;
char file_spec[ 80 ];
char tagfile_name[ 80 ];
/* Save current buffer context */
org_case_fold = case_fold; /* Search case sensitivity */
org_buff_id = bufname; /* Current buffer */
/* Set case sensitivity for tagname search */
case_fold = pctags_tagname_case; /* User-settable in when_loading() */
/* Create unique tagfile buffer and make it the current buffer */
buff_id = temp_buf();
bufname = buff_id;
/* Initialize flags */
found_match = FALSE;
pctags_first_call = TRUE; /* Global flag for get_tagfile() */
/* Cycle thru all RETRO (or default) tagfiles until done or match found */
while( !found_match ){
/* Get a tagfile */
get_tagfile( tagfile_name );
/* If no more tagfiles to get, quit */
if( *tagfile_name == '\0' ){
/* Reset current buffer and delete temporary */
bufname = org_buff_id;
delete_buffer( buff_id );
/* Exit while-loop */
break;
}
/* Set buffer-specific filename */
filename = tagfile_name;
/* Read tagfile contents into current buffer */
if( (error_code = file_read( tagfile_name, STRIP_CR )) != 0 ){
/* Error reading file */
file_error( error_code, tagfile_name, "Unrecognized error" );
continue; /* Try another tagfile */
}
/* Set up a loop to search through 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
* a line.
*/
/* Search tagfile for function name */
say( "Searching %s", tagfile_name );
continue_search = TRUE;
point = 0; /* Force to start of file */
while( continue_search ){
/* Search tagfile for function name */
if( !search( SEARCH_FORWARD, function_name ) ){
/* Did not find function name in this tagfile */
/* Stop searching this file, try another */
continue_search = FALSE;
continue;
}
/* 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.
*/
/* Set point to beginning of match */
point = matchstart;
if( current_column() != 0 ){
/* Not a valid match */
/* Advance buffer position so we don't
* keep finding the same match
*/
++point;
/* Continue searching tagfile */
continue;
}
/* 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 */
nl_forward(); /* Move point to end of line */
grab( matchstart, point, pctags_line );
pctags_line[ point - matchstart ] = '\0'; /* Terminate line */
/* Done with tagfile, close it */
bufname = org_buff_id; /* Reset to original buffer */
delete_buffer( buff_id ); /* Delete our temporary buffer */
/* Format of line:
* Column 1: Function name searching for (variable length)
* Single space terminator
* Optional RETRO commands (! and #)
* 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 = index( pctags_line, ' ' );
++file_start;
decl_start = index( file_start, ' ' );
*decl_start = '\0'; /* Terminate file spec */
decl_start += 2; /* Move decl_start past carot */
/* Extract file spec and decl line */
strcpy( file_spec, file_start );
strcpy( pctags_decl_line, decl_start );
/* Check file_spec for special leading characters */
continue_search = TRUE;
exec_env = FALSE;
line_number = FALSE;
file_start = file_spec;
while( continue_search ){
switch( *file_start ){
case '!' : /* 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;
/* Advance actual start of
* file_spec
*/
++file_start;
break;
case '#' : /* Line number of definition is
* stored in tagfile instead of line
* contents.
*/
line_number = TRUE;
++file_start; /* Advance past char */
break;
default : /* Stop parsing special characters */
continue_search = FALSE;
break;
}
}
/* Verify that file_spec exists */
if( check_file( file_start, (struct file_info *)NULL ) == 0 ){
/* If exec_env active, get RETROEXEC value */
if( exec_env ){
env_var = getenv( "RETROEXEC" );
/* If variable found, copy into workspace */
if( env_var != NULL ){
strcpy( pctags_env_pctagsexec,
env_var );
/* Execute the variable's value */
dos_shell( pctags_env_pctagsexec,
file_start );
}
}
/* Check for file's existence now */
if( check_file( file_start, (struct file_info *)NULL ) == 0 ){
/* Still not found */
maybe_ding();
say( "%s does not exist. Update tagfile.",
file_start );
/* Can't go any further, restore context
* and quit
*/
case_fold = org_case_fold;
return( UNSUCCESSFUL );
}
}
/* See if file is already loaded */
already_loaded = file_loaded( file_start );
/* Load file into buffer (if necessary) and make it
* the current buffer
*/
find_it( file_start );
/* Save current position in file, in case
* decl-file was already loaded and the
* definition is not in it (i.e. the
* tagfile is not up-to-date), in which
* case we'll want to restore the original
* file position.
*/
saved_position = point;
/* Move to top of file, in case file was
* already in buffer and desired function
* is BEFORE current position.
*/
point = 0;
/* Searching for line contents or line number? */
if( line_number == TRUE ){
/* Goto specified line number in source file */
go_line( atoi( pctags_decl_line ) );
/* Position line */
position_line_within_window();
/* Display filename */
say( "File: %s", file_start );
}else{
/* Search for definition line */
case_fold = CASE_SENSITIVE;
while( TRUE ){
if( search( SEARCH_FORWARD, pctags_decl_line ) ){
/* Found a match */
/* Note: Match must be at start of line
* to be valid. If not at start, it
* could be a "forward" declaration.
*/
/* Move point to start of line */
point = matchstart;
/* Check if match is at start of line */
if( current_column() != 0 ){
/* Invalid match, keep searching */
/* Advance buffer position so we
* won't keep finding the same
* match.
*/
++point;
/* Continue searching source file */
continue;
}
/* Found function definition! */
/* Position the line in the current window */
position_line_within_window();
/* Display filename */
say( "File: %s", file_start );
/* Break out of while-loop */
break;
}else{
/* Definition line not found in file */
maybe_ding();
say( "Update %s with %s", tagfile_name,
file_start );
/* Recovery */
/* If file was loaded from disk, dump it */
if( !already_loaded ){
/* Drop it and restore original buffer */
buff_id = bufname; /* Save current buffer id */
bufname = org_buff_id; /* Reset current buff to org */
drop_buffer( buff_id ); /* Dump file buffer */
}else{
/* Restore original position in buffer */
point = saved_position;
}
/* Return error code */
case_fold = org_case_fold;
return( UNSUCCESSFUL );
} /* if decl-line match found in file */
} /* while searching for definition line in source file */
} /* if going to specified line number or looking for line contents */
} /* while searching for function name in tagfile */
} /* while cycling through tagfiles searching for function name */
/* Restore original editor context */
case_fold = org_case_fold;
/* If match not found in any tagfile, that's not good... */
if( found_match ){
/* Congratulations */
return( SUCCESSFUL );
}else{
/* Display message */
/* Note: function_name ends with a single blank char */
say( "%snot found.", function_name );
return( UNSUCCESSFUL );
}
} /* pctags_main() */
/****************************************/