Add %(unmailto) format function for List-Post headers
[mmh] / sbr / path.c
index 502ad97..66bb8f1 100644 (file)
 /*
-** path.c -- return a pathname
+** path.c -- return or convert paths
 **
 ** This code is Copyright (c) 2002, by the authors of nmh.  See the
 ** COPYRIGHT file in the root directory of the nmh distribution for
 ** complete copyright information.
 */
 
+#include <sysexits.h>
 #include <h/mh.h>
+#include <pwd.h>
+#include <unistd.h>
 
-#define CWD     "./"
-#define NCWD    (sizeof(CWD) - 1)
-#define DOT     "."
-#define DOTDOT  ".."
-#define PWD     "../"
-#define NPWD    (sizeof(PWD) - 1)
-
-static char *pwds;
 
 /*
-** static prototypes
+** Find the location of a format or configuration
+** file, and return its absolute pathname.
+**
+** 1) If it begins with ~user, then expand it.
+** 2) Next, if already absolute pathname, then leave unchanged.
+** 3) Next, check in mmh directory.
+** 4) Next, check in mmh `etc' directory.
+** 5) As fall-back, return `file' unchanged.
 */
-static char *expath(char *,int);
-static void compath(char *);
-
 char *
-pluspath(char *name)
+etcpath(char *file)
 {
-       return path(name + 1, *name == '+' ? TFOLDER : TSUBCWF);
+       static char epath[PATH_MAX];
+       char *cp;
+       char *pp;
+       struct passwd *pw;
+
+       /* XXX: here was: ``context_read();'' -- why? */
+       if (*file == '~') {
+               /* Expand `~user' */
+               if ((cp = strchr(pp = file + 1, '/')))
+                       *cp++ = '\0';
+               if (*pp == '\0') {
+                       pp = mypath;
+               } else {
+                       if ((pw = getpwnam(pp)))
+                               pp = pw->pw_dir;
+                       else {
+                               if (cp)
+                                       *--cp = '/';
+                               goto try_it;
+                       }
+               }
+
+               snprintf(epath, sizeof epath, "%s/%s", pp, cp ? cp : "");
+               if (cp)
+                       *--cp = '/';
+
+               if (access(epath, R_OK) != NOTOK)
+                       return epath;  /* else fall */
+       }
+
+try_it:
+       if (*file == '/') {
+               /* absolute pathname, return it */
+               return file;
+       }
+
+       /* Check mmh directory */
+       snprintf(epath, sizeof epath, "%s/%s", mmhpath, file);
+       if (access(epath, R_OK) != NOTOK)
+               return epath;
+
+       /* Check nmh `etc' directory */
+       snprintf(epath, sizeof epath, "%s/%s", mhetcdir, file);
+       if  (access(epath, R_OK) != NOTOK)
+               return epath;
+
+       /* The fall-back */
+       return file;
 }
 
-char *
-path(char *name, int flag)
+
+/*
+**  Compactify a path name by removing unnecessary parts.
+**  Removes trailing slashes. Cares to never remove all characters.
+**  Modifies f (never enlarges it).
+**
+**  FIXME: Cannot use strcpy() as the areas overlap!
+*/
+static void
+packpath(char *f)
 {
-       register char *cp, *ep;
+       char *cp, *dp;
+       int abspath;
+
+       if (!f || !*f) {
+               return;
+       }
+       abspath = (*f == '/');
+
+       for (cp=f; *cp; ) {
+               if (*cp != '/') {
+                       /* Skip. Interesting places are only after slashes. */
+                       /* We don't care about "./" beginnings */
+                       cp++;
+                       continue;
+               }
 
-       if ((cp = expath (name, flag))
-               && (ep = cp + strlen (cp) - 1) > cp
-               && *ep == '/')
-               *ep = '\0';
+               /* Let's see what follows the slash ... */
+               switch (*++cp) {
+               case '\0':
+                       *--cp = '\0';
+                       continue;  /* ... and thus exit the loop */
 
-       return cp;
+               case '/':
+                       /* reduce subsequent slashes to one */
+                       for (dp = cp; *dp == '/'; dp++) {
+                               continue;
+                       }
+                       strcpy(cp, dp);
+                       cp--;
+                       continue;  /* ... at the slash */
+
+               case '.':
+                       if (cp[1] == '/' || cp[1] == '\0') {
+                               /* one-dot element */
+                               strcpy(cp-1, cp+1);
+                               cp--;
+                               continue;
+                       } else if ((strncmp(cp, "../", 3) == 0) ||
+                                       (strcmp(cp, "..") == 0)) {
+                               /* dot-dot element */
+                               /* crop out previous path element */
+                               for (dp=cp-2; dp>f && *dp!='/'; dp--) {
+                                       continue;
+                               }
+                               if (dp < f) {
+                                       /* path starts with "/.." */
+                                       dp = f;
+                               }
+                               strcpy(dp, cp+2);
+                               cp = dp;
+                               continue;
+                       } else {
+                               /* a normal hidden file */
+                               cp++;
+                               continue;
+                       }
+
+               default:
+                       /* nothing special */
+                       cp++;
+                       continue;
+               }
+       }
+
+       if (!strlen(f)) {
+               /* We have removed everything, but need something. */
+               strcpy(f, abspath ? "/" : ".");
+       }
 }
 
 
-static char *
-expath (char *name, int flag)
+
+
+/*
+**  Get the default folder
+**  Return the Inbox profile entry or, as fallback, the compile time default
+**  Returns a pointer to the abs folpath
+*/
+char *
+getdeffol(void)
 {
-       register char *cp, *ep;
-       char buffer[BUFSIZ];
-
-       if (flag == TSUBCWF) {
-               snprintf (buffer, sizeof(buffer), "%s/%s", getfolder (1), name);
-               name = m_mailpath (buffer);
-               compath (name);
-               snprintf (buffer, sizeof(buffer), "%s/", m_maildir (""));
-               if (ssequal (buffer, name)) {
-                       cp = name;
-                       name = getcpy (name + strlen (buffer));
-                       free (cp);
-               }
-               flag = TFOLDER;
+       char *folder = context_find(inbox);
+
+       if (!folder || !*folder) {
+               folder = defaultfolder;  /* the compile time default */
+       }
+       if (*folder == '+') {
+               folder++;
        }
+       return folder;
+}
 
-       if (*name == '/' || (flag == TFOLDER
-               && (strncmp (name, CWD, NCWD) && strcmp (name, DOT)
-               && strcmp (name, DOTDOT) && strncmp (name, PWD, NPWD))))
-               return getcpy (name);
 
-       if (pwds == NULL)
-               pwds = pwd ();
+/*
+**  Get the current folder
+**  Return the Current-Folder context entry or, as fallback, the default folder
+**  Returns a pointer to the abs folpath
+**
+**  Equivalent to: expandfol("@")
+*/
+char *
+getcurfol(void)
+{
+       char *folder = context_find(curfolder);
 
-       if (strcmp (name, DOT) == 0 || strcmp (name, CWD) == 0)
-               return getcpy (pwds);
+       if (!folder || !*folder) {
+               folder = getdeffol();
+       }
+       return folder;
+}
 
-       ep = pwds + strlen (pwds);
-       if ((cp = strrchr(pwds, '/')) == NULL)
-               cp = ep;
-       else
-               if (cp == pwds)
-                       cp++;
 
-       if (strncmp (name, CWD, NCWD) == 0)
-               name += NCWD;
+/*
+**  Expand folder path
+**  Convert rel folpaths (@) into abs folpaths
+**  dir paths are simply passed through
+**  Returns the abs folpath (without prefix), in static mem
+**
+**  TODO: Always copy into the static buffer, or just return the pointer?
+*/
+char *
+expandfol(char *f)
+{
+       static char buf[BUFSIZ];
 
-       if (strcmp (name, DOTDOT) == 0 || strcmp (name, PWD) == 0) {
-               snprintf (buffer, sizeof(buffer), "%.*s", (int)(cp - pwds), pwds);
-               return getcpy (buffer);
+       if (*f == '@') {
+               /* f = concat(getcurfol(), "/", f+1, NULL); */
+               snprintf(buf, sizeof buf, "%s/%s", getcurfol(), f+1);
+
+       } else if (*f == '+') {
+               strcpy(buf, f+1);
+
+       } else {
+               strcpy(buf, f);
        }
+       packpath(buf);
+       return buf;
+}
 
-       if (strncmp (name, PWD, NPWD) == 0)
-               name += NPWD;
-       else
-               cp = ep;
 
-       snprintf (buffer, sizeof(buffer), "%.*s/%s", (int)(cp - pwds), pwds, name);
-       return getcpy (buffer);
+/*
+**  Expand directory path
+**  Convert rel dirpath into abs dirpath
+**  The argument is assumed to be a dir path relative to the cwd,
+**  except when beginning with '/' (then it will be passed through).
+**  Returns the abs dirpath, in static mem
+**
+**  TODO: Always copy into the static buffer, or just return the pointer?
+*/
+char *
+expanddir(char *d)
+{
+       static char buf[BUFSIZ];
+       int len;
+
+       if (*d == '/') {
+               strcpy(buf, d);
+       } else {
+               getcwd(buf, sizeof buf);
+               len = strlen(buf);
+               snprintf(buf+len, sizeof buf - len, "/%s", d);
+       }
+       packpath(buf);
+       return buf;
 }
 
 
-static void
-compath (char *f)
+/*
+**  Anypath to absolute directory path
+**  Convert any kind of path into an abs dirpath
+**  A path without distinguishing prefix is assumed to be an abs folpath
+**  Abs dirpaths are passed unchanged
+**  Rel dirpaths ('.') get prefixed with the (abs) cwd
+**  Return pointer to static memory
+**
+**  To get the dir path of the mail storage root, call: toabsdir("+")
+**
+**  TODO: check lengths for copies
+*/
+char *
+toabsdir(char *path)
 {
-       register char *cp, *dp;
+       static char buf[BUFSIZ];
 
-       if (*f != '/')
-               return;
+       if (*path == '/') {
+               /* nothing to do */
+               strncpy(buf, path, sizeof buf);
+               packpath(buf);
+               return buf;
 
-       for (cp = f; *cp;)
-               if (*cp == '/') {
-                       switch (*++cp) {
-                               case 0:
-                                       if (--cp > f)
-                                               *cp = '\0';
-                                       break;
-
-                               case '/':
-                                       for (dp = cp; *dp == '/'; dp++)
-                                               continue;
-                                       strcpy (cp--, dp);
-                                       continue;
+       } else if (*path == '.') {
+               /* rel dir path */
+               strncpy(buf, expanddir(path), sizeof buf);
+               return buf;
 
-                               case '.':
-                                       if (strcmp (cp, DOT) == 0) {
-                                               if (cp > f + 1)
-                                                       cp--;
-                                               *cp = '\0';
-                                               break;
-                                       }
-                                       if (strcmp (cp, DOTDOT) == 0) {
-                                               for (cp -= 2; cp > f; cp--)
-                                                       if (*cp == '/')
-                                                               break;
-                                               if (cp <= f)
-                                                       cp = f + 1;
-                                               *cp = '\0';
-                                               break;
-                                       }
-                                       if (strncmp (cp, PWD, NPWD) == 0) {
-                                               for (dp = cp - 2; dp > f; dp--)
-                                                       if (*dp == '/')
-                                                               break;
-                                               if (dp <= f)
-                                                       dp = f;
-                                               strcpy (dp, cp + NPWD - 1);
-                                               cp = dp;
-                                               continue;
-                                       }
-                                       if (strncmp (cp, CWD, NCWD) == 0) {
-                                               strcpy (cp - 1, cp + NCWD - 1);
-                                               cp--;
-                                               continue;
-                                       }
-                                       continue;
+       } else {
+               /* folder path */
+               char *cp=buf, *pp;
 
-                               default:
-                                       cp++;
-                                       continue;
-                       }
-                       break;
-               } else
-                       cp++;
+               if (!(pp = context_find("path")) || !*pp) {
+                       adios(EX_CONFIG, NULL, "Non-empty profile entry `Path' required");
+               }
+               if (*pp != '/') {
+                       /* Path is relative to $HOME */
+                       snprintf(buf, sizeof buf, "%s/", mypath);
+                       cp += strlen(buf);
+               }
+               strcpy(cp, pp);
+               packpath(buf);
+               /* append the mail folder */
+               cp = buf + strlen(buf);
+               *cp++ = '/';
+               strcpy(cp, expandfol(path));
+               return buf;
+       }
 }