Hello,

This diff adds tags support to Mg. I am NOT an emacs user so if this
combination is a bit odd then please excuse. It parses the tags file
generated by ctags(1) and maintains a tree. M-. on first character of
a word jumps to it's definition and M-* jumps back to previous location.

Index: Makefile
===================================================================
RCS file: /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    2 Sep 2011 15:00:39 -0000
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.24 2011/02/02 05:21:36 lum Exp $
+# $OpenBSD: Makefile,v 1.23 2011/01/18 17:35:42 lum Exp $
 
 PROG=  mg
 
@@ -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 theo.c tags.c
 
 afterinstall:
        ${INSTALL} -d ${DESTDIR}${DOCDIR}/mg
Index: buffer.c
===================================================================
RCS file: /cvs/src/usr.bin/mg/buffer.c,v
retrieving revision 1.77
diff -u -p -r1.77 buffer.c
--- buffer.c    23 Jan 2011 00:45:03 -0000      1.77
+++ buffer.c    2 Sep 2011 15:00:39 -0000
@@ -1,4 +1,4 @@
-/*     $OpenBSD: buffer.c,v 1.77 2011/01/23 00:45:03 kjell Exp $       */
+/*     $OpenBSD: buffer.c,v 1.76 2011/01/21 19:10:13 kjell Exp $       */
 
 /* This file is in the public domain. */
 
Index: def.h
===================================================================
RCS file: /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       2 Sep 2011 15:00:39 -0000
@@ -1,4 +1,4 @@
-/*     $OpenBSD: def.h,v 1.116 2011/01/23 00:45:03 kjell Exp $ */
+/*     $OpenBSD: def.h,v 1.115 2011/01/18 16:25:40 kjell Exp $ */
 
 /* This file is in the public domain. */
 
@@ -514,6 +514,10 @@ int                 backdel(int, int);
 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);
Index: keymap.c
===================================================================
RCS file: /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    2 Sep 2011 15:00:40 -0000
@@ -1,4 +1,4 @@
-/*     $OpenBSD: keymap.c,v 1.45 2011/01/18 16:25:40 kjell Exp $       */
+/*     $OpenBSD: keymap.c,v 1.44 2011/01/17 03:12:06 kjell Exp $       */
 
 /* This file is in the public domain. */
 
@@ -204,8 +204,11 @@ static PF metapct[] = {
 };
 
 static PF metami[] = {
+       poptag,                 /* * */
+       rescan,                 /* + */
+       rescan,                 /* , */
        negative_argument,      /* - */
-       rescan,                 /* . */
+       pushtag,                /* . */
        rescan,                 /* / */
        digit_argument,         /* 0 */
        digit_argument,         /* 1 */
@@ -231,7 +234,7 @@ static PF metasqf[] = {
        joinline,               /* ^ */
        rescan,                 /* _ */
        rescan,                 /* ` */
-       rescan,                 /* a */
+       rescan,                 /* a */
        backword,               /* b */
        capword,                /* c */
        delfword,               /* d */
@@ -298,7 +301,7 @@ struct KEYMAPE (8 + IMAPEXT) metamap = {
                        '%', '%', metapct, NULL
                },
                {
-                       '-', '>', metami, NULL
+                       '*', '>', metami, NULL
                },
                {
                        '[', 'f', metasqf, (KEYMAP *) &metasqlmap
Index: main.c
===================================================================
RCS file: /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      2 Sep 2011 15:00:40 -0000
@@ -1,4 +1,4 @@
-/*     $OpenBSD: main.c,v 1.61 2009/06/04 02:23:37 kjell Exp $ */
+/*     $OpenBSD: main.c,v 1.60 2008/06/13 18:51:02 kjell Exp $ */
 
 /* This file is in the public domain. */
 
@@ -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,8 +238,11 @@ quit(int f, int n)
 #ifdef SYSCLEANUP
                SYSCLEANUP;
 #endif /* SYSCLEANUP */
+               tags_close();
                exit(GOOD);
        }
+
+       tags_close();
        return (TRUE);
 }
 
Index: tags.c
===================================================================
RCS file: tags.c
diff -N tags.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ tags.c      2 Sep 2011 15:00:40 -0000
@@ -0,0 +1,350 @@
+/* This file is in public domain. */
+
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/tree.h>
+
+#include <err.h>
+#include <regex.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "def.h"
+
+#define MAX_TOKEN 128
+
+/*
+ * ctags(1) entries are parsed and maintained in a tree.
+ */
+struct ctagnode {
+       RB_ENTRY(ctagnode) entry;
+       char *tok;
+       char *fname;
+       char *pat;
+};
+
+static int
+ctagcmp(struct ctagnode *s, struct ctagnode *t)
+{
+       return strcmp(s->tok, t->tok);
+}
+
+RB_HEAD(tagtree, ctagnode) tags = RB_INITIALIZER(&tags);
+RB_GENERATE(tagtree, ctagnode, entry, ctagcmp)
+
+/*
+ * The tag stack used by pushtag and poptag.
+ */
+struct stacknode {
+       SLIST_ENTRY(stacknode) entry;
+       char *bfname;
+       struct line *dotp;
+       int doto;
+};
+
+SLIST_HEAD(tagstack, stacknode) shead = SLIST_HEAD_INITIALIZER(shead);
+
+/*
+ * tags line is of the format "<token>\t<filename>\t<pattern>\n".
+ */
+static void
+addctag(char *ctagline)
+{
+       char *token;
+       struct ctagnode *t;
+       char *tok = NULL;
+       char *fname = NULL;
+       char *pat = NULL;
+       
+       token = strsep(&ctagline, "\t");
+       if(token == NULL)
+               goto cleanup;
+       if((tok = strdup(token)) == NULL)
+               err(1, NULL);
+       token = strsep(&ctagline, "\t");
+       if(token == NULL)
+               goto cleanup;
+       if((fname = strdup(token)) == NULL)
+               err(1, NULL);
+       token = strsep(&ctagline, "\n");
+       if(token == NULL)
+               goto cleanup;
+       if((pat = strdup(token)) == NULL)
+               err(1, NULL);
+       if((t = malloc(sizeof(struct ctagnode))) == NULL)
+               err(1, NULL);
+       t->tok = tok;
+       t->fname = fname;
+       t->pat = pat;
+       RB_INSERT(tagtree, &tags, t);
+       return;
+cleanup:
+       free(tok);
+       free(fname);
+       free(pat);
+}
+
+/*
+ * Parse the tags file if it's present and construct the tags tree.
+ */
+void
+tags_init()
+{
+       char line[NLINE];
+       FILE *tagfile;
+       if(!(tagfile = fopen("tags", "r"))) 
+               return;
+       while(fgets(line, sizeof(line), tagfile))
+               addctag(line);
+       fclose(tagfile);
+       return;
+}
+
+/*
+ * Cleanup and destroy tree and stack.
+ */
+void
+tags_close()
+{
+       struct ctagnode *var, *nxt;
+       struct stacknode *s;
+       
+       for(var = RB_MIN(tagtree, &tags); var !=NULL; var = nxt) {
+               nxt = RB_NEXT(tagtree, &tags, var);
+               RB_REMOVE(tagtree, &tags, var);
+               free(var->tok);
+               free(var->fname);
+               free(var->pat);
+               free(var);
+       }
+       
+       while(!SLIST_EMPTY(&shead)) {
+               s = SLIST_FIRST(&shead);
+               SLIST_REMOVE_HEAD(&shead, entry);
+               free(s->bfname);
+               free(s);
+       }
+}
+
+/*
+ * Helper function to append a character to a C string.
+ */
+static void
+append(char *s, int c)
+{
+        size_t l = strlen(s);
+       s[l++] = c;
+       s[l] = '\0';
+}
+
+RSIZE countfword();
+/*
+ * Extract the word at dot without changing dot position.
+ * TODO: lgetc returns when it encounters "_". If a function is named
+ * for example "tags_init", this function can only extact "tags" and
+ * tag lookup would fail.
+ */
+static int
+curtoken(int f, int n, char *token)
+{
+       struct line *dotp;
+       int doto;
+       int c, size;
+       dotp = curwp->w_dotp;
+       doto = curwp->w_doto;
+       if (n < 0)
+               return (FALSE);
+       while (n--) {
+               while (inword() == FALSE) {
+                       if (forwchar(FFRAND, 1) == FALSE)
+                               return (TRUE);
+               }
+               size = countfword();
+               while (inword() != FALSE) {
+                       c = lgetc(curwp->w_dotp, curwp->w_doto);
+                       if(size < MAX_TOKEN - 1)
+                               append(token, c);
+                       if (forwchar(FFRAND, 1) == FALSE)
+                               break;
+               }
+       }
+       curwp->w_dotp = dotp;
+       curwp->w_doto = doto;
+       return (TRUE);
+}
+
+/*
+ * Borrowed from ex.
+ * Convert ctags re to POSIX 1003.2 RE
+ *
+ */
+static void
+re_tag_conv(const char *p, char *t)
+{
+       int lastdollar;
+       int len = strlen(p);
+       
+       /* If the last character is '/' or '?', we just strip it. */
+       if(len > 0 && (p[len - 1] == '/' || p[len - 1] == '?'))
+               --len;
+               
+       /* If the next to last character is a '$',  it's magic. */
+       if(len > 0 && p[len - 1] == '$') {
+               --len;
+               lastdollar = 1;
+       }
+       else
+               lastdollar = 0;
+               
+       /* If the first character is '/' or '?', we just strip it. */
+       if(len > 0 && (p[0] == '/' || p[0] == '?')) {
+               ++p;
+               --len;
+       }
+       
+       /* If the first or second character is '^', it' magic. */
+       if(p[0] == '^') {
+               *t++ = *p++;
+               --len;
+       }
+       
+       /*
+        * Escape every other magic character we can find, meanwhile
+        * stripping the backslashes ctags inserts when escaping the
+        * search delimiter characters.
+        */
+       for(; len > 0; --len) {
+               if(p[0] == '\\' && (p[1] == '/' || p[1] == '?')) {
+                       ++p;
+                       --len;
+               }
+               else if(strchr("^.[]$*", p[0]))
+                       *t++ = '\\';
+               *t++ = *p++;
+               if(len == 0)
+                       break;
+       }
+       if(lastdollar)
+               *t++ = '$';
+       
+       *t = '\0';
+}
+
+#define PAT_MATCH 1
+static regex_t re_buff;
+static regmatch_t re_match[PAT_MATCH];
+
+/*
+ * Convert ctags pattern to POSIX 1003.2 RE format and iterate 
+ * through current buffer to match RE. If found move dot to 
+ * matched line.
+ * TODO: Cache searched pattern to reduce linear lookup.
+ */
+static int
+patsearch(char *pat)
+{
+       int error;
+       char re_pat[NLINE];
+       struct line *clp = lforw(curbp->b_headp);
+       re_tag_conv(pat, re_pat);
+       if(regcomp(&re_buff, re_pat, REG_BASIC) != 0) {
+               return (FALSE);
+       }       
+
+       while(clp != curbp->b_headp) {
+               re_match[0].rm_so = 0;
+               re_match[0].rm_eo = llength(clp);
+               error = regexec(&re_buff, ltext(clp), PAT_MATCH, 
+                   re_match, REG_STARTEND);
+               if(error != 0) {
+                       clp = lforw(clp);
+               }
+               else {
+                       curwp->w_doto = 0;
+                       curwp->w_dotp = clp;
+                       return (TRUE);
+               }
+       }
+       return (FALSE);
+}
+
+/*
+ * If the current word starting at dot has a ctags entry, then load the
+ * file as a new buffer if it's not already open and match the pattern.
+ * If the pattern matches, push previous buffer name and dot onto tag
+ * stack before setting the dot to the match site of the pattern.
+ */
+/*ARGSUSED */
+int
+pushtag(int f, int n)
+{
+       char *adjf;
+       struct buffer *bufp;
+       struct ctagnode t, *res;
+       struct stacknode *s;
+       char *bfname;
+       struct line *odotp;
+       int odoto;
+       char tok[MAX_TOKEN];
+       tok[0] = '\0';
+       if(!curtoken(f, n, tok))
+               return (FALSE);
+       t.tok = tok;    
+       if((res = RB_FIND(tagtree, &tags, &t)) == NULL) {
+               ewprintf("%s: tag not found", tok);
+               return (FALSE);
+       }
+       odotp = curwp->w_dotp;
+       odoto = curwp->w_doto;
+       bfname = curbp->b_fname;
+       adjf = adjustname(res->fname, TRUE);
+       bufp = findbuffer(adjf);
+       curbp = bufp;
+       showbuffer(bufp, curwp, WFFULL);
+       if(bufp->b_fname[0] == '\0') {
+               if(readin(adjf) != TRUE) {
+                       killbuffer(bufp);
+                       return (FALSE);
+               }
+       }
+       if(patsearch(res->pat) == TRUE) {
+               if((s = malloc(sizeof(struct stacknode))) == NULL)
+                       err(1, NULL);
+               s->bfname = strdup(bfname);
+               s->dotp = odotp;
+               s->doto = odoto;
+               SLIST_INSERT_HEAD(&shead, s, entry);
+               return (TRUE);
+       }
+       else {
+               ewprintf("%s: pattern not found", tok);
+               return (FALSE);
+       }
+       return (TRUE);  
+}
+
+/*
+ * 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 stacknode *s;
+       s = SLIST_FIRST(&shead);
+       if(s == NULL) {
+               ewprintf("tag stack empty.");
+               return (FALSE);
+       }
+       SLIST_REMOVE_HEAD(&shead, entry);
+       bufp = findbuffer(s->bfname);
+       curbp = bufp;
+       showbuffer(bufp, curwp, WFFRAME | WFFULL);
+       curwp->w_dotp = s->dotp;
+       curwp->w_doto = s->doto;
+       free(s->bfname);
+       free(s);
+       return (TRUE);
+}

Reply via email to