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

  1. {*
  2.  * ---------------------------------------------------
  3.  * PC-TAGS(tm) RETRO Program for the Multi-Edit Editor
  4.  * ---------------------------------------------------
  5.  *
  6.  * Multi-Edit 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.  * Entry Points
  17.  * ------------
  18.  *       pctags_auto      -- uses word under cursor for search
  19.  *       pctags_prompt    -- prompts for a word to use in search
  20.  *
  21.  * Support Macros
  22.  * --------------
  23.  *       _init            -- create and initialize global variables
  24.  *       _delimiter       -- determinse if a character is a word delimiter
  25.  *       _dos_shell       -- execute RETROEXEC environment value thru DOS
  26.  *       _pctags_main     -- search and retrieval workhorse code
  27.  *       _get_tagfile     -- retrieve the name of the next tagfile to search
  28.  *       _parse_file_spec -- break multiple tagfile specs into individual
  29.  *                              directory/filename parts
  30.  *       _convert_slashes -- convert all slashes in string to backslashes
  31.  *       _lower           -- convert all characters in string to lowercase
  32.  *       _getcd           -- get current drive and directory
  33.  *       _pos_line_in_win -- position current line to third line in window
  34.  *
  35.  * History:
  36.  * --------
  37.  *  1 Oct 89 -- Version 1.00
  38.  *
  39.  *}
  40.  
  41. {***************************************}
  42. { Specify compiled file name }
  43.  
  44. $MACRO_FILE RETRO;                       { Compile to RETRO.MAC }
  45.  
  46. {***************************************
  47.  * Macro : _init
  48.  * Syntax: _init;
  49.  * Entry : None.  Called at beginning of pctags_auto() and pctags_prompt()
  50.  *          macros to initialize the global variables.
  51.  * Exit  : All RETRO global variables are initialized.
  52.  * Note  : This macro must be called every time pctags_auto() and
  53.  *          pctags_prompt() are invoked.
  54.  *}
  55.  
  56. $MACRO _init;
  57.  
  58.         { Set this variable to CASE_INSENSITIVE if you want the search
  59.           for a matching tagname to ignore case.
  60.         }
  61.         SET_GLOBAL_STR( 'pctags_tagname_case',   'CASE_SENSITIVE' );
  62.  
  63.         { Initialize all global variables.  Create if necessary. }
  64.         SET_GLOBAL_STR( 'pctags_env_tagfiles',   ''    );
  65.         SET_GLOBAL_STR( 'pctags_env_path',       ''    );
  66.         SET_GLOBAL_STR( 'pctags_env_retroexec',  ''    );
  67.         SET_GLOBAL_INT( 'pctags_first_call',     FALSE );
  68.  
  69. END_MACRO;  { _init }
  70.  
  71. {**************************************
  72.  * Macro : pctags_auto
  73.  * Syntax: pctags_auto;
  74.  * Entry : None.  Callable from outside this module.
  75.  * Exit  : Complete RETRO operation is performed.  Tagname is extracted from
  76.  *           under current cursor position.
  77.  *         Returns TRUE if operation is a success, else FALSE.
  78.  * Note  : This is one of the two entry points into this module.
  79.  *
  80.  * Use of Global Variables:
  81.  *      pctags_function_name    -- assign
  82.  *}
  83.  
  84. $MACRO pctags_auto;
  85.  
  86.         DEF_INT( delimiter );
  87.         DEF_STR( tagname[ 80 ] );
  88.  
  89.  
  90.         { Initialize global variables }
  91.         RUN_MACRO( '_init' );
  92.  
  93.         { Make sure cursor is currently under a valid word }
  94.         RUN_MACRO( '_delimiter ' + CUR_CHAR );
  95.         IF RETURN_INT THEN
  96.                 { Cursor not under a word }
  97.                 MAKE_MESSAGE( 'Cursor is not under a valid word' );
  98.                 BEEP;
  99.                 RETURN_INT := TRUE;     { Multi-Edit chokes on FALSE values }
  100.                 GOTO END;               { Abort macro }
  101.         END;
  102.  
  103.         { Save current column position }
  104.         MARK_POS;
  105.  
  106.         { Backtrack to start of word }
  107.         delimiter := FALSE;
  108.         WHILE delimiter = FALSE DO
  109.                 { Stop backing up if at start of line }
  110.                 IF C_COL = 1 THEN
  111.                         { We're at the start of the word }
  112.                         delimiter := TRUE;
  113.                 ELSE
  114.                         { Move one character left }
  115.                         LEFT;
  116.  
  117.                         { Check this character for a delimiter }
  118.                         RUN_MACRO( '_delimiter ' + CUR_CHAR );
  119.                         delimiter := RETURN_INT;
  120.  
  121.                         { Have to check for delimiter here so we can adjust
  122.                           cursor.
  123.                         }
  124.                         IF delimiter THEN
  125.                                 { Move cursor to forward to start of word }
  126.                                 RIGHT;
  127.                         END;
  128.                 END;
  129.         END;
  130.  
  131.         { Grab the characters that make up this word }
  132.         tagname   := '';
  133.         delimiter := FALSE;
  134.         WHILE delimiter = FALSE DO
  135.                 { Append current character to tagname string }
  136.                 tagname := tagname + CUR_CHAR;
  137.  
  138.                 { Move cursor to next char }
  139.                 RIGHT;
  140.  
  141.                 { If we've reached the end of the line, we're done }
  142.                 IF AT_EOL THEN
  143.                         { Stop grabbing characters }
  144.                         delimiter := TRUE;
  145.                 ELSE
  146.                         { Check if this character is the end of the word }
  147.                         RUN_MACRO( '_delimiter ' + CUR_CHAR );
  148.                         delimiter := RETURN_INT;
  149.                 END;
  150.         END;
  151.  
  152.         { tagname now holds the tagname to retrieve }
  153.         { Restore original cursor position }
  154.         GOTO_MARK;
  155.  
  156.         { Perform retrieval operation }
  157.         RUN_MACRO( '_pctags_main ' + tagname + ' ' );
  158.  
  159. END:
  160. END_MACRO;  { pctags_auto }
  161.  
  162. {***************************************
  163.  * Macro : pctags_prompt
  164.  * Syntax: pctags_prompt;
  165.  * Entry : None. Callable from outside this module.
  166.  * Exit  : Complete RETRO operation is performed.  Tagname is prompted for
  167.  *           on the message line.
  168.  *         Returns TRUE if operation is a success, else FALSE.
  169.  * Notes : This is one of two entry points into this module.
  170.  *         The message line must be active or else ??? will happen.
  171.  *
  172.  * Use of Global Variables:
  173.  *      pctags_function_name    -- assign
  174.  *}
  175.  
  176. $MACRO pctags_prompt;
  177.  
  178.         DEF_STR( tagname[ 80 ] );
  179.  
  180.  
  181.         { Initialize global variables }
  182.         RUN_MACRO( '_init' );
  183.  
  184.         { Prompt for and get tagname }
  185.         IF STRING_IN( tagname, 'Tagname:',
  186.                       71, MESSAGE_COL, MESSAGE_ROW, '' ) = FALSE THEN
  187.                 { User pressed <Esc>, return FALSE }
  188. ABORT:
  189.                 RETURN_INT := TRUE;     { Multi-Edit chokes on FALSE values }
  190.                 GOTO END;               { Abort macro }
  191.         END;
  192.  
  193.         { Make sure empty string wasn't entered }
  194.         IF SVL( tagname ) = 0 THEN
  195.                 GOTO ABORT;
  196.         END;
  197.  
  198.         { Perform retrieval operation }
  199.         RUN_MACRO( '_pctags_main ' + tagname + ' ' );
  200.  
  201. END:
  202. END_MACRO;  { pctags_prompt }
  203.  
  204. {***************************************
  205.  * Macro : _delimiter
  206.  * Syntax: _delimiter character
  207.  * Entry : Character is a char to test if it is a word delimiter.
  208.  * Exit  : If character is an alphanumeric, underscore(_) or period(.) then it
  209.  *           is NOT a delimiter.  Returns RETURN_INT as FALSE.  Else return TRUE.
  210.  *}
  211.  
  212. $MACRO _delimiter;
  213.  
  214.         DEF_CHAR( ch );
  215.  
  216.  
  217.         { Get argument }
  218.         ch := MPARM_STR;
  219.  
  220.         { Convert to uppercase for fewer comparisons }
  221.         ch := CAPS( ch );
  222.  
  223.         { Check if alphabetic }
  224.         IF ( ch >= 'A' ) AND ( ch <= 'Z' ) THEN
  225.                 { Not a delimiter }
  226.                 RETURN_INT := FALSE;
  227.                 GOTO END;
  228.         END;
  229.  
  230.         { Check if numeric }
  231.         IF ( ch >= '0' ) AND ( ch <= '9' ) THEN
  232.                 { Not a delimiter }
  233.                 RETURN_INT := FALSE;
  234.                 GOTO END;
  235.         END;
  236.  
  237.         { Check for underscore or period }
  238.         IF ( ch = '_' ) OR ( ch = '.' ) THEN
  239.                 { Not a delimiter }
  240.                 RETURN_INT := FALSE;
  241.                 GOTO END;
  242.         END;
  243.  
  244.         { Character IS a delimiter }
  245.         RETURN_INT := TRUE;
  246.  
  247. END:
  248. END_MACRO;  { _delimiter }
  249.  
  250. {***************************************
  251.  * Macro : _lower
  252.  * Syntax: _lower string
  253.  * Entry : string is a character string of any case.
  254.  * Exit  : RETURN_STR = string with all lower case characters.
  255.  *}
  256.  
  257. $MACRO _lower;
  258.  
  259.         DEF_STR( string );
  260.         DEF_CHAR( ch );
  261.         DEF_INT( str_length,
  262.                  counter );
  263.  
  264.  
  265.         { Get argument }
  266.         string := MPARM_STR;
  267.  
  268.         { Get length of string }
  269.         str_length := SVL( string );
  270.  
  271.         { Convert each uppercase character to lowercase }
  272.         counter := 1;
  273.         WHILE counter <= str_length DO
  274.                 { Get the character at the 'counter' position }
  275.                 ch := COPY( string, counter, 1 );
  276.  
  277.                 { Check if character is uppercase alpha }
  278.                 IF ch >= 'A' THEN
  279.                         IF ch <= 'Z' THEN
  280.                                 { Convert to lowercase }
  281.                                 ch := CHAR( ASCII( ch ) + $20 );
  282.  
  283.                                 { Replace uppercase char in string with lowercase }
  284.                                 string := STR_DEL( string, counter, 1 );
  285.                                 string := STR_INS( ch, string, counter );
  286.                         END;
  287.                 END;
  288.  
  289.                 { Increment counter }
  290.                 counter := counter + 1;
  291.         END;
  292.  
  293.         { Return converted string }
  294.         RETURN_STR := string;
  295.  
  296. END_MACRO;  { _lower }
  297.  
  298. {***************************************
  299.  * Macro : _convert_slashes
  300.  * Syntax: _convert_slashes string
  301.  * Entry : string = string which may contain slash characters.
  302.  * Exit  : RETURN_STR = string with all slashes converted to backslashes
  303.  * Note  : This conversion is necessary because the LOAD_FILE procedure does
  304.  *           not support slashes in the file spec.  (The FIND_FIRST/NEXT
  305.  *           functions do.)
  306.  *}
  307.  
  308. $MACRO _convert_slashes;
  309.  
  310.         DEF_INT( slash );
  311.         DEF_STR( string );
  312.  
  313.  
  314.         { Get argument }
  315.         string := MPARM_STR;
  316.  
  317.         slash := XPOS( '/', string, 1 );
  318.         WHILE slash <> 0 DO
  319.                 { Convert slash to backslash }
  320.                 string := STR_DEL( string, slash, 1 );     { Remove slash }
  321.                 string := STR_INS( '\', string, slash );   { Insert backslash }
  322.  
  323.                 { Check for more slashes }
  324.                 slash := XPOS( '/', string, slash + 1 );
  325.         END;
  326.  
  327.         { Return converted string }
  328.         RETURN_STR := string;
  329.  
  330. END_MACRO;  { _convert_slashes }
  331.  
  332. {***************************************
  333.  * Macro : _getcd
  334.  * Syntax: _getcd
  335.  * Entry : None.
  336.  * Exit  : RETURN_STR is set to the current directory, including drive.
  337.  *}
  338.  
  339. $MACRO _getcd;
  340.  
  341.         DEF_INT( segment,
  342.                  offset );
  343.         DEF_STR( dos_buffer[ 64 ],
  344.                  curr_dir[ 66 ] );
  345.  
  346.  
  347.         { This is going to be ugly... }
  348.         { Initialize current directory string }
  349.         curr_dir := '';
  350.  
  351.         { Call DOS function $19 to get current drive }
  352.         R_AX := $1900;
  353.         INTR( $21 );                    { AL = drive code (0=A, 1=B, etc.) }
  354.         R_AX := R_AX AND $00FF;         { Clear AH }
  355.  
  356.         { Create drive spec with colon }
  357.         curr_dir := CHAR( R_AX + ASCII( 'A' ) ) + ':';
  358.  
  359.         { Call DOS function $47 to get current directory }
  360.         R_AX := $4700;
  361.         R_DX := $0000;
  362.         R_DS := SEG( dos_buffer );
  363.         R_SI := OFS( dos_buffer ) + 4;  { Don't trash length and max-length fields }
  364.         INTR( $21 );                    { DS:SI = ASCIIZ current directory }
  365.  
  366.         { Copy contents of dos_buffer into Multi-Edit string }
  367.         segment  := SEG( dos_buffer );
  368.         offset   := OFS( dos_buffer ) + 4;
  369.         WHILE PEEK( segment, offset ) <> $00 DO
  370.                 { Append char to Multi-Edit string }
  371.                 curr_dir := curr_dir + CHAR( PEEK( segment, offset ) );
  372.  
  373.                 { Advance to next character in dos_buffer }
  374.                 offset := offset + 1;
  375.         END;
  376.  
  377.         { If dos_buffer is empty, current directory is the root.
  378.           Append single backslash to current directory.
  379.         }
  380.         IF offset = OFS( dos_buffer ) + 4 THEN
  381.                 { No chars found in DS:SI }
  382.                 curr_dir := curr_dir + '\';
  383.         END;
  384.  
  385.         { Return constructed drive:current directory }
  386.         RETURN_STR := curr_dir;
  387.  
  388. END_MACRO;  { _getcd }
  389.  
  390. {***************************************
  391.  * Macro : _dos_shell
  392.  * Syntax: _dos_shell file_spec
  393.  * Entry : file_spec is the name of the file the RETROEXEC environment variable
  394.  *           command is supposed to make available.
  395.  *         Global variable pctags_env_retroexec contains the command line to
  396.  *           execute.  It may contain special parameters documented below.
  397.  *           Case of the special parameters is significant.  It may also
  398.  *           contain the DOS standard output redirection characters > or >>,
  399.  *           but the command must then be bracketed with double quotes.  The
  400.  *           quotes will be removed before sending the command line to DOS.
  401.  * Exit  : The command will have been executed.  No return value is sent back.
  402.  * Special command_line parameters:
  403.  *           %s - entire file specification of desired file (file_spec parm)
  404.  *           %d - drive of desired file
  405.  *           %p - path (no drive) of desired file (no terminating backslash)
  406.  *           %f - filename of desired file
  407.  *           %c - current directory (in drive:path format suitable for CD'ing)
  408.  *           %u - user-defined substitution string
  409.  *           %% - single %
  410.  *}
  411.  
  412. $MACRO _dos_shell;
  413.  
  414.         DEF_INT( last_path_separator,
  415.                  fname_start,
  416.                  sub,
  417.                  org_mem,
  418.                  org_refresh );
  419.         DEF_STR( file_spec[ 80 ],
  420.                  command_line[ 128 ],
  421.                  drive[ 2 ],
  422.                  path[ 64 ],
  423.                  fname[ 12 ],
  424.                  curr_dir[ 66 ],
  425.                  sub_str[ 80 ] );
  426.         DEF_CHAR( parm );
  427.  
  428.  
  429.         { Get parameter }
  430.         file_spec := MPARM_STR;
  431.  
  432.         { Move global variable into local storage }
  433.         command_line := GLOBAL_STR( 'pctags_env_retroexec' );
  434.         { ...May not want to do this here...
  435.         RUN_MACRO( '_convert_slashes ' + command_line );
  436.         command_line := RETURN_STR;
  437.         }
  438.  
  439.         { If command line is bracketed by double quotes, remove them }
  440.         IF COPY( command_line, 1, 1 ) = '"' THEN
  441.                 { Check last character }
  442.                 IF COPY( command_line, SVL( command_line ), 1 ) = '"' THEN
  443.                         { Remove double quotes }
  444.                         command_line := COPY( command_line, 2,
  445.                                               SVL( command_line ) - 2 );
  446.                 END;
  447.         END;
  448.  
  449.         { Create special parameter strings }
  450.         { Get drive:path }
  451.         last_path_separator := XPOS( '\', file_spec, 1 );
  452.         WHILE XPOS( '\', file_spec, last_path_separator + 1 ) <> 0 DO
  453.                 { Save position of this backslash }
  454.                 last_path_separator := XPOS( '\', file_spec, last_path_separator + 1 );
  455.         END;
  456.  
  457.         { Save start of filename }
  458.         fname_start := last_path_separator + 1;
  459.  
  460.         { If not in root, don't count last backslash }
  461.         IF last_path_separator <> 3 THEN
  462.                 last_path_separator := last_path_separator - 1;
  463.         END;
  464.  
  465.         { Break drive:path into separate strings }
  466.         drive := COPY( file_spec, 1, 2 );                       { %d special parm }
  467.         path  := COPY( file_spec, 3, last_path_separator - 2 ); { %p special parm }
  468.  
  469.         { Get filename only }                                   { %f special parm }
  470.         fname := COPY( file_spec, fname_start, SVL( file_spec ) - fname_start + 1 );
  471.  
  472.         { Get current directory }
  473.         RUN_MACRO( '_getcd' );
  474.         curr_dir := RETURN_STR;                                 { %c special parm }
  475.  
  476.         { Parse command line, replacing special parameters }
  477.         sub := XPOS( '%', command_line, 1 );
  478.         WHILE sub <> 0 DO
  479.                 { Get character after '%' }
  480.                 parm := COPY( command_line, sub + 1, 1 );
  481.  
  482.                 { Interpret substitution character }
  483.                 IF parm = 's' THEN
  484.                         { Entire file specification of desired file }
  485.                         sub_str := file_spec;
  486.                         GOTO REPLACE;
  487.                 END;
  488.                 IF parm = 'd' THEN
  489.                         { Drive of desired file }
  490.                         sub_str := drive;
  491.                         GOTO REPLACE;
  492.                 END;
  493.                 IF parm = 'p' THEN
  494.                         { Path (no drive) of desired file }
  495.                         sub_str := path;
  496.                         GOTO REPLACE;
  497.                 END;
  498.                 IF parm = 'f' THEN
  499.                         { Filename of desired file }
  500.                         sub_str := fname;
  501.                         GOTO REPLACE;
  502.                 END;
  503.                 IF parm = 'c' THEN
  504.                         { Current directory }
  505.                         sub_str := curr_dir;
  506.                         GOTO REPLACE;
  507.                 END;
  508.                 IF parm = 'u' THEN
  509.                         { User-defined substitution string }
  510.                         sub_str := '';          { In case user hits <Esc> }
  511.                         IF STRING_IN( sub_str, 'Enter substitution string:', 53,
  512.                                       MESSAGE_COL, MESSAGE_ROW, '' ) = 0 THEN
  513.                                 { Make sure input string is clear }
  514.                                 sub_str := '';
  515.                         END;
  516.                         GOTO REPLACE;
  517.                 END;
  518.                 IF parm = '%' THEN
  519.                         sub_str := '%';
  520.                         GOTO REPLACE;
  521.                 END;
  522.  
  523.                 { Else unrecognized substitution parameter }
  524.                 sub_str := '';
  525.  
  526. REPLACE:
  527.                 { Replace special parameter with substitution string }
  528.                 command_line := COPY( command_line, 1, sub - 1 )        { Portion before '%' }
  529.                                 +
  530.                                 sub_str                                 { Substitution string }
  531.                                 +                                       { Portion after '%' }
  532.                                 COPY( command_line, sub + 2, SVL( command_line ) - sub - 1 );
  533.  
  534.                 { Look for any more '%' }
  535.                 sub := XPOS( '%', command_line, sub + 1 );
  536.         END;
  537.  
  538.         { Let user know what's going on }
  539.         MAKE_MESSAGE( 'Executing RETROEXEC command' );
  540.  
  541.         { Save current memory usage and force maximum freeing of available
  542.           memory.
  543.         }
  544.         org_mem    := REDUCE_MEM;
  545.         REDUCE_MEM := TRUE;
  546.  
  547.         { Send command line to secondary DOS command processor for execution }
  548. {        SAVE_DOS_SCREEN; }
  549.         SHELL_TO_DOS( command_line, TRUE );
  550. {        REST_DOS_SCREEN; }
  551.  
  552.         { Remove any output displayed onscreen during execution }
  553.         org_refresh := REFRESH;
  554.         REFRESH := TRUE;
  555.         NEW_SCREEN;
  556.         REFRESH := org_refresh;
  557.  
  558.         { Restore memory-usage context }
  559.         REDUCE_MEM := org_mem;
  560.  
  561. END_MACRO;  { _dos_shell }
  562.  
  563. {***************************************
  564.  * Macro : _parse_file_spec
  565.  * Syntax: _parse_file_spec file_spec
  566.  * Entry : file_spec = file specification to parse.  May contain wildcards.
  567.  *           May also contain multiple specs, each separated by a semicolon.
  568.  *           For example, "c:\*.tag;d:\me\tagfiles\*.tag;c:\pctags.tag".
  569.  *           Both backslashes and slashes are recognized as path separators.
  570.  * Exit  : Any drive:path in the first file spec is stored in the global
  571.  *           variable pctags_env_path.
  572.  *         Any subsequent file specs are stored in global var
  573.  *           pctags_env_tagfiles.
  574.  *         The filename portion of the spec is returned in RETURN_STR (may
  575.  *           contain wildcards).
  576.  *
  577.  * Use of Global Variables:
  578.  *      pctags_env_tagfiles     -- assign
  579.  *      pctags_env_path         -- assign
  580.  *}
  581.  
  582. $MACRO _parse_file_spec;
  583.  
  584.         DEF_INT( last_path_separator,
  585.                  semicolon );
  586.         DEF_STR( file_spec[ 128 ] );
  587.  
  588.  
  589.         { Get the parameter }
  590.         file_spec := MPARM_STR;
  591.  
  592.         { Does file_spec contain multiple specs? }
  593.         semicolon := XPOS( ';', file_spec, 1 );
  594.         IF semicolon = 0 THEN
  595.                 { No, file_spec contains one drive:path.
  596.                   Clear pctags_env_tagfiles.
  597.                 }
  598.                 SET_GLOBAL_STR( 'pctags_env_tagfiles', '' );
  599.         ELSE
  600.                 { Yes, store subsequent specs in pctags_env_tagfiles }
  601.                 SET_GLOBAL_STR( 'pctags_env_tagfiles',
  602.                                 COPY( file_spec,
  603.                                       semicolon + 1,
  604.                                       SVL( file_spec ) - semicolon ) );
  605.  
  606.                 { Place first spec only in file_spec }
  607.                 file_spec := COPY( file_spec, 1, semicolon - 1 );
  608.         END;
  609.  
  610.         { Does file_spec contain a drive or path spec? }
  611.         { Find index to last path separator }
  612.         last_path_separator := XPOS( '\', file_spec, 1 );
  613.         WHILE XPOS( '\', file_spec, last_path_separator + 1 ) <> 0 DO
  614.                 { Save position of this backslash }
  615.                 last_path_separator := XPOS( '\', file_spec, last_path_separator + 1 );
  616.         END;
  617.  
  618.         { If no path was specified, check for a drive }
  619.         IF last_path_separator = 0 THEN
  620.                 last_path_separator := XPOS( ':', file_spec, 1 );
  621.         END;
  622.  
  623.         { If drive and/or path were specified, extract from file_spec and
  624.           store in pctags_env_path.
  625.         }
  626.         IF last_path_separator = 0 THEN
  627.                 SET_GLOBAL_STR( 'pctags_env_path', '' );
  628.         ELSE
  629.                 SET_GLOBAL_STR( 'pctags_env_path',
  630.                                 COPY( file_spec, 1, last_path_separator ) );
  631.         END;
  632.  
  633.         { Return file_spec (single spec with possible drive:path and wildcards) }
  634.         RETURN_STR := file_spec;
  635.  
  636. END_MACRO;  { _parse_file_spec }
  637.  
  638. {***************************************
  639.  * Macro : _get_tagfile
  640.  * Syntax: _get_tagfile
  641.  * Entry : None.
  642.  * Exit  : RETURN_STR is set to the retrieved tagfile name.  It equals '' if
  643.  *           no more tagfiles could be found.
  644.  *
  645.  * Use of Global Variables:
  646.  *      pctags_first_call       -- access and assign
  647.  *      pctags_env_tagfiles     -- access
  648.  *      pctags_env_path         -- access
  649.  *}
  650.  
  651. $MACRO _get_tagfile;
  652.  
  653.         DEF_STR( file_spec,
  654.                  single_tagfile );
  655.  
  656.  
  657.         { If this is the first call to this macro during this RETRO
  658.           invocation, get the RETRO environment variable setting, if any.
  659.         }
  660.         IF GLOBAL_INT( 'pctags_first_call' ) = FALSE THEN
  661.                 { Get the next matching file name for file_spec }
  662.                 IF NEXT_FILE = 0 THEN
  663.                         { Match found, get it }
  664.                         single_tagfile := LAST_FILE_NAME;
  665.                 ELSE
  666.                         { No match, set to empty string }
  667.                         single_tagfile := '';
  668.                 END;
  669.         ELSE
  670.                 { Go thru this code once per RETRO invocation }
  671.                 SET_GLOBAL_INT( 'pctags_first_call', FALSE );
  672.  
  673.                 { Get RETRO environment variable value }
  674.                 file_spec := GET_ENVIRONMENT( 'RETRO' );
  675.  
  676.                 { If no RETRO variable defined, use default }
  677.                 IF file_spec = '' THEN
  678.                         file_spec := '*.tags';
  679.                 END;
  680.  
  681.                 { Convert to all lowercase }
  682.                 RUN_MACRO( '_lower ' + file_spec );
  683.                 file_spec := RETURN_STR;
  684.  
  685.                 { Convert any slashes to backslashes }
  686.                 RUN_MACRO( '_convert_slashes ' + file_spec );
  687.                 file_spec := RETURN_STR;
  688.  
  689.                 { Parse any multiple file specs into single specs and
  690.                   set the file pattern to the first parse file_spec (may
  691.                   contain wildcards).
  692.                 }
  693.                 RUN_MACRO( '_parse_file_spec ' + file_spec );
  694.                 file_spec := RETURN_STR;
  695.  
  696.                 { Find first matching file }
  697.                 IF FIRST_FILE( file_spec ) = 0 THEN
  698.                         { Match found, get it }
  699.                         single_tagfile := LAST_FILE_NAME;
  700.                 ELSE
  701.                         { No match, set to empty string }
  702.                         single_tagfile := '';
  703.                 END;
  704.         END;
  705.  
  706.         { Check if we got a tagfile name }
  707.         WHILE single_tagfile = '' DO
  708.                 { Are there more file specs in pctags_env_tagfiles? }
  709.                 IF GLOBAL_STR( 'pctags_env_tagfiles' ) = '' THEN
  710.                         { Nope, this is the end of the road }
  711.                         GOTO EXIT;      { Out of while-loop }
  712.                 END;
  713.  
  714.                 { There are more tagfile specs.  Process them. }
  715.                 RUN_MACRO( '_parse_file_spec '
  716.                            +
  717.                            GLOBAL_STR( 'pctags_env_tagfiles' ) );
  718.                 file_spec := RETURN_STR;
  719.  
  720.                 { Get first matching file name for tagfile spec }
  721.                 IF FIRST_FILE( file_spec ) = 0 THEN
  722.                         { Match found, get it }
  723.                         single_tagfile := LAST_FILE_NAME;
  724.                 ELSE
  725.                         { No match, set to empty string }
  726.                         single_tagfile := '';
  727.                 END;
  728.         END;
  729.  
  730. EXIT:
  731.         { If filename found, convert to lowercase and insert any drive:path }
  732.         IF single_tagfile = '' THEN
  733.                 { Return empty filename }
  734.                 RETURN_STR := '';
  735.         ELSE
  736.                 { Convert to lowercase }
  737.                 RUN_MACRO( '_lower ' + single_tagfile );
  738.                 single_tagfile := RETURN_STR;
  739.  
  740.                 { Insert drive:path }
  741.                 RETURN_STR := GLOBAL_STR( 'pctags_env_path' ) + single_tagfile;
  742.         END;
  743.  
  744. END_MACRO;  { _get_tagfile }
  745.  
  746. {***************************************
  747.  * Macro : _pos_line_in_win
  748.  * Syntax: _pos_line_in_win
  749.  * Entry : None.
  750.  * Exit  : The current line is positioned on the third line in the window.
  751.  * Note  : The window is always a full-screen window.
  752.  *}
  753.  
  754. $MACRO _pos_line_in_win;
  755.  
  756.         IF C_LINE > 2 THEN
  757.                 UP;   UP;               { Move cursor up two lines }
  758.                 DOWN; DOWN;             { Then back down to original point }
  759.         END;
  760.  
  761. END_MACRO;  { _pos_line_in_win }
  762.  
  763. {***************************************
  764.  * Macro : _pctags_main
  765.  * Syntax: _pctags_main function_name
  766.  * Entry : function_name contains the tagname we're searching for.
  767.  * Exit  : TRUE is retrieval operation is successful, else FALSE.
  768.  * Description:
  769.  *      RETRO operation is performed.  This requires the following steps:
  770.  *              -- Cycle through all required tagfiles.  Default = *.tag or
  771.  *                      all tagfiles specified in RETRO environment variable
  772.  *                      which may contain multiple file specs, each with
  773.  *                      wildcards, separated by semicolons.  For example,
  774.  *                      SET RETRO=c:\me\tagfiles\*.tag;c:\prog\*.tag.
  775.  *              -- Search each tagfile for the pctags_function_name string.
  776.  *              -- If not found in any tagfile, display message and quit.
  777.  *              -- If found, get name of file and definition line from tagfile.
  778.  *              -- Close tagfile.
  779.  *              -- Load file for editing.
  780.  *              -- If file cannot be found, tag info contains EXEC command and
  781.  *                      environment variable RETROEXEC is defined, execute the
  782.  *                      command-line value assigned to it.  Try to load the file
  783.  *                      again.
  784.  *              -- If file still cannot be found, display message and quit.
  785.  *              -- Search file for definition line.
  786.  *              -- Place definition line on third line in window (if possible).
  787.  *              -- If any of these operations fail, display appropriate error
  788.  *                      message and quit.
  789.  *
  790.  * Use of Global Variables:
  791.  *      pctags_first_call       -- assign
  792.  *      pctags_function_name    -- access
  793.  *}
  794.  
  795. $MACRO _pctags_main;
  796.  
  797.         DEF_INT( org_win_id,
  798.                  org_case,
  799.                  org_re,
  800.                  org_refresh,
  801.                  file_start,
  802.                  decl_start,
  803.                  exec_env,
  804.                  line_number,
  805.                  special_chars,
  806.                  found_match,
  807.                  continue_search );
  808.         DEF_STR( tagfile_name[ 80 ],
  809.                  function_name[ 128 ],
  810.                  file_spec[ 80 ],
  811.                  decl_line[ 256 ],
  812.                  env_var[ 128 ],
  813.                  line[ 512 ] );
  814.  
  815.  
  816.         { Get argument }
  817.         function_name := MPARM_STR;
  818.  
  819.         { Save current context }
  820.         org_win_id  := WINDOW_ID;       { In case we have to return to it }
  821.         org_case    := IGNORE_CASE;
  822.         org_re      := REG_EXP_STAT;
  823.         org_refresh := REFRESH;
  824.  
  825.         { Set up our context }
  826.         REG_EXP_STAT := FALSE;          { Disable regular expression searches }
  827.         REFRESH      := FALSE;          { No flashing of windows }
  828.  
  829.         { Create hidden tagfile buffer/window }
  830.         CREATE_WINDOW;
  831.         SWITCH_WINDOW( WINDOW_COUNT );
  832.         WINDOW_ATTR := WINDOW_ATTR AND 3; { Make window completely hidden }
  833.  
  834.         { Initialize flags }
  835.         found_match := FALSE;
  836.         SET_GLOBAL_INT( 'pctags_first_call', TRUE );    { Global flag for _get_tagfile() }
  837.         tagfile_name := '';
  838.  
  839. NEXT_TAGFILE:
  840.         { Cycle thru all RETRO (or default) tagfiles until done or match found }
  841.         WHILE found_match = FALSE DO
  842.                 { Get a tagfile }
  843.                 RUN_MACRO( '_get_tagfile' );
  844.                 tagfile_name := RETURN_STR;
  845.  
  846.                 { If no more tagfiles to get, quit }
  847.                 IF tagfile_name = '' THEN
  848.                         { Remove our temporary tagfile window }
  849.                         DELETE_WINDOW;
  850.  
  851.                         { Reset to original window }
  852.                         IF SWITCH_WIN_ID( org_win_id ) THEN
  853.                                 { This should always be true }
  854.                                 { Macro compiler chokes unless return value is used }
  855.                         END;
  856.  
  857.                         { Display message }
  858.                         { Note: function_name ends with a single blank char }
  859.                         MAKE_MESSAGE( function_name + 'not found.' );
  860.                         BEEP;
  861.  
  862.                         { Exit while-loop }
  863.                         GOTO EXIT;
  864.                 END;
  865.  
  866.                 { Load tagfile into our hidden window }
  867.                 LOAD_FILE( tagfile_name );
  868.  
  869.                 { Set up a loop to search though the tagfile.  Multiple
  870.                   searches may be required because a match COULD be found on a
  871.                   string in the tagfile other than the function-name portion of
  872.                   the line.
  873.                 }
  874.                 { Search tagfile for function name }
  875.                 MAKE_MESSAGE( 'Searching ' + tagfile_name );
  876.                 continue_search := TRUE;
  877.                 TOF;            { Force to start of file }
  878.                 IF GLOBAL_STR( 'pctags_tagname_case' ) = 'CASE_SENSITIVE' THEN
  879.                         IGNORE_CASE := FALSE;
  880.                 ELSE
  881.                         IGNORE_CASE := TRUE;
  882.                 END;
  883.                 WHILE continue_search DO
  884. SEARCH_TAGFILE:
  885.                         { Search tagfile for function name }
  886.                         IF SEARCH_FWD( function_name, 0 ) = 0 THEN
  887.                                 { Did not find function name in this tagfile }
  888.                                 { Stop searching this file, try another }
  889.                                 ERASE_WINDOW;           { Guarantee empty buffer }
  890.                                 GOTO NEXT_TAGFILE;      { Back to outer while-loop }
  891.                         END;
  892.  
  893.                         { Found a match }
  894.                         { The match MUST start in the first column.  If it
  895.                           doesn't then a match was found somewhere other than
  896.                           in the function-name portion of the line.
  897.                         }
  898.                         IF C_COL <> 1 THEN
  899.                                 { Not a valid match }
  900.                                 { Advance buffer position so we don't
  901.                                   keep finding the same match.
  902.                                 }
  903.                                 RIGHT;
  904.  
  905.                                 { Continue searching tagfile }
  906.                                 GOTO SEARCH_TAGFILE;
  907.                         END;
  908.  
  909.                         { Found a good match! }
  910.                         { Stop searching this and all other tagfiles }
  911.                         continue_search := FALSE;       { This tagfile }
  912.                         found_match     := TRUE;        { All tagfiles }
  913.  
  914.                         { Read line with match, including newline }
  915.                         line := GET_LINE;
  916.  
  917.                         { Done with tagfile, close it }
  918.                         { Just clear the buffer, we'll use the same window
  919.                           for the source file.
  920.                         }
  921.                         ERASE_WINDOW;
  922.  
  923.                         { Format of line:
  924.                           Column 1: Function name searching for (variable length)
  925.                           Single space terminator
  926.                           Optional RETRO "commands" (# and/or !)
  927.                           Complete file specification of file containing
  928.                             definition of function (variable length)
  929.                           Single space terminator
  930.                           Carot (^) character signifying the start of the
  931.                             definition line
  932.                           Line from file that defines the function, including
  933.                             carriage return (variable length)
  934.                           End of line is the last character in the definition
  935.                             line, i.e. carriage return
  936.                           As an example:
  937.                           "function c:\dir\file.c ^int function( arg1, arg2 )\n"
  938.                         }
  939.  
  940.                         { Determine starting indices of file spec and definition
  941.                           line.
  942.                         }
  943.                         file_start := XPOS( ' ', line, 1 );
  944.                         file_start := file_start + 1;   { Advance past space }
  945.  
  946.                         decl_start := XPOS( ' ', line, file_start );
  947.                         decl_start := decl_start + 2;   { Advance past space and carot }
  948.  
  949.                         { Extract file spec and def line }
  950.                         file_spec := COPY( line, file_start,
  951.                                            decl_start - file_start - 2 );
  952.                         decl_line := COPY( line, decl_start,
  953.                                            SVL( line ) - decl_start + 1 );
  954.  
  955.                         { Check file_spec for embedded-command characters }
  956.                         exec_env        := FALSE;
  957.                         line_number     := FALSE;
  958.                         special_chars   := 1;           { Base-one strings }
  959. NEXT_SPECIAL_CHAR:
  960.                         IF COPY( file_spec, special_chars, 1 ) = '!' THEN
  961.                                 { If file_spec not found, execute RETROEXEC
  962.                                   environment variable command line.  This is
  963.                                   usually used to extract the file from a
  964.                                   source-control library.
  965.                                 }
  966.                                 exec_env := TRUE;
  967.  
  968.                                 { Increment string index }
  969.                                 special_chars := special_chars + 1;
  970.  
  971.                                 { Check for other special chars }
  972.                                 GOTO NEXT_SPECIAL_CHAR;
  973.                         END;
  974.  
  975.                         IF COPY( file_spec, special_chars, 1 ) = '#' THEN
  976.                                 { Line number of definition is stored in
  977.                                   tagfile instead of line contents
  978.                                 }
  979.                                 line_number := TRUE;
  980.                                 special_chars := special_chars + 1;
  981.                                 GOTO NEXT_SPECIAL_CHAR;
  982.                         END;
  983.  
  984.                         { Remove all special chars from file_spec }
  985.                         IF special_chars <> 1 THEN
  986.                                 file_spec := COPY( file_spec, special_chars,
  987.                                                    SVL( file_spec ) - special_chars + 1 );
  988.                         END;
  989.  
  990.                         { Verify that file_spec exists }
  991.                         IF FILE_EXISTS( file_spec ) = 0 THEN
  992.                                 { If exec_env active, get RETROEXEC value }
  993.                                 IF exec_env THEN
  994.                                         SET_GLOBAL_STR( 'pctags_env_retroexec',
  995.                                                         GET_ENVIRONMENT( 'RETROEXEC' ) );
  996.  
  997.                                         { If variable found, execute it }
  998.                                         IF GLOBAL_STR( 'pctags_env_retroexec' ) <> '' THEN
  999.                                                 RUN_MACRO( '_dos_shell '
  1000.                                                            + file_spec );
  1001.                                         END;
  1002.                                 END;
  1003.  
  1004.                                 { Check for file's existence now }
  1005.                                 IF FILE_EXISTS( file_spec ) = 0 THEN
  1006.                                         { Still not found }
  1007.                                         MAKE_MESSAGE( file_spec
  1008.                                                       + ' does not exist.  Update tagfile.' );
  1009.                                         BEEP;
  1010.  
  1011.                                         { Can't go any further }
  1012.                                         DELETE_WINDOW;
  1013.                                         IF SWITCH_WIN_ID( org_win_id ) THEN
  1014.                                                 { Always true }
  1015.                                         END;
  1016.                                         GOTO EXIT;
  1017.                                 END;
  1018.                         END;
  1019.  
  1020.                         { Load file into our (hidden) window }
  1021.                         LOAD_FILE( file_spec );
  1022.                         TOF;                    { Guaranteed at top of file }
  1023.  
  1024.                         { Searching for line contents or line number? }
  1025.                         IF line_number THEN
  1026.                                 { Goto specified line number in source file }
  1027.                                 { Convert string to int }
  1028.                                 IF VAL( line_number, decl_line ) THEN
  1029.                                         { Someone's been messing with the
  1030.                                           tagfile.
  1031.                                         }
  1032.                                         line_number := 1;       { Leave cursor at top of file }
  1033.                                 END;
  1034.                                 GOTO_LINE( line_number );
  1035.  
  1036.                                 { Position line }
  1037.                                 RUN_MACRO( '_pos_line_in_win' );
  1038.  
  1039.                                 { Display filename }
  1040.                                 MAKE_MESSAGE( 'File: ' + file_spec );
  1041.  
  1042.                                 { Set important system variables }
  1043.                                 FILE_NAME    := CAPS( file_spec );
  1044.                                 WINDOW_ATTR  := 0;
  1045.                                 FILE_CHANGED := FALSE;
  1046.                         ELSE
  1047.                                 { Search for definition line }
  1048.                                 IGNORE_CASE := FALSE;   { Force to case-sensitive search }
  1049.  
  1050. KEEP_SEARCHING:
  1051.                                 IF SEARCH_FWD( decl_line, 0 ) THEN
  1052.                                         { Found a match }
  1053.                                         { Match must be at start of line to be
  1054.                                           valid.  If not at start, it could be
  1055.                                           a "forward" declaration.
  1056.                                         }
  1057.                                         IF C_COL <> 1 THEN
  1058.                                                 { Invalid match, keep searching }
  1059.                                                 { Advance cursor position so we
  1060.                                                   won't keep finding the same
  1061.                                                   match.
  1062.                                                 }
  1063.                                                 RIGHT;
  1064.  
  1065.                                                 { Continue searching source file }
  1066.                                                 GOTO KEEP_SEARCHING;
  1067.                                         END;
  1068.  
  1069.                                         { Found function definition! }
  1070.                                         { Position the line in the window }
  1071.                                         RUN_MACRO( '_pos_line_in_win' );
  1072.  
  1073.                                         { Display filename }
  1074.                                         MAKE_MESSAGE( 'File: ' + file_spec );
  1075.  
  1076.                                         { Set important system variables }
  1077.                                         FILE_NAME    := CAPS( file_spec );
  1078.                                         WINDOW_ATTR  := 0;
  1079.                                         FILE_CHANGED := FALSE;
  1080.                                 ELSE
  1081.                                         { Definition file not found in file }
  1082.                                         MAKE_MESSAGE( 'Update ' + tagfile_name +
  1083.                                                       ' with ' + file_spec );
  1084.                                         BEEP;
  1085.  
  1086.                                         { Recovery }
  1087.                                         { Dump this file/window }
  1088.                                         DELETE_WINDOW;
  1089.                                         IF SWITCH_WIN_ID( org_win_id ) THEN
  1090.                                                 { Do this to satisfy macro compiler }
  1091.                                         END;
  1092.                                 END;
  1093.                         END;
  1094.                 END;
  1095.         END;
  1096.  
  1097. EXIT:
  1098.         { Restore original context }
  1099.         IGNORE_CASE  := org_case;
  1100.         REG_EXP_STAT := org_re;
  1101.         REFRESH      := org_refresh;
  1102.  
  1103.         { If match not found in any tagfile, that's not good... }
  1104.         IF found_match = TRUE THEN
  1105.                 { Congratulations! }
  1106.                 RETURN_INT := TRUE;
  1107.         ELSE
  1108.                 { Note: Would rather send FALSE here, but the Multi-Edit
  1109.                   "Run-Macro" menu chokes on it...
  1110.                 }
  1111.                 RETURN_INT := TRUE;
  1112.         END;
  1113.  
  1114. END_MACRO;  { _pctags_main }
  1115.  
  1116. {**************************************}
  1117.