2 * Copyright (c) 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 1993, 1994, 1995, 1996
5 * Keith Bostic. All rights reserved.
7 * See the LICENSE file for redistribution information.
12 #include <sys/types.h>
13 #include <sys/queue.h>
16 #include <bitstring.h>
23 #include "../common/common.h"
26 typedef enum { HORIZ_FOLLOW
, HORIZ_PRECEDE
, VERT_FOLLOW
, VERT_PRECEDE
} jdir_t
;
28 static SCR
*vs_getbg(SCR
*, char *);
29 static void vs_insert(SCR
*sp
, GS
*gp
);
30 static int vs_join(SCR
*, SCR
**, jdir_t
*);
34 * Create a new screen, horizontally.
36 * PUBLIC: int vs_split(SCR *, SCR *, int);
42 int ccl
) /* Colon-command line split. */
47 int issmallscreen
, splitup
;
51 /* Check to see if it's possible. */
52 /* XXX: The IS_ONELINE fix will change this, too. */
55 "222|Screen must be larger than %d lines to split", 4 - 1);
59 /* Wait for any messages in the screen. */
60 vs_resolve(sp
, NULL
, 1);
62 /* Get a new screen map. */
63 CALLOC(sp
, _HMAP(new), SIZE_HMAP(sp
), sizeof(SMAP
));
64 if (_HMAP(new) == NULL
)
66 _HMAP(new)->lno
= sp
->lno
;
70 /* Split the screen in half. */
76 * Small screens: see vs_refresh.c section 6a. Set a flag so
77 * we know to fix the screen up later.
79 issmallscreen
= IS_SMALL(sp
);
81 /* The columns in the screen don't change. */
86 * Split the screen, and link the screens together. If creating a
87 * screen to edit the colon command line or the cursor is in the top
88 * half of the current screen, the new screen goes under the current
89 * screen. Else, it goes above the current screen.
91 * Recalculate current cursor position based on sp->lno, we're called
92 * with the cursor on the colon command line. Then split the screen
93 * in half and update the shared information.
96 !ccl
&& (vs_sm_cursor(sp
, &smp
) ? 0 : (smp
- HMAP
) + 1) >= half
;
97 if (splitup
) { /* Old is bottom half. */
98 new->rows
= sp
->rows
- half
; /* New. */
100 sp
->rows
= half
; /* Old. */
101 sp
->roff
+= new->rows
;
104 * If the parent is the bottom half of the screen, shift
105 * the map down to match on-screen text.
107 memcpy(_HMAP(sp
), _HMAP(sp
) + new->rows
,
108 (sp
->t_maxrows
- new->rows
) * sizeof(SMAP
));
109 } else { /* Old is top half. */
110 new->rows
= half
; /* New. */
111 sp
->rows
-= half
; /* Old. */
112 new->roff
= sp
->roff
+ sp
->rows
;
115 /* Adjust maximum text count. */
116 sp
->t_maxrows
= IS_ONELINE(sp
) ? 1 : sp
->rows
- 1;
117 new->t_maxrows
= IS_ONELINE(new) ? 1 : new->rows
- 1;
120 * Small screens: see vs_refresh.c, section 6a.
122 * The child may have different screen options sizes than the parent,
123 * so use them. Guarantee that text counts aren't larger than the
127 /* Fix the text line count for the parent. */
129 sp
->t_rows
-= new->rows
;
131 /* Fix the parent screen. */
132 if (sp
->t_rows
> sp
->t_maxrows
)
133 sp
->t_rows
= sp
->t_maxrows
;
134 if (sp
->t_minrows
> sp
->t_maxrows
)
135 sp
->t_minrows
= sp
->t_maxrows
;
137 /* Fix the child screen. */
138 new->t_minrows
= new->t_rows
= O_VAL(sp
, O_WINDOW
);
139 if (new->t_rows
> new->t_maxrows
)
140 new->t_rows
= new->t_maxrows
;
141 if (new->t_minrows
> new->t_maxrows
)
142 new->t_minrows
= new->t_maxrows
;
144 sp
->t_minrows
= sp
->t_rows
= IS_ONELINE(sp
) ? 1 : sp
->rows
- 1;
147 * The new screen may be a small screen, even if the parent
148 * was not. Don't complain if O_WINDOW is too large, we're
149 * splitting the screen so the screen is much smaller than
152 new->t_minrows
= new->t_rows
= O_VAL(sp
, O_WINDOW
);
153 if (new->t_rows
> new->rows
- 1)
154 new->t_minrows
= new->t_rows
=
155 IS_ONELINE(new) ? 1 : new->rows
- 1;
158 /* Adjust the ends of the new and old maps. */
159 _TMAP(sp
) = IS_ONELINE(sp
) ?
160 _HMAP(sp
) : _HMAP(sp
) + (sp
->t_rows
- 1);
161 _TMAP(new) = IS_ONELINE(new) ?
162 _HMAP(new) : _HMAP(new) + (new->t_rows
- 1);
164 /* Reset the length of the default scroll. */
165 if ((sp
->defscroll
= sp
->t_maxrows
/ 2) == 0)
167 if ((new->defscroll
= new->t_maxrows
/ 2) == 0)
170 /* Fit the screen into the logical chain. */
171 vs_insert(new, sp
->gp
);
173 /* Tell the display that we're splitting. */
174 (void)gp
->scr_split(sp
, new);
177 * Initialize the screen flags:
179 * If we're in vi mode in one screen, we don't have to reinitialize.
180 * This isn't just a cosmetic fix. The path goes like this:
182 * return into vi(), SC_SSWITCH set
183 * call vs_refresh() with SC_STATUS set
184 * call vs_resolve to display the status message
185 * call vs_refresh() because the SC_SCR_VI bit isn't set
187 * Things go downhill at this point.
189 * Draw the new screen from scratch, and add a status line.
192 SC_SCR_REFORMAT
| SC_STATUS
|
193 F_ISSET(sp
, SC_EX
| SC_VI
| SC_SCR_VI
| SC_SCR_EX
| SC_READONLY
));
199 * Create a new screen, vertically.
201 * PUBLIC: int vs_vsplit(SCR *, SCR *);
204 vs_vsplit(SCR
*sp
, SCR
*new)
211 /* Check to see if it's possible. */
212 if (sp
->cols
/ 2 <= MINIMUM_SCREEN_COLS
) {
214 "288|Screen must be larger than %d columns to split",
215 MINIMUM_SCREEN_COLS
* 2);
219 /* Wait for any messages in the screen. */
220 vs_resolve(sp
, NULL
, 1);
222 /* Get a new screen map. */
223 CALLOC(sp
, _HMAP(new), SIZE_HMAP(sp
), sizeof(SMAP
));
224 if (_HMAP(new) == NULL
)
226 _HMAP(new)->lno
= sp
->lno
;
227 _HMAP(new)->coff
= 0;
228 _HMAP(new)->soff
= 1;
231 * Split the screen in half; we have to sacrifice a column to delimit
235 * We always split to the right... that makes more sense to me, and
236 * I don't want to play the stupid games that I play when splitting
240 * We reserve a column for the screen, "knowing" that curses needs
241 * one. This should be worked out with the display interface.
244 new->cols
= sp
->cols
- cols
- 1;
246 new->coff
= sp
->coff
+ cols
+ 1;
249 /* Nothing else changes. */
250 new->rows
= sp
->rows
;
251 new->t_rows
= sp
->t_rows
;
252 new->t_maxrows
= sp
->t_maxrows
;
253 new->t_minrows
= sp
->t_minrows
;
254 new->roff
= sp
->roff
;
255 new->defscroll
= sp
->defscroll
;
256 _TMAP(new) = _HMAP(new) + (new->t_rows
- 1);
258 /* Fit the screen into the logical chain. */
259 vs_insert(new, sp
->gp
);
261 /* Tell the display that we're splitting. */
262 (void)gp
->scr_split(sp
, new);
264 /* Redraw the old screen from scratch. */
265 F_SET(sp
, SC_SCR_REFORMAT
| SC_STATUS
);
268 * Initialize the screen flags:
270 * If we're in vi mode in one screen, we don't have to reinitialize.
271 * This isn't just a cosmetic fix. The path goes like this:
273 * return into vi(), SC_SSWITCH set
274 * call vs_refresh() with SC_STATUS set
275 * call vs_resolve to display the status message
276 * call vs_refresh() because the SC_SCR_VI bit isn't set
278 * Things go downhill at this point.
280 * Draw the new screen from scratch, and add a status line.
283 SC_SCR_REFORMAT
| SC_STATUS
|
284 F_ISSET(sp
, SC_EX
| SC_VI
| SC_SCR_VI
| SC_SCR_EX
| SC_READONLY
));
290 * Insert the new screen into the correct place in the logical
294 vs_insert(SCR
*sp
, GS
*gp
)
300 /* Move past all screens with lower row numbers. */
301 TAILQ_FOREACH(tsp
, gp
->dq
, q
)
302 if (tsp
->roff
>= sp
->roff
)
305 * Move past all screens with the same row number and lower
308 for (; tsp
!= NULL
; tsp
= TAILQ_NEXT(tsp
, q
))
309 if (tsp
->roff
!= sp
->roff
|| tsp
->coff
> sp
->coff
)
313 * If we reached the end, this screen goes there. Otherwise,
314 * put it before or after the screen where we stopped.
317 TAILQ_INSERT_TAIL(gp
->dq
, sp
, q
);
318 } else if (tsp
->roff
< sp
->roff
||
319 (tsp
->roff
== sp
->roff
&& tsp
->coff
< sp
->coff
)) {
320 TAILQ_INSERT_AFTER(gp
->dq
, tsp
, sp
, q
);
322 TAILQ_INSERT_BEFORE(tsp
, sp
, q
);
327 * Discard the screen, folding the real-estate into a related screen,
328 * if one exists, and return that screen.
330 * PUBLIC: int vs_discard(SCR *, SCR **);
333 vs_discard(SCR
*sp
, SCR
**spp
)
336 SCR
*tsp
, **lp
, *list
[100];
342 * Save the old screen's cursor information.
345 * If called after file_end(), and the underlying file was a tmp
346 * file, it may have gone away.
348 if (sp
->frp
!= NULL
) {
349 sp
->frp
->lno
= sp
->lno
;
350 sp
->frp
->cno
= sp
->cno
;
351 F_SET(sp
->frp
, FR_CURSORSET
);
354 /* If no other screens to join, we're done. */
356 (void)gp
->scr_discard(sp
, NULL
);
364 * Find a set of screens that cover one of the screen's borders.
365 * Check the vertical axis first, for no particular reason.
368 * It's possible (I think?), to create a screen that shares no full
369 * border with any other set of screens, so we can't discard it. We
370 * just complain at the user until they clean it up.
372 if (vs_join(sp
, list
, &jdir
))
376 * Modify the affected screens. Redraw the modified screen(s) from
377 * scratch, setting a status line. If this is ever a performance
378 * problem we could play games with the map, but I wrote that code
379 * before and it was never clean or easy.
381 * Don't clean up the discarded screen's information. If the screen
382 * isn't exiting, we'll do the work when the user redisplays it.
387 for (lp
= &list
[0]; (tsp
= *lp
) != NULL
; ++lp
) {
389 * Small screens: see vs_refresh.c section 6a. Adjust
390 * text line info, unless it's a small screen.
392 * Reset the length of the default scroll.
394 * Reset the map references.
396 tsp
->rows
+= sp
->rows
;
398 tsp
->t_rows
= tsp
->t_minrows
= tsp
->rows
- 1;
399 tsp
->t_maxrows
= tsp
->rows
- 1;
401 tsp
->defscroll
= tsp
->t_maxrows
/ 2;
403 *(_HMAP(tsp
) + (tsp
->t_rows
- 1)) = *_TMAP(tsp
);
404 _TMAP(tsp
) = _HMAP(tsp
) + (tsp
->t_rows
- 1);
408 tsp
->roff
= sp
->roff
;
409 vs_sm_fill(tsp
, OOBLNO
, P_TOP
);
412 vs_sm_fill(tsp
, OOBLNO
, P_BOTTOM
);
417 F_SET(tsp
, SC_STATUS
);
422 for (lp
= &list
[0]; (tsp
= *lp
) != NULL
; ++lp
) {
423 if (jdir
== VERT_FOLLOW
)
424 tsp
->coff
= sp
->coff
;
425 tsp
->cols
+= sp
->cols
+ 1; /* XXX: DIVIDER */
426 vs_sm_fill(tsp
, OOBLNO
, P_TOP
);
427 F_SET(tsp
, SC_STATUS
);
434 /* Find the closest screen that changed and move to it. */
439 /* Tell the display that we're discarding a screen. */
440 (void)gp
->scr_discard(sp
, list
);
447 * Find a set of screens that covers a screen's border.
450 vs_join(SCR
*sp
, SCR
**listp
, jdir_t
*jdirp
)
459 /* Check preceding vertical. */
460 for (lp
= listp
, tlen
= sp
->rows
,
461 tsp
= TAILQ_FIRST(gp
->dq
);
462 tsp
!= NULL
; tsp
= TAILQ_NEXT(tsp
, q
)) {
465 /* Test if precedes the screen vertically. */
466 if (tsp
->coff
+ tsp
->cols
+ 1 != sp
->coff
)
469 * Test if a subset on the vertical axis. If overlaps the
470 * beginning or end, we can't join on this axis at all.
472 if (tsp
->roff
> sp
->roff
+ sp
->rows
)
474 if (tsp
->roff
< sp
->roff
) {
475 if (tsp
->roff
+ tsp
->rows
>= sp
->roff
)
479 if (tsp
->roff
+ tsp
->rows
> sp
->roff
+ sp
->rows
)
482 if (tlen
< tsp
->rows
)
490 *jdirp
= VERT_PRECEDE
;
494 /* Check following vertical. */
495 for (lp
= listp
, tlen
= sp
->rows
,
496 tsp
= TAILQ_FIRST(gp
->dq
);
497 tsp
!= NULL
; tsp
= TAILQ_NEXT(tsp
, q
)) {
500 /* Test if follows the screen vertically. */
501 if (tsp
->coff
!= sp
->coff
+ sp
->cols
+ 1)
504 * Test if a subset on the vertical axis. If overlaps the
505 * beginning or end, we can't join on this axis at all.
507 if (tsp
->roff
> sp
->roff
+ sp
->rows
)
509 if (tsp
->roff
< sp
->roff
) {
510 if (tsp
->roff
+ tsp
->rows
>= sp
->roff
)
514 if (tsp
->roff
+ tsp
->rows
> sp
->roff
+ sp
->rows
)
517 if (tlen
< tsp
->rows
)
525 *jdirp
= VERT_FOLLOW
;
529 /* Check preceding horizontal. */
530 for (first
= 0, lp
= listp
, tlen
= sp
->cols
,
531 tsp
= TAILQ_FIRST(gp
->dq
);
532 tsp
!= NULL
; tsp
= TAILQ_NEXT(tsp
, q
)) {
535 /* Test if precedes the screen horizontally. */
536 if (tsp
->roff
+ tsp
->rows
!= sp
->roff
)
539 * Test if a subset on the horizontal axis. If overlaps the
540 * beginning or end, we can't join on this axis at all.
542 if (tsp
->coff
> sp
->coff
+ sp
->cols
)
544 if (tsp
->coff
< sp
->coff
) {
545 if (tsp
->coff
+ tsp
->cols
>= sp
->coff
)
549 if (tsp
->coff
+ tsp
->cols
> sp
->coff
+ sp
->cols
)
552 if (tlen
< tsp
->cols
)
555 tlen
-= tsp
->cols
+ first
;
561 *jdirp
= HORIZ_PRECEDE
;
565 /* Check following horizontal. */
566 for (first
= 0, lp
= listp
, tlen
= sp
->cols
,
567 tsp
= TAILQ_FIRST(gp
->dq
);
568 tsp
!= NULL
; tsp
= TAILQ_NEXT(tsp
, q
)) {
571 /* Test if precedes the screen horizontally. */
572 if (tsp
->roff
!= sp
->roff
+ sp
->rows
)
575 * Test if a subset on the horizontal axis. If overlaps the
576 * beginning or end, we can't join on this axis at all.
578 if (tsp
->coff
> sp
->coff
+ sp
->cols
)
580 if (tsp
->coff
< sp
->coff
) {
581 if (tsp
->coff
+ tsp
->cols
>= sp
->coff
)
585 if (tsp
->coff
+ tsp
->cols
> sp
->coff
+ sp
->cols
)
588 if (tlen
< tsp
->cols
)
591 tlen
-= tsp
->cols
+ first
;
597 *jdirp
= HORIZ_FOLLOW
;
605 * Background the current screen, and foreground a new one.
607 * PUBLIC: int vs_fg(SCR *, SCR **, CHAR_T *, int);
610 vs_fg(SCR
*sp
, SCR
**nspp
, CHAR_T
*name
, int newscreen
)
620 INT2CHAR(sp
, name
, STRLEN(name
) + 1, np
, nlen
);
624 /* Get the specified background screen. */
625 nsp
= vs_getbg(sp
, np
);
628 if (vs_swap(sp
, &nsp
, np
))
631 if ((*nspp
= nsp
) == NULL
) {
632 msgq_wstr(sp
, M_ERR
, name
,
634 "223|There are no background screens" :
635 "224|There's no background screen editing a file named %s");
640 /* Remove the new screen from the background queue. */
641 TAILQ_REMOVE(gp
->hq
, nsp
, q
);
643 /* Split the screen; if we fail, hook the screen back in. */
644 if (vs_split(sp
, nsp
, 0)) {
645 TAILQ_INSERT_TAIL(gp
->hq
, nsp
, q
);
649 /* Move the old screen to the background queue. */
650 TAILQ_REMOVE(gp
->dq
, sp
, q
);
651 TAILQ_INSERT_TAIL(gp
->hq
, sp
, q
);
658 * Background the screen, and switch to the next one.
660 * PUBLIC: int vs_bg(SCR *);
670 /* Try and join with another screen. */
671 if (vs_discard(sp
, &nsp
))
675 "225|You may not background your only displayed screen");
679 /* Move the old screen to the background queue. */
680 TAILQ_REMOVE(gp
->dq
, sp
, q
);
681 TAILQ_INSERT_TAIL(gp
->hq
, sp
, q
);
683 /* Toss the screen map. */
687 /* Switch screens. */
689 F_SET(sp
, SC_SSWITCH
);
696 * Swap the current screen with a backgrounded one.
698 * PUBLIC: int vs_swap(SCR *, SCR **, char *);
701 vs_swap(SCR
*sp
, SCR
**nspp
, char *name
)
708 /* Get the specified background screen. */
709 if ((*nspp
= nsp
= vs_getbg(sp
, name
)) == NULL
)
713 * Save the old screen's cursor information.
716 * If called after file_end(), and the underlying file was a tmp
717 * file, it may have gone away.
719 if (sp
->frp
!= NULL
) {
720 sp
->frp
->lno
= sp
->lno
;
721 sp
->frp
->cno
= sp
->cno
;
722 F_SET(sp
->frp
, FR_CURSORSET
);
725 /* Switch screens. */
727 F_SET(sp
, SC_SSWITCH
);
729 /* Initialize terminal information. */
730 VIP(nsp
)->srows
= VIP(sp
)->srows
;
732 /* Initialize screen information. */
733 nsp
->cols
= sp
->cols
;
734 nsp
->rows
= sp
->rows
; /* XXX: Only place in vi that sets rows. */
735 nsp
->roff
= sp
->roff
;
738 * Small screens: see vs_refresh.c, section 6a.
740 * The new screens may have different screen options sizes than the
741 * old one, so use them. Make sure that text counts aren't larger
742 * than the new screen sizes.
745 nsp
->t_minrows
= nsp
->t_rows
= O_VAL(nsp
, O_WINDOW
);
746 if (nsp
->t_rows
> sp
->t_maxrows
)
747 nsp
->t_rows
= nsp
->t_maxrows
;
748 if (nsp
->t_minrows
> sp
->t_maxrows
)
749 nsp
->t_minrows
= nsp
->t_maxrows
;
751 nsp
->t_rows
= nsp
->t_maxrows
= nsp
->t_minrows
= nsp
->rows
- 1;
753 /* Reset the length of the default scroll. */
754 nsp
->defscroll
= nsp
->t_maxrows
/ 2;
756 /* Allocate a new screen map. */
757 CALLOC_RET(nsp
, _HMAP(nsp
), SIZE_HMAP(nsp
), sizeof(SMAP
));
758 _TMAP(nsp
) = _HMAP(nsp
) + (nsp
->t_rows
- 1);
762 if (vs_sm_fill(nsp
, nsp
->lno
, P_FILL
))
766 * The new screen replaces the old screen in the parent/child list.
767 * We insert the new screen after the old one. If we're exiting,
768 * the exit will delete the old one, if we're foregrounding, the fg
769 * code will move the old one to the background queue.
771 TAILQ_REMOVE(gp
->hq
, nsp
, q
);
772 TAILQ_INSERT_AFTER(gp
->dq
, sp
, nsp
, q
);
775 * Don't change the screen's cursor information other than to
776 * note that the cursor is wrong.
778 F_SET(VIP(nsp
), VIP_CUR_INVALID
);
780 /* Draw the new screen from scratch, and add a status line. */
781 F_SET(nsp
, SC_SCR_REDRAW
| SC_STATUS
);
783 list
[0] = nsp
; list
[1] = NULL
;
784 (void)gp
->scr_discard(sp
, list
);
791 * Change the absolute size of the current screen.
793 * PUBLIC: int vs_resize(SCR *, long, adj_t);
796 vs_resize(SCR
*sp
, long int count
, adj_t adj
)
799 SCR
*g
, *s
, *prev
, *next
, *list
[3] = {NULL
, NULL
, NULL
};
805 * Figure out which screens will grow, which will shrink, and
806 * make sure it's possible.
811 if (sp
->t_maxrows
== count
)
813 if (sp
->t_maxrows
> count
) {
815 count
= sp
->t_maxrows
- count
;
818 count
= count
- sp
->t_maxrows
;
822 /* Find first overlapping screen */
823 for (next
= TAILQ_NEXT(sp
, q
); next
!= NULL
&&
824 (next
->coff
>= sp
->coff
+ sp
->cols
||
825 next
->coff
+ next
->cols
<= sp
->coff
);
826 next
= TAILQ_NEXT(next
, q
));
827 /* See if we can use it */
829 (sp
->coff
!= next
->coff
|| sp
->cols
!= next
->cols
))
831 for (prev
= TAILQ_PREV(sp
, _dqh
, q
); prev
!= NULL
&&
832 (prev
->coff
>= sp
->coff
+ sp
->cols
||
833 prev
->coff
+ prev
->cols
<= sp
->coff
);
834 prev
= TAILQ_PREV(prev
, _dqh
, q
));
836 (sp
->coff
!= prev
->coff
|| sp
->cols
!= prev
->cols
))
840 if (adj
== A_DECREASE
) {
844 if (s
->t_maxrows
< MINIMUM_SCREEN_ROWS
+ count
)
846 if ((g
= prev
) == NULL
) {
847 if ((g
= next
) == NULL
)
854 if ((s
= next
) != NULL
&&
855 s
->t_maxrows
>= MINIMUM_SCREEN_ROWS
+ count
)
860 if ((s
= prev
) == NULL
) {
861 toobig
: msgq(sp
, M_BERR
, adj
== A_DECREASE
?
862 "227|The screen cannot shrink" :
863 "228|The screen cannot grow");
866 if (s
->t_maxrows
< MINIMUM_SCREEN_ROWS
+ count
) {
867 toosmall
: msgq(sp
, M_BERR
,
868 "226|The screen can only shrink to %d rows",
869 MINIMUM_SCREEN_ROWS
);
877 * Fix up the screens; we could optimize the reformatting of the
878 * screen, but this isn't likely to be a common enough operation
879 * to make it worthwhile.
887 if (g
->t_minrows
== g
->t_maxrows
)
888 g
->t_minrows
+= count
;
889 g
->t_maxrows
+= count
;
891 F_SET(g
, SC_SCR_REFORMAT
| SC_STATUS
);
894 s
->t_maxrows
-= count
;
895 if (s
->t_minrows
> s
->t_maxrows
)
896 s
->t_minrows
= s
->t_maxrows
;
898 F_SET(s
, SC_SCR_REFORMAT
| SC_STATUS
);
901 list
[0] = g
; list
[1] = s
;
902 gp
->scr_discard(0, list
);
909 * Get the specified background screen, or, if name is NULL, the first
913 vs_getbg(SCR
*sp
, char *name
)
921 /* If name is NULL, return the first background screen on the list. */
923 return (TAILQ_FIRST(gp
->hq
));
925 /* Search for a full match. */
926 TAILQ_FOREACH(nsp
, gp
->hq
, q
)
927 if (!strcmp(nsp
->frp
->name
, name
))
932 /* Search for a last-component match. */
933 TAILQ_FOREACH(nsp
, gp
->hq
, q
) {
934 if ((p
= strrchr(nsp
->frp
->name
, '/')) == NULL
)
938 if (!strcmp(p
, name
))