The new file tags.c now has a proper license. Could you please review
this new diff.

Index: Makefile
===================================================================
RCS file: /home/sunil/cvs/src/usr.bin/mg/Makefile,v
retrieving revision 1.24
diff -u -p -r1.24 Makefile
--- Makefile    2 Feb 2011 05:21:36 -0000       1.24
+++ Makefile    9 Oct 2011 20:19:43 -0000
@@ -2,7 +2,7 @@
 
 PROG=  mg
 
-LDADD+=        -lcurses
+LDADD+=        -lcurses -lutil
 DPADD+=        ${LIBCURSES}
 
 # (Common) compile-time options:
@@ -24,7 +24,7 @@ SRCS= autoexec.c basic.c buffer.c cinfo.
 #
 # More or less standalone extensions.
 #
-SRCS+= cmode.c dired.c grep.c theo.c
+SRCS+= cmode.c dired.c grep.c tags.c theo.c
 
 afterinstall:
        ${INSTALL} -d ${DESTDIR}${DOCDIR}/mg
Index: def.h
===================================================================
RCS file: /home/sunil/cvs/src/usr.bin/mg/def.h,v
retrieving revision 1.116
diff -u -p -r1.116 def.h
--- def.h       23 Jan 2011 00:45:03 -0000      1.116
+++ def.h       8 Oct 2011 12:07:18 -0000
@@ -515,6 +515,10 @@ int                 space_to_tabstop(int, int);
 int             backtoindent(int, int);
 int             joinline(int, int);
 
+/* tags.c X */
+int             pushtag(int, int);
+int             poptag(int, int);
+
 /* extend.c X */
 int             insert(int, int);
 int             bindtokey(int, int);
Index: keymap.c
===================================================================
RCS file: /home/sunil/cvs/src/usr.bin/mg/keymap.c,v
retrieving revision 1.45
diff -u -p -r1.45 keymap.c
--- keymap.c    18 Jan 2011 16:25:40 -0000      1.45
+++ keymap.c    9 Oct 2011 20:23:08 -0000
@@ -204,8 +204,11 @@ static PF metapct[] = {
 };
 
 static PF metami[] = {
+       poptag,                 /* * */
+       rescan,                 /* + */
+       rescan,                 /* , */
        negative_argument,      /* - */
-       rescan,                 /* . */
+       pushtag,                /* . */
        rescan,                 /* / */
        digit_argument,         /* 0 */
        digit_argument,         /* 1 */
@@ -298,7 +301,7 @@ struct KEYMAPE (8 + IMAPEXT) metamap = {
                        '%', '%', metapct, NULL
                },
                {
-                       '-', '>', metami, NULL
+                       '*', '>', metami, NULL
                },
                {
                        '[', 'f', metasqf, (KEYMAP *) &metasqlmap
Index: main.c
===================================================================
RCS file: /home/sunil/cvs/src/usr.bin/mg/main.c,v
retrieving revision 1.61
diff -u -p -r1.61 main.c
--- main.c      4 Jun 2009 02:23:37 -0000       1.61
+++ main.c      9 Oct 2011 15:05:40 -0000
@@ -77,11 +77,13 @@ main(int argc, char **argv)
                extern void theo_init(void);
                extern void cmode_init(void);
                extern void dired_init(void);
-
+               extern void tags_init(void);
+               
                dired_init();
                grep_init();
                theo_init();
                cmode_init();
+               tags_init();
        }
 
        if (init_fcn_name &&
@@ -217,6 +219,7 @@ edinit(PF init_fcn)
        wp->w_rflag = WFMODE | WFFULL;          /* Full.                 */
 }
 
+extern void tags_close(void);
 /*
  * Quit command.  If an argument, always quit.  Otherwise confirm if a buffer
  * has been changed and not written out.  Normally bound to "C-X C-C".
@@ -235,6 +238,7 @@ quit(int f, int n)
 #ifdef SYSCLEANUP
                SYSCLEANUP;
 #endif /* SYSCLEANUP */
+               tags_close();
                exit(GOOD);
        }
        return (TRUE);
Index: tags.c
===================================================================
RCS file: tags.c
diff -N tags.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ tags.c      13 Oct 2011 17:38:24 -0000
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) 2011 Sunil Nimmagadda <su...@sunilnimmagadda.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <sys/types.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <util.h>
+
+#include "def.h"
+
+#define MAX_TOKEN 63
+
+struct ctag;
+
+static void              addctag(char *);
+static int               curtoken(int, int, char *);
+static struct ctag       *findtag(int, int);
+static int               loadbuffer(char *);
+static int               patsearch(char *);
+static char              *strip(char *, size_t);
+
+/* ctags(1) entries are parsed and maintained in a tree. */
+struct ctag {
+       RB_ENTRY(ctag) entry;
+       char *tag;
+       char *fname;
+       char *pat;
+};
+
+static int
+ctagcmp(struct ctag *s, struct ctag *t)
+{
+       return strcmp(s->tag, t->tag);
+}
+
+RB_HEAD(tagtree, ctag) tags = RB_INITIALIZER(&tags);
+RB_GENERATE(tagtree, ctag, entry, ctagcmp)
+
+struct tagpos {
+       SLIST_ENTRY(tagpos) entry;
+       struct line *dotp;
+       int    doto;
+       int    dotline;
+       char   *bfname;
+};
+
+/* The stack used by pushtag and poptag. */
+SLIST_HEAD(tagstack, tagpos) shead = SLIST_HEAD_INITIALIZER(shead);
+
+/*
+ * Lookup current word at dot in tagstree. If an entry is found record
+ * the current dot location, load the file and position the new dot at
+ * the pattern in the file.
+*/
+/*ARGSUSED */
+int
+pushtag(int f, int n)
+{
+       struct ctag *res;
+       struct tagpos *s;
+       char *bfname;
+       struct line *dotp;
+       int doto;
+       int dotline;
+       
+       if ((res = findtag(f, n)) == NULL)
+               return (FALSE);
+
+       dotp = curwp->w_dotp;
+       doto = curwp->w_doto;
+       dotline = curwp->w_dotline;
+       bfname = curbp->b_fname;
+       
+       if (loadbuffer(res->fname) == FALSE)
+               return (FALSE);
+       
+       if (patsearch(res->pat) == TRUE) {
+               if ((s = malloc(sizeof(struct tagpos))) == NULL)
+                       err(1, NULL);
+               if ((s->bfname = strdup(bfname)) == NULL)
+                           err(1, NULL);
+               s->dotp = dotp;
+               s->doto = doto;
+               s->dotline = dotline;
+               SLIST_INSERT_HEAD(&shead, s, entry);
+               return (TRUE);
+       } else {
+               ewprintf("%s: pattern not found", res->tag);
+               return (FALSE);
+       }
+       return (FALSE);
+}
+
+/*
+ * If tag stack is not empty pop stack and jump to recorded
+ * buffer and dot.
+ */
+/* ARGSUSED */
+int
+poptag(int f, int n)
+{
+       struct buffer *bufp;
+       struct tagpos *s;
+       if (SLIST_EMPTY(&shead)) {
+               ewprintf("tag stack empty.");
+               return (FALSE);
+       }
+       s = SLIST_FIRST(&shead);
+       SLIST_REMOVE_HEAD(&shead, entry);
+       bufp = findbuffer(s->bfname);
+       curbp = bufp;
+       curwp->w_dotline = s->dotline;
+       showbuffer(bufp, curwp, WFFRAME | WFFULL);
+       curwp->w_dotp = s->dotp;
+       curwp->w_doto = s->doto;
+       free(s->bfname);
+       free(s);
+       return (TRUE);
+}
+
+/*
+ * Parse the tags file if it's present and construct the tags tree.
+ * Remove escape character before "/" while parsing the file.
+ */
+void
+tags_init()
+{
+       char *l;
+       FILE *fd;
+       if ((fd = fopen("tags", "r")) == NULL) 
+               return;
+       while ((l = fparseln(fd, NULL, NULL, "\\\0\0", FPARSELN_UNESCREST)) != 
NULL)
+               addctag(l);
+       fclose(fd);
+       return;
+}
+
+/*
+ * Cleanup and destroy tree and stack.
+ */
+void
+tags_close()
+{
+       struct ctag *var, *nxt;
+       struct tagpos *s;
+       
+       for (var = RB_MIN(tagtree, &tags); var != NULL; var = nxt) {
+               nxt = RB_NEXT(tagtree, &tags, var);
+               RB_REMOVE(tagtree, &tags, var);
+               free(var->tag); /* line parsed with fparseln needs to be freed 
*/
+               free(var);
+       }
+       
+       while (!SLIST_EMPTY(&shead)) {
+               s = SLIST_FIRST(&shead);
+               SLIST_REMOVE_HEAD(&shead, entry);
+               free(s->bfname);
+               free(s);
+       }
+}
+
+/*
+ * Strip away any special characters in pattern.
+ * The pattern in ctags isn't a true regular expression. Its of the form
+ * /^xxx$/ or ?^xxx$? and in some cases the "$" would be missing. Strip 
+ * the leading and trailing special characters so the pattern matching
+ * would be a simple string compare. Escape character is taken care by 
+ * fparseln.
+ */
+char *
+strip(char *s, size_t len)
+{
+       /* first strip trailing special chars */        
+       s[len - 1] = '\0';
+       if (s[len - 2] == '$')
+               s[len - 2] = '\0';
+       
+       /* strip special chars at beginning */
+       s++;
+       if (*s == '^')
+               s++;
+       
+       return s;
+}
+
+/*
+ * tags line is of the format "<tag>\t<filename>\t<pattern>". Split them
+ * by replacing '\t' with '\0'. This wouldn't alter the size of malloc'ed 
+ * l, and can be freed during cleanup.
+ */
+void
+addctag(char *l)
+{
+       struct ctag *t;
+       if((t = malloc(sizeof(struct ctag))) == NULL)
+               err(1, NULL);
+       t->tag = l;
+       if((l = strchr(l, '\t')) == NULL)
+               goto cleanup;
+       *l++ = '\0';
+       t->fname = l;
+       if((l = strchr(l, '\t')) == NULL)
+               goto cleanup;
+       *l++ = '\0';
+       t->pat = strip(l, strlen(l));
+       RB_INSERT(tagtree, &tags, t);
+       return;
+
+cleanup:
+       free(t);
+       free(l);
+       
+}
+
+/*
+ * Search through each line of buffer for pattern.
+ */
+int
+patsearch(char *pat)
+{
+       struct line *lp;
+       int dotline = 1;
+       lp = lforw(curbp->b_headp);
+       while (lp != curbp->b_headp) {
+               if (ltext(lp) == NULL || strncmp(pat, ltext(lp), llength(lp))) {
+                       lp = lforw(lp);
+                       dotline++;
+               }
+               else {  
+                       curwp->w_doto = 0;
+                       curwp->w_dotp = lp;
+                       curwp->w_dotline = dotline;
+                       return (TRUE);
+               }
+       }
+       return (FALSE);
+}
+
+/*
+ * Extract the word at dot without changing dot position.
+ */
+int
+curtoken(int f, int n, char *token)
+{
+       struct line *dotp;
+       int doto, size, r;
+       
+       dotp = curwp->w_dotp;
+       doto = curwp->w_doto;
+       
+       if ((r = forwword(f, n)) == FALSE) {
+               goto cleanup;
+       }
+
+       size = curwp->w_doto - doto;
+       if (size >= MAX_TOKEN || ltext(curwp->w_dotp) == NULL) {
+               r = FALSE;
+               goto cleanup;
+       }
+       strncpy(token, ltext(curwp->w_dotp) + doto, size);
+       token[size] = '\0';
+       r = TRUE;
+       
+cleanup:
+       curwp->w_dotp = dotp;
+       curwp->w_doto = doto;
+       return (r);
+}
+
+/*
+ * Get the word at dot, search tagstree for that word.
+ */
+struct ctag *
+findtag(int f, int n)
+{
+       struct ctag t, *res;
+       char tok[MAX_TOKEN];
+
+       if (curtoken(f, n, tok) == FALSE)
+               return (NULL);
+       t.tag = tok;    
+       if ((res = RB_FIND(tagtree, &tags, &t)) == NULL) {
+               ewprintf("%s: tag not found", tok);
+       }
+       return res;
+}
+
+/*
+ * This is equivalent to filevisit from file.c.
+ * Look around to see if we can find the file in another buffer; if
+ * we can't find it, create a new buffer, read in the text, and 
+ * switch to the new buffer.
+ */
+int
+loadbuffer(char *bfname)
+{
+       struct buffer *bufp;
+       char *adjf;
+
+       if((adjf = adjustname(bfname, TRUE)) == NULL)
+               return (FALSE);
+       if((bufp = findbuffer(adjf)) == NULL)
+               return (FALSE);
+       curbp = bufp;
+       if(showbuffer(bufp, curwp, WFFULL) != TRUE)
+               return (FALSE);
+       if (bufp->b_fname[0] == '\0') {
+               if (readin(adjf) != TRUE) {
+                       killbuffer(bufp);
+                       return (FALSE);
+               }
+       }
+       return (TRUE);
+}

Reply via email to