$NetBSD$

--- ../ex/ex_tag.c.orig	2007-11-19 03:41:42.000000000 +1100
+++ ../ex/ex_tag.c
@@ -46,6 +46,8 @@ static char	*binary_search __P((char *, 
 static int	 compare __P((char *, char *, char *));
 static void	 ctag_file __P((SCR *, TAGF *, char *, char **, size_t *));
 static int	 ctag_search __P((SCR *, CHAR_T *, size_t, char *));
+static int	 getentry __P((char *, char **, char **, char **));
+static TAGQ	*gtag_slist __P((SCR *, CHAR_T *, int));
 static int	 ctag_sfile __P((SCR *, TAGF *, TAGQ *, char *));
 static TAGQ	*ctag_slist __P((SCR *, CHAR_T *));
 static char	*linear_search __P((char *, char *, char *, long));
@@ -87,6 +89,21 @@ ex_tag_first(SCR *sp, CHAR_T *tagarg)
 }
 
 /*
+ * ex_rtag_push -- ^]
+ *              :rtag[!] [string]
+ *
+ * Enter a new TAGQ context based on a ctag string.
+ *
+ * PUBLIC: int ex_rtag_push __P((SCR *, EXCMD *));
+ */
+int
+ex_rtag_push(SCR *sp, EXCMD *cmdp)
+{
+	F_SET(cmdp, E_REFERENCE);
+	return ex_tag_push(sp, cmdp);
+}
+
+/*
  * ex_tag_push -- ^]
  *		  :tag[!] [string]
  *
@@ -129,6 +146,11 @@ ex_tag_push(SCR *sp, EXCMD *cmdp)
 	}
 
 	/* Get the tag information. */
+	if (O_ISSET(sp, O_GTAGSMODE)) {
+		if ((tqp = gtag_slist(sp, exp->tag_last,
+		    F_ISSET(cmdp, E_REFERENCE))) == NULL)
+			return (1);
+	} else
 	if ((tqp = ctag_slist(sp, exp->tag_last)) == NULL)
 		return (1);
 
@@ -980,6 +1002,131 @@ notfound:			tag_msg(sp, TAG_SEARCH, tag)
 }
 
 /*
+ * getentry --
+ *	get tag information from current line.
+ *
+ * gtags temporary file format.
+ * <tag>   <lineno>  <file>         <image>
+ *
+ * sample.
+ * +------------------------------------------------
+ * |main     30      main.c         main(argc, argv)
+ * |func     21      subr.c         func(arg)
+ */
+static int
+getentry(char *buf, char **tag, char **file, char **line)
+{
+	char *p = buf;
+
+	for (*tag = p; *p && !isspace(*p); p++)		/* tag name */
+		;
+	if (*p == 0)
+		goto err;
+	*p++ = 0;
+	for (; *p && isspace(*p); p++)			/* (skip blanks) */
+		;
+	if (*p == 0)
+		goto err;
+	*line = p;					/* line no */
+	for (*line = p; *p && !isspace(*p); p++)
+		;
+	if (*p == 0)
+		goto err;
+	*p++ = 0;
+	for (; *p && isspace(*p); p++)			/* (skip blanks) */
+		;
+	if (*p == 0)
+		goto err;
+	*file = p;					/* file name */
+	for (*file = p; *p && !isspace(*p); p++)
+		;
+	if (*p == 0)
+		goto err;
+	*p = 0;
+
+	/* value check */
+	if (strlen(*tag) && strlen(*line) && strlen(*file) && atoi(*line) > 0)
+		return 1;	/* OK */
+err:
+	return 0;		/* ERROR */
+}
+
+/*
+ * gtag_slist --
+ *	Search the list of tags files for a tag, and return tag queue.
+ */
+static TAGQ *
+gtag_slist(SCR *sp, CHAR_T *tag, int ref)
+{
+	EX_PRIVATE *exp;
+	TAGF *tfp;
+	TAGQ *tqp;
+	size_t len, nlen, slen, wlen;
+	int echk;
+	TAG *tp;
+	char *np;
+	char *name, *file, *search;
+	char command[BUFSIZ];
+	char buf[BUFSIZ];
+	CHAR_T *wp;
+	FILE *fp;
+
+	/* Allocate and initialize the tag queue structure. */
+	INT2CHAR(sp, tag, STRLEN(tag) + 1, np, nlen);
+	len = nlen - 1;
+	CALLOC_GOTO(sp, tqp, TAGQ *, 1, sizeof(TAGQ) + len + 1);
+	CIRCLEQ_INIT(&tqp->tagq);
+	tqp->tag = tqp->buf;
+	memcpy(tqp->tag, np, (tqp->tlen = len) + 1);
+
+	/*
+	 * Find the tag, only display missing file messages once, and
+	 * then only if we didn't find the tag.
+	 */
+	snprintf(command, sizeof(command), "global -%s '%s'", ref ? "rx" : "x",
+	    np);
+	if (fp = popen(command, "r")) {
+		while (fgets(buf, sizeof(buf), fp)) {
+			if (buf[strlen(buf)-1] == '\n')		/* chop(buf) */
+				buf[strlen(buf)-1] = 0;
+			else
+				while (fgetc(fp) != '\n')
+					;
+			if (getentry(buf, &name, &file, &search) == 0) {
+				echk = 1;
+				F_SET(tfp, TAGF_ERR);
+				break;
+			}
+			slen = strlen(search);
+			CALLOC_GOTO(sp, tp,
+			    TAG *, 1, sizeof(TAG) + strlen(file) + 1 +
+			    (slen + 1) * sizeof(CHAR_T));
+			tp->fname = (char *)tp->buf;
+			strcpy(tp->fname, file);
+			tp->fnlen = strlen(file);
+			tp->search = (CHAR_T *)(tp->fname + tp->fnlen + 1);
+			CHAR2INT(sp, search, slen + 1, wp, wlen);
+			MEMCPYW(tp->search, wp, (tp->slen = slen) + 1);
+			CIRCLEQ_INSERT_TAIL(&tqp->tagq, tp, q);
+		}
+		pclose(fp);
+	}
+
+	/* Check to see if we found anything. */
+	if (tqp->tagq.cqh_first == (void *)&tqp->tagq) {
+		msgq_str(sp, M_ERR, np, "162|%s: tag not found");
+		free(tqp);
+		return (NULL);
+	}
+
+	tqp->current = tqp->tagq.cqh_first;
+	return (tqp);
+
+alloc_err:
+	return (NULL);
+}
+
+/*
  * ctag_slist --
  *	Search the list of tags files for a tag, and return tag queue.
  */
