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