File: | src/usr.bin/vi/build/../ex/ex_tag.c |
Warning: | line 543, column 2 Value stored to 'tqp' is never read |
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); |
Value stored to 'tqp' is never read | |
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 | } |