pathcomp.c revision 267654
190075Sobrien/*-
2132718Skan * Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org>
390075Sobrien * All rights reserved.
490075Sobrien *
5132718Skan * Redistribution and use in source and binary forms, with or without
690075Sobrien * modification, are permitted provided that the following conditions
7132718Skan * are met:
8132718Skan * 1. Redistributions of source code must retain the above copyright
9132718Skan *    notice, this list of conditions and the following disclaimer.
10132718Skan * 2. Redistributions in binary form must reproduce the above copyright
1190075Sobrien *    notice, this list of conditions and the following disclaimer in the
12132718Skan *    documentation and/or other materials provided with the distribution.
13132718Skan *
14132718Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15132718Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1690075Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17132718Skan * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18132718Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19169706Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20169706Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2190075Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2290075Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2390075Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24209867Snwhitehorn * SUCH DAMAGE.
25209867Snwhitehorn *
26209867Snwhitehorn * $FreeBSD: releng/9.3/usr.bin/csup/pathcomp.c 204556 2010-03-02 07:26:07Z lulf $
27209867Snwhitehorn */
28209867Snwhitehorn
29209867Snwhitehorn#include <assert.h>
30209867Snwhitehorn#include <stdlib.h>
31209867Snwhitehorn#include <string.h>
3290075Sobrien
33209867Snwhitehorn#include "misc.h"
34147823Sscottl#include "pathcomp.h"
35209867Snwhitehorn
36209867Snwhitehornstruct pathcomp {
37209867Snwhitehorn	char *target;
38209867Snwhitehorn	size_t targetlen;
39209867Snwhitehorn	char *trashed;
40218824Snwhitehorn	char *prev;
41218824Snwhitehorn	size_t prevlen;
42218824Snwhitehorn	size_t goal;
43218824Snwhitehorn	size_t curlen;
44218824Snwhitehorn};
45218824Snwhitehorn
46218824Snwhitehornstruct pathcomp	*
47209867Snwhitehornpathcomp_new(void)
48209867Snwhitehorn{
49209867Snwhitehorn	struct pathcomp *pc;
50209867Snwhitehorn
51209867Snwhitehorn	pc = xmalloc(sizeof(struct pathcomp));
52209867Snwhitehorn	pc->curlen = 0;
53209867Snwhitehorn	pc->target = NULL;
54209867Snwhitehorn	pc->targetlen = 0;
55209867Snwhitehorn	pc->trashed = NULL;
56209867Snwhitehorn	pc->prev = NULL;
57209867Snwhitehorn	pc->prevlen = 0;
58209867Snwhitehorn	return (pc);
59209867Snwhitehorn}
60209867Snwhitehorn
61209867Snwhitehornint
62209867Snwhitehornpathcomp_put(struct pathcomp *pc, int type, char *path)
63209867Snwhitehorn{
64209867Snwhitehorn	char *cp;
65209867Snwhitehorn
66209867Snwhitehorn	assert(pc->target == NULL);
67209867Snwhitehorn	if (*path == '/')
68209867Snwhitehorn		return (-1);
69209867Snwhitehorn
70209867Snwhitehorn	switch (type) {
71209867Snwhitehorn	case PC_DIRDOWN:
72209867Snwhitehorn		pc->target = path;
73209867Snwhitehorn		pc->targetlen = strlen(path);
74209867Snwhitehorn		break;
75209867Snwhitehorn	case PC_FILE:
76209867Snwhitehorn	case PC_DIRUP:
77209867Snwhitehorn		cp = strrchr(path, '/');
78209867Snwhitehorn		pc->target = path;
79209867Snwhitehorn		if (cp != NULL)
80209867Snwhitehorn			pc->targetlen = cp - path;
81209867Snwhitehorn		else
82209867Snwhitehorn			pc->targetlen = 0;
83209867Snwhitehorn		break;
84209867Snwhitehorn	}
85209867Snwhitehorn	if (pc->prev != NULL)
86209867Snwhitehorn		pc->goal = commonpathlength(pc->prev, pc->prevlen, pc->target,
87209867Snwhitehorn		    pc->targetlen);
88209867Snwhitehorn	else
89209867Snwhitehorn		pc->goal = 0;
90209867Snwhitehorn	if (pc->curlen == pc->goal)	/* No need to go up. */
91209867Snwhitehorn		pc->goal = pc->targetlen;
92209867Snwhitehorn	return (0);
93209867Snwhitehorn}
94209867Snwhitehorn
95209867Snwhitehornint
96209867Snwhitehornpathcomp_get(struct pathcomp *pc, int *type, char **name)
97209867Snwhitehorn{
98209867Snwhitehorn	char *cp;
99209867Snwhitehorn	size_t slashpos, start;
100209867Snwhitehorn
101209867Snwhitehorn	if (pc->curlen > pc->goal) {		/* Going up. */
102209867Snwhitehorn		assert(pc->prev != NULL);
103209867Snwhitehorn		pc->prev[pc->curlen] = '\0';
104209867Snwhitehorn		cp = pc->prev + pc->curlen - 1;
105209867Snwhitehorn		while (cp >= pc->prev) {
106209867Snwhitehorn			if (*cp == '/')
107209867Snwhitehorn				break;
108209867Snwhitehorn			cp--;
109209867Snwhitehorn		}
110209867Snwhitehorn		if (cp >= pc->prev)
111209867Snwhitehorn			slashpos = cp - pc->prev;
11290075Sobrien		else
11390075Sobrien			slashpos = 0;
11490075Sobrien		pc->curlen = slashpos;
11590075Sobrien		if (pc->curlen <= pc->goal) {	/* Done going up. */
11690075Sobrien			assert(pc->curlen == pc->goal);
11790075Sobrien			pc->goal = pc->targetlen;
11890075Sobrien		}
11990075Sobrien		*type = PC_DIRUP;
12090075Sobrien		*name = pc->prev;
12190075Sobrien		return (1);
12290075Sobrien	} else if (pc->curlen < pc->goal) {	/* Going down. */
12390075Sobrien		/* Restore the previously overwritten '/' character. */
12490075Sobrien		if (pc->trashed != NULL) {
12590075Sobrien			*pc->trashed = '/';
12690075Sobrien			pc->trashed = NULL;
127117395Skan		}
128132718Skan		if (pc->curlen == 0)
129117395Skan			start = pc->curlen;
130117395Skan		else
13190075Sobrien			start = pc->curlen + 1;
132117395Skan		slashpos = start;
13390075Sobrien		while (slashpos < pc->goal) {
13490075Sobrien			if (pc->target[slashpos] == '/')
13590075Sobrien				break;
13690075Sobrien			slashpos++;
13790075Sobrien		}
13890075Sobrien		if (pc->target[slashpos] != '\0') {
139117395Skan			assert(pc->target[slashpos] == '/');
140209867Snwhitehorn			pc->trashed = pc->target + slashpos;
141117395Skan			pc->target[slashpos] = '\0';
142209867Snwhitehorn		}
143209867Snwhitehorn		pc->curlen = slashpos;
144209867Snwhitehorn		*type = PC_DIRDOWN;
14590075Sobrien		*name = pc->target;
14690075Sobrien		return (1);
14790075Sobrien	} else {	/* Done. */
14890075Sobrien		if (pc->target != NULL) {
14990075Sobrien			if (pc->trashed != NULL) {
15090075Sobrien				*pc->trashed = '/';
15190075Sobrien				pc->trashed = NULL;
15290075Sobrien			}
15390075Sobrien			if (pc->prev != NULL)
15490075Sobrien				free(pc->prev);
15590075Sobrien			pc->prev = xmalloc(pc->targetlen + 1);
15690075Sobrien			memcpy(pc->prev, pc->target, pc->targetlen);
15790075Sobrien			pc->prev[pc->targetlen] = '\0';
15890075Sobrien			pc->prevlen = pc->targetlen;
15990075Sobrien			pc->target = NULL;
16090075Sobrien			pc->targetlen = 0;
161209867Snwhitehorn		}
162209867Snwhitehorn		return (0);
163209867Snwhitehorn	}
164209867Snwhitehorn}
165209867Snwhitehorn
166209867Snwhitehornvoid
167209867Snwhitehornpathcomp_finish(struct pathcomp *pc)
168209867Snwhitehorn{
169209867Snwhitehorn
170209867Snwhitehorn	pc->target = NULL;
171209867Snwhitehorn	pc->targetlen = 0;
172209867Snwhitehorn	pc->goal = 0;
173209867Snwhitehorn}
174209867Snwhitehorn
175209867Snwhitehornvoid
176209867Snwhitehornpathcomp_free(struct pathcomp *pc)
177209867Snwhitehorn{
178209867Snwhitehorn
179209867Snwhitehorn	if (pc->prev != NULL)
180209867Snwhitehorn		free(pc->prev);
181209867Snwhitehorn	free(pc);
182209867Snwhitehorn}
183209867Snwhitehorn