Move #include from h/mh.h to source files
[mmh] / sbr / path.c
1 /*
2 ** path.c -- return or convert paths
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 #include <pwd.h>
11 #include <unistd.h>
12
13
14 /*
15 ** Find the location of a format or configuration
16 ** file, and return its absolute pathname.
17 **
18 ** 1) If it begins with ~user, then expand it.
19 ** 2) Next, if already absolute pathname, then leave unchanged.
20 ** 3) Next, check in mmh directory.
21 ** 4) Next, check in mmh `etc' directory.
22 ** 5) As fall-back, return `file' unchanged.
23 */
24 char *
25 etcpath(char *file)
26 {
27         static char epath[PATH_MAX];
28         char *cp;
29         char *pp;
30         struct passwd *pw;
31
32         /* XXX: here was: ``context_read();'' -- why? */
33         if (*file == '~') {
34                 /* Expand `~user' */
35                 if ((cp = strchr(pp = file + 1, '/')))
36                         *cp++ = '\0';
37                 if (*pp == '\0') {
38                         pp = mypath;
39                 } else {
40                         if ((pw = getpwnam(pp)))
41                                 pp = pw->pw_dir;
42                         else {
43                                 if (cp)
44                                         *--cp = '/';
45                                 goto try_it;
46                         }
47                 }
48
49                 snprintf(epath, sizeof epath, "%s/%s", pp, cp ? cp : "");
50                 if (cp)
51                         *--cp = '/';
52
53                 if (access(epath, R_OK) != NOTOK)
54                         return epath;  /* else fall */
55         }
56
57 try_it:
58         if (*file == '/') {
59                 /* absolute pathname, return it */
60                 return file;
61         }
62
63         /* Check mmh directory */
64         snprintf(epath, sizeof epath, "%s/%s", mmhpath, file);
65         if (access(epath, R_OK) != NOTOK)
66                 return epath;
67
68         /* Check nmh `etc' directory */
69         snprintf(epath, sizeof epath, "%s/%s", mhetcdir, file);
70         if  (access(epath, R_OK) != NOTOK)
71                 return epath;
72
73         /* The fall-back */
74         return file;
75 }
76
77
78 /*
79 **  Compactify a path name by removing unneccessary parts.
80 **  Removes trailing slashes. Cares to never remove all characters.
81 **  Modifies f (never enlarges it).
82 **
83 **  FIXME: Cannot use strcpy() as the areas overlap!
84 */
85 static void
86 packpath(char *f)
87 {
88         char *cp, *dp;
89         int abspath;
90
91         if (!f || !*f) {
92                 return;
93         }
94         abspath = (*f == '/');
95
96         for (cp=f; *cp; ) {
97                 if (*cp != '/') {
98                         /* Skip. Interesting places are only after slashes. */
99                         /* We don't care about "./" beginnings */
100                         cp++;
101                         continue;
102                 }
103
104                 /* Let's see what follows the slash ... */
105                 switch (*++cp) {
106                 case '\0':
107                         *--cp = '\0';
108                         continue;  /* ... and thus exit the loop */
109
110                 case '/':
111                         /* reduce subsequent slashes to one */
112                         for (dp = cp; *dp == '/'; dp++) {
113                                 continue;
114                         }
115                         strcpy(cp, dp);
116                         cp--;
117                         continue;  /* ... at the slash */
118
119                 case '.':
120                         if (cp[1] == '/' || cp[1] == '\0') {
121                                 /* one-dot element */
122                                 strcpy(cp-1, cp+1);
123                                 cp--;
124                                 continue;
125                         } else if ((strncmp(cp, "../", 3) == 0) ||
126                                         (strcmp(cp, "..") == 0)) {
127                                 /* dot-dot element */
128                                 /* crop out previous path element */
129                                 for (dp=cp-2; dp>f && *dp!='/'; dp--) {
130                                         continue;
131                                 }
132                                 if (dp < f) {
133                                         /* path starts with "/.." */
134                                         dp = f;
135                                 }
136                                 strcpy(dp, cp+2);
137                                 cp = dp;
138                                 continue;
139                         } else {
140                                 /* a normal hidden file */
141                                 cp++;
142                                 continue;
143                         }
144
145                 default:
146                         /* nothing special */
147                         cp++;
148                         continue;
149                 }
150         }
151
152         if (!strlen(f)) {
153                 /* We have removed everything, but need something. */
154                 strcpy(f, abspath ? "/" : ".");
155         }
156 }
157
158
159
160
161 /*
162 **  Get the default folder
163 **  Return the Inbox profile entry or, as fallback, the compile time default
164 **  Returns a pointer to the abs folpath
165 */
166 char *
167 getdeffol(void)
168 {
169         char *folder = context_find(inbox);
170
171         if (!folder || !*folder) {
172                 folder = defaultfolder;  /* the compile time default */
173         }
174         if (*folder == '+') {
175                 folder++;
176         }
177         return folder;
178 }
179
180
181 /*
182 **  Get the current folder
183 **  Return the Current-Folder context entry or, as fallback, the default folder
184 **  Returns a pointer to the abs folpath
185 **
186 **  Equivalent to: expandfol("@")
187 */
188 char *
189 getcurfol(void)
190 {
191         char *folder = context_find(curfolder);
192
193         if (!folder || !*folder) {
194                 folder = getdeffol();
195         }
196         return folder;
197 }
198
199
200 /*
201 **  Expand folder path
202 **  Convert rel folpaths (@) into abs folpaths
203 **  dir paths are simply passed through
204 **  Returns the abs folpath (without prefix), in static mem
205 **
206 **  TODO: Always copy into the static buffer, or just return the pointer?
207 */
208 char *
209 expandfol(char *f)
210 {
211         static char buf[BUFSIZ];
212
213         if (*f == '@') {
214                 /* f = concat(getcurfol(), "/", f+1, NULL); */
215                 snprintf(buf, sizeof buf, "%s/%s", getcurfol(), f+1);
216
217         } else if (*f == '+') {
218                 strcpy(buf, f+1);
219
220         } else {
221                 strcpy(buf, f);
222         }
223         packpath(buf);
224         return buf;
225 }
226
227
228 /*
229 **  Expand directory path
230 **  Convert rel dirpath into abs dirpath
231 **  The argument is assumed to be a dir path relative to the cwd,
232 **  except when beginning with '/' (then it will be passed through).
233 **  Returns the abs dirpath, in static mem
234 **
235 **  TODO: Always copy into the static buffer, or just return the pointer?
236 */
237 char *
238 expanddir(char *d)
239 {
240         static char buf[BUFSIZ];
241         int len;
242
243         if (*d == '/') {
244                 strcpy(buf, d);
245         } else {
246                 getcwd(buf, sizeof buf);
247                 len = strlen(buf);
248                 snprintf(buf+len, sizeof buf - len, "/%s", d);
249         }
250         packpath(buf);
251         return buf;
252 }
253
254
255 /*
256 **  Anypath to absolute directory path
257 **  Convert any kind of path into an abs dirpath
258 **  A path without distinguishing prefix is assumed to be an abs folpath
259 **  Abs dirpaths are passed unchanged
260 **  Rel dirpaths ('.') get prefixed with the (abs) cwd
261 **  Return pointer to static memory
262 **
263 **  To get the dir path of the mail storage root, call: toabsdir("+")
264 **
265 **  TODO: check lengths for copies
266 */
267 char *
268 toabsdir(char *path)
269 {
270         static char buf[BUFSIZ];
271
272         if (*path == '/') {
273                 /* nothing to do */
274                 strncpy(buf, path, sizeof buf);
275                 packpath(buf);
276                 return buf;
277
278         } else if (*path == '.') {
279                 /* rel dir path */
280                 strncpy(buf, expanddir(path), sizeof buf);
281                 return buf;
282
283         } else {
284                 /* folder path */
285                 char *cp=buf, *pp;
286
287                 if (!(pp = context_find("path")) || !*pp) {
288                         adios(NULL, "Non-empty profile entry `Path' required");
289                 }
290                 if (*pp != '/') {
291                         /* Path is relative to $HOME */
292                         snprintf(buf, sizeof buf, "%s/", mypath);
293                         cp += strlen(buf);
294                 }
295                 strcpy(cp, pp);
296                 packpath(buf);
297                 /* append the mail folder */
298                 cp = buf + strlen(buf);
299                 *cp++ = '/';
300                 strcpy(cp, expandfol(path));
301                 return buf;
302         }
303 }