File: | src/usr.bin/vi/build/../ex/ex_tag.c |
Warning: | line 817, 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 == tqp) { | |||
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 | } |