1 /* path/merge.c - Merge one path into another.
2  * Copyright (C) 2001,2005  Bruce Guenter <bruce@untroubled.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library 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 GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  */
18 #include <string.h>
19 #include "path.h"
20 
path_merge_part(str * path,const char * part,unsigned long len)21 static int path_merge_part(str* path, const char* part, unsigned long len)
22 {
23   if (part[0] == '.') {
24     /* "." parts get dropped */
25     if (len == 1) return 1;
26     /* ".." parts cause truncation */
27     if (len == 2 && part[1] == '.') {
28       int sep = str_findlast(path, '/');
29       /* Don't truncate a leading '/' */
30       if (sep == 0) sep = 1;
31       /* If no '/' found, empty the string */
32       else if (sep == -1) sep = 0;
33       str_truncate(path, sep);
34       return 1;
35     }
36   }
37   if (path->len)
38     if (path->s[path->len-1] != '/')
39       if (!str_catc(path, '/')) return 0;
40   return str_catb(path, part, len);
41 }
42 
43 /** Merge two paths together.
44 
45 This function takes a starting path, and merges the second path into it.
46 If the second path starts with a "/", it replaces the existing path
47 completely.  If the second path contains any ".." components, the
48 preceding directory component in the current path (if any) is removed.
49 All "." components are removed.
50 
51 The effects of this function are equivalent to changing directory to the
52 first path and then using the second one.
53 */
path_merge(str * path,const char * start)54 int path_merge(str* path, const char* start)
55 {
56   /* If the path to merge in starts with a "/", drop the previous path */
57   if (*start == '/')
58     if (!str_copys(path, "/")) return 0;
59   /* This should possibly use a striter... */
60   while (*start) {
61     const char* end;
62     while (*start == '/') ++start;
63     if ((end = strchr(start, '/')) != 0) {
64       if (!path_merge_part(path, start, end - start)) return 0;
65       start = end + 1;
66     }
67     else {
68       if (!path_merge_part(path, start, strlen(start))) return 0;
69       break;
70     }
71   }
72   return 1;
73 }
74 
75 #ifdef SELFTEST_MAIN
76 #include "path.h"
77 static str path;
showpath(void)78 void showpath(void) { obuf_putstr(&outbuf, &path); NL(); }
79 MAIN
80 {
81   str_copys(&path, "/");
82   path_merge(&path, "a"); showpath();
83   path_merge(&path, "b"); showpath();
84   path_merge(&path, "../c"); showpath();
85   path_merge(&path, "/d"); showpath();
86   str_copys(&path, "");
87   path_merge(&path, "a"); showpath();
88   path_merge(&path, "b"); showpath();
89   path_merge(&path, "../c"); showpath();
90   path_merge(&path, "/d"); showpath();
91 }
92 
93 #endif
94 #ifdef SELFTEST_EXP
95 /a
96 /a/b
97 /a/c
98 /d
99 a
100 a/b
101 a/c
102 /d
103 #endif
104