3dd552c146a2115b3ffe0a2d48392ed88a908b34
[mmh] / sbr / path.c
1 /*
2 ** path.c -- return a pathname
3 **
4 ** This code is Copyright (c) 2002, by the authors of nmh.  See the
5 ** COPYRIGHT file in the root directory of the nmh distribution for
6 ** complete copyright information.
7 */
8
9 #include <h/mh.h>
10
11 static char *pwds;
12
13
14 /*
15 **  Compactify an absolute path name by removing unneccessary parts.
16 **  Removes trailing slashes, but not if it would empty the string then.
17 **  Modifies f.
18 */
19 static void
20 packpath(char *f)
21 {
22         char *cp, *dp;
23         int abspath;
24
25         if (!f || !*f) {
26                 return;
27         }
28         abspath = (*f == '/');
29
30         for (cp=f; *cp; ) {
31                 if (*cp != '/') {
32                         /* Skip. Interesting places are only after slashes. */
33                         /* We don't care about "./" beginnings */
34                         cp++;
35                         continue;
36                 }
37
38                 /* Let's see what follows the slash ... */
39                 switch (*++cp) {
40                 case '\0':
41                         *--cp = '\0';
42                         continue;  /* ... and thus exit the loop */
43
44                 case '/':
45                         /* reduce subsequent slashes to one */
46                         for (dp = cp; *dp == '/'; dp++) {
47                                 continue;
48                         }
49                         strcpy(cp, dp);
50                         cp--;
51                         continue;  /* ... at the slash */
52
53                 case '.':
54                         if (cp[1] == '/' || cp[1] == '\0') {
55                                 /* one-dot element */
56                                 strcpy(cp-1, cp+1);
57                                 cp--;
58                                 continue;
59                         } else if ((strncmp(cp, "../", 3) == 0) ||
60                                         (strcmp(cp, "..") == 0)) {
61                                 /* dot-dot element */
62                                 /* crop out previous path element */
63                                 for (dp=cp-2; dp>f && *dp!='/'; dp--) {
64                                         continue;
65                                 }
66                                 if (dp < f) {
67                                         /* path starts with "/.." */
68                                         dp = f;
69                                 }
70                                 strcpy(dp, cp+2);
71                                 cp = dp;
72                                 continue;
73                         } else {
74                                 /* a normal hidden file */
75                                 cp++;
76                                 continue;
77                         }
78
79                 default:
80                         /* nothing special */
81                         cp++;
82                         continue;
83                 }
84         }
85
86         if (!strlen(f)) {
87                 /* We have removed everything, but need something. */
88                 strcpy(f, abspath ? "/" : ".");
89         }
90 }
91
92
93 /*
94 **
95 */
96 static char *
97 expath(char *name, int type)
98 {
99         char *cp, *ep;
100         char buffer[BUFSIZ];
101
102         if (type == TSUBCWF) {
103                 /* convert TSUBCWF to TFOLDER */
104                 snprintf(buffer, sizeof(buffer), "%s/%s",
105                                 getfolder(FCUR), name);
106                 name = m_mailpath(buffer);
107                 packpath(name);
108                 snprintf(buffer, sizeof(buffer), "%s/", m_maildir(""));
109                 if (isprefix(buffer, name)) {
110                         cp = name;
111                         name = getcpy(name + strlen(buffer));
112                         free(cp);
113                 }
114                 type = TFOLDER;
115         }
116
117         if (*name == '/') {
118                 return getcpy(name);
119         }
120
121         if (type == TFOLDER &&
122                         (strncmp(name, "./", 2) && strcmp(name, ".") &&
123                         strcmp(name, "..") && strncmp(name, "../", 3))) {
124                 /*
125                 ** FIXME: Seems as if this check does not catch names like:
126                 **        ``foo/../../..''.
127                 */
128                 return getcpy(name);
129         }
130
131         if (pwds == NULL) {
132                 pwds = pwd();
133         }
134
135         if (strcmp(name, ".") == 0 || strcmp(name, "./") == 0) {
136                 return getcpy(pwds);
137         }
138
139         ep = pwds + strlen(pwds);
140         if ((cp = strrchr(pwds, '/')) == NULL) {
141                 cp = ep;
142         } else if (cp == pwds)  {
143                 cp++;
144         }
145
146         if (strncmp(name, "./", 2) == 0) {
147                 name += 2;
148         }
149
150         if (strcmp(name, "..") == 0 || strcmp(name, "../") == 0) {
151                 snprintf(buffer, sizeof(buffer), "%.*s",
152                                 (int)(cp - pwds), pwds);
153                 return getcpy(buffer);
154         }
155
156         if (strncmp(name, "../", 3) == 0) {
157                 name += 3;
158         } else {
159                 cp = ep;
160         }
161
162         snprintf(buffer, sizeof(buffer), "%.*s/%s",
163                         (int)(cp - pwds), pwds, name);
164         return getcpy(buffer);
165 }
166
167
168 /*
169 ** Return a path name
170 */
171 char *
172 path(char *name, int type)
173 {
174         char *cp, *ep;
175
176         if ((cp = expath(name, type)) &&
177                         (ep = cp+strlen(cp)-1) > cp &&
178                         *ep == '/') {
179                 *ep = '\0';
180         }
181
182         return cp;
183 }
184
185
186 /*
187 ** Call path() appropriately for ``+folder'' or ``@folder''
188 */
189 char *
190 pluspath(char *name)
191 {
192         switch (*name) {
193         case '+':
194                 return path(name+1, TFOLDER);
195         case '@':
196                 return path(name+1, TSUBCWF);
197         default:
198                 return path(name, TFILE);
199         }
200 }