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); +}