home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / UE311C.ZIP / TAGS.C < prev    next >
C/C++ Source or Header  |  1990-08-16  |  13KB  |  480 lines

  1. /*
  2.  * The routines in this file provides support for  vi-like tagging
  3.  * of defined identifiers.  We presuppose the "tags" file in the
  4.  * current directory (constructed by 'ctags' or 'etags'), with each
  5.  * entry on the following format:
  6.  *
  7.  *     identifier<tab>file<tab>vi-search-pattern
  8.  *
  9.  * Code will be generated if both  MAGIC and CTAGS  is wanted.
  10.  *                            880826mhs
  11.  * 890622mhs
  12.  * Changed code so as to take advantage of the new FAST search routine
  13.  * i.e. we do not use the MAGIC search.
  14.  * Also implemented indexing of the "tags" file to reduce the time used
  15.  * when tagging is performed more than once.  Moreover, we now support
  16.  * tagging of file(s) other than those in the current directory.  We
  17.  * automatically locates the correct "tags" and records its vital info
  18.  * (only the first time it's referenced!!).
  19.  * 890627mhs
  20.  * Added possibility to be prompted for word to tag.  You will be prompted
  21.  * if you execute the tag-word function whenever dot is not within a word.
  22.  */
  23.  
  24. #include <stdio.h>
  25. #include "estruct.h"
  26.  
  27. #if CTAGS
  28. #include "eproto.h"
  29. #include "edef.h"
  30. #include "elang.h"
  31.  
  32. static char SVER[] = "@(#) %M% %I% %H%";
  33.  
  34. #define min(x, y)    ((x) <= (y) ? (x) : (y))
  35.  
  36. #define INDEX(c)    (islower(c) ? c-'a'+27 : \
  37.              (isletter(c) ? c-'A'+1: ((c == '_') ? 0 : -1)))
  38. #define NINDEXES    26+26+1
  39. #define TAGWIDTH    30
  40.  
  41. typedef struct    TAG {
  42.     struct    TAG *t_tagp;        /* Link to the next 'tags' file    */
  43.     char    t_path[NFILEN];        /* Path of 'tags' file        */
  44.     FILE    *t_fp;            /* File pointer to 'tags' file    */
  45.     char    t_wd[TAGWIDTH+1];    /* Word last tagged (this file)    */
  46.     char    t_fname[NFILEN];    /* Holds name of file from where*/
  47.                     /* we tagged.            */
  48.     int    t_indexed;        /* Flag:  1=file is indexed    */
  49.     long    t_dotos[NINDEXES];    /* Offsets of first chars (used    */
  50.                     /* for speed-up purposes only).    */
  51. }    TAG;
  52.  
  53. static TAG *theadp = NULL;        /* Pointer to the head of the    */
  54.                     /* 'tags'-chain.        */
  55. static TAG *curtp  = NULL;        /* Currently in-use 'tags'.    */
  56.  
  57.  
  58. /*
  59.  * Look-up a 'tags' file in the directory given by
  60.  * 'path'.  If such a file exists and we are allowed
  61.  * to read it, we'll read it and construct an index
  62.  * of file positions of the first occurence of each
  63.  * starting character ('_', 'a'-'z', 'A'-'Z').  We
  64.  * return with TRUE only if we are succesfull.
  65.  */
  66.  
  67. newtags(path)
  68. char path[NFILEN];
  69. {
  70.     register TAG    *tnewp;
  71.     register int    i = NINDEXES;
  72.  
  73.     if ((tnewp = (TAG *)malloc(sizeof(TAG))) == NULL) {
  74.         mlwrite("[OUT OF MEMORY]");
  75.         return(FALSE);
  76.     }
  77.     strcpy(tnewp->t_path, path);
  78.     strcat(path, "tags");
  79.     if ((tnewp->t_fp = fopen(path, "r")) == NULL) {
  80.         free((char *)tnewp);
  81.         return(FALSE);
  82.     }
  83.  
  84.     tnewp->t_tagp  = theadp;
  85.     curtp = theadp = tnewp;
  86.     strcpy(tnewp->t_fname, curbp->b_fname);
  87.     strcpy(tnewp->t_wd, "");
  88.  
  89.     /* Initialize index...    */
  90.     tnewp->t_indexed = FALSE;
  91.     while (i > 0)
  92.         tnewp->t_dotos[--i] = -1L;
  93.  
  94.     return(TRUE);
  95. }
  96.  
  97.  
  98. /*
  99.  * Look-up 'tags' file; first in our list and if it isn't there
  100.  * try it the hard way.  If we find the file we return TRUE.
  101.  */
  102.  
  103. lookup()
  104. {
  105.     TAG        *tmp = curtp;    /* Remember current 'tags'    */
  106.     char        cpath[NFILEN];    /* Path of current file        */
  107.     register char    *cp;        /* Auxiliary pointer        */
  108.     register int    nope = TRUE;    /* True if 'tags' is unknown    */
  109.  
  110.     cp = curbp->b_fname + strlen(curbp->b_fname) - 1;
  111. #if    MSDOS
  112.     while (cp >= curbp->b_fname  &&  *cp != DIRSEPCHAR  &&  *cp != ':')
  113. #else
  114.     while (cp >= curbp->b_fname  &&  *cp != DIRSEPCHAR)
  115. #endif
  116.         cp--;
  117.  
  118.     memset(cpath, '\0', NFILEN);
  119.     if (cp >= curbp->b_fname)
  120.         strncpy(cpath, curbp->b_fname, (int)(cp-curbp->b_fname));
  121.     else
  122.         strcpy(cpath, ".");
  123.     /* Append a DIRSEPCHAR character to the path...    */
  124.     if (strlen(cpath) < NFILEN - 1)
  125.         cpath[strlen(cpath)] = DIRSEPCHAR;
  126.  
  127.     while (curtp != NULL  &&  (nope = strcmp(curtp->t_path, cpath)) != 0)
  128.         curtp = curtp->t_tagp;
  129.  
  130.     if (nope == 0)            /* We already knew the tags path*/
  131.         return(TRUE);
  132.  
  133.     /* We'll have to look it up...    */
  134.     if (newtags(cpath))
  135.         return(TRUE);
  136.     else
  137.         curtp = tmp;
  138.     return(FALSE);
  139. }
  140.  
  141.  
  142. /*
  143.  * Create an index of offsets into the 'tags' file at
  144.  * curtp->t_path.  We use the first character of the
  145.  * tagged words as our index index.
  146.  */
  147.  
  148. fix_index()
  149. {
  150.     register int    i  = -1;
  151.     register long    lastpos = 0L;
  152.     char        line[NLINE];
  153.  
  154.     if (curtp->t_indexed == TRUE)
  155.         return;
  156.  
  157.     mlwrite("[Indexing 'tags' file, please wait...]");
  158.  
  159.     while (fgets(line, NLINE, curtp->t_fp) != NULL) {
  160.         if (i != INDEX(line[0])  &&  (i=INDEX(line[0])) != -1)
  161.             curtp->t_dotos[i] = lastpos;
  162.         lastpos = (long)ftell(curtp->t_fp);
  163.     }
  164.     curtp->t_indexed = TRUE;
  165. }
  166.  
  167.  
  168. /*
  169.  * Put the rest of the characters of the current word at '.' in
  170.  * str (but maximum lmax characters).  '.' is preserved.
  171.  */
  172. restword(str, lmax)
  173. char *str;
  174. int  lmax;
  175. {
  176.     register int i;
  177.     register int go_on  = TRUE;
  178.     register LINE *dotp = curwp->w_dotp;    /* Preserve '.' info    */
  179.     register int   doto = curwp->w_doto;    /* Preserve '.' info    */
  180.  
  181.     for (i=0 ; go_on  &&  i < lmax-1  &&  inword() ; i++) {
  182.         str[i] = lgetc(curwp->w_dotp, curwp->w_doto);
  183.         go_on  = forwchar(FALSE, 1);
  184.     }
  185.  
  186.     str[i] = 0;            /* Terminate word        */
  187.     curwp->w_dotp = dotp;        /* Restore '.'             */
  188.     curwp->w_doto = doto;
  189.  
  190.     return(TRUE);
  191. }
  192.  
  193.  
  194. /*
  195.  * Move '.' backwards until start of current word.
  196.  * NOTE, we rely on inword(), which normally don't
  197.  * consider '_' part of a word.  You might want to
  198.  * change inword() in order to obtain satisfactory
  199.  * results from this code (I did).
  200.  */
  201.  
  202. backupword(f, n)
  203.  
  204. int f, n;
  205.  
  206. {
  207.     while (inword())
  208.         if (backchar(FALSE, 1) == FALSE)
  209.             break;
  210.     if (!inword())        /* Adjust for not beginning of file    */
  211.         forwchar(FALSE, 1);
  212.  
  213.     return(TRUE);
  214. }
  215.  
  216.  
  217. /*
  218.  * Alter the vi-search-pattern in pattern inorder to use it
  219.  * as a search pattern for uEMACS' search routines.
  220.  * The vi-pattern contains \-escaped characters...we have
  221.  * to get rid of the \'es.
  222.  * Moreover, as we want to make use of the new FAST search
  223.  * routine, we have to remove the pattern anchoring (^ and $)
  224.  * and search direction characters (? or /)
  225.  */
  226.  
  227. alterpattern(pattern)
  228. register char pattern[];
  229. {
  230.     register int    i   = 0;        /* EMACS pattern index    */
  231.     register int    j   = 1;        /* VI pattern -skip /or?*/
  232.     int        len = strlen(pattern)-1;/* pattern length - 1    */
  233.                         /* i.e. drop '/' or '?'    */
  234.  
  235.     if (pattern[len-1] == '$')  len--;
  236.  
  237.     while (++j < len)
  238.         if (pattern[j] != '\\')
  239.             pattern[i++] = pattern[j];
  240.         else if (pattern[j+1] == '\\')
  241.             pattern[i++] = pattern[++j];
  242.  
  243.     pattern[min(i, NPAT/2)] = '\0';    /* Terminate pattern string    */
  244.     return(TRUE);
  245. }
  246.  
  247.  
  248. /*
  249.  * Some locally shared variables
  250.  */
  251.  
  252. static int thisfile  = FALSE;        /* TRUE if curtp->t_fname equals*/
  253.                     /* curbp->fname when tagging    */
  254. static int tagvalid  = FALSE;        /* TRUE if last tag was a succes*/
  255.  
  256.  
  257. /*
  258.  * Tag current word if '.' is within a word, otherwise tag next word.
  259.  * '.' is preserved, and return information (= current filename) is saved.
  260.  */
  261. extern int PASCAL NEAR tagword(f, n)
  262.  
  263. int f, n;
  264.  
  265. {
  266.     LINE    *pretagdotp = curwp->w_dotp;    /* Preserve '.' info    */
  267.     int    pretagdoto  = curwp->w_doto;
  268.     int    i;
  269.  
  270.     if (restflag == TRUE)    /* Don't allow when in restricted mode    */
  271.         return(resterr());
  272.  
  273.     if (lookup() == FALSE) { /* Is 'tags' avaliable for this file?    */
  274.         mlwrite("[Sorry, can't find any 'tags']");
  275.         return(FALSE);
  276.     }
  277.  
  278.     /* Get word to tag    */
  279.     if (inword()) {
  280.         backupword(FALSE, 1);
  281.         restword(curtp->t_wd, TAGWIDTH);    /* Grab word to tag    */
  282.     }
  283.     else if (mlreply("Word to tag: ", curtp->t_wd, TAGWIDTH) != TRUE)
  284.         return(FALSE);
  285.  
  286.     fix_index();
  287.  
  288.     curwp->w_dotp = pretagdotp;    /* Restore '.'    */
  289.     curwp->w_doto = pretagdoto;
  290.  
  291.     /* Ok, set file offset according to  curtp->t_wd (if any)    */
  292.     if ((i=INDEX(*curtp->t_wd)) == -1  ||  curtp->t_dotos[i] == -1L) {
  293.         mlwrite("[No tag entry for '%s' found]", curtp->t_wd);
  294.         return(FALSE);
  295.     }
  296.     fseek(curtp->t_fp, curtp->t_dotos[i], 0);
  297.  
  298.     strcpy(curtp->t_fname, curbp->b_fname); /* Save name of current file */
  299.     return(tagvalid = tagger("[No tag entry for '%s' found]", FALSE));
  300. }
  301.  
  302.  
  303. /*
  304.  * Does the actual work.  Presumes that  tagw  containes the correct
  305.  * word to tag.  Note that we do not rewind the file pointer as to
  306.  * allow you to do a re-tag.
  307.  * If the tagged word is defined in the current file we mark '.', 
  308.  * goes to line 1 of file and searches for the pattern.  We do this
  309.  * so as to prevent loosing the return information.
  310.  */
  311.  
  312. tagger(errmsg, retag)
  313. char *errmsg;
  314. int  retag;
  315. {
  316.     char    tagf[NFILEN];            /* File of tagged word    */
  317.     char    pretagpat[NPAT];        /* Search pattern prior    */
  318.                         /* to our tagging.    */
  319.     char    line[NLINE];            /* Auxilliary string    */
  320.     int    ok = 1;                /* Tag search flag    */
  321.     int    result   = FALSE;        /* Default return value */
  322.     int    oldbmode;            /* For preserving bmode    */
  323.     int    taglen   = strlen(curtp->t_wd);
  324.     int    file_ok;            /* TRUE if file found    */
  325.  
  326.     /* Tell user what we are doing        */
  327.     mlwrite("[Tagging '%s'...]", curtp->t_wd);
  328.  
  329.     /* Search for  curtp->t_wd  in the 'tags' file    */
  330.     while (ok > 0  &&  fgets(line, NLINE, curtp->t_fp) != NULL) {
  331.         if ((ok = strncmp(curtp->t_wd, line, taglen)) < 0)
  332.             break;
  333.         else if (ok == 0  &&  line[taglen] != '\t')
  334.             ok = -1;
  335.     }
  336.  
  337.     if (ok < 0) {                /* We couldn't find it..*/
  338.         mlwrite(errmsg, curtp->t_wd);
  339.         return(FALSE);
  340.     }
  341.  
  342.     strcpy(pretagpat, pat);        /* Preserve old search pattern    */
  343.  
  344.     /* Scan line for file and pattern    */
  345.     sscanf(line, "%s %s %[^\n]", curtp->t_wd, tagf, pat);
  346.     /* Alter pattern... we cannot use vi's    */
  347.     alterpattern(pat);
  348.  
  349.     /* Add path to tagf if necessary...    */
  350.     add_path(tagf);
  351.     /* Should we search the current file?    */
  352.     thisfile = strcmp(tagf, curbp->b_fname) == 0;
  353.     file_ok  = thisfile ? TRUE : getfile(tagf, TRUE);
  354.     oldbmode = curbp->b_mode;    /* Preserve buffer mode        */
  355.     if (file_ok) {            /* Ok, we got the file. Search!    */
  356.         if (thisfile  &&  retag == FALSE)
  357.             /* It's the same file so just set mark    */
  358.             setmark(FALSE, FALSE);
  359.         gotoline(TRUE, 1);
  360.  
  361.         /* Set-up for searching... use exact mode, not magic    */
  362.         curbp->b_mode |= MDEXACT;
  363.         curbp->b_mode &= ~MDMAGIC;
  364.         setjtable();
  365.         mcclear();
  366.         rmcclear();
  367.  
  368.         if (scanner(FORWARD, PTBEG, 1) == FALSE) {
  369.             /* Sorry, we couldn't find pattern so return...    */
  370.             if (thisfile  &&  retag == FALSE)
  371.                 /* It's the same file so simply swapmark*/
  372.                 swapmark(FALSE, FALSE);
  373.             else    /* Get old file    */
  374.                 getfile(curtp->t_fname, TRUE);
  375.             /* Tell user about our misfortune    */
  376.             mlwrite("[Failed to tag '%s']", curtp->t_wd);
  377.         }
  378.         else {    /* We found the pattern.  Now point at word!    */
  379.             strcpy(pat, curtp->t_wd);
  380.             setjtable();
  381.             result = scanner(FORWARD, PTBEG, 1);
  382.         }
  383.     }
  384.  
  385.     curbp->b_mode = oldbmode;    /* Restore buffer mode        */
  386.  
  387.     strcpy(pat, pretagpat);        /* Restore search pattern    */
  388.     setjtable();
  389.     if (curbp->b_mode & MDMAGIC)
  390.         mcstr();
  391.     else {
  392.         mcclear();
  393.         rmcclear();
  394.     }
  395.  
  396.     return(result);
  397. }
  398.  
  399.  
  400. /*
  401.  * Prefix filename with path in curtp->t_path (if any)
  402.  * if filename doesn't include a full path.  This routine
  403.  * allways succeeds.
  404.  */
  405.  
  406. add_path(filename)
  407. char *filename;
  408. {
  409.     char temp[NFILEN];
  410.     char *tp;
  411.  
  412.     if (curtp->t_path[0] == '.'  &&  curtp->t_path[1] == DIRSEPCHAR  &&
  413.                             curtp->t_path[2]  == '\0')
  414.         return;
  415.         
  416.     for (tp=filename ; *tp  &&  *tp != DIRSEPCHAR ; tp++);
  417.  
  418.     if (*tp == DIRSEPCHAR)
  419.         return;
  420.  
  421.     strcpy(temp, curtp->t_path);
  422.     strcat(temp, filename);
  423.     strcpy(filename, temp);
  424. }
  425.  
  426.  
  427.  
  428. /*
  429.  * Sometimes the 'tags' file will contain multiple entries for the
  430.  * same identifier.  This occures whenever an identifier is multiple
  431.  * defined.  retagword  asks  tagger  to tag for the same  tagw  again
  432.  * but after the position where the last tag entry for tagw was found.
  433.  * Note, retagword  do not mess up the return information (tagf).
  434.  */
  435.  
  436. extern int PASCAL NEAR retagword(f, n)
  437.  
  438. int f, n;
  439.  
  440. {
  441.     if (restflag == TRUE)    /* Don't allow when in restricted mode    */
  442.         return(resterr());
  443.  
  444.     if (*curtp->t_fname == '\0'  ||  !tagvalid)
  445.         return(FALSE);
  446.  
  447.     return(tagger("[No additional tag entry for '%s' found]", TRUE));
  448. }
  449.  
  450.  
  451. /*
  452.  * Return to the file from where we tagged the  tagw  identifier.
  453.  * You can only return once for each tag.  If it's the same file
  454.  * we just swap mark with '.' .
  455.  */
  456.  
  457. extern int PASCAL NEAR backtagword(f, n)
  458.  
  459. int f, n;
  460.  
  461. {
  462.     if (restflag == TRUE)    /* Don't allow when in restricted mode    */
  463.         return(resterr());
  464.  
  465.     if (*curtp->t_fname != '\0') {
  466.         if (thisfile)
  467.             swapmark(FALSE, FALSE);
  468.         else
  469.             getfile(curtp->t_fname, TRUE);
  470.         *curtp->t_fname = '\0';
  471.     }
  472.  
  473.     return(TRUE);
  474. }
  475. #else
  476. tagshello()
  477. {
  478. }    
  479. #endif
  480.