📄 ex_tag.c
字号:
/* * ex_tagdisplay -- * Display the list of tags. */intex_tagdisplay(sp, ep) SCR *sp; EXF *ep;{ EX_PRIVATE *exp; TAG *tp; size_t len, maxlen; int cnt; char *name; exp = EXP(sp); if ((tp = exp->tagq.tqh_first) == NULL) { (void)ex_printf(EXCOOKIE, "No tags to display.\n"); return (0); } /* * Figure out the formatting. MNOC is the maximum * number of file name columns before we split the line. */#define MNOC 15 for (maxlen = 0, tp = exp->tagq.tqh_first; tp != NULL; tp = tp->q.tqe_next) { len = strlen(name = tp->frp->name); /* The original name. */ if (maxlen < len && len < MNOC) maxlen = len; } for (cnt = 1, tp = exp->tagq.tqh_first; tp != NULL; ++cnt, tp = tp->q.tqe_next) { len = strlen(name = tp->frp->name); /* The original name. */ if (len > maxlen || len + tp->slen > sp->cols) if (tp == NULL || tp->search == NULL) (void)ex_printf(EXCOOKIE, "%2d %s\n", cnt, name); else (void)ex_printf(EXCOOKIE, "%2d %s\n** %*.*s %s\n", cnt, name, (int)maxlen, (int)maxlen, "", tp->search); else if (tp == NULL || tp->search == NULL) (void)ex_printf(EXCOOKIE, "%2d %*.*s\n", cnt, (int)maxlen, (int)len, name); else (void)ex_printf(EXCOOKIE, "%2d %*.*s %s\n", cnt, (int)maxlen, (int)len, name, tp->search); } return (0);}/* * ex_tagalloc -- * Create a new list of tag files. */intex_tagalloc(sp, str) SCR *sp; char *str;{ EX_PRIVATE *exp; TAGF *tp; size_t len; char *p, *t; /* Free current queue. */ exp = EXP(sp); while ((tp = exp->tagfq.tqh_first) != NULL) FREETAGF(tp); /* Create new queue. */ for (p = t = str;; ++p) { if (*p == '\0' || isblank(*p)) { if ((len = p - t) > 1) { MALLOC_RET(sp, tp, TAGF *, sizeof(TAGF)); MALLOC(sp, tp->name, char *, len + 1); if (tp->name == NULL) { FREE(tp, sizeof(TAGF)); return (1); } memmove(tp->name, t, len); tp->name[len] = '\0'; tp->flags = 0; TAILQ_INSERT_TAIL(&exp->tagfq, tp, q); } t = p + 1; } if (*p == '\0') break; } return (0);} /* Free previous queue. *//* * ex_tagfree -- * Free the tags file list. */intex_tagfree(sp) SCR *sp;{ EX_PRIVATE *exp; TAG *tp; TAGF *tfp; /* Free up tag information. */ exp = EXP(sp); while ((tp = exp->tagq.tqh_first) != NULL) FREETAG(tp); while ((tfp = exp->tagfq.tqh_first) != NULL) FREETAGF(tfp); if (exp->tlast != NULL) free(exp->tlast); return (0);}/* * ex_tagcopy -- * Copy a screen's tag structures. */intex_tagcopy(orig, sp) SCR *orig, *sp;{ EX_PRIVATE *oexp, *nexp; TAG *ap, *tp; TAGF *atfp, *tfp; /* Copy tag stack. */ oexp = EXP(orig); nexp = EXP(sp); for (ap = oexp->tagq.tqh_first; ap != NULL; ap = ap->q.tqe_next) { MALLOC(sp, tp, TAG *, sizeof(TAG)); if (tp == NULL) goto nomem; *tp = *ap; if (ap->search != NULL && (tp->search = strdup(ap->search)) == NULL) goto nomem; TAILQ_INSERT_TAIL(&nexp->tagq, tp, q); } /* Copy list of tag files. */ for (atfp = oexp->tagfq.tqh_first; atfp != NULL; atfp = atfp->q.tqe_next) { MALLOC(sp, tfp, TAGF *, sizeof(TAGF)); if (tfp == NULL) goto nomem; *tfp = *atfp; if ((tfp->name = strdup(atfp->name)) == NULL) goto nomem; TAILQ_INSERT_TAIL(&nexp->tagfq, tfp, q); } /* Copy the last tag. */ if (oexp->tlast != NULL && (nexp->tlast = strdup(oexp->tlast)) == NULL) {nomem: msgq(sp, M_SYSERR, NULL); return (1); } return (0);}/* * tag_get -- * Get a tag from the tags files. */static inttag_get(sp, tag, tagp, filep, searchp) SCR *sp; char *tag, **tagp, **filep, **searchp;{ struct stat sb; EX_PRIVATE *exp; TAGF *tfp; size_t plen, slen, tlen; int dne; char *p, pbuf[MAXPATHLEN]; /* * Find the tag, only display missing file messages once, and * then only if we didn't find the tag. */ dne = 0; exp = EXP(sp); for (p = NULL, tfp = exp->tagfq.tqh_first; tfp != NULL && p == NULL; tfp = tfp->q.tqe_next) { errno = 0; F_CLR(tfp, TAGF_DNE); if (search(sp, tfp->name, tag, &p)) if (errno == ENOENT) { if (!F_ISSET(tfp, TAGF_DNE_WARN)) { dne = 1; F_SET(tfp, TAGF_DNE); } } else msgq(sp, M_SYSERR, tfp->name); else if (p != NULL) break; } if (p == NULL) { msgq(sp, M_ERR, "%s: tag not found.", tag); if (dne) for (tfp = exp->tagfq.tqh_first; tfp != NULL; tfp = tfp->q.tqe_next) if (F_ISSET(tfp, TAGF_DNE)) { errno = ENOENT; msgq(sp, M_SYSERR, tfp->name); F_SET(tfp, TAGF_DNE_WARN); } return (1); } /* * Set the return pointers; tagp points to the tag, and, incidentally * the allocated string, filep points to the file name, and searchp * points to the search string. All three are nul-terminated. */ for (*tagp = p; *p && !isblank(*p); ++p); if (*p == '\0') goto malformed; for (*p++ = '\0'; isblank(*p); ++p); for (*filep = p; *p && !isblank(*p); ++p); if (*p == '\0') goto malformed; for (*p++ = '\0'; isblank(*p); ++p); *searchp = p; if (*p == '\0') {malformed: free(*tagp); msgq(sp, M_ERR, "%s: corrupted tag in %s.", tag, tfp->name); return (1); } /* * !!! * If the tag file path is a relative path, see if it exists. If it * doesn't, look relative to the tags file path. It's okay for a tag * file to not exist, and, historically, vi simply displayed a "new" * file. However, if the path exists relative to the tag file, it's * pretty clear what's happening, so we may as well do it right. */ if ((*filep)[0] != '/' && stat(*filep, &sb) && (p = strrchr(tfp->name, '/')) != NULL) { *p = '\0'; plen = snprintf(pbuf, sizeof(pbuf), "%s/%s", tfp->name, *filep); *p = '/'; if (stat(pbuf, &sb) == 0) { slen = strlen(*searchp); tlen = strlen(*tagp); MALLOC(sp, p, char *, plen + slen + tlen + 5); if (p != NULL) { memmove(p, *tagp, tlen); free(*tagp); *tagp = p; *(p += tlen) = '\0'; memmove(++p, pbuf, plen); *filep = p; *(p += plen) = '\0'; memmove(++p, *searchp, slen); *searchp = p; *(p += slen) = '\0'; } } } return (0);}#define EQUAL 0#define GREATER 1#define LESS (-1)/* * search -- * Search a file for a tag. */static intsearch(sp, name, tname, tag) SCR *sp; char *name, *tname, **tag;{ struct stat sb; int fd, len; char *endp, *back, *front, *map, *p; if ((fd = open(name, O_RDONLY, 0)) < 0) return (1); /* * XXX * We'd like to test if the file is too big to mmap. Since we don't * know what size or type off_t's or size_t's are, what the largest * unsigned integral type is, or what random insanity the local C * compiler will perpetrate, doing the comparison in a portable way * is flatly impossible. Hope that malloc fails if the file is too * large. */ if (fstat(fd, &sb) || (map = mmap(NULL, (size_t)sb.st_size, PROT_READ, MAP_PRIVATE, fd, (off_t)0)) == (caddr_t)-1) { (void)close(fd); return (1); } front = map; back = front + sb.st_size; front = binary_search(tname, front, back); front = linear_search(tname, front, back); if (front == NULL || (endp = strchr(front, '\n')) == NULL) { *tag = NULL; goto done; } len = endp - front; MALLOC(sp, p, char *, len + 1); if (p == NULL) { *tag = NULL; goto done; } memmove(p, front, len); p[len] = '\0'; *tag = p;done: if (munmap(map, (size_t)sb.st_size)) msgq(sp, M_SYSERR, "munmap"); if (close(fd)) msgq(sp, M_SYSERR, "close"); return (0);}/* * Binary search for "string" in memory between "front" and "back". * * This routine is expected to return a pointer to the start of a line at * *or before* the first word matching "string". Relaxing the constraint * this way simplifies the algorithm. * * Invariants: * front points to the beginning of a line at or before the first * matching string. * * back points to the beginning of a line at or after the first * matching line. * * Base of the Invariants. * front = NULL; * back = EOF; * * Advancing the Invariants: * * p = first newline after halfway point from front to back. * * If the string at "p" is not greater than the string to match, * p is the new front. Otherwise it is the new back. * * Termination: * * The definition of the routine allows it return at any point, * since front is always at or before the line to print. * * In fact, it returns when the chosen "p" equals "back". This * implies that there exists a string is least half as long as * (back - front), which in turn implies that a linear search will * be no more expensive than the cost of simply printing a string or two. * * Trying to continue with binary search at this point would be * more trouble than it's worth. */#define SKIP_PAST_NEWLINE(p, back) while (p < back && *p++ != '\n');static char *binary_search(string, front, back) register char *string, *front, *back;{ register char *p; p = front + (back - front) / 2; SKIP_PAST_NEWLINE(p, back); while (p != back) { if (compare(string, p, back) == GREATER) front = p; else back = p; p = front + (back - front) / 2; SKIP_PAST_NEWLINE(p, back); } return (front);}/* * Find the first line that starts with string, linearly searching from front * to back. * * Return NULL for no such line. * * This routine assumes: * * o front points at the first character in a line. * o front is before or at the first line to be printed. */static char *linear_search(string, front, back) char *string, *front, *back;{ while (front < back) { switch (compare(string, front, back)) { case EQUAL: /* Found it. */ return (front); case LESS: /* No such string. */ return (NULL); case GREATER: /* Keep going. */ break; } SKIP_PAST_NEWLINE(front, back); } return (NULL);}/* * Return LESS, GREATER, or EQUAL depending on how the string1 compares * with string2 (s1 ??? s2). * * o Matches up to len(s1) are EQUAL. * o Matches up to len(s2) are GREATER. * * The string "s1" is null terminated. The string s2 is '\t', space, (or * "back") terminated. * * !!! * Reasonably modern ctags programs use tabs as separators, not spaces. * However, historic programs did use spaces, and, I got complaints. */static intcompare(s1, s2, back) register char *s1, *s2, *back;{ for (; *s1 && s2 < back && (*s2 != '\t' && *s2 != ' '); ++s1, ++s2) if (*s1 != *s2) return (*s1 < *s2 ? LESS : GREATER); return (*s1 ? GREATER : s2 < back && (*s2 != '\t' && *s2 != ' ') ? LESS : EQUAL);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -