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