+++ /dev/null
-/* $OpenBSD: tags.c,v 1.5 2012/07/02 08:08:31 lum Exp $ */
-
-/*
- * This file is in the public domain.
- *
- * Author: Sunil Nimmagadda <sunil@sunilnimmagadda.com>
- */
-
-#include <sys/queue.h>
-#include <sys/stat.h>
-#include <sys/tree.h>
-#include <sys/types.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <stdlib.h>
-#include <string.h>
-#include <util.h>
-
-#include "def.h"
-
-struct ctag;
-
-static int addctag(char *);
-static int atbow(void);
-void closetags(void);
-static int ctagcmp(struct ctag *, struct ctag *);
-static int loadbuffer(char *);
-static int loadtags(const char *);
-static int pushtag(char *);
-static int searchpat(char *);
-static struct ctag *searchtag(char *);
-static char *strip(char *, size_t);
-static void unloadtags(void);
-
-#define DEFAULTFN "tags"
-
-char *tagsfn = NULL;
-int loaded = FALSE;
-
-/* ctags(1) entries are parsed and maintained in a tree. */
-struct ctag {
- RB_ENTRY(ctag) entry;
- char *tag;
- char *fname;
- char *pat;
-};
-RB_HEAD(tagtree, ctag) tags = RB_INITIALIZER(&tags);
-RB_GENERATE(tagtree, ctag, entry, ctagcmp);
-
-struct tagpos {
- SLIST_ENTRY(tagpos) entry;
- int doto;
- int dotline;
- char *bname;
-};
-SLIST_HEAD(tagstack, tagpos) shead = SLIST_HEAD_INITIALIZER(shead);
-
-int
-ctagcmp(struct ctag *s, struct ctag *t)
-{
- return strcmp(s->tag, t->tag);
-}
-
-/*
- * Record the filename that contain tags to be used while loading them
- * on first use. If a filename is already recorded, ask user to retain
- * already loaded tags (if any) and unload them if user chooses not to.
- */
-/* ARGSUSED */
-int
-tagsvisit(int f, int n)
-{
- char fname[NFILEN], *bufp, *temp;
- struct stat sb;
-
- if (getbufcwd(fname, sizeof(fname)) == FALSE)
- fname[0] = '\0';
-
- if (strlcat(fname, DEFAULTFN, sizeof(fname)) >= sizeof(fname)) {
- ewprintf("Filename too long");
- return (FALSE);
- }
-
- bufp = eread("visit tags table (default %s): ", fname,
- NFILEN, EFFILE | EFCR | EFNEW | EFDEF, DEFAULTFN);
-
- if (stat(bufp, &sb) == -1) {
- ewprintf("stat: %s", strerror(errno));
- return (FALSE);
- } else if (S_ISREG(sb.st_mode) == 0) {
- ewprintf("Not a regular file");
- return (FALSE);
- } else if (access(bufp, R_OK) == -1) {
- ewprintf("Cannot access file %s", bufp);
- return (FALSE);
- }
-
- if (tagsfn == NULL) {
- if (bufp == NULL)
- return (ABORT);
- else if (bufp[0] == '\0') {
- if ((tagsfn = strdup(fname)) == NULL) {
- ewprintf("Out of memory");
- return (FALSE);
- }
- } else {
- /* bufp points to local variable, so duplicate. */
- if ((tagsfn = strdup(bufp)) == NULL) {
- ewprintf("Out of memory");
- return (FALSE);
- }
- }
- } else {
- if ((temp = strdup(bufp)) == NULL) {
- ewprintf("Out of memory");
- return (FALSE);
- }
- free(tagsfn);
- tagsfn = temp;
- if (eyorn("Keep current list of tags table also") == FALSE) {
- ewprintf("Starting a new list of tags table");
- unloadtags();
- }
- loaded = FALSE;
- }
- return (TRUE);
-}
-
-/*
- * Ask user for a tag while treating word at dot as default. Visit tags
- * file if not yet done, load tags and jump to definition of the tag.
- */
-int
-findtag(int f, int n)
-{
- char utok[MAX_TOKEN], dtok[MAX_TOKEN];
- char *tok, *bufp;
- int ret;
-
- if (curtoken(f, n, dtok) == FALSE)
- return (FALSE);
-
- bufp = eread("Find tag (default %s) ", utok, MAX_TOKEN,
- EFNUL | EFNEW, dtok);
-
- if (bufp == NULL)
- return (ABORT);
- else if (bufp[0] == '\0')
- tok = dtok;
- else
- tok = utok;
-
- if (tok[0] == '\0') {
- ewprintf("There is no default tag");
- return (FALSE);
- }
-
- if (tagsfn == NULL)
- if ((ret = tagsvisit(f, n)) != TRUE)
- return (ret);
- if (!loaded) {
- if (loadtags(tagsfn) == FALSE) {
- free(tagsfn);
- tagsfn = NULL;
- return (FALSE);
- }
- loaded = TRUE;
- }
- return pushtag(tok);
-}
-
-/*
- * Free tags tree.
- */
-void
-unloadtags(void)
-{
- struct ctag *var, *nxt;
-
- for (var = RB_MIN(tagtree, &tags); var != NULL; var = nxt) {
- nxt = RB_NEXT(tagtree, &tags, var);
- RB_REMOVE(tagtree, &tags, var);
- /* line parsed with fparseln needs to be freed */
- free(var->tag);
- free(var);
- }
-}
-
-/*
- * Lookup tag passed in tree and if found, push current location and
- * buffername onto stack, load the file with tag definition into a new
- * buffer and position dot at the pattern.
- */
-/*ARGSUSED */
-int
-pushtag(char *tok)
-{
- struct ctag *res;
- struct tagpos *s;
- char bname[NFILEN];
- int doto, dotline;
-
- if ((res = searchtag(tok)) == NULL)
- return (FALSE);
-
- doto = curwp->w_doto;
- dotline = curwp->w_dotline;
- /* record absolute filenames. Fixes issues when mg's cwd is not the
- * same as buffer's directory.
- */
- if (strlcpy(bname, curbp->b_cwd, sizeof(bname)) >= sizeof(bname)) {
- ewprintf("filename too long");
- return (FALSE);
- }
- if (strlcat(bname, curbp->b_bname, sizeof(bname)) >= sizeof(bname)) {
- ewprintf("filename too long");
- return (FALSE);
- }
-
- if (loadbuffer(res->fname) == FALSE)
- return (FALSE);
-
- if (searchpat(res->pat) == TRUE) {
- if ((s = malloc(sizeof(struct tagpos))) == NULL) {
- ewprintf("Out of memory");
- return (FALSE);
- }
- if ((s->bname = strdup(bname)) == NULL) {
- ewprintf("Out of memory");
- return (FALSE);
- }
- s->doto = doto;
- s->dotline = dotline;
- SLIST_INSERT_HEAD(&shead, s, entry);
- return (TRUE);
- } else {
- ewprintf("%s: pattern not found", res->tag);
- return (FALSE);
- }
- /* NOTREACHED */
- return (FALSE);
-}
-
-/*
- * If tag stack is not empty pop stack and jump to recorded buffer, dot.
- */
-/* ARGSUSED */
-int
-poptag(int f, int n)
-{
- struct line *dotp;
- struct tagpos *s;
-
- if (SLIST_EMPTY(&shead)) {
- ewprintf("No previous location for find-tag invocation");
- return (FALSE);
- }
- s = SLIST_FIRST(&shead);
- SLIST_REMOVE_HEAD(&shead, entry);
- if (loadbuffer(s->bname) == FALSE)
- return (FALSE);
- curwp->w_dotline = s->dotline;
- curwp->w_doto = s->doto;
-
- /* storing of dotp in tagpos wouldn't work out in cases when
- * that buffer is killed by user(dangling pointer). Explicitly
- * traverse till dotline for correct handling.
- */
- dotp = curwp->w_bufp->b_headp;
- while (s->dotline--)
- dotp = dotp->l_fp;
-
- curwp->w_dotp = dotp;
- free(s->bname);
- free(s);
- return (TRUE);
-}
-
-/*
- * Parse the tags file and construct the tags tree. Remove escape
- * characters while parsing the file.
- */
-int
-loadtags(const char *fn)
-{
- char *l;
- FILE *fd;
-
- if ((fd = fopen(fn, "r")) == NULL) {
- ewprintf("Unable to open tags file: %s", fn);
- return (FALSE);
- }
- while ((l = fparseln(fd, NULL, NULL, "\\\\\0",
- FPARSELN_UNESCCONT | FPARSELN_UNESCREST)) != NULL) {
- if (addctag(l) == FALSE) {
- fclose(fd);
- return (FALSE);
- }
- }
- fclose(fd);
- return (TRUE);
-}
-
-/*
- * Cleanup and destroy tree and stack.
- */
-void
-closetags(void)
-{
- struct tagpos *s;
-
- while (!SLIST_EMPTY(&shead)) {
- s = SLIST_FIRST(&shead);
- SLIST_REMOVE_HEAD(&shead, entry);
- free(s->bname);
- free(s);
- }
- unloadtags();
- free(tagsfn);
-}
-
-/*
- * 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';
-
- /* then strip leading special chars */
- 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.
- */
-int
-addctag(char *l)
-{
- struct ctag *t;
-
- if ((t = malloc(sizeof(struct ctag))) == NULL) {
- ewprintf("Out of memory");
- return (FALSE);
- }
- 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';
- if (*l == '\0')
- goto cleanup;
- t->pat = strip(l, strlen(l));
- RB_INSERT(tagtree, &tags, t);
- return (TRUE);
-cleanup:
- free(t);
- free(l);
- return (TRUE);
-}
-
-/*
- * Search through each line of buffer for pattern.
- */
-int
-searchpat(char *pat)
-{
- struct line *lp;
- int dotline;
- size_t plen;
-
- plen = strlen(pat);
- dotline = 1;
- lp = lforw(curbp->b_headp);
- while (lp != curbp->b_headp) {
- if (ltext(lp) != NULL && plen <= llength(lp) &&
- (strncmp(pat, ltext(lp), plen) == 0)) {
- curwp->w_doto = 0;
- curwp->w_dotp = lp;
- curwp->w_dotline = dotline;
- return (TRUE);
- } else {
- lp = lforw(lp);
- dotline++;
- }
- }
- return (FALSE);
-}
-
-/*
- * Return TRUE if dot is at beginning of a word or at beginning
- * of line, else FALSE.
- */
-int
-atbow(void)
-{
- if (curwp->w_doto == 0)
- return (TRUE);
- if (ISWORD(curwp->w_dotp->l_text[curwp->w_doto]) &&
- !ISWORD(curwp->w_dotp->l_text[curwp->w_doto - 1]))
- return (TRUE);
- return (FALSE);
-}
-
-/*
- * Extract the word at dot without changing dot position.
- */
-int
-curtoken(int f, int n, char *token)
-{
- struct line *odotp;
- int odoto, tdoto, odotline, size, r;
- char c;
-
- /* Underscore character is to be treated as "inword" while
- * processing tokens unlike mg's default word traversal. Save
- * and restore it's cinfo value so that tag matching works for
- * identifier with underscore.
- */
- c = cinfo['_'];
- cinfo['_'] = _MG_W;
-
- odotp = curwp->w_dotp;
- odoto = curwp->w_doto;
- odotline = curwp->w_dotline;
-
- /* Move backword unless we are at the beginning of a word or at
- * beginning of line.
- */
- if (!atbow())
- if ((r = backword(f, n)) == FALSE)
- goto cleanup;
-
- tdoto = curwp->w_doto;
-
- if ((r = forwword(f, n)) == FALSE)
- goto cleanup;
-
- /* strip away leading whitespace if any like emacs. */
- while (ltext(curwp->w_dotp) &&
- isspace(curwp->w_dotp->l_text[tdoto]))
- tdoto++;
-
- size = curwp->w_doto - tdoto;
- if (size <= 0 || size >= MAX_TOKEN ||
- ltext(curwp->w_dotp) == NULL) {
- r = FALSE;
- goto cleanup;
- }
- strncpy(token, ltext(curwp->w_dotp) + tdoto, size);
- token[size] = '\0';
- r = TRUE;
-
-cleanup:
- cinfo['_'] = c;
- curwp->w_dotp = odotp;
- curwp->w_doto = odoto;
- curwp->w_dotline = odotline;
- return (r);
-}
-
-/*
- * Search tagstree for a given token.
- */
-struct ctag *
-searchtag(char *tok)
-{
- struct ctag t, *res;
-
- t.tag = tok;
- if ((res = RB_FIND(tagtree, &tags, &t)) == NULL) {
- ewprintf("No tag containing %s", tok);
- return (NULL);
- }
- 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. *scratch*, *grep*, *compile* needs to be handled
- * differently from other buffers which have "filenames".
- */
-int
-loadbuffer(char *bname)
-{
- struct buffer *bufp;
- char *adjf;
-
- /* check for special buffers which begin with '*' */
- if (bname[0] == '*') {
- if ((bufp = bfind(bname, FALSE)) != NULL) {
- curbp = bufp;
- return (showbuffer(bufp, curwp, WFFULL));
- } else {
- return (FALSE);
- }
- } else {
- if ((adjf = adjustname(bname, 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);
-}