home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / epm603a.zip / EPMMAC.ZIP / TAGS.E < prev    next >
Text File  |  1995-10-26  |  38KB  |  1,095 lines

  1. ;
  2. ; This module is a general purpose engine for providing searching and
  3. ; completion for tagged function names.
  4. ;
  5. ; To add support for another language, update tag_case() if it's a case-sensitive
  6. ; language, update tags_supported to indicate what file extensions are supported,
  7. ; and update proc_search to call the procedure search routine for that language.
  8. ;           tag_case()        Returns 'e' for case sensitive languages and
  9. ;                            'c' for case insensitive languages.
  10. ;
  11. ;     xxxxx_proc_search(var proc_name,find_first)
  12. ;                             If proc_name is null, this function searches
  13. ;                             for a valid procedure in the current buffer. If
  14. ;                             successful, proc_name is set to the procedure
  15. ;                             name and 0 is returned.  The find_first parameter
  16. ;                             when non-zero indicates that the first search
  17. ;                             is being performed.
  18. ;
  19. ;                             If proc_name is NOT null, this function searches
  20. ;                             for the definition of the procedure proc_name in
  21. ;                             the current buffer.  If successful, cursor is
  22. ;                             placed on procedure definition and 0 is returned.
  23. ;                             See one of the procedures C_PROC_SEARCH,
  24. ;                             PAS_PROC_SEARCH, or ASM_PROC_SEARCH for an
  25. ;                             example.
  26.  
  27. define
  28. compile if not defined(C_EXTENSIONS)  -- Keep in sync with CKEYS.E
  29.    C_EXTENSIONS = 'C H SQC'
  30. compile endif
  31.  
  32. compile if not defined(CPP_EXTENSIONS)  -- Keep in sync with CKEYS.E
  33.    CPP_EXTENSIONS = 'CPP HPP CXX HXX SQX JAV JAVA'
  34. compile endif
  35.  
  36. compile if not defined(REXX_EXTENSIONS)  -- Keep in sync with REXXKEYS.E
  37.    REXX_EXTENSIONS = 'BAT CMD ERX EXC EXEC XEDIT REX REXX VRX'
  38. compile endif
  39.  
  40.  
  41. defproc tag_case(filename)
  42.    ext=filetype(filename)
  43.    if wordpos(ext, C_EXTENSIONS CPP_EXTENSIONS ) then   /* Case sensitive language? */
  44.       return 'e'
  45.    endif
  46.    return 'c'  /* Case insensitive language? */
  47.  
  48. defproc tags_supported(ext)
  49.    return wordpos(ext, C_EXTENSIONS CPP_EXTENSIONS  'E ASM PAS PASCAL MODULA' REXX_EXTENSIONS)
  50.  
  51. defproc proc_search(var proc_name, first_flag, ext)
  52.    if wordpos(ext, C_EXTENSIONS CPP_EXTENSIONS ) then
  53.       return c_proc_search(proc_name, first_flag, ext)
  54.    elseif ext = 'ASM' then
  55.       return asm_proc_search(proc_name, first_flag)
  56.    elseif ext = 'PAS' | ext = 'PASCAL' then
  57.       return pas_proc_search(proc_name, first_flag)
  58.    elseif ext = 'MOD' | ext = 'MODULA' then
  59.       return pas_proc_search(proc_name, first_flag, 'e')
  60.    elseif ext = 'E' then
  61.       return e_proc_search(proc_name, first_flag)
  62.    elseif wordpos(ext, REXX_EXTENSIONS) then
  63.       return rexx_proc_search(proc_name, first_flag)
  64.    else
  65.       return 1
  66.    endif
  67.  
  68. /****   The above is all that needs to be modified for adding other languages. *****/
  69.  
  70. compile if not defined(SMALL)  -- If SMALL not defined, then being separately
  71.  define INCLUDING_FILE = 'TAGS.E'
  72. const
  73.    tryinclude 'MYCNF.E'        -- the user's configuration customizations.
  74.  
  75.  compile if not defined(SITE_CONFIG)
  76.     const SITE_CONFIG = 'SITECNF.E'
  77.  compile endif
  78.  compile if SITE_CONFIG
  79.     tryinclude SITE_CONFIG
  80.  compile endif
  81.  
  82. include 'stdconst.e'
  83.  
  84. const
  85.  compile if not defined(NLS_LANGUAGE)
  86.    NLS_LANGUAGE = 'ENGLISH'
  87.  compile endif
  88. include NLS_LANGUAGE'.e'
  89. compile endif
  90.  
  91. const
  92. compile if not defined(TAGS_ANYWHERE)
  93.    TAGS_ANYWHERE = 1          -- Set to 0 if all your procedure definitions start in col. 1
  94. compile endif
  95. compile if not defined(C_TAGS_ANYWHERE)
  96.    C_TAGS_ANYWHERE = TAGS_ANYWHERE
  97. compile endif
  98. compile if not defined(E_TAGS_ANYWHERE)
  99.    E_TAGS_ANYWHERE = TAGS_ANYWHERE
  100. compile endif
  101. compile if not defined(ASM_TAGS_ANYWHERE)
  102.    ASM_TAGS_ANYWHERE = TAGS_ANYWHERE
  103. compile endif
  104. compile if not defined(KEEP_TAGS_FILE_LOADED)
  105.    KEEP_TAGS_FILE_LOADED = 1  -- If you do a lot with tags, you might want to keep the file loaded.
  106. compile endif
  107.    IDENTIFIER_STARTER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_$'
  108.  
  109.  
  110. defc tagsfile
  111.    universal tags_file
  112. compile if KEEP_TAGS_FILE_LOADED
  113.    universal tags_fileid
  114. compile endif
  115.  
  116.    orig_name = tags_file
  117.    if arg(1)='' then
  118.       parse value entrybox(TAGSNAME__MSG,'/'SET__MSG'/'SETP__MSG'/'Cancel__MSG'/'Help__MSG'/',tags_filename(),'',200,
  119.              atoi(1) || atoi(6070) || gethwndc(APP_HANDLE) ||
  120.              TAGSNAME_PROMPT__MSG) with button 2 newname \0
  121.       if button=\1 | button=\2 then
  122.          tags_file = newname
  123.          if button=\2 & tags_file<>'' then
  124.             call setini('TAGSFILE', tags_file)
  125.          endif
  126.       endif
  127.    else
  128.       tags_file = arg(1)
  129.    endif
  130. compile if KEEP_TAGS_FILE_LOADED
  131.    if tags_fileid <> '' & orig_name <> tags_file then  -- New name; drop tags file
  132.       getfileid startfid
  133.       rc = 0
  134.       activatefile tags_fileid
  135.       if rc=0 then 'quit'; endif
  136.       activatefile startfid
  137.    endif
  138. compile endif
  139.  
  140. defc tagsfile_perm
  141.    universal tags_file
  142. compile if KEEP_TAGS_FILE_LOADED
  143.    universal tags_fileid
  144. compile endif
  145.    orig_name = tags_file
  146.    if arg(1) <>'' then
  147.       tags_file = arg(1)
  148.       call setini('TAGSFILE', tags_file)
  149.    endif
  150. compile if KEEP_TAGS_FILE_LOADED
  151.    if tags_fileid <> '' & orig_name <> tags_file then  -- New name; drop tags file
  152.       getfileid startfid
  153.       rc = 0
  154.       activatefile tags_fileid
  155.       if rc=0 then 'quit'; endif
  156.       activatefile startfid
  157.    endif
  158. compile endif
  159.  
  160.  
  161. defproc tags_filename()
  162.    universal tags_file
  163.    if tags_file='' then
  164.       tags_file=checkini(0, 'TAGSFILE', '')
  165.    endif
  166.    if tags_file='' then
  167.       tags_file=get_env('TAGS.EPM')
  168.    endif
  169.    if tags_file='' then
  170.       tags_file='tags.epm'
  171.    endif
  172.    return(tags_file)
  173.  
  174. defc find_tag, findtag
  175. compile if KEEP_TAGS_FILE_LOADED
  176.    universal tags_fileid
  177. compile endif
  178.    button = ''
  179.    file_type = filetype()
  180.    if arg(1)='' then
  181.       /* Try to find the procedure at the cursor. */
  182.       if substr(textline(.line), .col, 1)='(' then left; endif  -- If on paren, shift
  183.       if wordpos(file_type, REXX_EXTENSIONS) then
  184.          token_separators = ' ~`$%^&*()-+=][{}|\:;/><,''"'\t  -- Rexx accepts '!' & '?' as part of the proc name.
  185.       else
  186.          token_separators = ''  -- Use the default defined in find_token()
  187.       endif
  188.       if not find_token(startcol, endcol) then
  189.          return 1
  190.       endif
  191.       if wordpos(file_type, CPP_EXTENSIONS) then
  192.          if substr(textline(.line), endcol+1, 2)='::' & pos(upcase(substr(textline(.line), endcol+3, 1)), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ$_') then
  193.             savecol = .col
  194.             .col = endcol+3
  195.             if find_token(startcol2, endcol2) then
  196.                endcol = endcol2
  197.             endif
  198.             .col = savecol
  199.          elseif .col>3 then
  200.             if substr(textline(.line), startcol-2, 2)='::' & pos(upcase(substr(textline(.line), startcol-3, 1)), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ$_') then
  201.                savecol = .col
  202.                .col = startcol-3
  203.                if find_token(startcol2, endcol2) then
  204.                   startcol = startcol2
  205.                endif
  206.                .col = savecol
  207.             endif
  208.          endif
  209.       endif
  210.  
  211.       proc_name = substr(textline(.line), startcol, (endcol-startcol)+1)
  212.       if pos('.', proc_name) then
  213.          proc_name = substr(proc_name, lastpos('.', proc_name)+1)
  214.       endif
  215.    elseif arg(1)='*' then
  216.       parse value entrybox(FINDTAG__MSG,'/'OK__MSG'/'LIST__MSG'/'Cancel__MSG'/'Help__MSG'/',checkini(0, 'FINDTAG_ARG', ''),'',200,
  217.              atoi(1) || atoi(6010) || gethwndc(APP_HANDLE) ||
  218.              FINDTAG_PROMPT__MSG) with button 2 proc_name \0
  219.       if button<>\1 & button<>\2 then return; endif
  220.       if button=\1 then
  221.          call setini('FINDTAG_ARG', proc_name)
  222.       endif
  223.    else
  224.       proc_name = arg(1)
  225.    endif
  226.    getfileid startfid
  227. compile if KEEP_TAGS_FILE_LOADED
  228.    if tags_fileid<>'' then
  229.       rc = 0
  230.       display -2
  231.       activatefile tags_fileid
  232.       display 2
  233.       if rc then
  234.          tags_fileid = ''
  235.       else
  236.          0              -- Go to top of file
  237.       endif
  238.    endif
  239.    if tags_fileid='' then
  240. compile endif
  241.       'e /d ' tags_filename()
  242.       if rc then
  243.          if rc=-282 then  -- -282 = sayerror("New file")
  244.             'quit'
  245.             sayerror "Tag file '"tags_filename()"' not found"
  246.          else
  247.             sayerror "Error loading tag file '"tags_filename()"' -" sayerrortext(rc)
  248.          endif
  249.          return 1
  250.       endif
  251.       getfileid tags_fileid
  252. compile if KEEP_TAGS_FILE_LOADED
  253.       .visible = 0
  254.    endif
  255. compile endif
  256.    if button=\2 then  -- List (delayed until tags_file was loaded)
  257.       sayerror BUILDING_LIST__MSG
  258.       'xcom e /c tagslist'
  259.       if rc<>-282 then  -- -282 = sayerror("New file")
  260.          sayerror ERROR__MSG rc BAD_TMP_FILE__MSG sayerrortext(rc)
  261.          return
  262.       endif
  263.       browse_mode = browse()     -- query current state
  264.       if browse_mode then call browse(0); endif
  265.       .autosave = 0
  266.       getfileid lb_fid
  267.       display -2
  268.       do i=1 to tags_fileid.last
  269.          getline line, i, tags_fileid
  270.          parse value line with tag .
  271.          if tag<>'' & tag<>'*' then
  272.             insertline tag, .last+1
  273.          endif
  274.       enddo
  275.       if browse_mode then call browse(1); endif  -- restore browse state
  276.       display 2
  277.       if not .modify then  -- Nothing added?
  278.          'xcom quit'
  279. compile if KEEP_TAGS_FILE_LOADED
  280.          activatefile startfid
  281. compile else
  282.          'quit'
  283. compile endif
  284.          sayerror NO_TAGS__MSG
  285.          return
  286.       endif
  287.       if listbox_buffer_from_file(tags_fileid, bufhndl, noflines, usedsize) then return; endif
  288.       parse value listbox(LIST_TAGS__MSG,
  289.     compile if 0 -- POWERPC
  290.                           \0 || atol(usedsize) || atol(bufhndl+32),
  291.     compile elseif EPM32
  292.                           \0 || atol(usedsize) || atoi(32) || atoi(bufhndl),
  293.     compile else
  294.                           \0 || atoi(usedsize) || atoi(bufhndl) || atoi(32),
  295.     compile endif -- EPM32
  296.                           '/'OK__MSG'/'Cancel__MSG'/'Help__MSG,1,5,min(noflines,12),0,
  297.     compile if EVERSION >= 5.60
  298.                           gethwndc(APP_HANDLE) || atoi(1) || atoi(1) || atoi(6012)) with button 2 proc_name \0
  299.     compile else
  300.                           atoi(1) || atoi(1) || atoi(6012) || gethwndc(APP_HANDLE)) with button 2 proc_name \0
  301.     compile endif
  302.       call buffer(FREEBUF, bufhndl)
  303.       if button<>\1 then
  304. compile if KEEP_TAGS_FILE_LOADED
  305.          activatefile startfid
  306. compile else
  307.          'quit'
  308. compile endif
  309.          return
  310.       endif
  311.    endif
  312.    name = proc_name  -- Preserve original name.
  313. compile if 1
  314.    if pos(':', proc_name) then
  315.       grep = 'g'  -- Use the older one, because extended GREP treats colons specially
  316.    else
  317.       grep = 'x'  -- Use the faster one!
  318.    endif
  319. compile else
  320.    tc = pos(':', proc_name)
  321.    if tc then
  322.       temp = ''
  323.       do while tc
  324.          temp = temp || leftstr(proc_name, tc-1) || '\:'
  325.          proc_name = substr(proc_name, tc+1)
  326.          tc = pos(':', proc_name)
  327.       enddo
  328.       proc_name = temp || proc_name
  329.    endif
  330.    grep = 'x'  -- Always use the faster one!
  331. compile endif
  332.    display -2
  333.    tc = tag_case(startfid.filename)
  334.    do i=1 to 2
  335.       'xcom l ^'proc_name' 'grep || tc
  336.       if not rc then leave; endif
  337.       proc_name = '_'proc_name  /* Handle case where C call to assembler function needs '_' */
  338.    enddo
  339.    display 2
  340.    long_msg='.  You may want to rebuild the tag file.'
  341.    if rc then
  342. compile if KEEP_TAGS_FILE_LOADED
  343.       activatefile startfid
  344. compile else
  345.       'quit'
  346. compile endif
  347.       sayerror 'Tag for function "'name'" not found in 'tags_filename()long_msg
  348.       return 1
  349.    endif
  350.    parse_tagline(name, filename, fileline, filedate)
  351.    /* Check if there is more than one */
  352.    if .line < .last then
  353.       found_line = .line
  354.       '+1'
  355.       parse_tagline(next_name, next_filename, next_fileline, next_filedate)
  356.       if upcase(name)=upcase(next_name) then
  357.          getfileid tags_fid
  358.          'xcom e /c temp'
  359.          if rc<>-282 then  -- -282 = sayerror("New file")
  360.             'xcom quit'
  361.             return 1
  362.          endif
  363.          browse_mode = browse()     -- query current state
  364.          if browse_mode then call browse(0); endif
  365.          getfileid temp_fid
  366.          .autosave = 0
  367.          insertline '1. 'filename, 2
  368.          activatefile tags_fid
  369.          i = 2
  370.          loop
  371.             if upcase(next_filename) <> upcase(filename) then
  372.                insertline i'. 'next_filename, temp_fid.last+1, temp_fid
  373.                i = i + 1
  374.             endif
  375.             if .line = .last then
  376.                leave
  377.             endif
  378.             '+1'
  379.             parse_tagline(next_name, next_filename, next_fileline, next_filedate)
  380.             if upcase(name)/==upcase(next_name) then
  381.                leave
  382.             endif
  383.          endloop
  384.          activatefile temp_fid
  385.          .modify = 0
  386.          if browse_mode then call browse(1); endif  -- restore browse state
  387.          if .last>2 then
  388.             if listbox_buffer_from_file(tags_fid, bufhndl, noflines, usedsize) then return; endif
  389.             parse value listbox('Select a file',
  390.           compile if 0 -- POWERPC
  391.                                 \0 || atol(usedsize) || atol(bufhndl+32),
  392.           compile elseif EPM32
  393.                                 \0 || atol(usedsize) || atoi(32) || atoi(bufhndl),
  394.           compile else
  395.                                 \0 || atoi(usedsize) || atoi(bufhndl) || atoi(32),
  396.           compile endif -- EPM32
  397.                                 '/'OK__MSG'/'Cancel__MSG'/'Help__MSG,0,0,min(noflines,12),60,
  398.           compile if EVERSION >= 5.60
  399.                                 gethwndc(APP_HANDLE) || atoi(1) || atoi(1) || atoi(6015) ||
  400.           compile else
  401.                                 atoi(1) || atoi(1) || atoi(6015) || gethwndc(APP_HANDLE) ||
  402.           compile endif
  403. ;;                              "'"name"' appears in multiple files.") with button 2 filename \0
  404.                                 "'"name"' appears in multiple files.") with button 2 i '.' \0
  405.             call buffer(FREEBUF, bufhndl)
  406.             if button<>\1 then  -- Didn't select OK
  407.                filename = ''
  408.             else
  409.                --fileline = ''; filedate = ''  -- For now, don't try to keep track.
  410.                found_line+i-1     -- Go to the corresponding line, & parse the correct info.
  411.                parse_tagline(name, filename, fileline, filedate)
  412.             endif
  413.          else
  414.             'xcom quit'
  415.          endif
  416.          if filename='' then
  417. compile if KEEP_TAGS_FILE_LOADED
  418.             activatefile startfid
  419. compile else
  420.             'quit'  -- quit tags file
  421. compile endif
  422.             return 1
  423.          endif
  424.       endif  -- duplicate names
  425.    endif  -- not on last line
  426. compile if KEEP_TAGS_FILE_LOADED
  427.    activatefile startfid
  428. compile else
  429.    'quit'  -- quit tags file
  430. compile endif
  431.  
  432.    getfileid already_loaded, filename
  433.    'e /v' filename
  434.    if rc then
  435.       if rc=-282 then  -- -282 = sayerror("New file")
  436.          'q'
  437.          sayerror "'"filename"' not found"long_msg
  438.       else
  439.          sayerror "Error loading '"filename"' -" sayerrortext(rc)
  440.       endif
  441.       return 1
  442.    endif
  443.    if tc='e' then
  444.       p = pos(proc_name, textline(fileline))
  445.       lp = lastpos(proc_name, textline(fileline))
  446.    else
  447.       p = pos(upcase(proc_name), upcase(textline(fileline)))
  448.       lp = lastpos(upcase(proc_name), upcase(textline(fileline)))
  449.    endif
  450.    if fileline & p & (p=lp) then
  451.       .cursory=.windowheight%2
  452.       fileline
  453.       .col = p
  454.       return
  455.    endif
  456. compile if 0  -- We already checked if the line # was good; the date no longer matters here.
  457.    if filedate<>''  then  -- Line number and file write date preserved
  458.       if filedate=get_file_date(filename) then  -- Same date means file has not been changed,
  459.          display -8
  460.          sayerror 'Jumping straight to line.'  /**/
  461.          display 8
  462.          fileline                               -- so we can jump right to the line.
  463.          .col = 1
  464.          call proc_search(proc_name, 1, file_type)
  465.          return
  466.       endif
  467.    endif
  468. compile endif
  469.    0
  470.    display -8
  471.    sayerror 'Searching for routine.'  /**/
  472.    display 8
  473.    rc=proc_search(proc_name,1,file_type)
  474.    if rc then
  475.       if already_loaded='' then 'quit' endif
  476.       sayerror proc_name" not found in '"filename"'"long_msg
  477.       return 1
  478.    endif
  479.    if already_loaded<>'' then
  480.       sayerror 'File already loaded, starting new view.'
  481.    endif
  482.  
  483. defproc parse_tagline(var name, var filename, var fileline, var filedate)
  484.    parse value textline(.line) with name filename fileline filedate .
  485.    if leftstr(filename,1)='"' & (rightstr(filename,1)<>'"' | length(filename)=1) then
  486.       parse value textline(.line) with name ' "'filename'"' fileline filedate .
  487.       filename = '"'filename'"'
  488.    endif
  489.  
  490. const
  491.    IGNORE_C_KEYWORDS = 'if while switch for case else return ='
  492. compile if not defined(LOG_TAG_MATCHES)
  493.    LOG_TAG_MATCHES = 0
  494. compile endif
  495.  
  496. defproc c_proc_search(var proc_name, find_first, ext)
  497. compile if LOG_TAG_MATCHES
  498.    universal TAG_LOG_FID
  499. compile endif
  500.    proc_len = length(proc_name)
  501.    if wordpos(ext, CPP_EXTENSIONS) then  -- Presumably C++, allow colons.
  502.       colon=':'
  503.    else                       -- Plain old C, colons are illegal in procedure names.
  504.       colon=''
  505.    endif
  506.    display -2
  507.    if find_first then
  508.       if proc_name=='' then
  509. compile if C_TAGS_ANYWHERE
  510.          'xcom l ^:fex'
  511. compile else
  512.          'xcom l ^[A-Za-z_$].*\(ex'
  513. compile endif
  514.       else
  515. ;;       'xcom l 'proc_name'e'
  516.          'xcom l 'proc_name':o\(x'
  517.       endif
  518.    else
  519.       repeat_find
  520.    endif
  521.    loop
  522.       if rc then
  523.          display 2
  524.          return rc
  525.       endif
  526.       getline line
  527.       line=translate(line, ' ', \t)
  528. compile if LOG_TAG_MATCHES
  529.          if TAG_LOG_FID then
  530.             insertline '  Found line' .line '= "'line'"', TAG_LOG_FID.last+1, TAG_LOG_FID
  531.          endif
  532. compile endif
  533.       if proc_len then  -- Determine if match is a substring of something else
  534.          if .col>1 then
  535.             if pos(upcase(substr(line, .col-1, 1)), IDENTIFIER_STARTER'0123456789') then
  536.                end_line; repeat_find; iterate
  537.             endif
  538.          endif
  539.          .col = .col + proc_len
  540.          if pos(upcase(substr(line, .col, 1)), IDENTIFIER_STARTER'0123456789') then
  541.             end_line; repeat_find; iterate
  542.          endif
  543.          p = pos('(', line, .col)
  544.          if not p then
  545.             end_line; repeat_find; iterate
  546.          endif
  547.          .col = p
  548.       else
  549.          .col = pos('(', line)
  550.       endif
  551.       /* Strip trailing comment.  */
  552.       i=pos('//',line)
  553.       if i then
  554.          line=leftstr(line,i-1)
  555.       endif
  556.       loop
  557.          i=pos('/*',line)
  558.          if not i then leave; endif
  559.          j=pos('*/', line, i+2)
  560.          if j then
  561.             /* line=delstr(line,i,j-i+2) */
  562.             line=overlay('', line, i, j-i+2)  -- Keep column alignment
  563.          else
  564.             line=leftstr(line,i-1)
  565.          endif
  566.       endloop
  567.       line = strip(line, 'T')
  568.       if substr(line, .col, 1)='(' & rightstr(line,1)<>';' then
  569.          call psave_pos(save_pos)
  570.          if rightstr(line,1)<>')' | pos('(',line, .col+1) then
  571. ;;          .col=pos('(',line,.col)
  572.             if find_matching_paren() then  -- No match found?
  573. compile if LOG_TAG_MATCHES
  574.                if TAG_LOG_FID then
  575.                   insertline '  ...skipping; no matching paren found.', TAG_LOG_FID.last+1, TAG_LOG_FID
  576.                endif
  577. compile endif
  578.                call prestore_pos(save_pos)
  579.                end_line; repeat_find; iterate  -- Keep looking
  580.             endif
  581.             after_paren_ch=leftstr(strip(substr(translate(textline(.line), ' ', \t),.col+1)),1)
  582.          else
  583.             after_paren_ch=' '
  584.          endif
  585.          do while after_paren_ch = ' ' & .line<.last
  586.             '+1'
  587.             after_paren_ch=leftstr(strip(translate(textline(.line), ' ', \t)),1)
  588.          enddo
  589.          if pos(after_paren_ch,';),([-+*.=?&|}!<>') then
  590. compile if LOG_TAG_MATCHES
  591.             if TAG_LOG_FID then
  592.                insertline '  ...skipping; after_paren_ch in list.  "'after_paren_ch'"', TAG_LOG_FID.last+1, TAG_LOG_FID
  593.             endif
  594. compile endif
  595.             end_line; repeat_find; iterate
  596.          endif
  597.          call prestore_pos(save_pos)
  598.          parse value strip(line) with line '('
  599.          proc_name = lastword(line)
  600.          v = verify(upcase(proc_name), IDENTIFIER_STARTER, 'M')
  601.          if not v then
  602. compile if LOG_TAG_MATCHES
  603.             if TAG_LOG_FID then
  604.                insertline '  ...skipping; verify =' v, TAG_LOG_FID.last+1, TAG_LOG_FID
  605.             endif
  606. compile endif
  607.             end_line; repeat_find; iterate
  608.          endif
  609.          proc_name = substr(proc_name, v)
  610.          if wordpos(proc_name, IGNORE_C_KEYWORDS) then
  611. compile if LOG_TAG_MATCHES
  612.             if TAG_LOG_FID then
  613.                insertline '  ...skipping; procname "'proc_name'" in ignore list', TAG_LOG_FID.last+1, TAG_LOG_FID
  614.             endif
  615. compile endif
  616.             end_line; repeat_find; iterate
  617.          endif
  618.          if verify(upcase(proc_name), IDENTIFIER_STARTER'0123456789'colon) then
  619. compile if LOG_TAG_MATCHES
  620.             if TAG_LOG_FID then
  621.                insertline '  ...skipping; procname "'proc_name'" contains invalid characters', TAG_LOG_FID.last+1, TAG_LOG_FID
  622.             endif
  623. compile endif
  624.             end_line; repeat_find; iterate
  625.          endif
  626.          w=words(line)
  627.          if w>1 then
  628.             if verify(upcase(subword(line,1,w-1)), IDENTIFIER_STARTER'0123456789*()[] 'colon) then
  629. compile if LOG_TAG_MATCHES
  630.                if TAG_LOG_FID then
  631.                   insertline '  ...skipping; character invalid in a declarator appears before "'proc_name'" in:  'line, TAG_LOG_FID.last+1, TAG_LOG_FID
  632.                endif
  633. compile endif
  634.                end_line; repeat_find; iterate
  635.             endif
  636.          endif
  637.  
  638.          display 2
  639. compile if LOG_TAG_MATCHES
  640.          if TAG_LOG_FID then
  641.             insertline '  ...accepted; proc_name = "'proc_name'"', TAG_LOG_FID.last+1, TAG_LOG_FID
  642.          endif
  643. compile endif
  644.          return 0
  645. compile if LOG_TAG_MATCHES
  646.       elseif not TAG_LOG_FID then  -- do nothing
  647.       elseif substr(line, .col, 1)<>'(' then
  648.          insertline '  ...skipping; .col =' .col'; char = "'substr(line, .col, 1)'"', TAG_LOG_FID.last+1, TAG_LOG_FID
  649.       elseif rightstr(line,1)=';' then
  650.          insertline '  ...skipping; ends in semicolon; line = "'line'"', TAG_LOG_FID.last+1, TAG_LOG_FID
  651.       else
  652.          insertline '  ...skipping; no idea why.', TAG_LOG_FID.last+1, TAG_LOG_FID
  653. compile endif
  654.       endif
  655.       end_line; repeat_find
  656.    endloop
  657.  
  658.  
  659.  
  660. defproc pas_proc_search(var proc_name,find_first)
  661.    tc=arg(3)
  662.    if tc='' then  /* pascal search?*/
  663.       tc='c'  /* ignore case */
  664.    endif
  665.    proc_len = length(proc_name)
  666.    display -2
  667.    if find_first then
  668.       if tc='e' then  /* Must be modula search */
  669.          keywords='(PROCEDURE)'
  670.       else
  671.          keywords='(overlay:b|)(pro(cedure|gram)|function)'
  672.       endif
  673.       if proc_name=='' then
  674.           'xcom l ^[ \t]*'keywords'[ \t]+[A-Za-z_$][A-Za-z0-9_$]*'||
  675.                  '[( \t;:]xc'
  676.       else
  677.          'xcom l 'proc_name''tc
  678.       endif
  679.    else
  680.       repeat_find
  681.    endif
  682. ;  if tc='e' then /* pos function does not support allow e option*/
  683.       tc=''
  684. ;  endif
  685.    loop
  686.       if rc then
  687.          display 2
  688.          return(rc)
  689.       endif
  690.       getline line
  691.       if proc_len then  -- Determine if match is a substring of something else
  692.          if .col>1 then
  693.             c = upcase(substr(line, .col-1, 1))
  694.             if (c>='A' & c<='Z') | (c>='0' & c<='9') | c='$' | c='_'  then
  695.                end_line; repeat_find; iterate
  696.             endif
  697.          endif
  698.          .col = .col + proc_len
  699.          c = upcase(substr(line, .col, 1))
  700.          if (c>='A' & c<='Z') | (c>='0' & c<='9') | c='$' | c='_'  then
  701.             end_line; repeat_find; iterate
  702.          endif
  703.       else
  704.          .col = pos('(', line)
  705.       endif
  706.       line=translate(line, ' ', \t)
  707.       col=.col
  708.       if not pos(' 'keywords'[ \t]',' 'line,'','x'/*||tc*/) then
  709.          end_line; repeat_find; iterate
  710.       endif
  711.       p=pos('[(;:]',line,'','x')
  712.       if p then
  713.          if substr(line,p,1)=='(' then
  714.             .col=p
  715.             call psave_pos(save_pos)
  716.             if find_matching_paren() then
  717.                end_line; repeat_find; iterate
  718.             endif
  719.             call prestore_pos(save_pos)
  720.          endif
  721.          if pos('forward;',textline(.line)) then
  722.             end_line; repeat_find; iterate
  723.          endif
  724.          line=substr(line,1,p-1)
  725.          i=lastpos(' ',strip(translate(line,' ',\t)))
  726.          proc_name=substr(line,i+1)
  727.          display 2
  728.          return 0
  729.       endif
  730.       end_line; repeat_find
  731.    endloop
  732.  
  733. defproc asm_proc_search(var proc_name, find_first)
  734. compile if LOG_TAG_MATCHES
  735.    universal TAG_LOG_FID
  736. compile endif
  737.    display -2
  738.    if find_first then
  739.       if proc_name=='' then
  740.           proc_name=':c'
  741.       endif
  742. compile if ASM_TAGS_ANYWHERE
  743.       'xcom l ^:o'proc_name':wproc(:w|$)xc'
  744. compile else
  745.       'xcom l ^'proc_name':wproc(:w|$)xc'
  746. compile endif
  747.    else
  748.       repeat_find
  749.    endif
  750.    display 2
  751.    parse value translate(textline(.line), ' ', \t) with proc_name .
  752. compile if LOG_TAG_MATCHES
  753.    if TAG_LOG_FID and not rc then
  754.       insertline '  Found proc_name = "'proc_name'" in line' .line '= "'textline(.line)'"', TAG_LOG_FID.last+1, TAG_LOG_FID
  755.    endif
  756. compile endif
  757.    return rc
  758.  
  759. defproc e_proc_search(var proc_name, find_first)
  760. compile if LOG_TAG_MATCHES
  761.    universal TAG_LOG_FID
  762. compile endif
  763.    display -2
  764.    proc_len = length(proc_name)
  765.    if find_first then
  766.       if proc_name=='' then
  767.           proc_name='[A-Z_]'
  768.       endif
  769. compile if E_TAGS_ANYWHERE
  770.       'xcom l ^:oDEFPROC:w'proc_name'cx'
  771. compile else
  772.       'xcom l ^DEFPROC:w'proc_name'cx'
  773. compile endif
  774.    else
  775.       repeat_find
  776.    endif
  777.    loop
  778.       if rc then
  779.          display 2
  780.          return rc
  781.       endif
  782.       parse value translate(textline(.line), ' ', \t) with . proc_name .
  783.       parse value proc_name with proc_name '('
  784.       if proc_len then
  785.          if length(proc_name)<>proc_len then  -- a substring of something else
  786.             end_line; repeat_find; iterate
  787.          endif
  788.       endif
  789. compile if LOG_TAG_MATCHES
  790.       if TAG_LOG_FID and not rc then
  791.          insertline '  Found proc_name = "'proc_name'" in line' .line '= "'textline(.line)'"', TAG_LOG_FID.last+1, TAG_LOG_FID
  792.       endif
  793. compile endif
  794.       leave
  795.    endloop
  796.    display 2
  797.    return rc
  798.  
  799. defproc rexx_proc_search(var proc_name, find_first)
  800. compile if LOG_TAG_MATCHES
  801.    universal TAG_LOG_FID
  802. compile endif
  803.    display -2
  804.    if find_first then
  805.       if proc_name=='' then
  806.          'xcom l :r\:xe'  -- Exact case is faster, & the :r doesn't care about case.
  807.       else
  808.          'xcom l 'proc_name':c'  -- Must do case-insensitive search.
  809.       endif
  810.    else
  811.       repeat_find
  812.    endif
  813.    proc_len = length(proc_name)
  814.    loop
  815.       if rc then
  816.          display 2
  817.          return rc
  818.       endif
  819.       getline line
  820. --    line=translate(line, ' ', \t)
  821. compile if LOG_TAG_MATCHES
  822.       if TAG_LOG_FID then
  823.          insertline '  Found line' .line '= "'line'"', TAG_LOG_FID.last+1, TAG_LOG_FID
  824.       endif
  825. compile endif
  826.       colon = pos(':', line, .col)
  827.       if proc_len then  -- Determine if match is a substring of something else
  828.          if .col>1 then
  829.             c = upcase(substr(line, .col-1, 1))
  830.             if (c>='A' & c<='Z') | (c>='0' & c<='9') | c='!' | c='?' | c='_'  then
  831.                .col = colon + 1
  832.                repeat_find; iterate
  833.             endif
  834.          endif
  835.       endif
  836.       i = 1
  837.       loop                -- Remove comments & quotes
  838.          c=pos('/*',line, i)
  839.          a=pos("'",line, i)
  840.          q=pos('"',line, i)
  841.          if not c & not a & not q then leave; endif
  842.          if c & (not a | a>c) & (not q | q>c) then  -- Open Comment appears first
  843.             j=pos('*/', line, i+2)
  844.             if j then
  845.                line=overlay('', line, c, j-c+2)  -- Keep column alignment
  846.             else
  847.                line=leftstr(line, c-1)
  848.             endif
  849.          else                           -- Single or double quote appears first
  850.             if not q then               -- Figure out which it is...
  851.                q = a;
  852.             elseif a then
  853.                q = min(q, a)
  854.             endif
  855.             j=pos(substr(line, q, 1), line, q+1)
  856.             if j then
  857.                line=overlay('', line, q, j-q+1)  -- Keep column alignment
  858.             else
  859.                line=leftstr(line, q-1)
  860.             endif
  861.          endif
  862.       endloop
  863.       if substr(line, colon, 1)<>':' then  -- Was in a comment or quoted string
  864. compile if LOG_TAG_MATCHES
  865.          if TAG_LOG_FID then
  866.             insertline "  ...skipping; ':' inside a comment or string.", TAG_LOG_FID.last+1, TAG_LOG_FID
  867.          endif
  868. compile endif
  869.          .col = colon + 1
  870.          repeat_find; iterate
  871.       endif
  872.       display 2
  873.       parse value substr(textline(.line), .col) with proc_name ':'
  874. compile if LOG_TAG_MATCHES
  875.       if TAG_LOG_FID then
  876.          insertline '  ...accepted; proc_name = "'proc_name'"', TAG_LOG_FID.last+1, TAG_LOG_FID
  877.       endif
  878. compile endif
  879.       return 0
  880.    endloop
  881.  
  882. defc make_tags
  883.    'maketags' arg(1)
  884.  
  885. defproc find_matching_paren
  886.    n=1
  887.    GETSEARCH search_command -- Save user's search command.
  888.    display -2
  889.    'xcom L /[\(\)]/ex+F'
  890.    loop
  891.       repeatfind
  892.       if rc then leave; endif
  893.       if substr(textline(.line), .col, 1) = '(' then n=n+1; else n=n-1; endif
  894.       if n=0 then leave; endif
  895.    endloop
  896.    display 2
  897.    SETSEARCH search_command -- Restores user's command so Ctrl-F works.
  898.    return rc  /* 0 if found, else sayerror('String not found') */
  899.  
  900. defproc get_file_date(filename)
  901.          pathname = filename\0
  902.          resultbuf = copies(\0,30)
  903. compile if EVERSION >= 6  -- EPM32:  32-bit version
  904.          ca = dynalink32('DOSCALLS',      /* dynamic link library name       */
  905.                          '#223',           /* ordinal value for DOS32QueryPathInfo  */
  906.                          address(pathname)         ||  -- pathname to be queried
  907.                          atol(1)                   ||  -- PathInfoLevel
  908.                          address(resultbuf)        ||  -- buffer where info is to be returned
  909.                          atol(length(resultbuf)) )     -- size of buffer
  910. compile else
  911.          ca = dynalink('DOSCALLS',      /* dynamic link library name       */
  912.                        '#98',           /* ordinal value for DOSQPATHINFO  */
  913.                        address(pathname)         ||  -- pathname to be queried
  914.                        atoi(1)                   ||  -- PathInfoLevel
  915.                        address(resultbuf)        ||  -- buffer where info is to be returned
  916.                        atoi(length(resultbuf))   ||  -- size of buffer
  917.                        atol(0) )                     -- (reserved)
  918. compile endif -- EVERSION >= 6
  919.          return ltoa(substr(resultbuf, 9, 4), 16)
  920.  
  921. compile if EPM32
  922. defc QueryTagsFiles
  923.    universal app_hini
  924.    parse arg hwnd .
  925.    App = INI_TAGSFILES\0
  926.    inidata = copies(' ', MAXCOL)
  927.    l = dynalink32('PMSHAPI',
  928.                   '#115',               -- PRF32QUERYPROFILESTRING
  929.                   atol(app_hini)    ||  -- HINI_PROFILE
  930.                   address(App)      ||  -- pointer to application name
  931.                   atol(0)           ||  -- Key name is NULL; returns all keys
  932.                   atol(0)           ||  -- Default return string is NULL
  933.                   address(inidata)  ||  -- pointer to returned string buffer
  934.                   atol(MAXCOL), 2)      -- max length of returned string
  935.  
  936.    if not l then  -- No tagsfiles saved
  937.       if tags_filename()<>'' then
  938.          maketags_parm = checkini(0, 'MAKETAGS_PARM', '')
  939.          if maketags_parm <> '' then
  940.             call windowmessage(0,  hwnd,
  941.                                32,               -- WM_COMMAND - 0x0020
  942.                                mpfrom2short(1, 4),  -- This is the default (and only one)
  943.                                put_in_buffer(tags_filename()) )
  944. ;           'querytagsfilelist' hwnd tags_filename()
  945.          endif
  946.       endif
  947.       return
  948.    endif
  949.    inidata=leftstr(inidata,l)
  950.  
  951.    tagsfileU = upcase(tags_filename())  -- loop invariant
  952.    do while inidata<>''
  953.       parse value inidata with tagsname \0 inidata
  954.       call windowmessage(0,  hwnd,
  955.                          32,               -- WM_COMMAND - 0x0020
  956.                          mpfrom2short((upcase(tagsname)=tagsfileU), 4),
  957.                          put_in_buffer(tagsname) )
  958.             'querytagsfilelist' hwnd tagsname
  959.    enddo
  960.  
  961. defc QueryTagsFileList
  962.    parse arg hwnd tagsname
  963.    call windowmessage(0,  hwnd,
  964.                       32,               -- WM_COMMAND - 0x0020
  965.                       5,
  966.                       put_in_buffer(TagsFileList(tagsname)))
  967.  
  968. defproc TagsFileList(tagsname)
  969.    universal app_hini
  970.    App = INI_TAGSFILES\0
  971.    tagsnameZ = upcase(tagsname)\0
  972.    inifilelist = copies(' ', MAXCOL)
  973.    l = dynalink32('PMSHAPI',
  974.                   '#115',               -- PRF32QUERYPROFILESTRING
  975.                   atol(app_hini)       ||  -- HINI_PROFILE
  976.                   address(App)         ||  -- pointer to application name
  977.                   address(tagsnameZ)   ||  -- Return value for this key
  978.                   atol(0)              ||  -- Default return string is NULL
  979.                   address(inifilelist) ||  -- pointer to returned string buffer
  980.                   atol(MAXCOL), 2)         -- max length of returned string
  981.    if not l then  -- Not found in .INI file; try the TAGS file's EA
  982.       getfileid startfid
  983.       getfileid fid, tagsname
  984.       continue = 1
  985.       if not fid then
  986.          'xcom e' tagsname
  987.          if rc then
  988.             continue = 0
  989.             if rc=sayerror('New file') then
  990.                'xcom quit'
  991.             endif
  992.          endif
  993.       else
  994.          activatefile fid
  995.       endif
  996.       if continue then
  997.          inifilelist = get_EAT_ASCII_value('EPM.TAGSARGS')
  998.          l = length(inifilelist)
  999.          if not fid then
  1000.             'xcom quit'
  1001.          endif
  1002.       endif
  1003.       activatefile startfid
  1004.    endif
  1005.    return leftstr(inifilelist, l)
  1006.  
  1007.  
  1008. defc poptagsdlg
  1009.    call windowmessage(0,  getpminfo(APP_HANDLE),
  1010.                       5158,               -- EPM_POPCTAGSDLG
  1011.                       0,
  1012.                       0)
  1013.  
  1014. defc tagsdlg_make
  1015.    universal appname, app_hini
  1016.    parse arg tagsfilename maketagsargs
  1017.    if maketagsargs='' then sayerror -263; return; endif  -- "Invalid argument"
  1018.    call setprofile(app_hini, INI_TAGSFILES, upcase(tagsfilename), maketagsargs)
  1019.    'tagsfile' tagsfilename
  1020.    'maketags' maketagsargs
  1021.  
  1022. defc add_tags_info
  1023.    universal appname, app_hini
  1024.    parse arg tagsfilename maketagsargs
  1025.    if maketagsargs='' then sayerror -263; return; endif  -- "Invalid argument"
  1026.    call setprofile(app_hini, INI_TAGSFILES, upcase(tagsfilename), maketagsargs)
  1027.  
  1028. defc delete_tags_info
  1029.    universal appname, app_hini
  1030.    if arg(1)='' then sayerror -263; return; endif  -- "Invalid argument"
  1031.    call setprofile(app_hini, INI_TAGSFILES, upcase(arg(1)), '')
  1032. compile endif -- EPM32
  1033.  
  1034. defc tagscan
  1035.    ext=filetype()
  1036.    if not tags_supported(ext) then
  1037.       sayerror "Don't know how to do tags for file of type '"ext"'"
  1038.       return 1
  1039.    endif
  1040.    call psave_pos(savepos)
  1041.    0
  1042.    getfileid sourcefid
  1043.    'xcom e /c tagslist'
  1044.    if rc<>-282 then  -- -282 = sayerror("New file")
  1045.       sayerror ERROR__MSG rc BAD_TMP_FILE__MSG sayerrortext(rc)
  1046.       return
  1047.    endif
  1048.    getfileid lb_fid
  1049.    browse_mode = browse()     -- query current state
  1050.    if browse_mode then call browse(0); endif
  1051.    .autosave = 0
  1052.    activatefile sourcefid
  1053.    proc_name=''
  1054.    sayerror 'Searching for procedures...'
  1055.    rc=proc_search(proc_name, 1, ext)
  1056.    while not rc do
  1057.       insertline proc_name '('.line')', lb_fid.last+1, lb_fid
  1058.       proc_name=''
  1059.       end_line
  1060.       rc=proc_search(proc_name, 0, ext)
  1061.    endwhile
  1062.    call prestore_pos(savepos)
  1063.    if browse_mode then call browse(1); endif  -- restore browse state
  1064.    activatefile lb_fid
  1065.  
  1066.    if not .modify then  -- Nothing added?
  1067.       'xcom quit'
  1068.       activatefile sourcefid
  1069.       sayerror NO_TAGS__MSG
  1070.       return
  1071.    endif
  1072.    sayerror 0
  1073.    if listbox_buffer_from_file(sourcefid, bufhndl, noflines, usedsize) then return; endif
  1074.    parse value listbox(LIST_TAGS__MSG,
  1075.  compile if 0 -- POWERPC
  1076.                        \0 || atol(usedsize) || atol(bufhndl+32),
  1077.  compile elseif EPM32
  1078.                        \0 || atol(usedsize) || atoi(32) || atoi(bufhndl),
  1079.  compile else
  1080.                        \0 || atoi(usedsize) || atoi(bufhndl) || atoi(32),
  1081.  compile endif -- EPM32
  1082.                        '/'OK__MSG'/'Cancel__MSG'/'Help__MSG,1,5,min(noflines,12),0,
  1083.  compile if EVERSION >= 5.60
  1084.                        gethwndc(APP_HANDLE) || atoi(1) || atoi(1) || atoi(6012)) with button 2 proc_name \0
  1085.  compile else
  1086.                        atoi(1) || atoi(1) || atoi(6012) || gethwndc(APP_HANDLE)) with button 2 proc_name \0
  1087.  compile endif
  1088.    call buffer(FREEBUF, bufhndl)
  1089.    if button<>\1 then
  1090.       return
  1091.    endif
  1092.    parse value proc_name with procname ' (' linenum ')'
  1093.    linenum; .col = 1
  1094.    '/'procname
  1095.