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