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