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: stable/10/usr.bin/csup/pathcomp.c 204556 2010-03-02 07:26:07Z lulf $
27  */
28 
29 #include <assert.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #include "misc.h"
34 #include "pathcomp.h"
35 
36 struct 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 
46 struct pathcomp	*
pathcomp_new(void)47 pathcomp_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 
61 int
pathcomp_put(struct pathcomp * pc,int type,char * path)62 pathcomp_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 
95 int
pathcomp_get(struct pathcomp * pc,int * type,char ** name)96 pathcomp_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 
166 void
pathcomp_finish(struct pathcomp * pc)167 pathcomp_finish(struct pathcomp *pc)
168 {
169 
170 	pc->target = NULL;
171 	pc->targetlen = 0;
172 	pc->goal = 0;
173 }
174 
175 void
pathcomp_free(struct pathcomp * pc)176 pathcomp_free(struct pathcomp *pc)
177 {
178 
179 	if (pc->prev != NULL)
180 		free(pc->prev);
181 	free(pc);
182 }
183