home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / PCTAG.ZIP / RETROMAC.COM / RETRO.E < prev    next >
Text File  |  1989-10-01  |  49KB  |  1,224 lines

  1. /*
  2.  * ------------------------------------------------
  3.  * PC-TAGS(tm) RETRO Program for the Epsilon Editor
  4.  * ------------------------------------------------
  5.  *
  6.  * EPSILON Macros to Search a PC-TAGS-Generated Tagfile and Retrieve a
  7.  * Function Definition
  8.  *
  9.  * Copyright (C) 1989 by Moderne Software.  All rights reserved.
  10.  * Moderne Software, P.O. Box 3638, Santa Clara, CA 95055-3638
  11.  *
  12.  * Limited permission is given to registered PC-TAGS users to modify this
  13.  * file for their own personal use only.  This file may not be used for any
  14.  * purpose other than in conjunction with the PC-TAGS software package.
  15.  *
  16.  * This macro will run with Epsilon versions 3.2x thru 4.0x.  By default, it
  17.  * will only work with version 4.0x.  To compile and use with Epsilon
  18.  * version 3.2x, set the constant "V40x" to FALSE and "V32x" to TRUE.  These
  19.  * constants are defined below.
  20.  *
  21.  * Entry Points
  22.  * ------------
  23.  *       when_loading        -- declare global variables
  24.  *       pctags_auto         -- uses word under cursor for search
  25.  *       pctags_prompt       -- prompts for a word to use in search
  26.  *
  27.  * Support macros
  28.  * --------------
  29.  *       delimiter           -- determines if character is a word delimiter
  30.  *       atoi                -- convert ASCII characters to integer
  31.  *       strlwr              -- convert an ASCIIZ string to lowercase
  32.  *       dos_shell           -- execute RETROEXEC environment value thru DOS
  33.  *       pctags_main         -- search and retrieval workhorse code
  34.  *       get_tagfile         -- retrieve the next tagfile to search
  35.  *       parse_file_spec     -- break multiple tagfile specifications into
  36.  *                               individual directory/filename parts
  37.  *       position_line_within_window -- position current line to third line in
  38.  *                               current window, if possible
  39.  *       file_loaded         -- determine whether given file is currently loaded
  40.  *       find_first          -- DOS file-directory function
  41.  *       find_next           -- DOS file-directory function
  42.  *       get_dta             -- DOS retrieve disk transfer address (DTA)
  43.  *       set_dta             -- DOS set disk transfer address (DTA)
  44.  *
  45.  * History
  46.  * -------
  47.  *  1 Oct 89 -- Version 1.00
  48.  *
  49.  */
  50.  
  51. /****************************************/
  52. /* Global constants */
  53.  
  54. #include "eel.h"                        /* Epsilon standard definitions */
  55. #include "lowlevel.h"                   /* Epsilon low-level data types */
  56.  
  57. #define TRUE                    1
  58. #define FALSE                   0
  59.  
  60. /* Epsilon Version */
  61. /* **************************** IMPORTANT!!! *******************************/
  62. /* Only ONE of the version constants can be set to a value of "TRUE".  All
  63.  * others must be set to "FALSE".
  64.  *
  65.  * If this macro will be used with Epsilon 4.00 or above, the "V40x" constant
  66.  * should be set to TRUE (this is the default).
  67.  * If using Epsilon 3.20-3.23, set the constant "V32x" to TRUE.
  68.  *
  69.  * BE SURE ONLY ONE OF THE VERSION CONSTANTS IS TRUE AND ALL OTHERS ARE FALSE!!!
  70.  *
  71.  */
  72. #define V40x                    TRUE    /* Versions 4.00 and above */
  73. #define V32x                    FALSE   /* Versions 3.20-3.23      */
  74.  
  75. /* Return values */
  76. #define SUCCESSFUL              TRUE
  77. #define UNSUCCESSFUL            FALSE
  78.  
  79. #define CASE_SENSITIVE          0       /* String searches are case sensitive */
  80. #define CASE_INSENSITIVE        1       /* Searches are case insensitive      */
  81.     
  82. /****************************************/
  83. /* Global data */
  84.  
  85.  
  86.         /* You can set the pctags_tagname_case variable to CASE_INSENSITIVE if
  87.          * you want the search for a matching tagname to ignore case.
  88.          */
  89.         int     pctags_tagname_case = CASE_SENSITIVE;
  90.  
  91.         char    pctags_function_name[ 80 ];     /* Function to retrieve          */
  92.         char    pctags_env_tagfiles[ 125 ];     /* RETRO env var value           */
  93.         char    pctags_env_path[ 80 ];          /* Drive:path of current tagfile */
  94.         int     pctags_first_call;              /* Flag used by get_tagfile()    */
  95.  
  96.         struct {
  97.                 char    ignored_data[ 30 ];     /* File info                     */
  98.                 char    filename[ 13 ];         /* ASCIIZ filename               */
  99.         } pctags_file_info;
  100.  
  101. /* Global data that should be local to pctags_main(), but Epsilon does not
  102.  * support such large local data allocations.
  103.  */
  104.         char    pctags_decl_line[ 256 ];
  105.         char    pctags_line[ 512 ];
  106.         char    pctags_env_pctagsexec[ 129 ];
  107.  
  108. /***************************************
  109. * Macro : when_loading
  110. * Syntax: None, automatically executed when loaded, then discarded
  111. * Entry : None.
  112. * Exit  : Any necessary RETRO initialization is performed.
  113. * Notes : This macro is called by EPSILON automatically upon loading.
  114. */
  115.  
  116. when_loading()
  117. {
  118.  
  119.         /* Nothing so far... */
  120.  
  121. }  /* when_loading() */
  122.  
  123. /***************************************
  124. * Macro : pctags_auto
  125. * Syntax: command int pctags_auto( void )
  126. * Entry : None, will extract the word under the current cursor position.
  127. * Exit  : Non-zero if entire retrieval operation is successfully completed.
  128. *         Zero if operation is not successfully completed.
  129. * Description :
  130. *         Function under cursor is extracted from line and used to search thru
  131. *           the tagfile(s).  If a match is found, the file containing the
  132. *           function's definition is loaded and the cursor is placed at the
  133. *           start of the definition.
  134. * Use of Global Variables:
  135. *       pctags_function_name    -- assign
  136. */
  137.  
  138. command
  139. int
  140. pctags_auto()
  141. {
  142.  
  143.         char    ch;
  144.         int     start;
  145.         int     end;
  146.  
  147.  
  148.         /* Make sure cursor is currently under a valid word */
  149.         if( delimiter( curchar() ) ){
  150.                 /* Not under a word */
  151.                 maybe_ding();
  152.                 say( "Cursor is not under a valid word" );
  153.                 return( UNSUCCESSFUL );
  154.         }
  155.  
  156.         /* Advance to end of this word */
  157.         end = point;
  158.         while( (++end < size()) && (!delimiter( character( end ) )) )
  159.                 ;
  160.  
  161.         /* Backtrack to start of word */
  162.         start = point;
  163.         while( (start != 0) && (!delimiter( character( --start ) )) )
  164.                 ;
  165.  
  166.         /* Adjust start to first char in word */
  167.         if( (start != 0) || (delimiter( character( start ) )) ){
  168.                 ++start;
  169.         }
  170.  
  171.         /* Copy word into our storage space */
  172.         grab( start, end, pctags_function_name );
  173.  
  174.         /* Append a blank to end of word */
  175.         end = end - start;      /* end = index immediately after last char */
  176.         pctags_function_name[ end ]     = ' ';
  177.         pctags_function_name[ end + 1 ] = '\0';
  178.  
  179.         /* Perform retrieval operation */
  180.         return( pctags_main( pctags_function_name ) );
  181.  
  182. }  /* pctags_auto() */
  183.  
  184. /***************************************
  185. * Macro : pctags_prompt
  186. * Syntax: command int pctags_prompt( void )
  187. * Entry : None, will prompt for function to retrieve.
  188. * Exit  : Non-zero if specified function is found, file containing function
  189. *           definition is loaded and cursor is placed at beginning of
  190. *           definition.
  191. *         Zero if specified function is not found.
  192. * Description :
  193. *       User is prompted for the name of a function.  The macro loads the file
  194. *       containing the function definition and places the cursor on the
  195. *       definition's line.
  196. * Use of Global Variables:
  197. *       pctags_function_name    -- assign
  198. */
  199.  
  200. command
  201. int
  202. pctags_prompt()
  203. {
  204.  
  205.  
  206.         /* Prompt the user for a function name */
  207.         get_string( pctags_function_name, "Enter tagname: " );
  208.  
  209.         /* If empty string was entered, abort */
  210.         if( strlen( pctags_function_name ) == 0 ){
  211.                 /* Just return */
  212.                 return( UNSUCCESSFUL );
  213.         }
  214.  
  215.         /* Append a blank to the function name */
  216.         strcat( pctags_function_name, " " );
  217.  
  218.         /* Perform retrieval operation */
  219.         return( pctags_main( pctags_function_name ) );
  220.  
  221. }  /* pctags_prompt() */
  222.  
  223. /***************************************************************************/
  224. /* The following functions are valid under MS-DOS only */
  225.  
  226. #ifdef MSDOS
  227.  
  228. /***************************************
  229. * Macro : find_first
  230. * Syntax: [static] char *find_first( char *file_spec )
  231. * Entry : file_spec is a DOS file specification.  It may contain a drive, path
  232. *           and/or wildcard characters.
  233. * Exit  : Returns the first matching file name to file_spec or empty string if
  234. *           no match was found.
  235. * Use of Global Variables:
  236. *       pctags_file_info        -- access and assign
  237. */
  238.  
  239. char *
  240. find_first( file_spec )
  241.   char  *file_spec;
  242. {
  243.  
  244. #define NORMAL_FILES    0                       /* File attributes */
  245.  
  246.         int     dta_segment, dta_offset;        /* Global DTA */
  247.         EEL_PTR *fspec_ptr;
  248.         char    *file_info_ptr;
  249.  
  250.  
  251.         /* Save current DTA address */
  252.         get_dta( &dta_segment, &dta_offset );
  253.  
  254.         /* Point DTA to our data structure */
  255. #if V40x
  256.         set_dta( get_pointer( &pctags_file_info, SEGMENT ),
  257.                  get_pointer( &pctags_file_info, !SEGMENT ) );
  258. #endif
  259. #if V32x
  260.         file_info_ptr = (char *)&pctags_file_info;
  261.         fspec_ptr = (EEL_PTR *)&file_info_ptr;  /* Convert long pointer to components */
  262.         set_dta( fspec_ptr->value.hiword, fspec_ptr->value.loword );
  263. #endif
  264.  
  265.         /* Use LOWLEVEL.H register variable to call DOS function */
  266.         m_regs.b.ah = 0x4E;                     /* Find-first function */
  267.         m_regs.w.cx = NORMAL_FILES;             /* Match normal files, i.e. no dirs */
  268.  
  269. #if V40x
  270.         m_regs.w.ds = get_pointer( file_spec, SEGMENT );
  271.         m_regs.w.dx = get_pointer( file_spec, !SEGMENT );
  272. #endif
  273. #if V32x
  274.         fspec_ptr = (EEL_PTR *)&file_spec;      /* Convert long pointer to components */
  275.         m_regs.w.ds = fspec_ptr->value.hiword;  /* DS=pointer's segment */
  276.         m_regs.w.dx = fspec_ptr->value.loword;  /* DS:DX->file spec */
  277. #endif
  278.  
  279.         do_interrupt( DOS_SERVICES, &m_regs );  /* Do it! */
  280.  
  281.         /* Check carry flag for successful match */
  282.         if( m_regs.w.flags & CARRYFLAG ){
  283.                 /* Check for bad file spec */
  284.                 if( m_regs.w.ax == 0x12 ){
  285.                         /* User should change this spec */
  286.                         maybe_ding();
  287.                         say( "Bad tagfile spec: %s", file_spec );
  288.                 }
  289.  
  290.                 /* Set filename to empty string */
  291.                 *(pctags_file_info.filename) = '\0';
  292.         }
  293.  
  294.         /* Reset DTA */
  295.         set_dta( dta_segment, dta_offset );
  296.  
  297.         /* Return pointer to filename match */
  298.         return( pctags_file_info.filename );
  299.  
  300. }  /* find_first() */
  301.  
  302. /***************************************
  303. * Macro : find_next
  304. * Syntax: [static] char *find_next( void );
  305. * Entry : pctags_file_info data structure must be filled from a previous call
  306. *            to find_first().
  307. * Exit  : Returns the next matching filename or empty string if no match was
  308. *            found.
  309. * Use of Global Variables:
  310. *       pctags_file_info        -- access and assign
  311. */
  312.  
  313. char *
  314. find_next()
  315. {
  316.  
  317.         int     dta_segment, dta_offset;        /* Global DTA */
  318.         EEL_PTR *fspec_ptr;
  319.         char    *file_info_ptr;
  320.  
  321.  
  322.         /* Get current DTA */
  323.         get_dta( &dta_segment, &dta_offset );
  324.  
  325.         /* Point DTA to our data structure */
  326. #if V40x
  327.         set_dta( get_pointer( &pctags_file_info, SEGMENT ),
  328.                  get_pointer( &pctags_file_info, !SEGMENT ) );
  329. #endif
  330. #if V32x
  331.         file_info_ptr = (char *)&pctags_file_info;
  332.         fspec_ptr = (EEL_PTR *)&file_info_ptr;  /* Convert long pointer to components */
  333.         set_dta( fspec_ptr->value.hiword, fspec_ptr->value.loword );
  334. #endif
  335.  
  336.         /* Use LOWLEVEL.H register variable to call DOS function */
  337.         m_regs.b.ah = 0x4F;                     /* Find-next function */
  338.         do_interrupt( DOS_SERVICES, &m_regs );  /* DTA is filled */
  339.  
  340.         /* Check if a match was found */
  341.         if( m_regs.w.flags & CARRYFLAG ){
  342.                 /* No match, clear filename string */
  343.                 *(pctags_file_info.filename) = '\0';
  344.         }
  345.  
  346.         /* Reset DTA */
  347.         set_dta( dta_segment, dta_offset );
  348.  
  349.         /* Return pointer to matching filename */
  350.         return( pctags_file_info.filename );
  351.  
  352. }  /* find_next() */
  353.  
  354. /***************************************
  355. * Macro : get_dta
  356. * Syntax: [static][void] get_dta( int *dta_segment, int *dta_offset )
  357. * Entry : dta_segment is a pointer to storage for the current DTA segment
  358. *            address.
  359. *         dta_offset is a pointer to storage for the DTA offset address.
  360. * Exit  : dta_segment and dta_offset are appropriately set
  361. */
  362.  
  363. get_dta( dta_segment, dta_offset )
  364.   int   *dta_segment;
  365.   int   *dta_offset;
  366. {
  367.  
  368.         /* Use EEL.H register variable to call DOS function */
  369.         m_regs.b.ah = 0x2F;                     /* Get DTA address */
  370.         do_interrupt( DOS_SERVICES, &m_regs );  /* ES:BX->DTA */
  371.  
  372.         /* Save DTA address in argument variables */
  373.         *dta_segment = m_regs.w.es;
  374.         *dta_offset  = m_regs.w.bx;
  375.  
  376. }  /* get_dta() */
  377.  
  378. /***************************************
  379. * Macro : set_dta
  380. * Syntax: [static][void] set_dta( int dta_segment, int dta_offset )
  381. * Entry : dta_segment is the segment address at which to set the DTA.
  382. *         dta_offset is the offset address at which to set the DTA address.
  383. * Exit  : The DTA address is set accordingly.
  384. */
  385.  
  386. set_dta( dta_segment, dta_offset )
  387.   int   dta_segment;
  388.   int   dta_offset;
  389. {
  390.  
  391.         /* Use LOWLEVEL.H register variable to call DOS function */
  392.         m_regs.b.ah = 0x1A;                     /* Set DTA address */
  393.         m_regs.w.ds = dta_segment;
  394.         m_regs.w.dx = dta_offset;               /* DS:DX->DTA address */
  395.  
  396.         do_interrupt( DOS_SERVICES, &m_regs );  /* That's it */
  397.  
  398. }  /* set_dta() */
  399.   
  400. /***************************************/
  401.  
  402. #endif  /* MSDOS */
  403.  
  404. /*****************************************************************************/
  405. /*
  406. * Macro : delimiter
  407. * Syntax: [static] int delimiter( char ch )
  408. * Entry : ch = character to check as a delimiter
  409. * Exit  : Non-zero if character is a delimiter, else zero.
  410. * Note  : Character is considered a delimiter if it is not an alphanumeric
  411. *           or underscore or dot (for Turbo Pascal 5.5 only) character.
  412. */
  413.  
  414. int
  415. delimiter( ch )
  416.   char  ch;
  417. {
  418.  
  419. /* Return values */
  420. #define DELIMITER       1
  421. #define NOT_DELIMITER   0
  422.  
  423. /* ASCII values */
  424. #define DOT             0x2E
  425. #define UNDERSCORE      0x5F
  426.  
  427.  
  428.         /* Check character as delimiter */
  429.         if( isalpha( ch ) || isdigit( ch ) || ch == UNDERSCORE || ch == DOT ){
  430.                 /* Char is not a delimiter */
  431.                 return( NOT_DELIMITER );
  432.         }
  433.  
  434.         /* Char is a delimiter */
  435.         return( DELIMITER );
  436.  
  437. }  /* delimiter() */
  438.  
  439. /***************************************
  440. * Macro : atoi
  441. * Syntax: [static] [int] atoi( char *string )
  442. * Entry : string is an ASCIIZ character string
  443. * Exit  : All contiguous digits in string are converted to a positive, base-10
  444. *           integer.
  445. */
  446.  
  447. int
  448. atoi( string )
  449.   char  *string;
  450. {
  451.  
  452.         int     num;
  453.         char    *ch;
  454.  
  455.  
  456.         /* Cycle thru characters until a non-digit is found */
  457.         /* Note: No check is made for numeric overflow; it is assumed that
  458.          * source files will not exceed 32,767 lines in length.
  459.          */
  460.         for( ch = string, num = 0; isdigit( *ch ); ++ch ){
  461.                 /* Accumulate into converted value */
  462.                 num = (num * 10) + (*ch - '0');
  463.         }
  464.  
  465.         /* Return converted number */
  466.         return( num );
  467.  
  468. }  /* atoi() */
  469.  
  470. /***************************************
  471. * Macro : strlwr
  472. * Syntax: [static] [void] strlwr( char *string )
  473. * Entry : string is an ASCIIZ character string
  474. * Exit  : All uppercase characters in string are converted to lowercase.
  475. */
  476.  
  477. strlwr( string )
  478.   char  *string;
  479. {
  480.  
  481.         /* Cycle through each character in string, converting if necessary */
  482.         while( *string ){
  483.                 *string = tolower( *string );
  484.                 ++string;
  485.         }
  486.  
  487. }  /* strlwr() */
  488.  
  489. /***************************************
  490. * Macro : dos_shell
  491. * Syntax: [static][void] dos_shell( char *command_line char *file_spec )
  492. * Entry : Command_line is the DOS command line to have the shell execute.  It
  493. *           may contain special parameters documented below.  Case of the
  494. *           special parameters is significant.  It may also contain the DOS
  495. *           standard output redirection characters > or >>, but the command
  496. *           must then be bracketed with double quotes.  The quotes will be
  497. *           removed before sending the command line to DOS.
  498. * Exit  : The command will have been executed.  No return value is sent back.
  499. * Special command_line parameters:
  500. *           %s      - entire file specification of desired file (file_spec parm)
  501. *           %d      - drive of desired file
  502. *           %p      - path (no drive) of desired file (no terminating (back)slash)
  503. *           %f      - filename of desired file
  504. *           %c      - current directory (in drive:path format suitable for CD'ing)
  505. *           %u      - user-defined substitution string
  506. *           %%      - single %
  507. */
  508.  
  509. dos_shell( command_line, file_spec )
  510.   char  *command_line;
  511.   char  *file_spec;             /* Absolute file spec, %s special parm */
  512. {
  513.  
  514.         int     path_length;
  515.         char    *cptr;
  516.         char    drive[ 3 ];
  517.         char    path[ 65 ];
  518.         char    fname[ 13 ];
  519.         char    curr_dir[ 80 ];
  520.         char    sub_str[ 129 ];
  521.         char    workspace[ 129 ];
  522.  
  523.  
  524.         /* If command_line is bracketed by double quotes, remove them */
  525.         if( *command_line == '"'
  526.             &&
  527.             command_line[ strlen( command_line ) - 1 ] == '"' ){
  528.                 /* Remove double quotes */
  529.                 command_line[ strlen( command_line ) - 1 ] = '\0';
  530.                 strcpy( command_line, &command_line[ 1 ] );
  531.         }
  532.  
  533.         /* Create special parameter strings */
  534.         /* Get drive:path */
  535.         cptr = rindex( file_spec, file_spec[ 2 ] );     /* cptr->last path separator */
  536.         path_length = cptr - file_spec - 1;             /* Don't count drive spec(2) in length */
  537.  
  538.         /* If not in root, don't count last "/" or "\" */
  539.         /* Note: PCTAGS.EXE will always write backslashes in path specs */
  540.         if( path_length > 1 ){
  541.                 --path_length;
  542.         }
  543.  
  544.         /* Break drive:path into separate strings */
  545.         strncpy( drive, file_spec, 2 );                 /* %d special parm */
  546.         drive[ 2 ] = '\0';
  547.         strncpy( path, &file_spec[ 2 ], path_length );  /* %p special parm */
  548.         path[ path_length ] = '\0';
  549.  
  550.         /* Get filename only */
  551.         strcpy( fname, ++cptr );                        /* %f special parm */
  552.  
  553.         /* Get current directory */
  554.         getcd( curr_dir );                              /* %c special parm */
  555.  
  556.         /* Parse command line, replacing special parameters */
  557.         cptr = command_line;
  558.         while( (cptr = index( cptr, '%' )) != NULL ){
  559.                 /* Break line at '%' */
  560.                 *cptr = '\0';
  561.  
  562.                 /* Interpret substitution character */
  563.                 switch( *++cptr ){
  564.                 case 's' :  /* Entire file specification of desired file */
  565.                             strcpy( sub_str, file_spec );
  566.                             break;
  567.  
  568.                 case 'd' :  /* Drive of desired file */
  569.                             strcpy( sub_str, drive );
  570.                             break;
  571.  
  572.                 case 'p' :  /* Path (no drive) of desired file */
  573.                             strcpy( sub_str, path );
  574.                             break;
  575.  
  576.                 case 'f' :  /* Filename of desired file */
  577.                             strcpy( sub_str, fname );
  578.                             break;
  579.  
  580.                 case 'c' :  /* Current directory */
  581.                             strcpy( sub_str, curr_dir );
  582.                             break;
  583.  
  584.                 case 'u' :  /* User-defined substitution string */
  585.                             get_string( sub_str, "Enter substitution string: " );
  586.                             break;
  587.  
  588.                 case '%' :  /* Single percent sign */
  589.                             strcpy( sub_str, "%" );
  590.                             break;
  591.  
  592.                 default  :  /* Unrecognized, replace with empty string */
  593.                             *sub_str = '\0';
  594.                             break;
  595.                 }
  596.  
  597.                 /* Replace special parameter with substitution string */
  598.                 strcpy( workspace, command_line );      /* Portion before '%'  */
  599.                 strcat( workspace, sub_str );           /* Substitution string */
  600.                 strcat( workspace, cptr + 1 );          /* Portion after "%x"  */
  601.                 strcpy( command_line, workspace );      /* Copy back into original */
  602.  
  603.         }
  604.  
  605.         /* Let user know what's going on */
  606.         say( "Executing RETROEXEC command" );
  607.  
  608.         /* Send command line to secondary DOS command processor for execution */
  609. #if V40x
  610.         shell( "", command_line, "" );
  611. #endif
  612. #if V32x
  613.         shell( "", command_line );
  614. #endif
  615.  
  616. }  /* dos_shell() */
  617.  
  618. /***************************************
  619. * Macro : get_tagfile
  620. * Syntax: [static][void] get_tagfile( char *file_spec )
  621. * Entry : file_spec is a return parameter.  Its value upon entry is not used.
  622. * Exit  : file_spec contains tagfile name or empty string if no tagfiles left to
  623. *           search.
  624. *
  625. * Use of Global Variables:
  626. *       pctags_first_call       -- access and assign
  627. *       pctags_env_tagfiles     -- access only
  628. *       pctags_env_path         -- access only
  629. */
  630.  
  631. get_tagfile( file_spec )
  632.   char  *file_spec;
  633. {
  634.  
  635.         char    env_tagfiles[ 128 ];
  636.         char    *single_tagfile;
  637.  
  638.  
  639.         /* If this is not the first call to this macro in this RETRO
  640.          * invocation, get the next matching filename.
  641.          */
  642.         if( !pctags_first_call ){
  643.                 /* Get next matching file name for file_spec */
  644.                 single_tagfile = find_next();
  645.         }else{
  646.                 /* Go thru this code once per pctags invocation */
  647.                 pctags_first_call = FALSE;
  648.  
  649.                 /* Get RETRO environment variable */
  650.                 if( (single_tagfile = getenv( "RETRO" )) == NULL ){
  651.                         /* No environment variable defined, use default */
  652.                         strcpy( env_tagfiles, "*.tag" );
  653.  
  654.                 }else{
  655.                         /* Copy environment variable value into workspace */
  656.                         strcpy( env_tagfiles, single_tagfile );
  657.  
  658.                         /* Convert to lowercase */
  659.                         strlwr( env_tagfiles );
  660.                 }
  661.  
  662.                 /* Parse any multiple file specs into single specs (file_spec
  663.                  * may contain wildcards).
  664.                  */
  665.                 parse_file_spec( env_tagfiles, file_spec );
  666.  
  667.                 /* Get first matching file name for file_spec */
  668.                 single_tagfile = find_first( file_spec );
  669.         }
  670.  
  671.         /* Check if we got a tagfile name */
  672.         while( *single_tagfile == '\0' ){
  673.                 /* Are there more file specs in the
  674.                  * pctags_env_tagfiles variable?
  675.                  */
  676.                 if( *pctags_env_tagfiles == '\0' ){
  677.                         /* Nope, this is the end of the road */
  678.                         break;                  /* Out of the while-loop */
  679.                 }
  680.  
  681.                 /* There are more tagfile specs.  Process them. */
  682.                 parse_file_spec( pctags_env_tagfiles, file_spec );
  683.  
  684.                 /* Get first matching file name for tagfile spec */
  685.                 single_tagfile = find_first( file_spec );
  686.         }
  687.  
  688.         /* If filename found, convert to lowercase and insert any drive:path */
  689.         if( *single_tagfile == '\0' ){
  690.                 /* Return empty filename */
  691.                 *file_spec = '\0';
  692.         }else{
  693.                 /* Convert to lowercase */
  694.                 strlwr( single_tagfile );
  695.  
  696.                 /* Insert drive:path */
  697.                 strcpy( file_spec, pctags_env_path );
  698.                 strcat( file_spec, single_tagfile );
  699.  
  700.                 /* Make sure tagfile is an absolute spec */
  701.                 absolute( file_spec );
  702.         }
  703.  
  704. }  /* get_tagfile() */
  705.  
  706. /***************************************
  707. * Macro : parse_file_spec
  708. * Syntax: [static] [void] parse_file_spec( char *fspec, char *file_spec )
  709. * Entry : fspec = file specification to parse.  May contain wildcards.  May
  710. *           also contain multiple specs, each separated by a semicolon.  For
  711. *           example, "c:\*.tag;d:\epsilon\tagfiles\*.tag;c:\pctags.tag".
  712. *         file_spec is an output parameter.  A single parsed file spec will be
  713. *           stored in it.
  714. * Exit  : Any subsequent file specs are stored in pctags_env_tagfiles.
  715. *         The first file spec in the RETRO environment variable or
  716. *           pctags_env_tagfiles is returned in file_spec (may contain wildcards).
  717. *         Any drive:path in the first file spec is copied into pctags_env_path
  718. *           for later insertion before matched filename.
  719. * Use of Global Variables:
  720. *       pctags_env_tagfiles     -- assign
  721. *       pctags_env_path         -- assign
  722. */
  723.  
  724. parse_file_spec( fspec, file_spec )
  725.   char  *fspec;                         /* Input parameter  */
  726.   char  *file_spec;                     /* Output parameter */
  727. {
  728.  
  729.         char    *semicolon;
  730.         char    *last_slash;
  731.         char    *last_path_separator;
  732.  
  733.  
  734.         /* Copy argument into return parameter for modification */
  735.         strcpy( file_spec, fspec );
  736.  
  737.         /* Does file_spec contain multiple specs? */
  738.         if( (semicolon = index( file_spec, ';' )) == NULL ){
  739.                 /* No, file_spec contains one drive:path.
  740.                  * Clear pctags_env_tagfiles.
  741.                  */
  742.                 *pctags_env_tagfiles = '\0';
  743.         }else{
  744.                 /* Yes, separate single spec from subsequent specs */
  745.                 *semicolon = '\0';      /* file_spec now is single spec */
  746.  
  747.                 /* Store subsequent specs in pctags_env_tagfiles */
  748.                 strcpy( pctags_env_tagfiles, ++semicolon );
  749.         }
  750.  
  751.         /* Does file_spec contain a drive or path spec? */
  752.         /* Find index to last path separator (support slashes and backslashes) */
  753.         last_path_separator = rindex( file_spec, '\\' );
  754.         last_slash          = rindex( file_spec, '/' );
  755.         if( last_slash > last_path_separator ){
  756.                 /* Point to last slash */
  757.                 last_path_separator = last_slash;
  758.         }
  759.  
  760.         /* If no path was specified, check for a drive */
  761.         if( last_path_separator == NULL ){
  762.                 last_path_separator = rindex( file_spec, ':' );
  763.         }
  764.  
  765.         /* If drive and/or path were specified, copy into pctags_env_path */
  766.         if( last_path_separator == NULL ){
  767.                 /* No drive:path specified, clear pctags_env_path */
  768.                 *pctags_env_path = '\0';
  769.         }else{
  770.                 /* Copy drive:path */
  771.                 strncpy( pctags_env_path, file_spec,
  772.                          (last_path_separator - file_spec + 1) );
  773.         }
  774.  
  775. }  /* parse_file_spec() */
  776.  
  777. /***************************************
  778. * Macro : file_loaded
  779. * Syntax: [static] int file_loaded( char *file_spec )
  780. * Entry : file_spec is checked to see if it is already loaded into a buffer.
  781. * Exit  : TRUE if file_spec is already loaded, else FALSE.
  782. */
  783.  
  784. int
  785. file_loaded( file_spec )
  786.   char  *file_spec;
  787. {
  788.  
  789.         char    buff_name[ FNAMELEN + 10 ];
  790.         char    *buff_str;
  791.         char    *org_buffname;
  792.  
  793.  
  794.         /* Save current buffer */
  795.         org_buffname = bufname;
  796.  
  797.         /* Get first name in buffer list */
  798.         buff_str = buffer_list( 1 );
  799.  
  800.         /* Cycle through buffer list searching for a matching filename */
  801.         do{
  802.                 /* Set current buffer */
  803.                 bufname = buff_str;
  804.  
  805.                 /* Compare filename associated with this buffer to our filename */
  806.                 if( !strcmp( filename, file_spec ) ){
  807.                         /* Found a match, file is already loaded */
  808.                         /* Restore current buffer */
  809.                         bufname = org_buffname;
  810.                         return( TRUE );
  811.         }
  812.  
  813.         }while( buff_str = buffer_list( 0 ) );
  814.  
  815.         /* File is not currently loaded */
  816.         bufname = org_buffname;                 /* Restore current buffer */
  817.         return( FALSE );
  818.  
  819. }  /* file_loaded() */
  820.  
  821. /***************************************
  822. * Macro : position_line_within_window
  823. * Syntax: [static] [void] position_line_within_window( void )
  824. * Entry : None.
  825. * Exit  : If current window is at least five lines high, the current line
  826. *           is positioned on the third line in the window.  If the window
  827. *           is less than five lines high, no change.
  828. */
  829.  
  830. position_line_within_window()
  831. {
  832.  
  833.         int     saved_point;
  834.  
  835.  
  836.         /* If possible, position current line to third in window.  Window
  837.          * must be at least five lines high to do so.
  838.          */
  839.         if( window_size >= 5 ){
  840.                 /* Save current position */
  841.                 saved_point = point;
  842.  
  843.                 /* Back up two lines */
  844.                 nl_reverse(); to_begin_line();
  845.                 nl_reverse(); to_begin_line();
  846.  
  847.                 /* Set start of the window here */
  848.                 window_start = point;
  849.  
  850.                 /* Restore point to definition line */
  851.                 point = saved_point;
  852.         }
  853.  
  854. }  /* position_line_within_window() */
  855.  
  856. /***************************************
  857. * Macro : pctags_main
  858. * Syntax: [static] int pctags_main( char *function_name )
  859. * Entry : function_name = function name to locate and retrieve the file in
  860. *           which it is declared.
  861. * Exit  : Non-zero value if function definition is found, file containing the
  862. *           definition is loaded and cursor is placed on the definition's
  863. *           line.
  864. *         Else return zero.
  865. * Description :
  866. *         RETRO operation is performed.  This requires the following steps:
  867. *               -- Cycle through all required tagfiles.  Default = *.tag or
  868. *                       all tagfiles specified in RETRO environment variable
  869. *                       which may contain multiple file specs, each with
  870. *                       wildcards, separated by semicolons.  For example,
  871. *                       SET RETRO=c:\epsilon\tagfiles\*.tag;c:\prog\*.tag
  872. *               -- Search each tagfile for the function_name string.
  873. *               -- If not found in any tagfile, display message and quit.
  874. *               -- If found, get name of file and definition line from tagfile.
  875. *               -- Close tagfile.
  876. *               -- Load file for editing.
  877. *               -- If file cannot be found, tag information contains an "EXEC"
  878. *                       command and environment varible RETROEXEC is defined,
  879. *                       execute the command line value assigned to it.  Try to
  880. *                       load the file again.
  881. *               -- Search file for definition line.
  882. *               -- Place definition line on third line in window (if possible).
  883. *               -- If any of these operations fail, display appropriate error
  884. *                       message and quit.
  885. *
  886. * Use of Global Variables:
  887. *       pctags_first_call       -- assign
  888. *       pctags_tagname_case     -- access
  889. */
  890.  
  891. int
  892. pctags_main( function_name )
  893.   char  *function_name;
  894. {
  895.  
  896. #define STRIP_CR        1       /* Remove CR of CR-LF pair when file_read() */
  897.  
  898. #define SEARCH_FORWARD  1
  899. #define SEARCH_BACKWARD (-1)
  900.  
  901.         int     found_match;
  902.         int     org_case_fold;
  903.         int     error_code;
  904.         int     continue_search;
  905.         int     exec_env;
  906.         int     line_number;
  907.         int     saved_position;
  908.         int     already_loaded;
  909.  
  910.         char    *org_buff_id;
  911.         char    *buff_id;
  912.         char    *file_start;
  913.         char    *decl_start;
  914.         char    *env_var;
  915.         char    file_spec[ 80 ];
  916.         char    tagfile_name[ 80 ];
  917.  
  918.  
  919.         /* Save current buffer context */
  920.         org_case_fold = case_fold;              /* Search case sensitivity */
  921.         org_buff_id   = bufname;                /* Current buffer          */
  922.  
  923.         /* Set case sensitivity for tagname search */
  924.         case_fold = pctags_tagname_case;        /* User-settable in when_loading() */
  925.  
  926.         /* Create unique tagfile buffer and make it the current buffer */
  927.         buff_id = temp_buf();
  928.         bufname = buff_id;
  929.  
  930.         /* Initialize flags */
  931.         found_match       = FALSE;
  932.         pctags_first_call = TRUE;               /* Global flag for get_tagfile() */
  933.  
  934.         /* Cycle thru all RETRO (or default) tagfiles until done or match found */
  935.         while( !found_match ){
  936.                 /* Get a tagfile */
  937.                 get_tagfile( tagfile_name );
  938.  
  939.                 /* If no more tagfiles to get, quit */
  940.                 if( *tagfile_name == '\0' ){
  941.                         /* Reset current buffer and delete temporary */
  942.                         bufname = org_buff_id;
  943.                         delete_buffer( buff_id );
  944.  
  945.                         /* Exit while-loop */
  946.                         break;
  947.                 }
  948.  
  949.                 /* Set buffer-specific filename */
  950.                 filename = tagfile_name;
  951.  
  952.                 /* Read tagfile contents into current buffer */
  953.                 if( (error_code = file_read( tagfile_name, STRIP_CR )) != 0 ){
  954.                         /* Error reading file */
  955.                         file_error( error_code, tagfile_name, "Unrecognized error" );
  956.                         continue;               /* Try another tagfile */
  957.                 }
  958.  
  959.                 /* Set up a loop to search through the tagfile.  Multiple
  960.                  * searches may be required because a match COULD be found on a
  961.                  * string in the tagfile other than the function-name portion of
  962.                  * a line.
  963.                  */
  964.                 /* Search tagfile for function name */
  965.                 say( "Searching %s", tagfile_name );
  966.                 continue_search = TRUE;
  967.                 point = 0;                      /* Force to start of file */
  968.                 while( continue_search ){
  969.                         /* Search tagfile for function name */
  970.                         if( !search( SEARCH_FORWARD, function_name ) ){
  971.                                 /* Did not find function name in this tagfile */
  972.                                 /* Stop searching this file, try another */
  973.                                 continue_search = FALSE;
  974.                                 continue;
  975.                         }
  976.  
  977.                         /* Found a match */
  978.                         /* The match MUST start in the first column.  If
  979.                          * it doesn't then a match was found somewhere
  980.                          * other than in the function-name portion of the line.
  981.                          */
  982.                         /* Set point to beginning of match */
  983.                         point = matchstart;
  984.                         if( current_column() != 0 ){
  985.                                 /* Not a valid match */
  986.                                 /* Advance buffer position so we don't
  987.                                  * keep finding the same match
  988.                                  */
  989.                                 ++point;
  990.  
  991.                                 /* Continue searching tagfile */
  992.                                 continue;
  993.                         }
  994.  
  995.                         /* Found a good match! */
  996.                         /* Stop searching this and all other tagfiles */
  997.                         continue_search = FALSE;        /* This tagfile */
  998.                         found_match     = TRUE;         /* All tagfiles */
  999.                         
  1000.                         /* Read line with match, including newline */
  1001.                         nl_forward();           /* Move point to end of line */
  1002.                         grab( matchstart, point, pctags_line );
  1003.                         pctags_line[ point - matchstart ] = '\0';       /* Terminate line */
  1004.  
  1005.                         /* Done with tagfile, close it */
  1006.                         bufname = org_buff_id;          /* Reset to original buffer */
  1007.                         delete_buffer( buff_id );       /* Delete our temporary buffer */
  1008.  
  1009.                         /* Format of line:
  1010.                          * Column 1: Function name searching for (variable length)
  1011.                          * Single space terminator
  1012.                          * Optional RETRO commands (! and #)
  1013.                          * Complete file specification of file containing
  1014.                          *   definition of function (variable length)
  1015.                          * Single space terminator
  1016.                          * Carot (^) character signifying the start of the
  1017.                          *   definition line
  1018.                          * Line from file that defines the function, including carriage
  1019.                          *   return (variable length)
  1020.                          * End of line is the last character in the definition
  1021.                          *   line, i.e. carriage return
  1022.                          * As an example:
  1023.                          * "function c:\dir\file.c ^int function( arg1, arg2)\n"
  1024.                          */
  1025.  
  1026.                         /* Determine starting indices of file spec
  1027.                          * and definition line.
  1028.                          */
  1029.                         file_start = index( pctags_line, ' ' );
  1030.                         ++file_start;
  1031.  
  1032.                         decl_start = index( file_start, ' ' );
  1033.                         *decl_start = '\0';             /* Terminate file spec */
  1034.                         decl_start += 2;                /* Move decl_start past carot */
  1035.  
  1036.                         /* Extract file spec and decl line */
  1037.                         strcpy( file_spec, file_start );
  1038.                         strcpy( pctags_decl_line, decl_start );
  1039.  
  1040.                         /* Check file_spec for special leading characters */
  1041.                         continue_search = TRUE;
  1042.                         exec_env        = FALSE;
  1043.                         line_number     = FALSE;
  1044.                         file_start      = file_spec;
  1045.                         while( continue_search ){
  1046.                                 switch( *file_start ){
  1047.                                 case '!' : /* If file_spec not found,
  1048.                                             * execute RETROEXEC environment
  1049.                                             * variable command line.  This is
  1050.                                             * usually used to extract the file
  1051.                                             * from a source-control library.
  1052.                                             */
  1053.                                            exec_env = TRUE;
  1054.  
  1055.                                            /* Advance actual start of
  1056.                                             * file_spec
  1057.                                             */
  1058.                                            ++file_start;
  1059.                                            break;
  1060.  
  1061.                                 case '#' : /* Line number of definition is
  1062.                                             * stored in tagfile instead of line
  1063.                                             * contents.
  1064.                                             */
  1065.                                            line_number = TRUE;
  1066.                                            ++file_start;        /* Advance past char */
  1067.                                            break;
  1068.  
  1069.                                 default : /* Stop parsing special characters */
  1070.                                           continue_search = FALSE;
  1071.                                           break;
  1072.                                 }
  1073.                         }
  1074.  
  1075.                         /* Verify that file_spec exists */
  1076.                         if( check_file( file_start, (struct file_info *)NULL ) == 0 ){
  1077.                                 /* If exec_env active, get RETROEXEC value */
  1078.                                 if( exec_env ){
  1079.                                         env_var = getenv( "RETROEXEC" );
  1080.  
  1081.                                         /* If variable found, copy into workspace */
  1082.                                         if( env_var != NULL ){
  1083.                                                 strcpy( pctags_env_pctagsexec,
  1084.                                                         env_var );
  1085.  
  1086.                                                 /* Execute the variable's value */
  1087.                                                 dos_shell( pctags_env_pctagsexec,
  1088.                                                            file_start );
  1089.                                         }
  1090.                                 }
  1091.  
  1092.                                 /* Check for file's existence now */
  1093.                                 if( check_file( file_start, (struct file_info *)NULL ) == 0 ){
  1094.                                         /* Still not found */
  1095.                                         maybe_ding();
  1096.                                         say( "%s does not exist.  Update tagfile.",
  1097.                                              file_start );
  1098.  
  1099.                                         /* Can't go any further, restore context
  1100.                                          * and quit
  1101.                                          */
  1102.                                         case_fold = org_case_fold;
  1103.                                         return( UNSUCCESSFUL );
  1104.                                 }
  1105.                         }
  1106.  
  1107.                         /* See if file is already loaded */
  1108.                         already_loaded = file_loaded( file_start );
  1109.  
  1110.                         /* Load file into buffer (if necessary) and make it
  1111.                          * the current buffer
  1112.                          */
  1113.                         find_it( file_start );
  1114.  
  1115.                         /* Save current position in file, in case
  1116.                          * decl-file was already loaded and the
  1117.                          * definition is not in it (i.e. the
  1118.                          * tagfile is not up-to-date), in which
  1119.                          * case we'll want to restore the original
  1120.                          * file position.
  1121.                          */
  1122.                         saved_position = point;
  1123.  
  1124.                         /* Move to top of file, in case file was
  1125.                          * already in buffer and desired function
  1126.                          * is BEFORE current position.
  1127.                          */
  1128.                         point = 0;
  1129.  
  1130.                         /* Searching for line contents or line number? */
  1131.                         if( line_number == TRUE ){
  1132.                                 /* Goto specified line number in source file */
  1133.                                 go_line( atoi( pctags_decl_line ) );
  1134.  
  1135.                                 /* Position line */
  1136.                                 position_line_within_window();
  1137.  
  1138.                                 /* Display filename */
  1139.                                 say( "File: %s", file_start );
  1140.                         }else{
  1141.                                 /* Search for definition line */
  1142.                                 case_fold = CASE_SENSITIVE;
  1143.                                 while( TRUE ){
  1144.                                         if( search( SEARCH_FORWARD, pctags_decl_line ) ){
  1145.                                                 /* Found a match */
  1146.                                                 /* Note: Match must be at start of line
  1147.                                                  * to be valid.  If not at start, it
  1148.                                                  * could be a "forward" declaration.
  1149.                                                  */
  1150.                                                 /* Move point to start of line */
  1151.                                                 point = matchstart;
  1152.  
  1153.                                                 /* Check if match is at start of line */
  1154.                                                 if( current_column() != 0 ){
  1155.                                                         /* Invalid match, keep searching */
  1156.                                                         /* Advance buffer position so we
  1157.                                                          * won't keep finding the same
  1158.                                                          * match.
  1159.                                                          */
  1160.                                                         ++point;
  1161.  
  1162.                                                         /* Continue searching source file */
  1163.                                                         continue;
  1164.                                                 }
  1165.  
  1166.                                                 /* Found function definition! */
  1167.                                                 /* Position the line in the current window */
  1168.                                                 position_line_within_window();
  1169.  
  1170.                                                 /* Display filename */
  1171.                                                 say( "File: %s", file_start );
  1172.  
  1173.                                                 /* Break out of while-loop */
  1174.                                                 break;
  1175.                                         }else{
  1176.                                                 /* Definition line not found in file */
  1177.                                                 maybe_ding();
  1178.                                                 say( "Update %s with %s", tagfile_name,
  1179.                                                                           file_start );
  1180.  
  1181.                                                 /* Recovery */
  1182.                                                 /* If file was loaded from disk, dump it */
  1183.                                                 if( !already_loaded ){
  1184.                                                         /* Drop it and restore original buffer */
  1185.                                                         buff_id = bufname;      /* Save current buffer id */
  1186.                                                         bufname = org_buff_id;  /* Reset current buff to org */
  1187.                                                         drop_buffer( buff_id ); /* Dump file buffer */
  1188.                                                 }else{
  1189.                                                         /* Restore original position in buffer */
  1190.                                                         point = saved_position;
  1191.                                                 }
  1192.  
  1193.                                                 /* Return error code */
  1194.                                                 case_fold = org_case_fold;
  1195.                                                 return( UNSUCCESSFUL );
  1196.  
  1197.                                         } /* if decl-line match found in file */
  1198.  
  1199.                                 } /* while searching for definition line in source file */
  1200.  
  1201.                         } /* if going to specified line number or looking for line contents */
  1202.  
  1203.                 } /* while searching for function name in tagfile */
  1204.  
  1205.         } /* while cycling through tagfiles searching for function name */
  1206.  
  1207.         /* Restore original editor context */
  1208.         case_fold = org_case_fold;
  1209.  
  1210.         /* If match not found in any tagfile, that's not good... */
  1211.         if( found_match ){
  1212.                 /* Congratulations */
  1213.                 return( SUCCESSFUL );
  1214.         }else{
  1215.                 /* Display message */
  1216.                 /* Note: function_name ends with a single blank char */
  1217.                 say( "%snot found.", function_name );
  1218.                 return( UNSUCCESSFUL );
  1219.         }
  1220.  
  1221. }  /* pctags_main() */
  1222.  
  1223. /****************************************/
  1224.