| File: | src/usr.bin/vi/build/../ex/ex_tag.c |
| Warning: | line 858, column 3 Use of memory after it is freed |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: ex_tag.c,v 1.26 2021/10/24 21:24:17 deraadt Exp $ */ | |||
| 2 | ||||
| 3 | /*- | |||
| 4 | * Copyright (c) 1992, 1993, 1994 | |||
| 5 | * The Regents of the University of California. All rights reserved. | |||
| 6 | * Copyright (c) 1992, 1993, 1994, 1995, 1996 | |||
| 7 | * Keith Bostic. All rights reserved. | |||
| 8 | * | |||
| 9 | * This code is derived from software contributed to Berkeley by | |||
| 10 | * David Hitz of Auspex Systems, Inc. | |||
| 11 | * | |||
| 12 | * See the LICENSE file for redistribution information. | |||
| 13 | */ | |||
| 14 | ||||
| 15 | #include "config.h" | |||
| 16 | ||||
| 17 | #include <sys/mman.h> | |||
| 18 | #include <sys/queue.h> | |||
| 19 | #include <sys/stat.h> | |||
| 20 | #include <sys/time.h> | |||
| 21 | ||||
| 22 | #include <bitstring.h> | |||
| 23 | #include <ctype.h> | |||
| 24 | #include <errno(*__errno()).h> | |||
| 25 | #include <fcntl.h> | |||
| 26 | #include <limits.h> | |||
| 27 | #include <stddef.h> | |||
| 28 | #include <stdio.h> | |||
| 29 | #include <stdlib.h> | |||
| 30 | #include <string.h> | |||
| 31 | #include <unistd.h> | |||
| 32 | ||||
| 33 | #include "../common/common.h" | |||
| 34 | #include "../vi/vi.h" | |||
| 35 | #include "tag.h" | |||
| 36 | ||||
| 37 | static char *binary_search(char *, char *, char *); | |||
| 38 | static int compare(char *, char *, char *); | |||
| 39 | static void ctag_file(SCR *, TAGF *, char *, char **, size_t *); | |||
| 40 | static int ctag_search(SCR *, char *, size_t, char *); | |||
| 41 | static int ctag_sfile(SCR *, TAGF *, TAGQ *, char *); | |||
| 42 | static TAGQ *ctag_slist(SCR *, char *); | |||
| 43 | static char *linear_search(char *, char *, char *); | |||
| 44 | static int tag_copy(SCR *, TAG *, TAG **); | |||
| 45 | static int tag_pop(SCR *, TAGQ *, int); | |||
| 46 | static int tagf_copy(SCR *, TAGF *, TAGF **); | |||
| 47 | static int tagf_free(SCR *, TAGF *); | |||
| 48 | static int tagq_copy(SCR *, TAGQ *, TAGQ **); | |||
| 49 | ||||
| 50 | /* | |||
| 51 | * ex_tag_first -- | |||
| 52 | * The tag code can be entered from main, e.g., "vi -t tag". | |||
| 53 | * | |||
| 54 | * PUBLIC: int ex_tag_first(SCR *, char *); | |||
| 55 | */ | |||
| 56 | int | |||
| 57 | ex_tag_first(SCR *sp, char *tagarg) | |||
| 58 | { | |||
| 59 | ARGS *ap[2], a; | |||
| 60 | EXCMD cmd; | |||
| 61 | ||||
| 62 | /* Build an argument for the ex :tag command. */ | |||
| 63 | ex_cinit(&cmd, C_TAG, 0, OOBLNO0, OOBLNO0, 0, ap); | |||
| 64 | ex_cadd(&cmd, &a, tagarg, strlen(tagarg)); | |||
| 65 | ||||
| 66 | /* | |||
| 67 | * XXX | |||
| 68 | * Historic vi went ahead and created a temporary file when it failed | |||
| 69 | * to find the tag. We match historic practice, but don't distinguish | |||
| 70 | * between real error and failure to find the tag. | |||
| 71 | */ | |||
| 72 | if (ex_tag_push(sp, &cmd)) | |||
| 73 | return (0); | |||
| 74 | ||||
| 75 | /* Display tags in the center of the screen. */ | |||
| 76 | F_CLR(sp, SC_SCR_TOP)(((sp)->flags) &= ~((0x00000100))); | |||
| 77 | F_SET(sp, SC_SCR_CENTER)(((sp)->flags) |= ((0x00000080))); | |||
| 78 | ||||
| 79 | return (0); | |||
| 80 | } | |||
| 81 | ||||
| 82 | /* | |||
| 83 | * ex_tag_push -- ^] | |||
| 84 | * :tag[!] [string] | |||
| 85 | * | |||
| 86 | * Enter a new TAGQ context based on a ctag string. | |||
| 87 | * | |||
| 88 | * PUBLIC: int ex_tag_push(SCR *, EXCMD *); | |||
| 89 | */ | |||
| 90 | int | |||
| 91 | ex_tag_push(SCR *sp, EXCMD *cmdp) | |||
| 92 | { | |||
| 93 | EX_PRIVATE *exp; | |||
| 94 | FREF *frp; | |||
| 95 | TAG *rtp; | |||
| 96 | TAGQ *rtqp, *tqp; | |||
| 97 | recno_t lno; | |||
| 98 | size_t cno; | |||
| 99 | long tl; | |||
| 100 | int force, istmp; | |||
| 101 | ||||
| 102 | exp = EXP(sp)((EX_PRIVATE *)((sp)->ex_private)); | |||
| 103 | switch (cmdp->argc) { | |||
| 104 | case 1: | |||
| 105 | free(exp->tag_last); | |||
| 106 | ||||
| 107 | if ((exp->tag_last = strdup(cmdp->argv[0]->bp)) == NULL((void*)0)) { | |||
| 108 | msgq(sp, M_SYSERR, NULL((void*)0)); | |||
| 109 | return (1); | |||
| 110 | } | |||
| 111 | ||||
| 112 | /* Taglength may limit the number of characters. */ | |||
| 113 | if ((tl = | |||
| 114 | O_VAL(sp, O_TAGLENGTH)((((&((sp))->opts[((O_TAGLENGTH))])->flags) & ( (0x01))) ? ((sp))->gp->opts[((sp))->opts[((O_TAGLENGTH ))].o_cur.val].o_cur.val : ((sp))->opts[((O_TAGLENGTH))].o_cur .val)) != 0 && strlen(exp->tag_last) > tl) | |||
| 115 | exp->tag_last[tl] = '\0'; | |||
| 116 | break; | |||
| 117 | case 0: | |||
| 118 | if (exp->tag_last == NULL((void*)0)) { | |||
| 119 | msgq(sp, M_ERR, "No previous tag entered"); | |||
| 120 | return (1); | |||
| 121 | } | |||
| 122 | break; | |||
| 123 | default: | |||
| 124 | abort(); | |||
| 125 | } | |||
| 126 | ||||
| 127 | /* Get the tag information. */ | |||
| 128 | if ((tqp = ctag_slist(sp, exp->tag_last)) == NULL((void*)0)) | |||
| 129 | return (1); | |||
| 130 | ||||
| 131 | /* | |||
| 132 | * Allocate all necessary memory before swapping screens. Initialize | |||
| 133 | * flags so we know what to free. | |||
| 134 | */ | |||
| 135 | rtp = NULL((void*)0); | |||
| 136 | rtqp = NULL((void*)0); | |||
| 137 | if (TAILQ_EMPTY(&exp->tq)(((&exp->tq)->tqh_first) == ((void*)0))) { | |||
| 138 | /* Initialize the `local context' tag queue structure. */ | |||
| 139 | CALLOC_GOTO(sp, rtqp, 1, sizeof(TAGQ)){ if (((rtqp) = calloc((1), (sizeof(TAGQ)))) == ((void*)0)) goto alloc_err; }; | |||
| 140 | TAILQ_INIT(&rtqp->tagq)do { (&rtqp->tagq)->tqh_first = ((void*)0); (&rtqp ->tagq)->tqh_last = &(&rtqp->tagq)->tqh_first ; } while (0); | |||
| 141 | ||||
| 142 | /* Initialize and link in its tag structure. */ | |||
| 143 | CALLOC_GOTO(sp, rtp, 1, sizeof(TAG)){ if (((rtp) = calloc((1), (sizeof(TAG)))) == ((void*)0)) goto alloc_err; }; | |||
| 144 | TAILQ_INSERT_HEAD(&rtqp->tagq, rtp, q)do { if (((rtp)->q.tqe_next = (&rtqp->tagq)->tqh_first ) != ((void*)0)) (&rtqp->tagq)->tqh_first->q.tqe_prev = &(rtp)->q.tqe_next; else (&rtqp->tagq)->tqh_last = &(rtp)->q.tqe_next; (&rtqp->tagq)->tqh_first = (rtp); (rtp)->q.tqe_prev = &(&rtqp->tagq)-> tqh_first; } while (0); | |||
| 145 | rtqp->current = rtp; | |||
| 146 | } | |||
| 147 | ||||
| 148 | /* | |||
| 149 | * Stick the current context information in a convenient place, we're | |||
| 150 | * about to lose it. Note, if we're called on editor startup, there | |||
| 151 | * will be no FREF structure. | |||
| 152 | */ | |||
| 153 | frp = sp->frp; | |||
| 154 | lno = sp->lno; | |||
| 155 | cno = sp->cno; | |||
| 156 | istmp = frp == NULL((void*)0) || | |||
| 157 | (F_ISSET(frp, FR_TMPFILE)(((frp)->flags) & ((0x0080))) && !F_ISSET(cmdp, E_NEWSCREEN)(((cmdp)->flags) & ((0x00000100)))); | |||
| 158 | ||||
| 159 | /* Try to switch to the tag. */ | |||
| 160 | force = FL_ISSET(cmdp->iflags, E_C_FORCE)((cmdp->iflags) & (0x00100)); | |||
| 161 | if (F_ISSET(cmdp, E_NEWSCREEN)(((cmdp)->flags) & ((0x00000100)))) { | |||
| 162 | if (ex_tag_Nswitch(sp, TAILQ_FIRST(&tqp->tagq)((&tqp->tagq)->tqh_first), force)) | |||
| 163 | goto err; | |||
| 164 | ||||
| 165 | /* Everything else gets done in the new screen. */ | |||
| 166 | sp = sp->nextdisp; | |||
| 167 | exp = EXP(sp)((EX_PRIVATE *)((sp)->ex_private)); | |||
| 168 | } else | |||
| 169 | if (ex_tag_nswitch(sp, TAILQ_FIRST(&tqp->tagq)((&tqp->tagq)->tqh_first), force)) | |||
| 170 | goto err; | |||
| 171 | ||||
| 172 | /* | |||
| 173 | * If this is the first tag, put a `current location' queue entry | |||
| 174 | * in place, so we can pop all the way back to the current mark. | |||
| 175 | * Note, it doesn't point to much of anything, it's a placeholder. | |||
| 176 | */ | |||
| 177 | if (TAILQ_EMPTY(&exp->tq)(((&exp->tq)->tqh_first) == ((void*)0))) { | |||
| 178 | TAILQ_INSERT_HEAD(&exp->tq, rtqp, q)do { if (((rtqp)->q.tqe_next = (&exp->tq)->tqh_first ) != ((void*)0)) (&exp->tq)->tqh_first->q.tqe_prev = &(rtqp)->q.tqe_next; else (&exp->tq)->tqh_last = &(rtqp)->q.tqe_next; (&exp->tq)->tqh_first = (rtqp); (rtqp)->q.tqe_prev = &(&exp->tq)-> tqh_first; } while (0); | |||
| 179 | } else | |||
| 180 | rtqp = TAILQ_FIRST(&exp->tq)((&exp->tq)->tqh_first); | |||
| 181 | ||||
| 182 | /* Link the new TAGQ structure into place. */ | |||
| 183 | TAILQ_INSERT_HEAD(&exp->tq, tqp, q)do { if (((tqp)->q.tqe_next = (&exp->tq)->tqh_first ) != ((void*)0)) (&exp->tq)->tqh_first->q.tqe_prev = &(tqp)->q.tqe_next; else (&exp->tq)->tqh_last = &(tqp)->q.tqe_next; (&exp->tq)->tqh_first = (tqp); (tqp)->q.tqe_prev = &(&exp->tq)->tqh_first ; } while (0); | |||
| 184 | ||||
| 185 | (void)ctag_search(sp, | |||
| 186 | tqp->current->search, tqp->current->slen, tqp->tag); | |||
| 187 | ||||
| 188 | /* | |||
| 189 | * Move the current context from the temporary save area into the | |||
| 190 | * right structure. | |||
| 191 | * | |||
| 192 | * If we were in a temporary file, we don't have a context to which | |||
| 193 | * we can return, so just make it be the same as what we're moving | |||
| 194 | * to. It will be a little odd that ^T doesn't change anything, but | |||
| 195 | * I don't think it's a big deal. | |||
| 196 | */ | |||
| 197 | if (istmp) { | |||
| 198 | rtqp->current->frp = sp->frp; | |||
| 199 | rtqp->current->lno = sp->lno; | |||
| 200 | rtqp->current->cno = sp->cno; | |||
| 201 | } else { | |||
| 202 | rtqp->current->frp = frp; | |||
| 203 | rtqp->current->lno = lno; | |||
| 204 | rtqp->current->cno = cno; | |||
| 205 | } | |||
| 206 | return (0); | |||
| 207 | ||||
| 208 | err: | |||
| 209 | alloc_err: | |||
| 210 | free(rtqp); | |||
| 211 | free(rtp); | |||
| 212 | tagq_free(sp, tqp); | |||
| 213 | return (1); | |||
| 214 | } | |||
| 215 | ||||
| 216 | /* | |||
| 217 | * ex_tag_next -- | |||
| 218 | * Switch context to the next TAG. | |||
| 219 | * | |||
| 220 | * PUBLIC: int ex_tag_next(SCR *, EXCMD *); | |||
| 221 | */ | |||
| 222 | int | |||
| 223 | ex_tag_next(SCR *sp, EXCMD *cmdp) | |||
| 224 | { | |||
| 225 | EX_PRIVATE *exp; | |||
| 226 | TAG *tp; | |||
| 227 | TAGQ *tqp; | |||
| 228 | ||||
| 229 | exp = EXP(sp)((EX_PRIVATE *)((sp)->ex_private)); | |||
| 230 | if ((tqp = TAILQ_FIRST(&exp->tq)((&exp->tq)->tqh_first)) == NULL((void*)0)) { | |||
| 231 | tag_msg(sp, TAG_EMPTY, NULL((void*)0)); | |||
| 232 | return (1); | |||
| 233 | } | |||
| 234 | if ((tp = TAILQ_NEXT(tqp->current, q)((tqp->current)->q.tqe_next)) == NULL((void*)0)) { | |||
| 235 | msgq(sp, M_ERR, "Already at the last tag of this group"); | |||
| 236 | return (1); | |||
| 237 | } | |||
| 238 | if (ex_tag_nswitch(sp, tp, FL_ISSET(cmdp->iflags, E_C_FORCE)((cmdp->iflags) & (0x00100)))) | |||
| 239 | return (1); | |||
| 240 | tqp->current = tp; | |||
| 241 | ||||
| 242 | (void)ctag_search(sp, tp->search, tp->slen, tqp->tag); | |||
| 243 | ||||
| 244 | return (0); | |||
| 245 | } | |||
| 246 | ||||
| 247 | /* | |||
| 248 | * ex_tag_prev -- | |||
| 249 | * Switch context to the next TAG. | |||
| 250 | * | |||
| 251 | * PUBLIC: int ex_tag_prev(SCR *, EXCMD *); | |||
| 252 | */ | |||
| 253 | int | |||
| 254 | ex_tag_prev(SCR *sp, EXCMD *cmdp) | |||
| 255 | { | |||
| 256 | EX_PRIVATE *exp; | |||
| 257 | TAG *tp; | |||
| 258 | TAGQ *tqp; | |||
| 259 | ||||
| 260 | exp = EXP(sp)((EX_PRIVATE *)((sp)->ex_private)); | |||
| 261 | if ((tqp = TAILQ_FIRST(&exp->tq)((&exp->tq)->tqh_first)) == NULL((void*)0)) { | |||
| 262 | tag_msg(sp, TAG_EMPTY, NULL((void*)0)); | |||
| 263 | return (0); | |||
| 264 | } | |||
| 265 | if ((tp = TAILQ_PREV(tqp->current, _tagqh, q)(*(((struct _tagqh *)((tqp->current)->q.tqe_prev))-> tqh_last))) == NULL((void*)0)) { | |||
| 266 | msgq(sp, M_ERR, "Already at the first tag of this group"); | |||
| 267 | return (1); | |||
| 268 | } | |||
| 269 | if (ex_tag_nswitch(sp, tp, FL_ISSET(cmdp->iflags, E_C_FORCE)((cmdp->iflags) & (0x00100)))) | |||
| 270 | return (1); | |||
| 271 | tqp->current = tp; | |||
| 272 | ||||
| 273 | (void)ctag_search(sp, tp->search, tp->slen, tqp->tag); | |||
| 274 | ||||
| 275 | return (0); | |||
| 276 | } | |||
| 277 | ||||
| 278 | /* | |||
| 279 | * ex_tag_nswitch -- | |||
| 280 | * Switch context to the specified TAG. | |||
| 281 | * | |||
| 282 | * PUBLIC: int ex_tag_nswitch(SCR *, TAG *, int); | |||
| 283 | */ | |||
| 284 | int | |||
| 285 | ex_tag_nswitch(SCR *sp, TAG *tp, int force) | |||
| 286 | { | |||
| 287 | /* Get a file structure. */ | |||
| 288 | if (tp->frp == NULL((void*)0) && (tp->frp = file_add(sp, tp->fname)) == NULL((void*)0)) | |||
| 289 | return (1); | |||
| 290 | ||||
| 291 | /* If not changing files, return, we're done. */ | |||
| 292 | if (tp->frp == sp->frp) | |||
| 293 | return (0); | |||
| 294 | ||||
| 295 | /* Check for permission to leave. */ | |||
| 296 | if (file_m1(sp, force, FS_ALL0x001 | FS_POSSIBLE0x010)) | |||
| 297 | return (1); | |||
| 298 | ||||
| 299 | /* Initialize the new file. */ | |||
| 300 | if (file_init(sp, tp->frp, NULL((void*)0), FS_SETALT0x020)) | |||
| 301 | return (1); | |||
| 302 | ||||
| 303 | /* Display tags in the center of the screen. */ | |||
| 304 | F_CLR(sp, SC_SCR_TOP)(((sp)->flags) &= ~((0x00000100))); | |||
| 305 | F_SET(sp, SC_SCR_CENTER)(((sp)->flags) |= ((0x00000080))); | |||
| 306 | ||||
| 307 | /* Switch. */ | |||
| 308 | F_SET(sp, SC_FSWITCH)(((sp)->flags) |= ((0x00000800))); | |||
| 309 | return (0); | |||
| 310 | } | |||
| 311 | ||||
| 312 | /* | |||
| 313 | * ex_tag_Nswitch -- | |||
| 314 | * Switch context to the specified TAG in a new screen. | |||
| 315 | * | |||
| 316 | * PUBLIC: int ex_tag_Nswitch(SCR *, TAG *, int); | |||
| 317 | */ | |||
| 318 | int | |||
| 319 | ex_tag_Nswitch(SCR *sp, TAG *tp, int force) | |||
| 320 | { | |||
| 321 | SCR *new; | |||
| 322 | ||||
| 323 | /* Get a file structure. */ | |||
| 324 | if (tp->frp == NULL((void*)0) && (tp->frp = file_add(sp, tp->fname)) == NULL((void*)0)) | |||
| 325 | return (1); | |||
| 326 | ||||
| 327 | /* Get a new screen. */ | |||
| 328 | if (screen_init(sp->gp, sp, &new)) | |||
| 329 | return (1); | |||
| 330 | if (vs_split(sp, new, 0)) { | |||
| 331 | (void)file_end(new, new->ep, 1); | |||
| 332 | (void)screen_end(new); | |||
| 333 | return (1); | |||
| 334 | } | |||
| 335 | ||||
| 336 | /* Get a backing file. */ | |||
| 337 | if (tp->frp == sp->frp) { | |||
| 338 | /* Copy file state. */ | |||
| 339 | new->ep = sp->ep; | |||
| 340 | ++new->ep->refcnt; | |||
| 341 | ||||
| 342 | new->frp = tp->frp; | |||
| 343 | new->frp->flags = sp->frp->flags; | |||
| 344 | } else if (file_init(new, tp->frp, NULL((void*)0), force)) { | |||
| 345 | (void)vs_discard(new, NULL((void*)0)); | |||
| 346 | (void)screen_end(new); | |||
| 347 | return (1); | |||
| 348 | } | |||
| 349 | ||||
| 350 | /* Create the argument list. */ | |||
| 351 | new->cargv = new->argv = ex_buildargv(sp, NULL((void*)0), tp->frp->name); | |||
| 352 | ||||
| 353 | /* Display tags in the center of the screen. */ | |||
| 354 | F_CLR(new, SC_SCR_TOP)(((new)->flags) &= ~((0x00000100))); | |||
| 355 | F_SET(new, SC_SCR_CENTER)(((new)->flags) |= ((0x00000080))); | |||
| 356 | ||||
| 357 | /* Switch. */ | |||
| 358 | sp->nextdisp = new; | |||
| 359 | F_SET(sp, SC_SSWITCH)(((sp)->flags) |= ((0x00001000))); | |||
| 360 | ||||
| 361 | return (0); | |||
| 362 | } | |||
| 363 | ||||
| 364 | /* | |||
| 365 | * ex_tag_pop -- ^T | |||
| 366 | * :tagp[op][!] [number | file] | |||
| 367 | * | |||
| 368 | * Pop to a previous TAGQ context. | |||
| 369 | * | |||
| 370 | * PUBLIC: int ex_tag_pop(SCR *, EXCMD *); | |||
| 371 | */ | |||
| 372 | int | |||
| 373 | ex_tag_pop(SCR *sp, EXCMD *cmdp) | |||
| 374 | { | |||
| 375 | EX_PRIVATE *exp; | |||
| 376 | TAGQ *tqp, *dtqp = NULL((void*)0); | |||
| 377 | size_t arglen; | |||
| 378 | long off; | |||
| 379 | char *arg, *p, *t; | |||
| 380 | ||||
| 381 | /* Check for an empty stack. */ | |||
| 382 | exp = EXP(sp)((EX_PRIVATE *)((sp)->ex_private)); | |||
| 383 | if (TAILQ_EMPTY(&exp->tq)(((&exp->tq)->tqh_first) == ((void*)0))) { | |||
| 384 | tag_msg(sp, TAG_EMPTY, NULL((void*)0)); | |||
| 385 | return (1); | |||
| 386 | } | |||
| 387 | ||||
| 388 | /* Find the last TAG structure that we're going to DISCARD! */ | |||
| 389 | switch (cmdp->argc) { | |||
| 390 | case 0: /* Pop one tag. */ | |||
| 391 | dtqp = TAILQ_FIRST(&exp->tq)((&exp->tq)->tqh_first); | |||
| 392 | break; | |||
| 393 | case 1: /* Name or number. */ | |||
| 394 | arg = cmdp->argv[0]->bp; | |||
| 395 | off = strtol(arg, &p, 10); | |||
| 396 | if (*p != '\0') | |||
| 397 | goto filearg; | |||
| 398 | ||||
| 399 | /* Number: pop that many queue entries. */ | |||
| 400 | if (off < 1) | |||
| 401 | return (0); | |||
| 402 | TAILQ_FOREACH(tqp, &exp->tq, q)for((tqp) = ((&exp->tq)->tqh_first); (tqp) != ((void *)0); (tqp) = ((tqp)->q.tqe_next)) { | |||
| 403 | if (--off <= 1) | |||
| 404 | break; | |||
| 405 | } | |||
| 406 | if (tqp == NULL((void*)0)) { | |||
| 407 | msgq(sp, M_ERR, | |||
| 408 | "Less than %s entries on the tags stack; use :display t[ags]", | |||
| 409 | arg); | |||
| 410 | return (1); | |||
| 411 | } | |||
| 412 | dtqp = tqp; | |||
| 413 | break; | |||
| 414 | ||||
| 415 | /* File argument: pop to that queue entry. */ | |||
| 416 | filearg: arglen = strlen(arg); | |||
| 417 | for (tqp = TAILQ_FIRST(&exp->tq)((&exp->tq)->tqh_first); tqp; | |||
| 418 | dtqp = tqp, tqp = TAILQ_NEXT(tqp, q)((tqp)->q.tqe_next)) { | |||
| 419 | /* Don't pop to the current file. */ | |||
| 420 | if (tqp == TAILQ_FIRST(&exp->tq)((&exp->tq)->tqh_first)) | |||
| 421 | continue; | |||
| 422 | p = tqp->current->frp->name; | |||
| 423 | if ((t = strrchr(p, '/')) == NULL((void*)0)) | |||
| 424 | t = p; | |||
| 425 | else | |||
| 426 | ++t; | |||
| 427 | if (!strncmp(arg, t, arglen)) | |||
| 428 | break; | |||
| 429 | } | |||
| 430 | if (tqp == NULL((void*)0)) { | |||
| 431 | msgq_str(sp, M_ERR, arg, | |||
| 432 | "No file %s on the tags stack to return to; use :display t[ags]"); | |||
| 433 | return (1); | |||
| 434 | } | |||
| 435 | if (tqp == TAILQ_FIRST(&exp->tq)((&exp->tq)->tqh_first)) | |||
| 436 | return (0); | |||
| 437 | break; | |||
| 438 | default: | |||
| 439 | abort(); | |||
| 440 | /* NOTREACHED */ | |||
| 441 | } | |||
| 442 | ||||
| 443 | return (tag_pop(sp, dtqp, FL_ISSET(cmdp->iflags, E_C_FORCE)((cmdp->iflags) & (0x00100)))); | |||
| 444 | } | |||
| 445 | ||||
| 446 | /* | |||
| 447 | * ex_tag_top -- :tagt[op][!] | |||
| 448 | * Clear the tag stack. | |||
| 449 | * | |||
| 450 | * PUBLIC: int ex_tag_top(SCR *, EXCMD *); | |||
| 451 | */ | |||
| 452 | int | |||
| 453 | ex_tag_top(SCR *sp, EXCMD *cmdp) | |||
| 454 | { | |||
| 455 | EX_PRIVATE *exp; | |||
| 456 | ||||
| 457 | exp = EXP(sp)((EX_PRIVATE *)((sp)->ex_private)); | |||
| 458 | ||||
| 459 | /* Check for an empty stack. */ | |||
| 460 | if (TAILQ_EMPTY(&exp->tq)(((&exp->tq)->tqh_first) == ((void*)0))) { | |||
| 461 | tag_msg(sp, TAG_EMPTY, NULL((void*)0)); | |||
| 462 | return (1); | |||
| 463 | } | |||
| 464 | ||||
| 465 | /* Return to the oldest information. */ | |||
| 466 | return (tag_pop(sp, | |||
| 467 | TAILQ_PREV(TAILQ_LAST(&exp->tq, _tqh), _tqh, q)(*(((struct _tqh *)(((*(((struct _tqh *)((&exp->tq)-> tqh_last))->tqh_last)))->q.tqe_prev))->tqh_last)), | |||
| 468 | FL_ISSET(cmdp->iflags, E_C_FORCE)((cmdp->iflags) & (0x00100)))); | |||
| 469 | } | |||
| 470 | ||||
| 471 | /* | |||
| 472 | * tag_pop -- | |||
| 473 | * Pop up to and including the specified TAGQ context. | |||
| 474 | */ | |||
| 475 | static int | |||
| 476 | tag_pop(SCR *sp, TAGQ *dtqp, int force) | |||
| 477 | { | |||
| 478 | EX_PRIVATE *exp; | |||
| 479 | TAG *tp; | |||
| 480 | TAGQ *tqp; | |||
| 481 | ||||
| 482 | exp = EXP(sp)((EX_PRIVATE *)((sp)->ex_private)); | |||
| 483 | ||||
| 484 | /* | |||
| 485 | * Update the cursor from the saved TAG information of the TAG | |||
| 486 | * structure we're moving to. | |||
| 487 | */ | |||
| 488 | tp = TAILQ_NEXT(dtqp, q)((dtqp)->q.tqe_next)->current; | |||
| 489 | if (tp->frp == sp->frp) { | |||
| 490 | sp->lno = tp->lno; | |||
| 491 | sp->cno = tp->cno; | |||
| 492 | } else { | |||
| 493 | if (file_m1(sp, force, FS_ALL0x001 | FS_POSSIBLE0x010)) | |||
| 494 | return (1); | |||
| 495 | ||||
| 496 | tp->frp->lno = tp->lno; | |||
| 497 | tp->frp->cno = tp->cno; | |||
| 498 | F_SET(sp->frp, FR_CURSORSET)(((sp->frp)->flags) |= ((0x0001))); | |||
| 499 | if (file_init(sp, tp->frp, NULL((void*)0), FS_SETALT0x020)) | |||
| 500 | return (1); | |||
| 501 | ||||
| 502 | F_SET(sp, SC_FSWITCH)(((sp)->flags) |= ((0x00000800))); | |||
| 503 | } | |||
| 504 | ||||
| 505 | /* Pop entries off the queue up to and including dtqp. */ | |||
| 506 | do { | |||
| 507 | tqp = TAILQ_FIRST(&exp->tq)((&exp->tq)->tqh_first); | |||
| 508 | if (tagq_free(sp, tqp)) | |||
| 509 | return (0); | |||
| 510 | } while (tqp != dtqp); | |||
| 511 | ||||
| 512 | /* | |||
| 513 | * If only a single tag left, we've returned to the first tag point, | |||
| 514 | * and the stack is now empty. | |||
| 515 | */ | |||
| 516 | if (TAILQ_NEXT(TAILQ_FIRST(&exp->tq), q)((((&exp->tq)->tqh_first))->q.tqe_next) == NULL((void*)0)) | |||
| 517 | tagq_free(sp, TAILQ_FIRST(&exp->tq)((&exp->tq)->tqh_first)); | |||
| 518 | ||||
| 519 | return (0); | |||
| 520 | } | |||
| 521 | ||||
| 522 | /* | |||
| 523 | * ex_tag_display -- | |||
| 524 | * Display the list of tags. | |||
| 525 | * | |||
| 526 | * PUBLIC: int ex_tag_display(SCR *); | |||
| 527 | */ | |||
| 528 | int | |||
| 529 | ex_tag_display(SCR *sp) | |||
| 530 | { | |||
| 531 | EX_PRIVATE *exp; | |||
| 532 | TAG *tp; | |||
| 533 | TAGQ *tqp; | |||
| 534 | int cnt; | |||
| 535 | size_t len; | |||
| 536 | char *p; | |||
| 537 | ||||
| 538 | exp = EXP(sp)((EX_PRIVATE *)((sp)->ex_private)); | |||
| 539 | if (TAILQ_EMPTY(&exp->tq)(((&exp->tq)->tqh_first) == ((void*)0))) { | |||
| 540 | tag_msg(sp, TAG_EMPTY, NULL((void*)0)); | |||
| 541 | return (0); | |||
| 542 | } | |||
| 543 | tqp = TAILQ_FIRST(&exp->tq)((&exp->tq)->tqh_first); | |||
| 544 | ||||
| 545 | /* | |||
| 546 | * We give the file name 20 columns and the search string the rest. | |||
| 547 | * If there's not enough room, we don't do anything special, it's | |||
| 548 | * not worth the effort, it just makes the display more confusing. | |||
| 549 | * | |||
| 550 | * We also assume that characters in file names map 1-1 to printing | |||
| 551 | * characters. This might not be true, but I don't think it's worth | |||
| 552 | * fixing. (The obvious fix is to pass the filenames through the | |||
| 553 | * msg_print function.) | |||
| 554 | */ | |||
| 555 | #define L_NAME30 30 /* Name. */ | |||
| 556 | #define L_SLOP4 4 /* Leading number plus trailing *. */ | |||
| 557 | #define L_SPACE5 5 /* Spaces after name, before tag. */ | |||
| 558 | #define L_TAG20 20 /* Tag. */ | |||
| 559 | if (sp->cols <= L_NAME30 + L_SLOP4) { | |||
| 560 | msgq(sp, M_ERR, "Display too small."); | |||
| 561 | return (0); | |||
| 562 | } | |||
| 563 | ||||
| 564 | /* | |||
| 565 | * Display the list of tags for each queue entry. The first entry | |||
| 566 | * is numbered, and the current tag entry has an asterisk appended. | |||
| 567 | */ | |||
| 568 | cnt = 0; | |||
| 569 | TAILQ_FOREACH(tqp, &exp->tq, q)for((tqp) = ((&exp->tq)->tqh_first); (tqp) != ((void *)0); (tqp) = ((tqp)->q.tqe_next)) { | |||
| 570 | if (INTERRUPTED(sp)(((((sp)->gp)->flags) & ((0x0004))) || (!v_event_get ((sp), ((void*)0), 0, 0x001) && ((((sp)->gp)->flags ) & ((0x0004)))))) | |||
| 571 | break; | |||
| 572 | ++cnt; | |||
| 573 | TAILQ_FOREACH(tp, &tqp->tagq, q)for((tp) = ((&tqp->tagq)->tqh_first); (tp) != ((void *)0); (tp) = ((tp)->q.tqe_next)) { | |||
| 574 | if (tp == TAILQ_FIRST(&tqp->tagq)((&tqp->tagq)->tqh_first)) | |||
| 575 | (void)ex_printf(sp, "%2d ", cnt); | |||
| 576 | else | |||
| 577 | (void)ex_printf(sp, " "); | |||
| 578 | p = tp->frp == NULL((void*)0) ? tp->fname : tp->frp->name; | |||
| 579 | if ((len = strlen(p)) > L_NAME30) { | |||
| 580 | len = len - (L_NAME30 - 4); | |||
| 581 | (void)ex_printf(sp, " ... %*.*s", | |||
| 582 | L_NAME30 - 4, L_NAME30 - 4, p + len); | |||
| 583 | } else | |||
| 584 | (void)ex_printf(sp, | |||
| 585 | " %*.*s", L_NAME30, L_NAME30, p); | |||
| 586 | if (tqp->current == tp) | |||
| 587 | (void)ex_printf(sp, "*"); | |||
| 588 | ||||
| 589 | if (tp == TAILQ_FIRST(&tqp->tagq)((&tqp->tagq)->tqh_first) && tqp->tag != NULL((void*)0) && | |||
| 590 | (sp->cols - L_NAME30) >= L_TAG20 + L_SPACE5) { | |||
| 591 | len = strlen(tqp->tag); | |||
| 592 | if (len > sp->cols - (L_NAME30 + L_SPACE5)) | |||
| 593 | len = sp->cols - (L_NAME30 + L_SPACE5); | |||
| 594 | (void)ex_printf(sp, "%s%.*s", | |||
| 595 | tqp->current == tp ? " " : " ", | |||
| 596 | (int)len, tqp->tag); | |||
| 597 | } | |||
| 598 | (void)ex_printf(sp, "\n"); | |||
| 599 | } | |||
| 600 | } | |||
| 601 | return (0); | |||
| 602 | } | |||
| 603 | ||||
| 604 | /* | |||
| 605 | * ex_tag_copy -- | |||
| 606 | * Copy a screen's tag structures. | |||
| 607 | * | |||
| 608 | * PUBLIC: int ex_tag_copy(SCR *, SCR *); | |||
| 609 | */ | |||
| 610 | int | |||
| 611 | ex_tag_copy(SCR *orig, SCR *sp) | |||
| 612 | { | |||
| 613 | EX_PRIVATE *oexp, *nexp; | |||
| 614 | TAGQ *aqp, *tqp; | |||
| 615 | TAG *ap, *tp; | |||
| 616 | TAGF *atfp, *tfp; | |||
| 617 | ||||
| 618 | oexp = EXP(orig)((EX_PRIVATE *)((orig)->ex_private)); | |||
| 619 | nexp = EXP(sp)((EX_PRIVATE *)((sp)->ex_private)); | |||
| 620 | ||||
| 621 | /* Copy tag queue and tags stack. */ | |||
| 622 | TAILQ_FOREACH(aqp, &oexp->tq, q)for((aqp) = ((&oexp->tq)->tqh_first); (aqp) != ((void *)0); (aqp) = ((aqp)->q.tqe_next)) { | |||
| 623 | if (tagq_copy(sp, aqp, &tqp)) | |||
| 624 | return (1); | |||
| 625 | TAILQ_FOREACH(ap, &aqp->tagq, q)for((ap) = ((&aqp->tagq)->tqh_first); (ap) != ((void *)0); (ap) = ((ap)->q.tqe_next)) { | |||
| 626 | if (tag_copy(sp, ap, &tp)) | |||
| 627 | return (1); | |||
| 628 | /* Set the current pointer. */ | |||
| 629 | if (aqp->current == ap) | |||
| 630 | tqp->current = tp; | |||
| 631 | TAILQ_INSERT_TAIL(&tqp->tagq, tp, q)do { (tp)->q.tqe_next = ((void*)0); (tp)->q.tqe_prev = ( &tqp->tagq)->tqh_last; *(&tqp->tagq)->tqh_last = (tp); (&tqp->tagq)->tqh_last = &(tp)->q.tqe_next ; } while (0); | |||
| 632 | } | |||
| 633 | TAILQ_INSERT_TAIL(&nexp->tq, tqp, q)do { (tqp)->q.tqe_next = ((void*)0); (tqp)->q.tqe_prev = (&nexp->tq)->tqh_last; *(&nexp->tq)->tqh_last = (tqp); (&nexp->tq)->tqh_last = &(tqp)->q. tqe_next; } while (0); | |||
| 634 | } | |||
| 635 | ||||
| 636 | /* Copy list of tag files. */ | |||
| 637 | TAILQ_FOREACH(atfp, &oexp->tagfq, q)for((atfp) = ((&oexp->tagfq)->tqh_first); (atfp) != ((void*)0); (atfp) = ((atfp)->q.tqe_next)) { | |||
| 638 | if (tagf_copy(sp, atfp, &tfp)) | |||
| 639 | return (1); | |||
| 640 | TAILQ_INSERT_TAIL(&nexp->tagfq, tfp, q)do { (tfp)->q.tqe_next = ((void*)0); (tfp)->q.tqe_prev = (&nexp->tagfq)->tqh_last; *(&nexp->tagfq)-> tqh_last = (tfp); (&nexp->tagfq)->tqh_last = &( tfp)->q.tqe_next; } while (0); | |||
| 641 | } | |||
| 642 | ||||
| 643 | /* Copy the last tag. */ | |||
| 644 | if (oexp->tag_last != NULL((void*)0) && | |||
| 645 | (nexp->tag_last = strdup(oexp->tag_last)) == NULL((void*)0)) { | |||
| 646 | msgq(sp, M_SYSERR, NULL((void*)0)); | |||
| 647 | return (1); | |||
| 648 | } | |||
| 649 | return (0); | |||
| 650 | } | |||
| 651 | ||||
| 652 | /* | |||
| 653 | * tagf_copy -- | |||
| 654 | * Copy a TAGF structure and return it in new memory. | |||
| 655 | */ | |||
| 656 | static int | |||
| 657 | tagf_copy(SCR *sp, TAGF *otfp, TAGF **tfpp) | |||
| 658 | { | |||
| 659 | TAGF *tfp; | |||
| 660 | ||||
| 661 | MALLOC_RET(sp, tfp, sizeof(TAGF)){ if (((tfp) = malloc(sizeof(TAGF))) == ((void*)0)) { msgq((sp ), M_SYSERR, ((void*)0)); return (1); } }; | |||
| 662 | *tfp = *otfp; | |||
| 663 | ||||
| 664 | /* XXX: Allocate as part of the TAGF structure!!! */ | |||
| 665 | if ((tfp->name = strdup(otfp->name)) == NULL((void*)0)) { | |||
| 666 | free(tfp); | |||
| 667 | return (1); | |||
| 668 | } | |||
| 669 | ||||
| 670 | *tfpp = tfp; | |||
| 671 | return (0); | |||
| 672 | } | |||
| 673 | ||||
| 674 | /* | |||
| 675 | * tagq_copy -- | |||
| 676 | * Copy a TAGQ structure and return it in new memory. | |||
| 677 | */ | |||
| 678 | static int | |||
| 679 | tagq_copy(SCR *sp, TAGQ *otqp, TAGQ **tqpp) | |||
| 680 | { | |||
| 681 | TAGQ *tqp; | |||
| 682 | size_t len; | |||
| 683 | ||||
| 684 | len = sizeof(TAGQ); | |||
| 685 | if (otqp->tag != NULL((void*)0)) | |||
| 686 | len += otqp->tlen + 1; | |||
| 687 | MALLOC_RET(sp, tqp, len){ if (((tqp) = malloc(len)) == ((void*)0)) { msgq((sp), M_SYSERR , ((void*)0)); return (1); } }; | |||
| 688 | memcpy(tqp, otqp, len); | |||
| 689 | ||||
| 690 | TAILQ_INIT(&tqp->tagq)do { (&tqp->tagq)->tqh_first = ((void*)0); (&tqp ->tagq)->tqh_last = &(&tqp->tagq)->tqh_first ; } while (0); | |||
| 691 | tqp->current = NULL((void*)0); | |||
| 692 | if (otqp->tag != NULL((void*)0)) | |||
| 693 | tqp->tag = tqp->buf; | |||
| 694 | ||||
| 695 | *tqpp = tqp; | |||
| 696 | return (0); | |||
| 697 | } | |||
| 698 | ||||
| 699 | /* | |||
| 700 | * tag_copy -- | |||
| 701 | * Copy a TAG structure and return it in new memory. | |||
| 702 | */ | |||
| 703 | static int | |||
| 704 | tag_copy(SCR *sp, TAG *otp, TAG **tpp) | |||
| 705 | { | |||
| 706 | TAG *tp; | |||
| 707 | size_t len; | |||
| 708 | ||||
| 709 | len = sizeof(TAG); | |||
| 710 | if (otp->fname != NULL((void*)0)) | |||
| 711 | len += otp->fnlen + 1; | |||
| 712 | if (otp->search != NULL((void*)0)) | |||
| 713 | len += otp->slen + 1; | |||
| 714 | MALLOC_RET(sp, tp, len){ if (((tp) = malloc(len)) == ((void*)0)) { msgq((sp), M_SYSERR , ((void*)0)); return (1); } }; | |||
| 715 | memcpy(tp, otp, len); | |||
| 716 | ||||
| 717 | if (otp->fname != NULL((void*)0)) | |||
| 718 | tp->fname = tp->buf; | |||
| 719 | if (otp->search != NULL((void*)0)) | |||
| 720 | tp->search = tp->fname + otp->fnlen + 1; | |||
| 721 | ||||
| 722 | *tpp = tp; | |||
| 723 | return (0); | |||
| 724 | } | |||
| 725 | ||||
| 726 | /* | |||
| 727 | * tagf_free -- | |||
| 728 | * Free a TAGF structure. | |||
| 729 | */ | |||
| 730 | static int | |||
| 731 | tagf_free(SCR *sp, TAGF *tfp) | |||
| 732 | { | |||
| 733 | EX_PRIVATE *exp; | |||
| 734 | ||||
| 735 | exp = EXP(sp)((EX_PRIVATE *)((sp)->ex_private)); | |||
| 736 | TAILQ_REMOVE(&exp->tagfq, tfp, q)do { if (((tfp)->q.tqe_next) != ((void*)0)) (tfp)->q.tqe_next ->q.tqe_prev = (tfp)->q.tqe_prev; else (&exp->tagfq )->tqh_last = (tfp)->q.tqe_prev; *(tfp)->q.tqe_prev = (tfp)->q.tqe_next; ; ; } while (0); | |||
| 737 | free(tfp->name); | |||
| 738 | free(tfp); | |||
| 739 | return (0); | |||
| 740 | } | |||
| 741 | ||||
| 742 | /* | |||
| 743 | * tagq_free -- | |||
| 744 | * Free a TAGQ structure (and associated TAG structures). | |||
| 745 | * | |||
| 746 | * PUBLIC: int tagq_free(SCR *, TAGQ *); | |||
| 747 | */ | |||
| 748 | int | |||
| 749 | tagq_free(SCR *sp, TAGQ *tqp) | |||
| 750 | { | |||
| 751 | EX_PRIVATE *exp; | |||
| 752 | TAGQ *ttqp; | |||
| 753 | TAG *tp; | |||
| 754 | ||||
| 755 | exp = EXP(sp)((EX_PRIVATE *)((sp)->ex_private)); | |||
| 756 | while ((tp = TAILQ_FIRST(&tqp->tagq)((&tqp->tagq)->tqh_first))) { | |||
| 757 | TAILQ_REMOVE(&tqp->tagq, tp, q)do { if (((tp)->q.tqe_next) != ((void*)0)) (tp)->q.tqe_next ->q.tqe_prev = (tp)->q.tqe_prev; else (&tqp->tagq )->tqh_last = (tp)->q.tqe_prev; *(tp)->q.tqe_prev = ( tp)->q.tqe_next; ; ; } while (0); | |||
| 758 | free(tp); | |||
| 759 | } | |||
| 760 | /* | |||
| 761 | * !!! | |||
| 762 | * If allocated and then the user failed to switch files, the TAGQ | |||
| 763 | * structure was never attached to any list. | |||
| 764 | */ | |||
| 765 | TAILQ_FOREACH(ttqp, &exp->tq, q)for((ttqp) = ((&exp->tq)->tqh_first); (ttqp) != ((void *)0); (ttqp) = ((ttqp)->q.tqe_next)) { | |||
| 766 | if (ttqp
| |||
| 767 | TAILQ_REMOVE(&exp->tq, tqp, q)do { if (((tqp)->q.tqe_next) != ((void*)0)) (tqp)->q.tqe_next ->q.tqe_prev = (tqp)->q.tqe_prev; else (&exp->tq )->tqh_last = (tqp)->q.tqe_prev; *(tqp)->q.tqe_prev = (tqp)->q.tqe_next; ; ; } while (0); | |||
| 768 | break; | |||
| 769 | } | |||
| 770 | } | |||
| 771 | free(tqp); | |||
| 772 | return (0); | |||
| 773 | } | |||
| 774 | ||||
| 775 | /* | |||
| 776 | * tag_msg | |||
| 777 | * A few common messages. | |||
| 778 | * | |||
| 779 | * PUBLIC: void tag_msg(SCR *, tagmsg_t, char *); | |||
| 780 | */ | |||
| 781 | void | |||
| 782 | tag_msg(SCR *sp, tagmsg_t msg, char *tag) | |||
| 783 | { | |||
| 784 | switch (msg) { | |||
| 785 | case TAG_BADLNO: | |||
| 786 | msgq_str(sp, M_ERR, tag, | |||
| 787 | "%s: the tag's line number is past the end of the file"); | |||
| 788 | break; | |||
| 789 | case TAG_EMPTY: | |||
| 790 | msgq(sp, M_INFO, "The tags stack is empty"); | |||
| 791 | break; | |||
| 792 | case TAG_SEARCH: | |||
| 793 | msgq_str(sp, M_ERR, tag, "%s: search pattern not found"); | |||
| 794 | break; | |||
| 795 | default: | |||
| 796 | abort(); | |||
| 797 | } | |||
| 798 | } | |||
| 799 | ||||
| 800 | /* | |||
| 801 | * ex_tagf_alloc -- | |||
| 802 | * Create a new list of ctag files. | |||
| 803 | * | |||
| 804 | * PUBLIC: int ex_tagf_alloc(SCR *, char *); | |||
| 805 | */ | |||
| 806 | int | |||
| 807 | ex_tagf_alloc(SCR *sp, char *str) | |||
| 808 | { | |||
| 809 | EX_PRIVATE *exp; | |||
| 810 | TAGF *tfp; | |||
| 811 | size_t len; | |||
| 812 | char *p, *t; | |||
| 813 | ||||
| 814 | /* Free current queue. */ | |||
| 815 | exp = EXP(sp)((EX_PRIVATE *)((sp)->ex_private)); | |||
| 816 | while ((tfp = TAILQ_FIRST(&exp->tagfq)((&exp->tagfq)->tqh_first)) != NULL((void*)0)) | |||
| 817 | tagf_free(sp, tfp); | |||
| 818 | ||||
| 819 | /* Create new queue. */ | |||
| 820 | for (p = t = str;; ++p) { | |||
| 821 | if (*p == '\0' || isblank(*p)) { | |||
| 822 | if ((len = p - t) > 1) { | |||
| 823 | MALLOC_RET(sp, tfp, sizeof(TAGF)){ if (((tfp) = malloc(sizeof(TAGF))) == ((void*)0)) { msgq((sp ), M_SYSERR, ((void*)0)); return (1); } }; | |||
| 824 | MALLOC(sp, tfp->name, len + 1){ if (((tfp->name) = malloc(len + 1)) == ((void*)0)) msgq( (sp), M_SYSERR, ((void*)0)); }; | |||
| 825 | if (tfp->name == NULL((void*)0)) { | |||
| 826 | free(tfp); | |||
| 827 | return (1); | |||
| 828 | } | |||
| 829 | memcpy(tfp->name, t, len); | |||
| 830 | tfp->name[len] = '\0'; | |||
| 831 | tfp->flags = 0; | |||
| 832 | TAILQ_INSERT_TAIL(&exp->tagfq, tfp, q)do { (tfp)->q.tqe_next = ((void*)0); (tfp)->q.tqe_prev = (&exp->tagfq)->tqh_last; *(&exp->tagfq)-> tqh_last = (tfp); (&exp->tagfq)->tqh_last = &(tfp )->q.tqe_next; } while (0); | |||
| 833 | } | |||
| 834 | t = p + 1; | |||
| 835 | } | |||
| 836 | if (*p == '\0') | |||
| 837 | break; | |||
| 838 | } | |||
| 839 | return (0); | |||
| 840 | } | |||
| 841 | /* Free previous queue. */ | |||
| 842 | /* | |||
| 843 | * ex_tag_free -- | |||
| 844 | * Free the ex tag information. | |||
| 845 | * | |||
| 846 | * PUBLIC: int ex_tag_free(SCR *); | |||
| 847 | */ | |||
| 848 | int | |||
| 849 | ex_tag_free(SCR *sp) | |||
| 850 | { | |||
| 851 | EX_PRIVATE *exp; | |||
| 852 | TAGF *tfp; | |||
| 853 | TAGQ *tqp; | |||
| 854 | ||||
| 855 | /* Free up tag information. */ | |||
| 856 | exp = EXP(sp)((EX_PRIVATE *)((sp)->ex_private)); | |||
| 857 | while ((tqp = TAILQ_FIRST(&exp->tq)((&exp->tq)->tqh_first))) | |||
| ||||
| 858 | tagq_free(sp, tqp); /* tagq_free removes tqp from queue. */ | |||
| ||||
| 859 | while ((tfp = TAILQ_FIRST(&exp->tagfq)((&exp->tagfq)->tqh_first)) != NULL((void*)0)) | |||
| 860 | tagf_free(sp, tfp); | |||
| 861 | free(exp->tag_last); | |||
| 862 | return (0); | |||
| 863 | } | |||
| 864 | ||||
| 865 | /* | |||
| 866 | * ctag_search -- | |||
| 867 | * Search a file for a tag. | |||
| 868 | */ | |||
| 869 | static int | |||
| 870 | ctag_search(SCR *sp, char *search, size_t slen, char *tag) | |||
| 871 | { | |||
| 872 | MARK m; | |||
| 873 | char *p; | |||
| 874 | ||||
| 875 | /* | |||
| 876 | * !!! | |||
| 877 | * The historic tags file format (from a long, long time ago...) | |||
| 878 | * used a line number, not a search string. I got complaints, so | |||
| 879 | * people are still using the format. POSIX 1003.2 permits it. | |||
| 880 | */ | |||
| 881 | if (isdigit(search[0])) { | |||
| 882 | m.lno = atoi(search); | |||
| 883 | if (!db_exist(sp, m.lno)) { | |||
| 884 | tag_msg(sp, TAG_BADLNO, tag); | |||
| 885 | return (1); | |||
| 886 | } | |||
| 887 | } else { | |||
| 888 | /* | |||
| 889 | * Search for the tag; cheap fallback for C functions | |||
| 890 | * if the name is the same but the arguments have changed. | |||
| 891 | */ | |||
| 892 | m.lno = 1; | |||
| 893 | m.cno = 0; | |||
| 894 | if (f_search(sp, &m, &m, | |||
| 895 | search, slen, NULL((void*)0), SEARCH_FILE0x0004 | SEARCH_TAG0x0080)) { | |||
| 896 | if ((p = strrchr(search, '(')) != NULL((void*)0)) { | |||
| 897 | slen = p - search; | |||
| 898 | if (f_search(sp, &m, &m, search, slen, | |||
| 899 | NULL((void*)0), SEARCH_FILE0x0004 | SEARCH_TAG0x0080)) | |||
| 900 | goto notfound; | |||
| 901 | } else { | |||
| 902 | notfound: tag_msg(sp, TAG_SEARCH, tag); | |||
| 903 | return (1); | |||
| 904 | } | |||
| 905 | } | |||
| 906 | /* | |||
| 907 | * !!! | |||
| 908 | * Historically, tags set the search direction if it wasn't | |||
| 909 | * already set. | |||
| 910 | */ | |||
| 911 | if (sp->searchdir == NOTSET) | |||
| 912 | sp->searchdir = FORWARD; | |||
| 913 | } | |||
| 914 | ||||
| 915 | /* | |||
| 916 | * !!! | |||
| 917 | * Tags move to the first non-blank, NOT the search pattern start. | |||
| 918 | */ | |||
| 919 | sp->lno = m.lno; | |||
| 920 | sp->cno = 0; | |||
| 921 | (void)nonblank(sp, sp->lno, &sp->cno); | |||
| 922 | return (0); | |||
| 923 | } | |||
| 924 | ||||
| 925 | /* | |||
| 926 | * ctag_slist -- | |||
| 927 | * Search the list of tags files for a tag, and return tag queue. | |||
| 928 | */ | |||
| 929 | static TAGQ * | |||
| 930 | ctag_slist(SCR *sp, char *tag) | |||
| 931 | { | |||
| 932 | EX_PRIVATE *exp; | |||
| 933 | TAGF *tfp; | |||
| 934 | TAGQ *tqp; | |||
| 935 | size_t len; | |||
| 936 | int echk; | |||
| 937 | ||||
| 938 | exp = EXP(sp)((EX_PRIVATE *)((sp)->ex_private)); | |||
| 939 | ||||
| 940 | /* Allocate and initialize the tag queue structure. */ | |||
| 941 | len = strlen(tag); | |||
| 942 | CALLOC_GOTO(sp, tqp, 1, sizeof(TAGQ) + len + 1){ if (((tqp) = calloc((1), (sizeof(TAGQ) + len + 1))) == ((void *)0)) goto alloc_err; }; | |||
| 943 | TAILQ_INIT(&tqp->tagq)do { (&tqp->tagq)->tqh_first = ((void*)0); (&tqp ->tagq)->tqh_last = &(&tqp->tagq)->tqh_first ; } while (0); | |||
| 944 | tqp->tag = tqp->buf; | |||
| 945 | memcpy(tqp->tag, tag, (tqp->tlen = len) + 1); | |||
| 946 | ||||
| 947 | /* | |||
| 948 | * Find the tag, only display missing file messages once, and | |||
| 949 | * then only if we didn't find the tag. | |||
| 950 | */ | |||
| 951 | echk = 0; | |||
| 952 | TAILQ_FOREACH(tfp, &exp->tagfq, q)for((tfp) = ((&exp->tagfq)->tqh_first); (tfp) != (( void*)0); (tfp) = ((tfp)->q.tqe_next)) | |||
| 953 | if (ctag_sfile(sp, tfp, tqp, tag)) { | |||
| 954 | echk = 1; | |||
| 955 | F_SET(tfp, TAGF_ERR)(((tfp)->flags) |= ((0x01))); | |||
| 956 | } else | |||
| 957 | F_CLR(tfp, TAGF_ERR | TAGF_ERR_WARN)(((tfp)->flags) &= ~((0x01 | 0x02))); | |||
| 958 | ||||
| 959 | /* Check to see if we found anything. */ | |||
| 960 | if (TAILQ_EMPTY(&tqp->tagq)(((&tqp->tagq)->tqh_first) == ((void*)0))) { | |||
| 961 | msgq_str(sp, M_ERR, tag, "%s: tag not found"); | |||
| 962 | if (echk) | |||
| 963 | TAILQ_FOREACH(tfp, &exp->tagfq, q)for((tfp) = ((&exp->tagfq)->tqh_first); (tfp) != (( void*)0); (tfp) = ((tfp)->q.tqe_next)) | |||
| 964 | if (F_ISSET(tfp, TAGF_ERR)(((tfp)->flags) & ((0x01))) && | |||
| 965 | !F_ISSET(tfp, TAGF_ERR_WARN)(((tfp)->flags) & ((0x02)))) { | |||
| 966 | errno(*__errno()) = tfp->errnum; | |||
| 967 | msgq_str(sp, M_SYSERR, tfp->name, "%s"); | |||
| 968 | F_SET(tfp, TAGF_ERR_WARN)(((tfp)->flags) |= ((0x02))); | |||
| 969 | } | |||
| 970 | free(tqp); | |||
| 971 | return (NULL((void*)0)); | |||
| 972 | } | |||
| 973 | ||||
| 974 | tqp->current = TAILQ_FIRST(&tqp->tagq)((&tqp->tagq)->tqh_first); | |||
| 975 | return (tqp); | |||
| 976 | ||||
| 977 | alloc_err: | |||
| 978 | return (NULL((void*)0)); | |||
| 979 | } | |||
| 980 | ||||
| 981 | /* | |||
| 982 | * ctag_sfile -- | |||
| 983 | * Search a tags file for a tag, adding any found to the tag queue. | |||
| 984 | */ | |||
| 985 | static int | |||
| 986 | ctag_sfile(SCR *sp, TAGF *tfp, TAGQ *tqp, char *tname) | |||
| 987 | { | |||
| 988 | struct stat sb; | |||
| 989 | TAG *tp; | |||
| 990 | size_t dlen, nlen, slen; | |||
| 991 | int fd, i, nf1, nf2; | |||
| 992 | char *back, *cname, *dname, *front, *map, *name, *p, *search, *t; | |||
| 993 | ||||
| 994 | if ((fd = open(tfp->name, O_RDONLY0x0000)) < 0) { | |||
| 995 | tfp->errnum = errno(*__errno()); | |||
| 996 | return (1); | |||
| 997 | } | |||
| 998 | ||||
| 999 | /* | |||
| 1000 | * XXX | |||
| 1001 | * We'd like to test if the file is too big to mmap. Since we don't | |||
| 1002 | * know what size or type off_t's or size_t's are, what the largest | |||
| 1003 | * unsigned integral type is, or what random insanity the local C | |||
| 1004 | * compiler will perpetrate, doing the comparison in a portable way | |||
| 1005 | * is flatly impossible. Hope mmap fails if the file is too large. | |||
| 1006 | */ | |||
| 1007 | if (fstat(fd, &sb) != 0 || | |||
| 1008 | (map = mmap(NULL((void*)0), (size_t)sb.st_size, PROT_READ0x01 | PROT_WRITE0x02, | |||
| 1009 | MAP_PRIVATE0x0002, fd, (off_t)0)) == MAP_FAILED((void *)-1)) { | |||
| 1010 | tfp->errnum = errno(*__errno()); | |||
| 1011 | (void)close(fd); | |||
| 1012 | return (1); | |||
| 1013 | } | |||
| 1014 | ||||
| 1015 | front = map; | |||
| 1016 | back = front + sb.st_size; | |||
| 1017 | front = binary_search(tname, front, back); | |||
| 1018 | front = linear_search(tname, front, back); | |||
| 1019 | if (front == NULL((void*)0)) | |||
| 1020 | goto done; | |||
| 1021 | ||||
| 1022 | /* | |||
| 1023 | * Initialize and link in the tag structure(s). The historic ctags | |||
| 1024 | * file format only permitted a single tag location per tag. The | |||
| 1025 | * obvious extension to permit multiple tags locations per tag is to | |||
| 1026 | * output multiple records in the standard format. Unfortunately, | |||
| 1027 | * this won't work correctly with historic ex/vi implementations, | |||
| 1028 | * because their binary search assumes that there's only one record | |||
| 1029 | * per tag, and so will use a random tag entry if there si more than | |||
| 1030 | * one. This code handles either format. | |||
| 1031 | * | |||
| 1032 | * The tags file is in the following format: | |||
| 1033 | * | |||
| 1034 | * <tag> <filename> <line number> | <pattern> | |||
| 1035 | * | |||
| 1036 | * Figure out how long everything is so we can allocate in one swell | |||
| 1037 | * foop, but discard anything that looks wrong. | |||
| 1038 | */ | |||
| 1039 | for (;;) { | |||
| 1040 | /* Nul-terminate the end of the line. */ | |||
| 1041 | for (p = front; p < back && *p != '\n'; ++p); | |||
| 1042 | if (p == back || *p != '\n') | |||
| 1043 | break; | |||
| 1044 | *p = '\0'; | |||
| 1045 | ||||
| 1046 | /* Update the pointers for the next time. */ | |||
| 1047 | t = p + 1; | |||
| 1048 | p = front; | |||
| 1049 | front = t; | |||
| 1050 | ||||
| 1051 | /* Break the line into tokens. */ | |||
| 1052 | for (i = 0; i < 2 && (t = strsep(&p, "\t ")) != NULL((void*)0); ++i) | |||
| 1053 | switch (i) { | |||
| 1054 | case 0: /* Tag. */ | |||
| 1055 | cname = t; | |||
| 1056 | break; | |||
| 1057 | case 1: /* Filename. */ | |||
| 1058 | name = t; | |||
| 1059 | nlen = strlen(name); | |||
| 1060 | break; | |||
| 1061 | } | |||
| 1062 | ||||
| 1063 | /* Check for corruption. */ | |||
| 1064 | if (i != 2 || p == NULL((void*)0) || t == NULL((void*)0)) | |||
| 1065 | goto corrupt; | |||
| 1066 | ||||
| 1067 | /* The rest of the string is the search pattern. */ | |||
| 1068 | search = p; | |||
| 1069 | if ((slen = strlen(p)) == 0) { | |||
| 1070 | corrupt: p = msg_print(sp, tname, &nf1); | |||
| 1071 | t = msg_print(sp, tfp->name, &nf2); | |||
| 1072 | msgq(sp, M_ERR, "%s: corrupted tag in %s", p, t); | |||
| 1073 | if (nf1) | |||
| 1074 | FREE_SPACE(sp, p, 0){ GS *L__gp = (sp) == ((void*)0) ? ((void*)0) : (sp)->gp; if (L__gp != ((void*)0) && (p) == L__gp->tmp_bp) ((( L__gp)->flags) &= ~((0x0100))); else free(p); }; | |||
| 1075 | if (nf2) | |||
| 1076 | FREE_SPACE(sp, t, 0){ GS *L__gp = (sp) == ((void*)0) ? ((void*)0) : (sp)->gp; if (L__gp != ((void*)0) && (t) == L__gp->tmp_bp) ((( L__gp)->flags) &= ~((0x0100))); else free(t); }; | |||
| 1077 | continue; | |||
| 1078 | } | |||
| 1079 | ||||
| 1080 | /* Check for passing the last entry. */ | |||
| 1081 | if (strcmp(tname, cname)) | |||
| 1082 | break; | |||
| 1083 | ||||
| 1084 | /* Resolve the file name. */ | |||
| 1085 | ctag_file(sp, tfp, name, &dname, &dlen); | |||
| 1086 | ||||
| 1087 | CALLOC_GOTO(sp, tp,{ if (((tp) = calloc((1), (sizeof(TAG) + dlen + 2 + nlen + 1 + slen + 1))) == ((void*)0)) goto alloc_err; } | |||
| 1088 | 1, sizeof(TAG) + dlen + 2 + nlen + 1 + slen + 1){ if (((tp) = calloc((1), (sizeof(TAG) + dlen + 2 + nlen + 1 + slen + 1))) == ((void*)0)) goto alloc_err; }; | |||
| 1089 | tp->fname = tp->buf; | |||
| 1090 | if (dlen != 0) { | |||
| 1091 | memcpy(tp->fname, dname, dlen); | |||
| 1092 | tp->fname[dlen] = '/'; | |||
| 1093 | ++dlen; | |||
| 1094 | } | |||
| 1095 | memcpy(tp->fname + dlen, name, nlen + 1); | |||
| 1096 | tp->fnlen = dlen + nlen; | |||
| 1097 | tp->search = tp->fname + tp->fnlen + 1; | |||
| 1098 | memcpy(tp->search, search, (tp->slen = slen) + 1); | |||
| 1099 | TAILQ_INSERT_TAIL(&tqp->tagq, tp, q)do { (tp)->q.tqe_next = ((void*)0); (tp)->q.tqe_prev = ( &tqp->tagq)->tqh_last; *(&tqp->tagq)->tqh_last = (tp); (&tqp->tagq)->tqh_last = &(tp)->q.tqe_next ; } while (0); | |||
| 1100 | } | |||
| 1101 | ||||
| 1102 | alloc_err: | |||
| 1103 | done: if (munmap(map, (size_t)sb.st_size)) | |||
| 1104 | msgq(sp, M_SYSERR, "munmap"); | |||
| 1105 | if (close(fd)) | |||
| 1106 | msgq(sp, M_SYSERR, "close"); | |||
| 1107 | return (0); | |||
| 1108 | } | |||
| 1109 | ||||
| 1110 | /* | |||
| 1111 | * ctag_file -- | |||
| 1112 | * Search for the right path to this file. | |||
| 1113 | */ | |||
| 1114 | static void | |||
| 1115 | ctag_file(SCR *sp, TAGF *tfp, char *name, char **dirp, size_t *dlenp) | |||
| 1116 | { | |||
| 1117 | struct stat sb; | |||
| 1118 | char *p, buf[PATH_MAX1024]; | |||
| 1119 | ||||
| 1120 | /* | |||
| 1121 | * !!! | |||
| 1122 | * If the tag file path is a relative path, see if it exists. If it | |||
| 1123 | * doesn't, look relative to the tags file path. It's okay for a tag | |||
| 1124 | * file to not exist, and historically, vi simply displayed a "new" | |||
| 1125 | * file. However, if the path exists relative to the tag file, it's | |||
| 1126 | * pretty clear what's happening, so we may as well get it right. | |||
| 1127 | */ | |||
| 1128 | *dlenp = 0; | |||
| 1129 | if (name[0] != '/' && | |||
| 1130 | stat(name, &sb) && (p = strrchr(tfp->name, '/')) != NULL((void*)0)) { | |||
| 1131 | *p = '\0'; | |||
| 1132 | (void)snprintf(buf, sizeof(buf), "%s/%s", tfp->name, name); | |||
| 1133 | if (stat(buf, &sb) == 0) { | |||
| 1134 | *dirp = tfp->name; | |||
| 1135 | *dlenp = strlen(*dirp); | |||
| 1136 | } | |||
| 1137 | *p = '/'; | |||
| 1138 | } | |||
| 1139 | } | |||
| 1140 | ||||
| 1141 | /* | |||
| 1142 | * Binary search for "string" in memory between "front" and "back". | |||
| 1143 | * | |||
| 1144 | * This routine is expected to return a pointer to the start of a line at | |||
| 1145 | * *or before* the first word matching "string". Relaxing the constraint | |||
| 1146 | * this way simplifies the algorithm. | |||
| 1147 | * | |||
| 1148 | * Invariants: | |||
| 1149 | * front points to the beginning of a line at or before the first | |||
| 1150 | * matching string. | |||
| 1151 | * | |||
| 1152 | * back points to the beginning of a line at or after the first | |||
| 1153 | * matching line. | |||
| 1154 | * | |||
| 1155 | * Base of the Invariants. | |||
| 1156 | * front = NULL; | |||
| 1157 | * back = EOF; | |||
| 1158 | * | |||
| 1159 | * Advancing the Invariants: | |||
| 1160 | * | |||
| 1161 | * p = first newline after halfway point from front to back. | |||
| 1162 | * | |||
| 1163 | * If the string at "p" is not greater than the string to match, | |||
| 1164 | * p is the new front. Otherwise it is the new back. | |||
| 1165 | * | |||
| 1166 | * Termination: | |||
| 1167 | * | |||
| 1168 | * The definition of the routine allows it return at any point, | |||
| 1169 | * since front is always at or before the line to print. | |||
| 1170 | * | |||
| 1171 | * In fact, it returns when the chosen "p" equals "back". This | |||
| 1172 | * implies that there exists a string is least half as long as | |||
| 1173 | * (back - front), which in turn implies that a linear search will | |||
| 1174 | * be no more expensive than the cost of simply printing a string or two. | |||
| 1175 | * | |||
| 1176 | * Trying to continue with binary search at this point would be | |||
| 1177 | * more trouble than it's worth. | |||
| 1178 | */ | |||
| 1179 | #define EQUAL0 0 | |||
| 1180 | #define GREATER1 1 | |||
| 1181 | #define LESS(-1) (-1) | |||
| 1182 | ||||
| 1183 | #define SKIP_PAST_NEWLINE(p, back)while ((p) < (back) && *(p)++ != '\n'); while ((p) < (back) && *(p)++ != '\n'); | |||
| 1184 | ||||
| 1185 | static char * | |||
| 1186 | binary_search(char *string, char *front, char *back) | |||
| 1187 | { | |||
| 1188 | char *p; | |||
| 1189 | ||||
| 1190 | p = front + (back - front) / 2; | |||
| 1191 | SKIP_PAST_NEWLINE(p, back)while ((p) < (back) && *(p)++ != '\n');; | |||
| 1192 | ||||
| 1193 | while (p != back) { | |||
| 1194 | if (compare(string, p, back) == GREATER1) | |||
| 1195 | front = p; | |||
| 1196 | else | |||
| 1197 | back = p; | |||
| 1198 | p = front + (back - front) / 2; | |||
| 1199 | SKIP_PAST_NEWLINE(p, back)while ((p) < (back) && *(p)++ != '\n');; | |||
| 1200 | } | |||
| 1201 | return (front); | |||
| 1202 | } | |||
| 1203 | ||||
| 1204 | /* | |||
| 1205 | * Find the first line that starts with string, linearly searching from front | |||
| 1206 | * to back. | |||
| 1207 | * | |||
| 1208 | * Return NULL for no such line. | |||
| 1209 | * | |||
| 1210 | * This routine assumes: | |||
| 1211 | * | |||
| 1212 | * o front points at the first character in a line. | |||
| 1213 | * o front is before or at the first line to be printed. | |||
| 1214 | */ | |||
| 1215 | static char * | |||
| 1216 | linear_search(char *string, char *front, char *back) | |||
| 1217 | { | |||
| 1218 | while (front < back) { | |||
| 1219 | switch (compare(string, front, back)) { | |||
| 1220 | case EQUAL0: /* Found it. */ | |||
| 1221 | return (front); | |||
| 1222 | case LESS(-1): /* No such string. */ | |||
| 1223 | return (NULL((void*)0)); | |||
| 1224 | case GREATER1: /* Keep going. */ | |||
| 1225 | break; | |||
| 1226 | } | |||
| 1227 | SKIP_PAST_NEWLINE(front, back)while ((front) < (back) && *(front)++ != '\n');; | |||
| 1228 | } | |||
| 1229 | return (NULL((void*)0)); | |||
| 1230 | } | |||
| 1231 | ||||
| 1232 | /* | |||
| 1233 | * Return LESS, GREATER, or EQUAL depending on how the string1 compares | |||
| 1234 | * with string2 (s1 ??? s2). | |||
| 1235 | * | |||
| 1236 | * o Matches up to len(s1) are EQUAL. | |||
| 1237 | * o Matches up to len(s2) are GREATER. | |||
| 1238 | * | |||
| 1239 | * The string "s1" is null terminated. The string s2 is '\t', space, (or | |||
| 1240 | * "back") terminated. | |||
| 1241 | * | |||
| 1242 | * !!! | |||
| 1243 | * Reasonably modern ctags programs use tabs as separators, not spaces. | |||
| 1244 | * However, historic programs did use spaces, and, I got complaints. | |||
| 1245 | */ | |||
| 1246 | static int | |||
| 1247 | compare(char *s1, char *s2, char *back) | |||
| 1248 | { | |||
| 1249 | for (; *s1 && s2 < back && (*s2 != '\t' && *s2 != ' '); ++s1, ++s2) | |||
| 1250 | if (*s1 != *s2) | |||
| 1251 | return (*s1 < *s2 ? LESS(-1) : GREATER1); | |||
| 1252 | return (*s1 ? GREATER1 : s2 < back && | |||
| 1253 | (*s2 != '\t' && *s2 != ' ') ? LESS(-1) : EQUAL0); | |||
| 1254 | } |