1 /* pathdup.c - reproduces a path stripping /../ /./ and resolving symlinks
2 Copyright (C) 1996-2017 Paul Sheer
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17 02111-1307, USA.
18 */
19
20 #include <config.h>
21 #include "global.h"
22 #include "pipe-headers.h"
23 #include <my_string.h>
24 #include "mad.h"
25
26
27 struct comp {
28 struct comp *prev;
29 struct comp *next;
30 char name[2];
31 };
32
comp_last(struct comp * p)33 static struct comp *comp_last (struct comp *p)
34 {
35 while (p->next)
36 p = p->next;
37 return p;
38 }
39
comp_first(struct comp * p)40 static struct comp *comp_first (struct comp *p)
41 {
42 while (p->prev)
43 p = p->prev;
44 return p;
45 }
46
comp_cat(struct comp * s,struct comp * t)47 static inline struct comp *comp_cat (struct comp *s, struct comp *t)
48 {
49 s = comp_last (s);
50 t = comp_first (t);
51 s->next = t;
52 t->prev = s;
53 return comp_first (s);
54 }
55
comp_insert(struct comp * p,struct comp * s)56 static inline struct comp *comp_insert (struct comp *p, struct comp *s)
57 {
58 struct comp *t;
59 t = comp_last (s);
60 s = comp_first (s);
61 if (p->prev)
62 p->prev->next = s;
63 if (p->next)
64 p->next->prev = t;
65 t->next = p->next;
66 s->prev = p->prev;
67 memset (p, 0, sizeof (*p));
68 free (p);
69 return t;
70 }
71
comp_replace(struct comp * p,struct comp * s)72 static inline struct comp *comp_replace (struct comp *p, struct comp *s)
73 {
74 struct comp *t, *prev, *r;
75 t = comp_last (s);
76 if (p->next)
77 p->next->prev = t;
78 t->next = p->next;
79 for (r = p; r; r = prev) {
80 prev = r->prev;
81 memset (r, 0, sizeof (*r));
82 free (r);
83 }
84 return t;
85 }
86
comp_free(struct comp * p)87 static inline void comp_free (struct comp *p)
88 {
89 struct comp *next;
90 p = comp_first (p);
91 for (; p; p = next) {
92 next = p->next;
93 memset (p, 0, sizeof (*p));
94 free (p);
95 }
96 }
97
98 #define COMP_DUMP(p) \
99 if (u == p) \
100 u = p->next; \
101 if (p->next) \
102 p->next->prev = p->prev; \
103 if (p->prev) \
104 p->prev->next = p->next; \
105 memset (p, 0, sizeof (*p)); \
106 free (p);
107
108
109 /* dump .. . and nothings, but remember the place in the list of p */
comp_strip(struct comp * p)110 static struct comp *comp_strip (struct comp *p)
111 {
112 struct comp *u, *next;
113 u = comp_first (p);
114 for (p = u; p; p = next) {
115 next = p->next;
116 if (!*p->name || !strcmp (p->name, ".")) {
117 COMP_DUMP (p);
118 } else if (!strcmp (p->name, "..")) {
119 struct comp *t;
120 if ((t = p->prev)) {
121 COMP_DUMP (t);
122 }
123 COMP_DUMP (p);
124 }
125 }
126 if (!u) {
127 /* mustn't strip everything */
128 u = malloc (sizeof (struct comp));
129 memset (u, 0, sizeof (struct comp));
130 }
131 return u;
132 }
133
134 /* split into a list along / */
135 #ifdef HAVE_MAD
mad_comp_combine(struct comp * s,char * file,int line)136 static char *mad_comp_combine (struct comp *s, char *file, int line)
137 #define comp_combine(s) mad_comp_combine(s, __FILE__, __LINE__)
138 #else
139 static char *comp_combine (struct comp *s)
140 #endif
141 {
142 int n;
143 struct comp *t, *f;
144 char *p, *r;
145 f = comp_first (s);
146 for (n = 0, t = f; t != s->next; t = t->next)
147 n += strlen (t->name) + 1;
148 #ifdef HAVE_MAD
149 r = mad_alloc (n + 2, file, line);
150 #else
151 r = malloc (n + 2);
152 #endif
153 for (p = r, t = f; t != s->next; t = t->next) {
154 *p++ = '/';
155 strcpy (p, t->name);
156 p += strlen (p);
157 }
158 return r;
159 }
160
161 /* split into a list along / */
comp_tize(char * s)162 static struct comp *comp_tize (char *s)
163 {
164 struct comp *u, *p = 0;
165 char *t;
166 int done = 0;
167 while (!done) {
168 int l;
169 t = (char *) strchr (s, '/');
170 if (!t) {
171 t = s + strlen (s);
172 done = 1;
173 }
174 l = (unsigned long) t - (unsigned long) s;
175 u = malloc (sizeof (struct comp) + l);
176 u->prev = p;
177 u->next = 0;
178 if (p)
179 p->next = u;
180 p = u;
181 memcpy (u->name, s, l);
182 u->name[l] = '\0';
183 s = t + 1;
184 }
185 return p;
186 }
187
comp_readlink(struct comp * p)188 static inline char *comp_readlink (struct comp *p)
189 {
190 char *s;
191 int r;
192 static char buf[2048];
193 s = comp_combine (p);
194 r = readlink (s, buf, 2047);
195 if (r == -1 && errno == EINVAL) {
196 free (s);
197 return "";
198 }
199 if (r == -1) {
200 free (s);
201 return 0;
202 }
203 buf[r] = '\0';
204 free (s);
205 return buf;
206 }
207
208 /* if there is an error, this just returns as far as it got */
resolve_symlink(struct comp * path)209 static inline struct comp *resolve_symlink (struct comp *path)
210 {
211 int i;
212 struct comp *t;
213 path = comp_strip (comp_first (path));
214 path = comp_last (path);
215 for (i = 0;; i++) {
216 char *l;
217 if (i >= 1000)
218 break;
219 l = comp_readlink (path);
220 if (!l)
221 break;
222 if (l[0] == '/') {
223 /* absolute symlink */
224 t = comp_tize (l);
225 path = comp_replace (path, t);
226 path = comp_strip (path);
227 path = comp_last (path);
228 continue;
229 } else if (*l) {
230 /* relative symlink */
231 t = comp_tize (l);
232 path = comp_insert (path, t);
233 path = comp_strip (path);
234 path = comp_last (path);
235 continue;
236 } else if (path->prev) {
237 /* not a symlink */
238 path = path->prev;
239 continue;
240 }
241 break;
242 }
243 return path;
244 }
245
246
247 extern char *home_dir;
248
249 #ifdef HAVE_MAD
mad_pathdup(char * p,char * file,int line)250 char *mad_pathdup (char *p, char *file, int line)
251 #else
252 char *pathdup (char *p)
253 #endif
254 {
255 struct comp *s;
256 s = comp_tize (p);
257 if (!strcmp (comp_first (s)->name, "~")) {
258 s = comp_replace (comp_first (s), comp_tize (home_dir));
259 } else if (*p != '/') {
260 char *cwd;
261 cwd = malloc (2048);
262 #ifdef HAVE_GETCWD
263 getcwd (cwd, 2047);
264 #else
265 getwd (cwd);
266 #endif
267 s = comp_cat (comp_tize (cwd), comp_tize (p));
268 free (cwd);
269 }
270 s = resolve_symlink (s);
271 #ifdef HAVE_MAD
272 p = mad_comp_combine (comp_last (s), file, line);
273 #else
274 p = comp_combine (comp_last (s));
275 #endif
276 comp_free (s);
277 return p;
278 }
279
280 #if 0
281 char *home_dir = "/root";
282
283 int main (int argc, char **argv)
284 {
285 printf ("%s\n", pathdup (argv[1]));
286 return 0;
287 }
288 #endif
289
290