Add %(unmailto) format function for List-Post headers
[mmh] / sbr / path.c
index 0ae27d3..66bb8f1 100644 (file)
-
 /*
- * path.c -- return a pathname
- *
- * $Id$
- *
- * 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.
- */
+** 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>
+
+
+/*
+** 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.
+*/
+char *
+etcpath(char *file)
+{
+       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;
+}
+
+
+/*
+**  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)
+{
+       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;
+               }
+
+               /* Let's see what follows the slash ... */
+               switch (*++cp) {
+               case '\0':
+                       *--cp = '\0';
+                       continue;  /* ... and thus exit the loop */
+
+               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;
+               }
+       }
 
-#define        CWD     "./"
-#define        NCWD    (sizeof(CWD) - 1)
-#define        DOT     "."
-#define        DOTDOT  ".."
-#define        PWD     "../"
-#define        NPWD    (sizeof(PWD) - 1)
+       if (!strlen(f)) {
+               /* We have removed everything, but need something. */
+               strcpy(f, abspath ? "/" : ".");
+       }
+}
+
+
+
+
+/*
+**  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)
+{
+       char *folder = context_find(inbox);
+
+       if (!folder || !*folder) {
+               folder = defaultfolder;  /* the compile time default */
+       }
+       if (*folder == '+') {
+               folder++;
+       }
+       return folder;
+}
 
-static char *pwds;
 
 /*
- * static prototypes
- */
-static char *expath(char *,int);
-static void compath(char *);
+**  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 (!folder || !*folder) {
+               folder = getdeffol();
+       }
+       return folder;
+}
 
 
+/*
+**  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 *
-path(char *name, int flag)
+expandfol(char *f)
 {
-    register char *cp, *ep;
+       static char buf[BUFSIZ];
+
+       if (*f == '@') {
+               /* f = concat(getcurfol(), "/", f+1, NULL); */
+               snprintf(buf, sizeof buf, "%s/%s", getcurfol(), f+1);
 
-    if ((cp = expath (name, flag))
-           && (ep = cp + strlen (cp) - 1) > cp
-           && *ep == '/')
-       *ep = '\0';
+       } else if (*f == '+') {
+               strcpy(buf, f+1);
 
-    return cp;
+       } else {
+               strcpy(buf, f);
+       }
+       packpath(buf);
+       return buf;
 }
 
 
-static char *
-expath (char *name, int flag)
+/*
+**  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)
 {
-    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);
+       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);
        }
-       flag = TFOLDER;
-    }
-
-    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 ();
-
-    if (strcmp (name, DOT) == 0 || strcmp (name, CWD) == 0)
-       return getcpy (pwds);
-
-    ep = pwds + strlen (pwds);
-    if ((cp = strrchr(pwds, '/')) == NULL)
-       cp = ep;
-    else
-       if (cp == pwds)
-           cp++;
-
-    if (strncmp (name, CWD, NCWD) == 0)
-       name += NCWD;
-
-    if (strcmp (name, DOTDOT) == 0 || strcmp (name, PWD) == 0) {
-       snprintf (buffer, sizeof(buffer), "%.*s", cp - pwds, pwds);
-       return getcpy (buffer);
-    }
-
-    if (strncmp (name, PWD, NPWD) == 0)
-       name += NPWD;
-    else
-       cp = ep;
-
-    snprintf (buffer, sizeof(buffer), "%.*s/%s", cp - pwds, pwds, name);
-    return getcpy (buffer);
+       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;
+       } else if (*path == '.') {
+               /* rel dir path */
+               strncpy(buf, expanddir(path), sizeof buf);
+               return buf;
 
-               case '/': 
-                   for (dp = cp; *dp == '/'; dp++)
-                       continue;
-                   strcpy (cp--, dp);
-                   continue;
-
-               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;
-
-               default: 
-                   cp++;
-                   continue;
-           }
-           break;
+       } else {
+               /* folder path */
+               char *cp=buf, *pp;
+
+               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;
        }
-       else
-           cp++;
 }