📄 ex_tag.c
字号:
/*- * Copyright (c) 1992, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * David Hitz of Auspex Systems, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#ifndef lintstatic char sccsid[] = "@(#)ex_tag.c 8.40 (Berkeley) 3/22/94";#endif /* not lint */#include <sys/param.h>#include <sys/mman.h>#include <sys/queue.h>#include <sys/stat.h>#include <sys/time.h>#include <bitstring.h>#include <ctype.h>#include <errno.h>#include <fcntl.h>#include <limits.h>#include <signal.h>#include <stddef.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <termios.h>#include <unistd.h>#include "compat.h"#include <db.h>#include <regex.h>#include "vi.h"#include "excmd.h"#include "tag.h"static char *binary_search __P((char *, char *, char *));static int compare __P((char *, char *, char *));static char *linear_search __P((char *, char *, char *));static int search __P((SCR *, char *, char *, char **));static int tag_get __P((SCR *, char *, char **, char **, char **));/* * ex_tagfirst -- * The tag code can be entered from main, i.e. "vi -t tag". */intex_tagfirst(sp, tagarg) SCR *sp; char *tagarg;{ FREF *frp; MARK m; long tl; u_int flags; int sval; char *p, *tag, *name, *search; /* Taglength may limit the number of characters. */ if ((tl = O_VAL(sp, O_TAGLENGTH)) != 0 && strlen(tagarg) > tl) tagarg[tl] = '\0'; /* Get the tag information. */ if (tag_get(sp, tagarg, &tag, &name, &search)) return (1); /* Create the file entry. */ if ((frp = file_add(sp, NULL, name, 0)) == NULL) return (1); if (file_init(sp, frp, NULL, 0)) return (1); /* * !!! * The historic tags file format (from a long, long time ago...) * used a line number, not a search string. I got complaints, so * people are still using the format. */ if (isdigit(search[0])) { m.lno = atoi(search); m.cno = 0; } else { /* * Search for the tag; cheap fallback for C functions if * the name is the same but the arguments have changed. */ m.lno = 1; m.cno = 0; flags = SEARCH_FILE | SEARCH_TAG | SEARCH_TERM; sval = f_search(sp, sp->ep, &m, &m, search, NULL, &flags); if (sval && (p = strrchr(search, '(')) != NULL) { p[1] = '\0'; sval = f_search(sp, sp->ep, &m, &m, search, NULL, &flags); } if (sval) msgq(sp, M_ERR, "%s: search pattern not found.", tag); } /* Set up the screen. */ frp->lno = m.lno; frp->cno = m.cno; F_SET(frp, FR_CURSORSET); /* Might as well make this the default tag. */ if ((EXP(sp)->tlast = strdup(tagarg)) == NULL) { msgq(sp, M_SYSERR, NULL); return (1); } return (0);}/* Free a tag or tagf structure from a queue. */#define FREETAG(tp) { \ TAILQ_REMOVE(&exp->tagq, (tp), q); \ if ((tp)->search != NULL) \ free((tp)->search); \ FREE((tp), sizeof(TAGF)); \}#define FREETAGF(tfp) { \ TAILQ_REMOVE(&exp->tagfq, (tfp), q); \ free((tfp)->name); \ FREE((tfp), sizeof(TAGF)); \}/* * ex_tagpush -- :tag [file] * Move to a new tag. * * The tags stacks in nvi are a bit tricky. Each tag contains a file name, * search string, and line/column numbers. The search string is only used * for the first access and for user display. The first record on the stack * is the place where we first did a tag, so it has no search string. The * second record is the first tag, and so on. Note, this means that the * "current" tag is always on the stack. Each tag has a line/column which is * the location from which the user tagged the following TAG entry, and which * is used as the return location. */intex_tagpush(sp, ep, cmdp) SCR *sp; EXF *ep; EXCMDARG *cmdp;{ enum {TC_CHANGE, TC_CURRENT} which; EX_PRIVATE *exp; FREF *frp; MARK m; TAG *tp; u_int flags; int sval; long tl; char *name, *p, *search, *tag; exp = EXP(sp); switch (cmdp->argc) { case 1: if (exp->tlast != NULL) FREE(exp->tlast, strlen(exp->tlast) + 1); if ((exp->tlast = strdup(cmdp->argv[0]->bp)) == NULL) { msgq(sp, M_SYSERR, NULL); return (1); } break; case 0: if (exp->tlast == NULL) { msgq(sp, M_ERR, "No previous tag entered."); return (1); } break; default: abort(); } /* Taglength may limit the number of characters. */ if ((tl = O_VAL(sp, O_TAGLENGTH)) != 0 && strlen(exp->tlast) > tl) exp->tlast[tl] = '\0'; /* Get the tag information. */ if (tag_get(sp, exp->tlast, &tag, &name, &search)) return (1); /* Get the (possibly new) FREF structure. */ if ((frp = file_add(sp, sp->frp, name, 1)) == NULL) goto modify_err; if (sp->frp == frp) which = TC_CURRENT; else { MODIFY_GOTO(sp, sp->ep, F_ISSET(cmdp, E_FORCE)); which = TC_CHANGE; } /* * Get a tag structure -- if this is the first tag, push it on the * stack as a placeholder and get another tag structure. Set the * line/column of the most recent element on the stack to be the * current values, including the file pointer. Then push the new * TAG onto the stack with the new file and search string for user * display. */ CALLOC(sp, tp, TAG *, 1, sizeof(TAG)); if (tp != NULL && exp->tagq.tqh_first == NULL) { TAILQ_INSERT_HEAD(&exp->tagq, tp, q); CALLOC(sp, tp, TAG *, 1, sizeof(TAG)); } if (exp->tagq.tqh_first != NULL) { exp->tagq.tqh_first->frp = sp->frp; exp->tagq.tqh_first->lno = sp->lno; exp->tagq.tqh_first->cno = sp->cno; } if (tp != NULL) { if ((tp->search = strdup(search)) == NULL) msgq(sp, M_SYSERR, NULL); else tp->slen = strlen(search); tp->frp = frp; TAILQ_INSERT_HEAD(&exp->tagq, tp, q); } /* Switch files. */ if (which == TC_CHANGE && file_init(sp, frp, NULL, 0)) { if (tp != NULL) FREETAG(tp); /* Handle special, first-tag case. */ if (exp->tagq.tqh_first->q.tqe_next == NULL) TAILQ_REMOVE(&exp->tagq, exp->tagq.tqh_first, q);modify_err: free(tag); return (1); } /* * !!! * Historic vi accepted a line number as well as a search * string, and people are apparently still using the format. */ if (isdigit(search[0])) { m.lno = atoi(search); m.cno = 0; sval = 0; } else { /* * Search for the tag; cheap fallback for C functions * if the name is the same but the arguments have changed. */ m.lno = 1; m.cno = 0; flags = SEARCH_FILE | SEARCH_TAG | SEARCH_TERM; sval = f_search(sp, sp->ep, &m, &m, search, NULL, &flags); if (sval && (p = strrchr(search, '(')) != NULL) { p[1] = '\0'; sval = f_search(sp, sp->ep, &m, &m, search, NULL, &flags); p[1] = '('; } if (sval) msgq(sp, M_ERR, "%s: search pattern not found.", tag); } free(tag); switch (which) { case TC_CHANGE: frp->lno = m.lno; frp->cno = m.cno; F_SET(frp, FR_CURSORSET); F_SET(sp, S_FSWITCH); break; case TC_CURRENT: if (sval) return (1); sp->lno = m.lno; sp->cno = m.cno; break; } return (0);}/* * ex_tagpop -- :tagp[op][!] [number | file] * Pop the tag stack. */intex_tagpop(sp, ep, cmdp) SCR *sp; EXF *ep; EXCMDARG *cmdp;{ EX_PRIVATE *exp; TAG *ntp, *tp; long off; size_t arglen; char *arg, *p, *t; /* Check for an empty stack. */ exp = EXP(sp); if (exp->tagq.tqh_first == NULL) { msgq(sp, M_INFO, "The tags stack is empty."); return (1); } switch (cmdp->argc) { case 0: /* Pop one tag. */ ntp = exp->tagq.tqh_first; break; case 1: /* Name or number. */ arg = cmdp->argv[0]->bp; off = strtol(arg, &p, 10); if (*p == '\0') { if (off < 1) return (0); for (tp = exp->tagq.tqh_first; tp != NULL && --off > 1; tp = tp->q.tqe_next); if (tp == NULL) { msgq(sp, M_ERR,"Less than %s entries on the tags stack; use :display to see the tags stack.", arg); return (1); } ntp = tp; } else { arglen = strlen(arg); for (tp = exp->tagq.tqh_first; tp != NULL; ntp = tp, tp = tp->q.tqe_next) { /* Use the user's original file name. */ p = tp->frp->name; if ((t = strrchr(p, '/')) == NULL) t = p; else ++t; if (!strncmp(arg, t, arglen)) break; } if (tp == NULL) { msgq(sp, M_ERR,"No file named %s on the tags stack; use :display to see the tags stack.", arg); return (1); } } break; default: abort(); } /* Update the cursor from the saved TAG information. */ tp = ntp->q.tqe_next; if (tp->frp == sp->frp) { sp->lno = tp->lno; sp->cno = tp->cno; } else { MODIFY_RET(sp, ep, F_ISSET(cmdp, E_FORCE)); if (file_init(sp, tp->frp, NULL, 0)) return (1); tp->frp->lno = tp->lno; tp->frp->cno = tp->cno; F_SET(sp->frp, FR_CURSORSET); F_SET(sp, S_FSWITCH); } /* Pop entries off the queue up to ntp. */ for (;;) { tp = exp->tagq.tqh_first; FREETAG(tp); if (tp == ntp) break; } /* If returning to the first tag, the stack is now empty. */ if (exp->tagq.tqh_first->q.tqe_next == NULL) TAILQ_REMOVE(&exp->tagq, exp->tagq.tqh_first, q); return (0);}/* * ex_tagtop -- :tagt[op][!] * Clear the tag stack. */intex_tagtop(sp, ep, cmdp) SCR *sp; EXF *ep; EXCMDARG *cmdp;{ EX_PRIVATE *exp; TAG *tp; /* Find oldest saved information. */ exp = EXP(sp); for (tp = exp->tagq.tqh_first; tp != NULL && tp->q.tqe_next != NULL; tp = tp->q.tqe_next); if (tp == NULL) { msgq(sp, M_INFO, "The tags stack is empty."); return (1); } /* If not switching files, it's easy; else do the work. */ if (tp->frp == sp->frp) { sp->lno = tp->lno; sp->cno = tp->cno; } else { MODIFY_RET(sp, sp->ep, F_ISSET(cmdp, E_FORCE)); if (file_init(sp, tp->frp, NULL, 0)) return (1); tp->frp->lno = tp->lno; tp->frp->cno = tp->cno; F_SET(sp->frp, FR_CURSORSET); F_SET(sp, S_FSWITCH); } /* Empty out the queue. */ while ((tp = exp->tagq.tqh_first) != NULL) FREETAG(tp); return (0);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -