1156230Smux/*-
2156230Smux * Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org>
3156230Smux * All rights reserved.
4156230Smux *
5156230Smux * Redistribution and use in source and binary forms, with or without
6156230Smux * modification, are permitted provided that the following conditions
7156230Smux * are met:
8156230Smux * 1. Redistributions of source code must retain the above copyright
9156230Smux *    notice, this list of conditions and the following disclaimer.
10156230Smux * 2. Redistributions in binary form must reproduce the above copyright
11156230Smux *    notice, this list of conditions and the following disclaimer in the
12156230Smux *    documentation and/or other materials provided with the distribution.
13156230Smux *
14156230Smux * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15156230Smux * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16156230Smux * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17156230Smux * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18156230Smux * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19156230Smux * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20156230Smux * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21156230Smux * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22156230Smux * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23156230Smux * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24156230Smux * SUCH DAMAGE.
25156230Smux *
26156230Smux * $FreeBSD$
27156230Smux */
28156230Smux
29156230Smux#include <assert.h>
30156230Smux#include <stdlib.h>
31156230Smux#include <string.h>
32156230Smux
33156230Smux#include "misc.h"
34156230Smux#include "pathcomp.h"
35156230Smux
36156230Smuxstruct pathcomp {
37156230Smux	char *target;
38156230Smux	size_t targetlen;
39156230Smux	char *trashed;
40156230Smux	char *prev;
41156230Smux	size_t prevlen;
42156230Smux	size_t goal;
43156230Smux	size_t curlen;
44156230Smux};
45156230Smux
46156230Smuxstruct pathcomp	*
47156230Smuxpathcomp_new(void)
48156230Smux{
49156230Smux	struct pathcomp *pc;
50156230Smux
51156230Smux	pc = xmalloc(sizeof(struct pathcomp));
52156230Smux	pc->curlen = 0;
53156230Smux	pc->target = NULL;
54156230Smux	pc->targetlen = 0;
55156230Smux	pc->trashed = NULL;
56156230Smux	pc->prev = NULL;
57156230Smux	pc->prevlen = 0;
58156230Smux	return (pc);
59156230Smux}
60156230Smux
61156230Smuxint
62156230Smuxpathcomp_put(struct pathcomp *pc, int type, char *path)
63156230Smux{
64156230Smux	char *cp;
65156230Smux
66156230Smux	assert(pc->target == NULL);
67156230Smux	if (*path == '/')
68156230Smux		return (-1);
69156230Smux
70156230Smux	switch (type) {
71156230Smux	case PC_DIRDOWN:
72156230Smux		pc->target = path;
73156230Smux		pc->targetlen = strlen(path);
74156230Smux		break;
75156230Smux	case PC_FILE:
76156230Smux	case PC_DIRUP:
77156230Smux		cp = strrchr(path, '/');
78156230Smux		pc->target = path;
79156230Smux		if (cp != NULL)
80156230Smux			pc->targetlen = cp - path;
81156230Smux		else
82156230Smux			pc->targetlen = 0;
83156230Smux		break;
84156230Smux	}
85156230Smux	if (pc->prev != NULL)
86156230Smux		pc->goal = commonpathlength(pc->prev, pc->prevlen, pc->target,
87156230Smux		    pc->targetlen);
88156230Smux	else
89156230Smux		pc->goal = 0;
90156230Smux	if (pc->curlen == pc->goal)	/* No need to go up. */
91156230Smux		pc->goal = pc->targetlen;
92156230Smux	return (0);
93156230Smux}
94156230Smux
95156230Smuxint
96156230Smuxpathcomp_get(struct pathcomp *pc, int *type, char **name)
97156230Smux{
98156230Smux	char *cp;
99156230Smux	size_t slashpos, start;
100156230Smux
101156230Smux	if (pc->curlen > pc->goal) {		/* Going up. */
102156230Smux		assert(pc->prev != NULL);
103156230Smux		pc->prev[pc->curlen] = '\0';
104156230Smux		cp = pc->prev + pc->curlen - 1;
105156230Smux		while (cp >= pc->prev) {
106156230Smux			if (*cp == '/')
107156230Smux				break;
108156230Smux			cp--;
109156230Smux		}
110156230Smux		if (cp >= pc->prev)
111156230Smux			slashpos = cp - pc->prev;
112156230Smux		else
113156230Smux			slashpos = 0;
114156230Smux		pc->curlen = slashpos;
115156230Smux		if (pc->curlen <= pc->goal) {	/* Done going up. */
116156230Smux			assert(pc->curlen == pc->goal);
117156230Smux			pc->goal = pc->targetlen;
118156230Smux		}
119156230Smux		*type = PC_DIRUP;
120156230Smux		*name = pc->prev;
121156230Smux		return (1);
122156230Smux	} else if (pc->curlen < pc->goal) {	/* Going down. */
123156230Smux		/* Restore the previously overwritten '/' character. */
124156230Smux		if (pc->trashed != NULL) {
125156230Smux			*pc->trashed = '/';
126156230Smux			pc->trashed = NULL;
127156230Smux		}
128156230Smux		if (pc->curlen == 0)
129156230Smux			start = pc->curlen;
130156230Smux		else
131156230Smux			start = pc->curlen + 1;
132156230Smux		slashpos = start;
133156230Smux		while (slashpos < pc->goal) {
134156230Smux			if (pc->target[slashpos] == '/')
135156230Smux				break;
136156230Smux			slashpos++;
137156230Smux		}
138156230Smux		if (pc->target[slashpos] != '\0') {
139156230Smux			assert(pc->target[slashpos] == '/');
140156230Smux			pc->trashed = pc->target + slashpos;
141156230Smux			pc->target[slashpos] = '\0';
142156230Smux		}
143156230Smux		pc->curlen = slashpos;
144156230Smux		*type = PC_DIRDOWN;
145156230Smux		*name = pc->target;
146156230Smux		return (1);
147156230Smux	} else {	/* Done. */
148156230Smux		if (pc->target != NULL) {
149156230Smux			if (pc->trashed != NULL) {
150156230Smux				*pc->trashed = '/';
151156230Smux				pc->trashed = NULL;
152156230Smux			}
153156230Smux			if (pc->prev != NULL)
154156230Smux				free(pc->prev);
155156230Smux			pc->prev = xmalloc(pc->targetlen + 1);
156156230Smux			memcpy(pc->prev, pc->target, pc->targetlen);
157156230Smux			pc->prev[pc->targetlen] = '\0';
158156230Smux			pc->prevlen = pc->targetlen;
159156230Smux			pc->target = NULL;
160156230Smux			pc->targetlen = 0;
161156230Smux		}
162156230Smux		return (0);
163156230Smux	}
164156230Smux}
165156230Smux
166156230Smuxvoid
167156230Smuxpathcomp_finish(struct pathcomp *pc)
168156230Smux{
169156230Smux
170156230Smux	pc->target = NULL;
171156230Smux	pc->targetlen = 0;
172156230Smux	pc->goal = 0;
173156230Smux}
174156230Smux
175156230Smuxvoid
176156230Smuxpathcomp_free(struct pathcomp *pc)
177156230Smux{
178156230Smux
179156230Smux	if (pc->prev != NULL)
180156230Smux		free(pc->prev);
181156230Smux	free(pc);
182156230Smux}
183