Introduced FDEF and FCUR for speaking arguments to getfolder().
[mmh] / sbr / path.c
index 0ae27d3..b3a8fff 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 a pathname
+**
+** 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 <h/mh.h>
 
-#define        CWD     "./"
-#define        NCWD    (sizeof(CWD) - 1)
-#define        DOT     "."
-#define        DOTDOT  ".."
-#define        PWD     "../"
-#define        NPWD    (sizeof(PWD) - 1)
-
 static char *pwds;
 
-/*
- * static prototypes
- */
-static char *expath(char *,int);
-static void compath(char *);
-
 
-char *
-path(char *name, int flag)
+/*
+**  Compactify an absolute path name by removing unneccessary parts.
+**  Removes trailing slashes, but not if it would empty the string then.
+**  Modifies f.
+*/
+static void
+packpath(char *f)
 {
-    register char *cp, *ep;
+       char *cp, *dp;
+       int abspath;
 
-    if ((cp = expath (name, flag))
-           && (ep = cp + strlen (cp) - 1) > cp
-           && *ep == '/')
-       *ep = '\0';
+       if (!f || !*f) {
+               return;
+       }
+       abspath = (*f == '/');
 
-    return cp;
+       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;
+               }
+       }
+
+       if (!strlen(f)) {
+               /* We have removed everything, but need something. */
+               strcpy(f, abspath ? "/" : ".");
+       }
 }
 
 
+/*
+**
+*/
 static char *
-expath (char *name, int flag)
+expath(char *name, int type)
 {
-    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);
+       char *cp, *ep;
+       char buffer[BUFSIZ];
+
+       if (type == TSUBCWF) {
+               /* @folder to +folder */
+               snprintf(buffer, sizeof(buffer), "%s/%s",
+                               getfolder(FCUR), name);
+               name = m_mailpath(buffer);
+               packpath(name);
+               snprintf(buffer, sizeof(buffer), "%s/", m_maildir(""));
+               if (isprefix(buffer, name)) {
+                       cp = name;
+                       name = getcpy(name + strlen(buffer));
+                       free(cp);
+               }
+               type = TFOLDER;
+       }
+
+       if (*name == '/') {
+               return getcpy(name);
+       }
+
+       if (type == TFOLDER &&
+                       (strncmp(name, "./", 2) && strcmp(name, ".") &&
+                       strcmp(name, "..") && strncmp(name, "../", 3))) {
+               /*
+               ** FIXME: Seems as if this check does not catch names like:
+               **        ``foo/../../..''.
+               */
+               return getcpy(name);
+       }
+
+       if (pwds == NULL) {
+               pwds = pwd();
+       }
+
+       if (strcmp(name, ".") == 0 || strcmp(name, "./") == 0) {
+               return getcpy(pwds);
        }
-       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);
+
+       ep = pwds + strlen(pwds);
+       if ((cp = strrchr(pwds, '/')) == NULL) {
+               cp = ep;
+       } else if (cp == pwds)  {
+               cp++;
+       }
+
+       if (strncmp(name, "./", 2) == 0) {
+               name += 2;
+       }
+
+       if (strcmp(name, "..") == 0 || strcmp(name, "../") == 0) {
+               snprintf(buffer, sizeof(buffer), "%.*s",
+                               (int)(cp - pwds), pwds);
+               return getcpy(buffer);
+       }
+
+       if (strncmp(name, "../", 3) == 0) {
+               name += 3;
+       } else {
+               cp = ep;
+       }
+
+       snprintf(buffer, sizeof(buffer), "%.*s/%s",
+                       (int)(cp - pwds), pwds, name);
+       return getcpy(buffer);
 }
 
 
-static void
-compath (char *f)
+/*
+**
+*/
+char *
+path(char *name, int type)
 {
-    register char *cp, *dp;
+       char *cp, *ep;
 
-    if (*f != '/')
-       return;
+       if ((cp = expath(name, type)) &&
+                       (ep = cp+strlen(cp)-1) > cp &&
+                       *ep == '/') {
+               *ep = '\0';
+       }
 
-    for (cp = f; *cp;)
-       if (*cp == '/') {
-           switch (*++cp) {
-               case 0: 
-                   if (--cp > f)
-                       *cp = '\0';
-                   break;
+       return cp;
+}
 
-               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
-           cp++;
+
+/*
+** Call path() appropriately for ``+folder'' or ``@folder''
+*/
+char *
+pluspath(char *name)
+{
+       return path(name+1, (*name == '+') ? TFOLDER : TSUBCWF);
 }