4641bae88368988aa32f9a45babbd38a408309be
[oweals/busybox.git] / libbb / simplify_path.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * simplify_path implementation for busybox
4  *
5  *
6  * Copyright (C) 2001  Vladimir N. Oleynik <dzo@simtreas.ru>
7  *
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22  *
23  */
24
25 #include <stdlib.h>
26
27 #include "libbb.h"
28
29 static inline char *strcpy_overlap(char *dst, const char *src)
30 {
31         char *ptr = dst;
32
33         do *dst++ = *src; while (*src++);
34         return ptr;
35 }
36
37 char *simplify_path(const char *path)
38 {
39         char *s, *start, *next;
40
41         if (path[0] == '/')
42                start = xstrdup(path);
43         else {
44                s = xgetcwd(NULL);
45                start = concat_path_file(s, path);
46                free(s);
47         }
48         s = start;
49         /* remove . and .. */
50         while(*s) {
51                 if(*s++ == '/' && (*s == '/' || *s == 0)) {
52                         /* remove duplicate and trailing slashes */
53                         s = strcpy_overlap(s-1, s);
54                 }
55                 else if(*(s-1) == '.' && *(s-2)=='/') {
56                         if(*s == '/' || *s == 0) {
57                                 /* remove . */
58                                 s = strcpy_overlap(s-1, s); /* maybe set // */
59                                 s--;
60                         } else if(*s == '.') {
61                                 next = s+1;     /* set after ".." */
62                                 if(*next == '/' || *next == 0) {  /* "../" */
63                                         if((s-=2) > start)
64                                                 /* skip previous dir */
65                                                 do s--; while(*s != '/');
66                                         /* remove previous dir */
67                                         strcpy_overlap(s, next);
68                                 }
69
70                         }
71                 }
72         }
73         if(start[0]==0) {
74                 start[0]='/';
75                 start[1]=0;
76         }
77         return start;
78 }