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