1/*-
2 * Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD$
27 */
28
29#include <assert.h>
30#include <stdlib.h>
31#include <string.h>
32
33#include "misc.h"
34#include "pathcomp.h"
35
36struct pathcomp {
37	char *target;
38	size_t targetlen;
39	char *trashed;
40	char *prev;
41	size_t prevlen;
42	size_t goal;
43	size_t curlen;
44};
45
46struct pathcomp	*
47pathcomp_new(void)
48{
49	struct pathcomp *pc;
50
51	pc = xmalloc(sizeof(struct pathcomp));
52	pc->curlen = 0;
53	pc->target = NULL;
54	pc->targetlen = 0;
55	pc->trashed = NULL;
56	pc->prev = NULL;
57	pc->prevlen = 0;
58	return (pc);
59}
60
61int
62pathcomp_put(struct pathcomp *pc, int type, char *path)
63{
64	char *cp;
65
66	assert(pc->target == NULL);
67	if (*path == '/')
68		return (-1);
69
70	switch (type) {
71	case PC_DIRDOWN:
72		pc->target = path;
73		pc->targetlen = strlen(path);
74		break;
75	case PC_FILE:
76	case PC_DIRUP:
77		cp = strrchr(path, '/');
78		pc->target = path;
79		if (cp != NULL)
80			pc->targetlen = cp - path;
81		else
82			pc->targetlen = 0;
83		break;
84	}
85	if (pc->prev != NULL)
86		pc->goal = commonpathlength(pc->prev, pc->prevlen, pc->target,
87		    pc->targetlen);
88	else
89		pc->goal = 0;
90	if (pc->curlen == pc->goal)	/* No need to go up. */
91		pc->goal = pc->targetlen;
92	return (0);
93}
94
95int
96pathcomp_get(struct pathcomp *pc, int *type, char **name)
97{
98	char *cp;
99	size_t slashpos, start;
100
101	if (pc->curlen > pc->goal) {		/* Going up. */
102		assert(pc->prev != NULL);
103		pc->prev[pc->curlen] = '\0';
104		cp = pc->prev + pc->curlen - 1;
105		while (cp >= pc->prev) {
106			if (*cp == '/')
107				break;
108			cp--;
109		}
110		if (cp >= pc->prev)
111			slashpos = cp - pc->prev;
112		else
113			slashpos = 0;
114		pc->curlen = slashpos;
115		if (pc->curlen <= pc->goal) {	/* Done going up. */
116			assert(pc->curlen == pc->goal);
117			pc->goal = pc->targetlen;
118		}
119		*type = PC_DIRUP;
120		*name = pc->prev;
121		return (1);
122	} else if (pc->curlen < pc->goal) {	/* Going down. */
123		/* Restore the previously overwritten '/' character. */
124		if (pc->trashed != NULL) {
125			*pc->trashed = '/';
126			pc->trashed = NULL;
127		}
128		if (pc->curlen == 0)
129			start = pc->curlen;
130		else
131			start = pc->curlen + 1;
132		slashpos = start;
133		while (slashpos < pc->goal) {
134			if (pc->target[slashpos] == '/')
135				break;
136			slashpos++;
137		}
138		if (pc->target[slashpos] != '\0') {
139			assert(pc->target[slashpos] == '/');
140			pc->trashed = pc->target + slashpos;
141			pc->target[slashpos] = '\0';
142		}
143		pc->curlen = slashpos;
144		*type = PC_DIRDOWN;
145		*name = pc->target;
146		return (1);
147	} else {	/* Done. */
148		if (pc->target != NULL) {
149			if (pc->trashed != NULL) {
150				*pc->trashed = '/';
151				pc->trashed = NULL;
152			}
153			if (pc->prev != NULL)
154				free(pc->prev);
155			pc->prev = xmalloc(pc->targetlen + 1);
156			memcpy(pc->prev, pc->target, pc->targetlen);
157			pc->prev[pc->targetlen] = '\0';
158			pc->prevlen = pc->targetlen;
159			pc->target = NULL;
160			pc->targetlen = 0;
161		}
162		return (0);
163	}
164}
165
166void
167pathcomp_finish(struct pathcomp *pc)
168{
169
170	pc->target = NULL;
171	pc->targetlen = 0;
172	pc->goal = 0;
173}
174
175void
176pathcomp_free(struct pathcomp *pc)
177{
178
179	if (pc->prev != NULL)
180		free(pc->prev);
181	free(pc);
182}
183