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