home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / PCTAG.ZIP / RETROMAC.COM / RETRO next >
Text File  |  1989-10-01  |  37KB  |  978 lines

  1. /*
  2.  * -------------------------------------------
  3.  * PC-TAGS(tm) RETRO Program for the ME Editor
  4.  * -------------------------------------------
  5.  *
  6.  * ME 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 ME 2.20 and greater.  It can be used with an
  17.  * earlier version of ME by resetting the VER220_PLUS from "TRUE" to "FALSE".
  18.  *
  19.  * ME versions prior to 2.20 do not support the "%c" (current directory)
  20.  * parameter to the RETROEXEC special command.
  21.  *
  22.  * Entry Points
  23.  * ------------
  24.  *      init             -- initialize global variables
  25.  *      pctags_auto      -- uses word under cursor for search
  26.  *      pctags_prompt    -- prompts for a word to use in search
  27.  *
  28.  * Support Macros
  29.  * --------------
  30.  *      _delimiter       -- determine if character is a word delimiter
  31.  *      _convert_slashes -- convert all slashes in string to backslashes
  32.  *      _get_tagfile     -- retrieve the next tagfile to search
  33.  *      _parse_file_spec -- break multiple tagfile specifications into
  34.  *                           individual directory/filename parts
  35.  *      _escape          -- escape all instances of a particular character
  36.  *      _line_to_reg_exp -- escape any would-be meta-characters
  37.  *      _dos_shell       -- execute RETROEXEC environment value thru DOS
  38.  *      _pctags_main     -- search and retrieval workhorse code
  39.  *
  40.  * History
  41.  * -------
  42.  *  1 Oct 89 -- Version 1.00
  43.  *
  44.  */
  45.  
  46. /**************************************/
  47. /* Global constants */
  48.  
  49. #define TRUE                    1
  50. #define FALSE                   0
  51.  
  52. /* ME Version */
  53. /* ********************************* IMPORTANT !!! ****************************/
  54. /* If you are using this macro with a ME version prior to 2.20, the following
  55.  * constant value should be set to FALSE.  The MACCOMP compiler will generate
  56.  * an error if it is not.
  57.  *
  58.  * When run with versions of ME before 2.20, RETRO will not support the
  59.  * "%c" substitution parameter in the RETROEXEC environment variable.  All
  60.  * other operations are identical.
  61.  */
  62. #define VER220_PLUS             TRUE
  63.  
  64. /* Return values */
  65. #define SUCCESSFUL              TRUE
  66. #define UNSUCCESSFUL            FALSE
  67.  
  68. #define CASE_SENSITIVE          0
  69. #define CASE_INSENSITIVE        1
  70.  
  71. /**************************************/
  72. /* Global data */
  73.  
  74.         string  pctags_env_tagfiles;            /* RETRO env var value          */
  75.         string  pctags_env_path;                /* Drive:path of current tagfile*/
  76.         int     pctags_first_call;              /* Flag used by get_tagfile()   */
  77.         int     pctags_tagname_case;            /* Case-sensitivity of tagname search */
  78.  
  79. /**************************************
  80.  * Macro : init
  81.  * Syntax: void init( void )
  82.  * Entry : None, called automatically upon macro loading
  83.  * Exit  : None.
  84.  * Description:
  85.  *         Global variables are initialized.  Key assignments can be made here.
  86.  *
  87.  * Use of Global Variables:
  88.  *      pctags_tagname_case     -- assigned
  89.  */
  90.  
  91. init()
  92. {
  93.  
  94. #define CTRL_A          1
  95. #define CTRL_P          16
  96.  
  97.  
  98.         /* Key assignments for "pctags_auto" and "pctags_prompt" */
  99.         assign_key( "pctags_auto",   CTRL_A );
  100.         assign_key( "pctags_prompt", CTRL_P );
  101.  
  102.         /* You can set the next variable to CASE_INSENSITIVE if
  103.          * you want to ignore case when entering a tagname.
  104.          */
  105.         pctags_tagname_case = CASE_SENSITIVE;
  106.  
  107. }  /* init() */
  108.  
  109. /**************************************
  110.  * Macro : pctags_auto
  111.  * Syntax: int pctags_auto( void )
  112.  * Entry : None, will extract the word under the current cursor position.
  113.  * Exit  : Non-zero if entire retrieval operation was successfully completed.
  114.  *         Zero if operation was not successful.
  115.  * Description:
  116.  *         Function/tag under cursor is extracted from line and used to search
  117.  *          thru the tagfile(s).  If a match is found, the file containing the
  118.  *          function's definition is loaded and the cursor is placed at the
  119.  *          start of the definition.
  120.  */
  121.  
  122. pctags_auto()
  123. {
  124.  
  125.         int     ch;
  126.         string  tagname;
  127.  
  128.  
  129.         /* Make sure cursor is currently under a valid word */
  130.         if( _delimiter( currchar() ) ){
  131.                 /* Not under a word */
  132.                 message( "Cursor is not under a valid word.  Hit any key..." );
  133.                 bell();
  134.                 get_tty_char();
  135.  
  136.                 /* Abort */
  137.                 return( UNSUCCESSFUL );
  138.         }
  139.  
  140.         /* Save current cursor position on position stack */
  141.         save_position();
  142.  
  143.         /* Backtrack to the start of the word (first delimiter or beginning
  144.          * of line)
  145.          */
  146.         while( !_delimiter( currchar() ) ){
  147.                 /* If at beginning of line, stop backtracking */
  148.                 if( is_bol() ){
  149.                         /* Stop backtracking */
  150.                         left();         /* Artificial backtrack for adjustment */
  151.                         break;          /* Out of while-loop */
  152.                 }
  153.  
  154.                 /* Do backtrack */
  155.                 left();
  156.         }
  157.  
  158.         /* Move to first character in word */
  159.         right();
  160.  
  161.         /* Grab characters until end of word (delimiter or end of line) */
  162.         tagname = "";                   /* Initialize tagname */
  163.         while( !_delimiter( currchar() ) ){
  164.                 /* Grab char and append to tagname */
  165.                 tagname = strcat( tagname, chr( currchar() ) );
  166.  
  167.                 /* If at end of line, stop grabbing characters */
  168.                 if( is_eol() ){
  169.                         break;          /* Out of while-loop */
  170.                 }
  171.  
  172.                 /* Advance to next character */
  173.                 right();
  174.         }
  175.  
  176.         /* Restore original cursor position */
  177.         restore_position();
  178.  
  179.         /* Append a blank to the tagname */
  180.         tagname = strcat( tagname, " " );
  181.  
  182.         /* Perform retrieval operation */
  183.         return( _pctags_main( tagname ) );
  184.  
  185. }  /* pctags_auto() */
  186.  
  187. /**************************************
  188.  * Macro : pctags_prompt
  189.  * Syntax: int pctags_prompt( void )
  190.  * Entry : None, will prompt for function to retrieve.
  191.  * Exit  : Non-zero if specified function is found, file containing function
  192.  *          definition is loaded and cursor is placed at beginning of
  193.  *          definition.
  194.  *         Zero if specified function is not found.
  195.  * Description:
  196.  *         User is prompted for the name of a function.  The macro loads the
  197.  *         file containing the function definition and places the cursor on the
  198.  *         definition's line.
  199.  */
  200.  
  201. pctags_prompt()
  202. {
  203.  
  204.         string  tagname;
  205.  
  206.  
  207.         /* Prompt the user for a tagname */
  208.         tagname = get_tty_str( "Enter tagname: " );
  209.  
  210.         /* Trim any leading and trailing spaces from tagname */
  211.         tagname = ltrim( rtrim( tagname ) );
  212.  
  213.         /* If empty string was entered, abort */
  214.         if( strlen( tagname ) == 0 ){
  215.                 /* Just return */
  216.                 return( UNSUCCESSFUL );
  217.         }
  218.  
  219.         /* Append a blank to the tagname */
  220.         tagname = strcat( tagname, " " );
  221.  
  222.         /* Perform retrieval operation */
  223.         return( _pctags_main( tagname ) );
  224.  
  225. }  /* pctags_prompt() */
  226.  
  227. /**************************************
  228.  * Macro : _delimiter
  229.  * Syntax: static int _delimiter( ch )
  230.  * Entry : ch = integer character
  231.  * Exit  : Non-zero if ch is a delimiter character.  Else zero.
  232.  * Note  : Character is considered a delimiter if it is not an alphanumeric,
  233.  *          underscore or dot (for Turbo Pascal 5.5 and Microsoft QuickPascal)
  234.  *          character.
  235.  */
  236.  
  237. _delimiter( ch )
  238.   int   ch;
  239. {
  240.  
  241. /* Return values */
  242. #define DELIMITER       1
  243. #define NOT_DELIMITER   0
  244.  
  245. /* Hex constants for quicker execution (eliminates atoi() calls) */
  246. #define UPPER_A         0x41
  247. #define UPPER_Z         0x5A
  248. #define LOWER_A         0x61
  249. #define LOWER_Z         0x7A
  250. #define ZERO            0x30
  251. #define NINE            0x39
  252. #define UNDERSCORE      0x5F
  253. #define DOT             0x2E
  254.  
  255.  
  256.         /* Check if delimiter */
  257.         if( ( ch >= UPPER_A && ch <= UPPER_Z )
  258.             ||
  259.             ( ch >= LOWER_A && ch <= LOWER_Z )
  260.             ||
  261.             ( ch >= ZERO && ch <= NINE )
  262.             ||
  263.             ( ch == UNDERSCORE )
  264.             ||
  265.             ( ch == DOT ) ){
  266.                 /* No, it is not a delimiter */
  267.                 return( NOT_DELIMITER );
  268.         }
  269.  
  270.         /* Yes, it is a delimiter */
  271.         return( DELIMITER );
  272.  
  273. }  /* _delimiter() */
  274.  
  275. /**************************************
  276.  * Macro : _convert_slashes
  277.  * Syntax: static string _convert_slashes( str )
  278.  * Entry : str = string which may contain slash (/) characters.
  279.  * Exit  : str with all slashes converted to backslashes.
  280.  * Note  : This is done to simplify the parsing of file specs.
  281.  */
  282.  
  283. _convert_slashes( str )
  284.   string        str;
  285. {
  286.  
  287.         int     i;
  288.  
  289.  
  290.         /* Convert all slashes to backslashes */
  291.         while( (i = index( str, "/" )) ){
  292.                 /* Convert slash to backslash */
  293.                 str = strcat( strcat( substr( str, 1, i - 1 ), "\\" ),
  294.                               substr( str, i + 1, strlen( str ) - i ) );
  295.         }
  296.  
  297.         /* Return converted string */
  298.         return( str );
  299.  
  300. }  /* _convert_slashes() */
  301.  
  302. /**************************************
  303.  * Macro : _get_tagfile
  304.  * Syntax: static string _get_tagfile( void )
  305.  * Entry : None.
  306.  * Exit  : Name of next tagfile to search.  Empty string if no tagfiles left to
  307.  *           search.
  308.  *
  309.  * Use of Global Variables:
  310.  *      pctags_first_call       -- access and assign
  311.  *      pctags_env_tagfiles     -- access
  312.  *      pctags_env_path         -- access
  313.  */
  314.  
  315. _get_tagfile()
  316. {
  317.  
  318.         string  file_spec;
  319.         string  single_tagfile;
  320.  
  321.  
  322.         /* If this is the first call to this macro in this RETRO invocation,
  323.          * get the RETRO environment variable setting, if any.
  324.          */
  325.         if( pctags_first_call == FALSE ){
  326.                 /* Get the next matching file name */
  327.                 single_tagfile = next_filespec();
  328.         }else{
  329.                 /* Go thru this code once per RETRO invocation */
  330.                 pctags_first_call = FALSE;
  331.  
  332.                 /* Get RETRO environment variable */
  333.                 if( (file_spec = getenv( "RETRO" )) == "" ){
  334.                         /* No such variable, use the default */
  335.                         file_spec = "*.tag";
  336.                 }
  337.  
  338.                 /* Convert any slash path separators to backslashes */
  339.                 file_spec = _convert_slashes( file_spec );
  340.  
  341.                 /* Parse any multiple file specs into single spec (may contain
  342.                  * wildcards).
  343.                  */
  344.                 file_spec = _parse_file_spec( file_spec );
  345.  
  346.                 /* Find first matching file */
  347.                 if( set_filespec( file_spec ) == 0 ){
  348.                         /* No matching files */
  349.                         single_tagfile = "";
  350.                 }else{
  351.                         /* Match found, get it */
  352.                         single_tagfile = next_filespec();
  353.                 }
  354.         }
  355.  
  356.         /* Check if we got a tagfile name */
  357.         while( single_tagfile == "" ){
  358.                 /* Are there more file specs in pctags_env_tagfiles? */
  359.                 if( pctags_env_tagfiles == "" ){
  360.                         /* Nope, this is the end of the road */
  361.                         break;                  /* Out of while-loop */
  362.                 }
  363.  
  364.                 /* There are more tagfile specs.  Process them. */
  365.                 file_spec = _parse_file_spec( pctags_env_tagfiles );
  366.  
  367.                 /* Get first matching file name for tagfile spec */
  368.                 if( set_filespec( file_spec ) == 0 ){
  369.                         /* No matching files */
  370.                         single_tagfile = "";
  371.                 }else{
  372.                         /* Match found, get it */
  373.                         single_tagfile = next_filespec();
  374.                 }
  375.         }
  376.  
  377.         /* If filename found, insert any drive:path */
  378.         if( single_tagfile == "" ){
  379.                 /* Return empty filename */
  380.                 file_spec = "";
  381.         }else{
  382.                 /* Insert drive:path */
  383.                 file_spec = pctags_env_path;
  384.                 file_spec = strcat( file_spec, single_tagfile );
  385.         }
  386.  
  387.         /* Return tagfile spec */
  388.         return( file_spec );
  389.  
  390. }  /* _get_tagfile() */
  391.  
  392. /**************************************
  393.  * Macro : _parse_file_spec
  394.  * Syntax: static string _parse_file_spec( file_spec )
  395.  * Entry : file_spec is a string file specification to parse.  May contain
  396.  *          wildcards.  May also contain multiple specs, each separated by a
  397.  *          semicolon.
  398.  * Exit  : Any subsequent file specs are stored in pctags_env_tagfiles.
  399.  *         The first file spec is returned (may contain wildcards).
  400.  *         Any drive:path in the first file spec is copied into pctags_env_path
  401.  *          for later insertion before matched filename.
  402.  *
  403.  * Use of Global Variables:
  404.  *      pctags_env_tagfiles     -- assign
  405.  *      pctags_env_path         -- assign
  406.  */
  407.  
  408. _parse_file_spec( file_spec )
  409.   string        file_spec;
  410. {
  411.  
  412.         int     i;
  413.         int     last_path_separator;
  414.         int     semicolon;
  415.  
  416.  
  417.         /* Does file_spec contain multiple specs? */
  418.         if( (semicolon = index( file_spec, ";" )) == 0 ){
  419.                 /* No, file_spec contains one drive:path.
  420.                  * Clear pctags_env_tagfiles.
  421.                  */
  422.                 pctags_env_tagfiles = "";
  423.         }else{
  424.                 /* Yes, store subsequent specs in pctags_env_tagfiles */
  425.                 pctags_env_tagfiles = substr( file_spec,
  426.                                               semicolon + 1,
  427.                                               strlen( file_spec ) - semicolon );
  428.  
  429.                 /* Get first spec */
  430.                 file_spec = substr( file_spec, 1, semicolon - 1 );
  431.         }
  432.  
  433.         /* Does file_spec contain a drive or path spec? */
  434.         /* Find index to last path separator */
  435.         last_path_separator = index( file_spec, "\\" );
  436.         while( (i = index( substr( file_spec,
  437.                                    last_path_separator + 1,
  438.                                    strlen( file_spec ) - last_path_separator ),
  439.                            "\\" )) ){
  440.                 last_path_separator = last_path_separator + i;
  441.         }
  442.  
  443.         /* If no path was specified, check for a drive */
  444.         if( last_path_separator == 0 ){
  445.                 last_path_separator = index( file_spec, ":" );
  446.         }
  447.  
  448.         /* If drive and/or path were specified, extract from file_spec and
  449.          * store in pctags_env_path.
  450.          */
  451.         if( last_path_separator == 0 ){
  452.                 /* No drive:path specified, clear pctags_env_path */
  453.                 pctags_env_path = "";
  454.         }else{
  455.                 pctags_env_path = substr( file_spec, 1, last_path_separator );
  456.         }
  457.  
  458.         /* Return single file_spec */
  459.         return( file_spec );
  460.  
  461. }  /* _parse_file_spec() */
  462.  
  463. /**************************************
  464.  * Macro : _escape
  465.  * Syntax: static string _escape( ch, line )
  466.  * Entry : ch is a single-character string of the char to be escaped in the line
  467.  * Exit  : All instances of ch in line are escaped.  line is returned.
  468.  */
  469.  
  470. _escape( ch, line )
  471.   string        ch;
  472.   string        line;
  473. {
  474.  
  475.         int     i, i2;
  476.  
  477.  
  478.         /* Escape all instances of ch in line */
  479.         i = index( line, ch );
  480.         while( i ){
  481.                 /* Insert a backslash before character */
  482.                 line = sprintf( "%s\\%s",
  483.                                 substr( line, 1, i - 1 ),
  484.                                 substr( line, i, strlen( line ) - i + 1 ) );
  485.  
  486.                 /* Check for more ch's */
  487.                 if( (i2 = index( substr( line,
  488.                                          i + 2,
  489.                                          strlen( line ) - i - 1 ),
  490.                                  ch )) == 0 ){
  491.                         /* No more */
  492.                         i = 0;
  493.                 }else{
  494.                         /* Escape it, calculate index from start of line */
  495.                         i = i + i2 + 1;
  496.                 }
  497.         }
  498.  
  499.         /* Return escaped line */
  500.         return( line );
  501.  
  502. }  /* _escape() */
  503.  
  504. /**************************************
  505.  * Macro : _line_to_reg_exp
  506.  * Syntax: static string _line_to_reg_exp( line )
  507.  * Entry : line is a string which may contain characters that would be
  508.  *          interpreted as meta-characters during a search operation.  None
  509.  *          of these characters should really be meta-characters.
  510.  * Exit  : All would-be meta-characters are escaped.  Line can now be used in
  511.  *          a regular-expression search.  Returns line.
  512.  * Note  : The entry line should not contain any characters that you want to
  513.  *          be interpreted as meta-characters; those should be added after this
  514.  *          operation.
  515.  */
  516.  
  517. _line_to_reg_exp( line )
  518.   string        line;
  519. {
  520.  
  521.         /* Escape certain characters so they won't be interpreted as
  522.          * meta-characters.  The following chars are escaped:
  523.          *              \ ? * + [ ] ^ $ | : { }
  524.          */
  525.  
  526.         /* Escape each character in turn */
  527.         line = _escape( "\\", line );   /* Must do backslash first */
  528.         line = _escape( "?", line );
  529.         line = _escape( "*", line );
  530.         line = _escape( "+", line );
  531.         line = _escape( "[", line );
  532.         line = _escape( "]", line );
  533.         line = _escape( "^", line );
  534.         line = _escape( "$", line );
  535.         line = _escape( "|", line );
  536.         line = _escape( ":", line );
  537.         line = _escape( "{", line );
  538.         line = _escape( "}", line );
  539.  
  540.         /* Return fully-escaped line */
  541.         return( line );
  542.  
  543. }  /* _line_to_reg_exp() */
  544.  
  545. /**************************************
  546.  * Macro : _dos_shell
  547.  * Syntax: static void _dos_shell( command_line, file_spec )
  548.  * Entry : command_line is the DOS command line to have the shell
  549.  *          execute.  It may contain special parameters documented below.
  550.  *          Case of the special parameters is significant.  It may also
  551.  *          contain the DOS standard output redirection characters > or >>,
  552.  *          but the command must then be bracketed with double quotes.  The
  553.  *          quotes will be removed before executing the command line.
  554.  *         file_spec is the complete name of the desired source file.
  555.  * Exit  : The command line will have been executed.  No return value is sent
  556.  *          back.
  557.  * Special command_line parameters:
  558.  *          %s  - entire file specification of desired file (file_spec parm)
  559.  *          %d  - drive of desired file
  560.  *          %p  - path (no drive) of desired file (no terminating backslash)
  561.  *          %f  - filename of desired file (no drive or path)
  562.  *          %c  - current directory (in drive:path format suitable for CD'ing)
  563.  *                NOTE: The %c parameter is only supported in ME v2.20 and
  564.  *                greater.
  565.  *          %u  - user-defined substitution string
  566.  *          %%  - single %
  567.  */
  568.  
  569. _dos_shell( command_line, file_spec )
  570.   string        command_line;
  571.   string        file_spec;
  572. {
  573.  
  574.         int     i, i2;
  575.         int     fname_start;
  576.         int     last_path_separator;
  577.         string  drive;
  578.         string  path;
  579.         string  fname;
  580.         string  curr_dir;
  581.         string  sub_str;
  582.  
  583.  
  584.         /* Remove any leading and trailing whitespace from command_line */
  585.         command_line = ltrim( rtrim( command_line ) );
  586.  
  587.         /* If command_line is bracketed by double quotes, remove them */
  588.         if( substr( command_line, 1, 1 ) == "\""
  589.             &&
  590.             substr( command_line, strlen( command_line ), 1 ) == "\"" ){
  591.                 /* Remove double quotes */
  592.                 command_line = substr( command_line,
  593.                                        2,
  594.                                        strlen( command_line ) - 2 );
  595.         }
  596.  
  597.         /* Create special parameter strings */
  598.         /* Get drive:path */
  599.         last_path_separator = index( file_spec, "\\" );
  600.         while( (i = index( substr( file_spec,
  601.                                    last_path_separator + 1,
  602.                                    strlen( file_spec ) - last_path_separator ),
  603.                            "\\" )) ){
  604.                 last_path_separator = last_path_separator + i;
  605.         }
  606.  
  607.         /* Save starting index of filename */
  608.         fname_start = last_path_separator + 1;
  609.  
  610.         /* If not in root, don't count last backslash */
  611.         if( last_path_separator != 3 ){
  612.                 --last_path_separator;
  613.         }
  614.  
  615.         /* Break drive:path into separate strings */
  616.         drive = substr( file_spec, 1, 2 );                      /* %d special parm */
  617.         path  = substr( file_spec, 3, last_path_separator - 2 );/* %p special parm */
  618.  
  619.         /* Get filename only */
  620.         fname = substr( file_spec,
  621.                         fname_start,
  622.                         strlen( file_spec ) - fname_start + 1 );/* %f special parm */
  623.  
  624.         /* Get current directory */
  625.         /* NOTE: Only supported in ME v2.20 or later */
  626.         if( VER220_PLUS ){
  627.                 curr_dir = getcwd();                            /* %c special parm */
  628.         }else{
  629.                 curr_dir = "";                                  /* %c special parm */
  630.         }
  631.  
  632.         /* Parse command line, replacing special parameters */
  633.         i = index( command_line, "%" );
  634.         while( i ){
  635.                 /* Get character after "%" */
  636.                 switch( substr( command_line, i + 1, 1 ) ){
  637.                 case "s" : /* Entire file specification of desired file */
  638.                            sub_str = file_spec;
  639.                            break;
  640.  
  641.                 case "d" : /* Drive of desired file */
  642.                            sub_str = drive;
  643.                            break;
  644.  
  645.                 case "p" : /* Path (no drive) of desired file */
  646.                            sub_str = path;
  647.                            break;
  648.  
  649.                 case "f" : /* Filename of desired file */
  650.                            sub_str = fname;
  651.                            break;
  652.  
  653.                 case "c" : /* Current directory (ME v2.20+ only) */
  654.                            sub_str = curr_dir;
  655.                            break;
  656.  
  657.                 case "u" : /* User-defined substitution string */
  658.                            sub_str = get_tty_str( "Enter substitution string: " );
  659.                            break;
  660.  
  661.                 case "%" : /* Single percent sign */
  662.                            sub_str = "%";
  663.                            break;
  664.  
  665.                 default  : /* Unrecognized, replace with empty string */
  666.                            sub_str = "";
  667.                            break;
  668.                 }
  669.  
  670.                 /* Replace special parameter with substitution string */
  671.                 command_line = strcat( strcat( substr( command_line, 1, i - 1 ), /* First portion */
  672.                                                sub_str ),                        /* Substitution string */
  673.                                        substr( command_line,                     /* Last portion  */
  674.                                                i + 2,
  675.                                                strlen( command_line ) - sub - 1 ) );
  676.  
  677.                 /* Look for any more "%" */
  678.                 if( (i2 = index( substr( command_line,
  679.                                          i + 1,
  680.                                          strlen( command_line ) - i + 1 ),
  681.                                  "%" )) ){
  682.                         /* There are more parameters, process them */
  683.                         i = i + i2;
  684.                 }else{
  685.                         /* No more parameters */
  686.                         i = 0;
  687.                 }
  688.         }
  689.  
  690.         /* Send command line to secondary DOS command processor for execution */
  691.         /* Note: ME will notify the user of the executing command line */
  692.         os_command( command_line );
  693.  
  694. }  /* _dos_shell() */
  695.  
  696. /**************************************
  697.  * Macro : _pctags_main
  698.  * Syntax: static int _pctags_main( function_name )
  699.  * Entry : function_name = function name to locate and retrieve the file in
  700.  *          which it is defined
  701.  * Exit  : Non-zero value if function definition is found, file containing the
  702.  *          definition is loaded and cursor is placed on the definition's
  703.  *          line.
  704.  *         Else return zero.
  705.  * Description:
  706.  *         RETRO operation is performed.  This requires the following steps:
  707.  *              -- Cycle through all required tagfiles.  Default = *.tag or
  708.  *                 all tagfiles specified in RETRO environment variable
  709.  *                 which may contain multiple file specs, each with wildcards,
  710.  *                 separated by semicolons.  For example,
  711.  *                 SET RETRO=c:\me\tagfiles\*.tag;c:\prog\*.tag
  712.  *              -- Search each tagfile for the function_name string.
  713.  *              -- If not found in any tagfile, display message and quit.
  714.  *              -- If found, get name of file and definition line from tagfile.
  715.  *              -- Close tagfile.
  716.  *              -- Load file for editing.
  717.  *              -- If file cannot be found, tag information contains an "EXEC"
  718.  *                 command and environment variable RETROEXEC is defined,
  719.  *                 execute the command line value assigned to it.  Try to load
  720.  *                 the file again.
  721.  *              -- Search file for definition line.
  722.  *              -- If any of these operations fail, display appropriate error
  723.  *                 message and quit.
  724.  *
  725.  * Use of Global Variables:
  726.  *      pctags_first_call       -- assign
  727.  *      pctags_tagname_case     -- access
  728.  */
  729.  
  730. _pctags_main( function_name )
  731.   string        function_name;
  732. {
  733.  
  734.         int     org_buff_id;
  735.         int     buff_id;
  736.         int     found_match;
  737.         int     ret_value;
  738.         int     file_start;
  739.         int     decl_start;
  740.         int     continue_search;
  741.         int     exec_env;
  742.         int     line_number;
  743.         int     special_chars;
  744.  
  745.         string  tagfile_name;
  746.         string  line;
  747.         string  file_spec;
  748.         string  decl_line;
  749.         string  env_retroexec;
  750.  
  751.  
  752.         /* Save current buffer context */
  753.         org_buff_id = currbuf();
  754.  
  755.         /* Set case sensitivity for tagname search */
  756.         ignore_case( pctags_tagname_case );     /* User-settable in init() */
  757.  
  758.         /* Insert start-of-line metacharacter into function_name string */
  759.         function_name = sprintf( "^%s", function_name );
  760.  
  761.         /* Initialize flags */
  762.         found_match       = FALSE;
  763.         pctags_first_call = TRUE;               /* Global flag for _get_tagfile() */
  764.  
  765.         /* Cycle thru all RETRO (or default) tagfiles until done or match found */
  766.         while( !found_match ){
  767.                 /* Get a tagfile */
  768.                 tagfile_name = _get_tagfile();
  769.  
  770.                 /* If no more tagfiles to get, quit */
  771.                 if( strlen( tagfile_name ) == 0 ){
  772.                         /* Tagname not found in tagfiles */
  773.                         message( "%snot found.  Hit any key...",
  774.                                  substr( function_name,
  775.                                          2,
  776.                                          strlen( function_name ) - 1 ) );
  777.                         bell();
  778.                         get_tty_char();
  779.  
  780.                         /* Exit RETRO, return "failure" code */
  781.                         ret_value = UNSUCCESSFUL;
  782.                         goto exit;
  783.                 }
  784.  
  785.                 /* Create temporary tagfile buffer and load tagfile */
  786.                 /* Note: This is guaranteed to work because we already know
  787.                  * the file exists.
  788.                  */
  789.                 buff_id = create_buffer( tagfile_name );
  790.                 setcurrbuf( buff_id );          /* Make it the current buffer */
  791.                 gobof();                        /* Force to start of file */
  792.  
  793.                 /* Search tagfile for function name */
  794.                 if( fsearch( function_name ) == 0 ){
  795.                         /* Did not find function_name in this tagfile */
  796.                         /* Delete the buffer */
  797.                         setcurrbuf( org_buff_id ); /* Reset to valid buffer */
  798.                         delete_buffer( buff_id );  /* Delete tagfile buffer */
  799.  
  800.                         /* Try another tagfile */
  801.                         continue;               /* Back to while-loop */
  802.                 }
  803.  
  804.                 /* Found a good match! */
  805.                 /* Stop searching tagfiles */
  806.                 found_match = TRUE;
  807.  
  808.                 /* Read line with match, including newline */
  809.                 line = currline();
  810.  
  811.                 /* Done with tagfile, close it */
  812.                 setcurrbuf( org_buff_id );      /* Reset to valid buffer */
  813.                 delete_buffer( buff_id );
  814.  
  815.                 /* Format of line:
  816.                  * Column 1: Function name searching for (variable length)
  817.                  * Single space terminator
  818.                  * Optional RETRO commands (! and #)
  819.                  * Complete file specification of file containing definition
  820.                  *   of function (variable length)
  821.                  * Single space terminator
  822.                  * Caret (^) character signifying the start of the definition
  823.                  *   line
  824.                  * Line from file that defines the function, including carriage
  825.                  *   return (variable length)
  826.                  * End of line is the last character in the definition line,
  827.                  *   i.e. carriage return
  828.                  * As an example:
  829.                  *   "function c:\dir\file.c ^int function( arg1, arg2 )\n"
  830.                  */
  831.  
  832.                 /* Determine starting indices of file spec and definition line */
  833.                 file_start = index( line, " " );
  834.                 ++file_start;
  835.  
  836.                 decl_start = index( substr( line,
  837.                                             file_start,
  838.                                             strlen( line ) - file_start + 1 ),
  839.                                     " " ) + file_start;
  840.  
  841.                 /* Extract file spec and definition line */
  842.                 file_spec = substr( line, file_start, decl_start - file_start );
  843.                 /* Note: Do not include leading '^' in decl_line */
  844.                 decl_line = substr( line,
  845.                                     decl_start + 1,
  846.                                     strlen( line ) - decl_start );
  847.  
  848.                 /* Check file_spec for special leading characters */
  849.                 continue_search = TRUE;
  850.                 exec_env        = FALSE;
  851.                 line_number     = FALSE;
  852.                 special_chars   = 1;            /* One-base strings */
  853.                 while( continue_search ){
  854.                         switch( substr( file_spec, special_chars, 1 ) ){
  855.                         case "!" :
  856.                                 /* If file_spec not found, execute RETROEXEC
  857.                                  * environment variable command line.  This is
  858.                                  * usually used to extract the file from a
  859.                                  * version-control library.
  860.                                  */
  861.                                 exec_env = TRUE;
  862.  
  863.                                 /* Increment string index */
  864.                                 ++special_chars;
  865.                                 break;
  866.  
  867.                         case "#" :
  868.                                 /* Line number of definition is stored in
  869.                                  * tagfile instead of line contents.
  870.                                  */
  871.                                 line_number = TRUE;
  872.                                 ++special_chars;
  873.                                 break;
  874.  
  875.                         default :
  876.                                 /* Stop parsing special chars */
  877.                                 continue_search = FALSE;
  878.                                 break;
  879.                         }
  880.                 }
  881.  
  882.                 /* Remove all leading special chars from file_spec */
  883.                 file_spec = substr( file_spec,
  884.                                     special_chars,
  885.                                     strlen( file_spec ) - special_chars );
  886.  
  887.         }
  888.  
  889.         /* Verify that file_spec exists */
  890.         if( set_filespec( file_spec ) == 0 ){
  891.                 /* If exec_env active, get RETROEXEC value */
  892.                 if( exec_env ){
  893.                         if( (env_retroexec = getenv( "RETROEXEC" )) != "" ){
  894.                                 /* Execute the variable's value */
  895.                                 _dos_shell( env_retroexec, file_spec );
  896.                         }
  897.                 }
  898.  
  899.                 /* Check for file's existence now */
  900.                 if( set_filespec( file_spec ) == 0 ){
  901.                         /* Still not found */
  902.                         message( "%s does not exist.  Update tagfile.  Hit any key...",
  903.                                  file_spec );
  904.                         bell();
  905.                         get_tty_char();
  906.  
  907.                         /* Return "failure" value */
  908.                         ret_value = UNSUCCESSFUL;
  909.                         goto exit;              /* Cleanup and quit */
  910.                 }
  911.         }
  912.  
  913.         /* Create new buffer for file, make it the current buffer and load
  914.          * the file into it.
  915.          */
  916.         buff_id = create_buffer( file_spec );   /* Should never fail, file guaranteed to exist */
  917.         setcurrbuf( buff_id );
  918.         gobof();                                /* Force to beginning of file */
  919.  
  920.         /* Searching for line contents or line number? */
  921.         if( line_number ){
  922.                 /* Goto specified line number in source file */
  923.                 goline( atoi( decl_line ) );
  924.  
  925.         }else{
  926.                 /* Search for definition line */
  927.                 /* Escape all chars in line that would be interpreted as
  928.                  * meta-characters.
  929.                  */
  930.                 decl_line = _line_to_reg_exp( decl_line );
  931.  
  932.                 /* Surround line with start-of-line(^) and end-of-line($)
  933.                  * meta-chars.
  934.                  */
  935.                 decl_line = sprintf( "^%s$", decl_line );
  936.  
  937.                 /* Search file for definition line */
  938.                 if( fsearch( decl_line ) == 0 ){
  939.                         /* Line not found, tagfile is obsolete */
  940.                         /* Delete file and buffer */
  941.                         setcurrbuf( org_buff_id );      /* Reset current buffer */
  942.                         delete_buffer( buff_id );
  943.  
  944.                         /* Tell user */
  945.                         message( "Update %s with %s.  Hit any key...",
  946.                                  tagfile_name,
  947.                                  file_spec );
  948.                         bell();
  949.                         get_tty_char();
  950.  
  951.                         /* Return "failure" code */
  952.                         ret_value = UNSUCCESSFUL;
  953.                         goto exit;
  954.                 }
  955.         }
  956.  
  957.         /* Found function definition! */
  958.         /* Show buffer in new window */
  959.         show_buffer( buff_id );
  960.  
  961.         /* Set "success" return code */
  962.         ret_value = SUCCESSFUL;
  963.  
  964. exit:
  965.         /* Set case-sensitivity upon exit */
  966.         /* Note: You may change this to TRUE if you want to ignore case; ME
  967.          * does not provide a method of getting the case status so we don't
  968.          * know what it was set to on entry.
  969.          */
  970.         ignore_case( FALSE );
  971.  
  972.         /* Return SUCCESSFUL or UNSUCCESSFUL */
  973.         return( ret_value );
  974.  
  975. }  /* _pctags_main() */
  976.  
  977. /**************************************/
  978.