add m_dirname that implement a non broken libgen-like function.
[apps/madmutt.git] / lib-lib / file.c
index b51b1ff..5e37996 100644 (file)
  * please see the file GPL in the top level source directory.
  */
 
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include "macros.h"
-#include "mem.h"
-#include "str.h"
-#include "file.h"
+#include "lib-lib.h"
+
+#include <utime.h>
 
 #ifndef O_NOFOLLOW
 #  define O_NOFOLLOW  0
@@ -210,7 +202,7 @@ void mutt_unlink(const char *s)
                 fwrite(buf, 1, MIN(ssizeof(buf), sb.st_size), f);
                 sb.st_size -= MIN(ssizeof(buf), sb.st_size);
             }
-            fclose (f);
+            m_fclose(&f);
         }
     }
 }
@@ -242,14 +234,41 @@ FILE *safe_fopen(const char *path, const char *mode)
     return fopen(path, mode);
 }
 
-int safe_fclose(FILE **f)
+/* If rfc1524_expand_command() is used on a recv'd message, then
+ * the filename doesn't exist yet, but if its used while sending a message,
+ * then we need to rename the existing file.
+ *
+ * This function returns 0 on successful move, 1 on old file doesn't exist,
+ * 2 on new file already exists, and 3 on other failure.
+ */
+
+/* note on access(2) use: No dangling symlink problems here due to
+ * safe_fopen().
+ */
+int mutt_rename_file(char *oldfile, char *newfile)
 {
-    int r = 0;
+    FILE *ofp, *nfp;
+
+    if (access(oldfile, F_OK) != 0)
+        return 1;
+    if (access(newfile, F_OK) == 0)
+        return 2;
+
+    ofp = fopen(oldfile, "r");
+    if (!ofp)
+        return 3;
+
+    nfp = safe_fopen(newfile, "w");
+    if (!nfp) {
+        m_fclose(&ofp);
+        return 3;
+    }
 
-    if (*f)
-        r = fclose (*f);
-    *f = NULL;
-    return r;
+    mutt_copy_stream(ofp, nfp);
+    m_fclose(&nfp);
+    m_fclose(&ofp);
+    mutt_unlink(oldfile);
+    return 0;
 }
 
 /* Read a line from ``fp'' into the dynamically allocated ``s'',
@@ -336,7 +355,7 @@ int mutt_copy_bytes(FILE *in, FILE *out, ssize_t size)
 
 
 /****************************************************************************/
-/* ligben-like funcs                                                        */
+/* path manipulations                                                       */
 /****************************************************************************/
 
 const char *mutt_basename(const char *f)
@@ -345,6 +364,26 @@ const char *mutt_basename(const char *f)
     return p ? p + 1 : f;
 }
 
+ssize_t m_dirname(char *dst, ssize_t dlen, const char *path)
+{
+    int plen = m_strlen(path);
+
+    while (plen > 0 && path[plen - 1] == '/')
+        plen--;
+
+    while (plen > 0 && path[plen - 1] != '/')
+        plen--;
+
+    while (plen > 0 && path[plen - 1] == '/')
+        plen--;
+
+    if (plen)
+        return m_strncpy(dst, dlen, path, plen);
+
+    if (*path == '/')
+        return m_strcpy(dst, dlen, "/");
+    return m_strcpy(dst, dlen, ".");
+}
 
 char *
 mutt_concat_path(char *d, ssize_t n, const char *dir, const char *fname)
@@ -412,3 +451,123 @@ ssize_t mutt_quote_filename(char *d, ssize_t l, const char *s)
 
     return j;
 }
+
+ssize_t m_file_fmt(char *dst, ssize_t n, const char *fmt, const char *src)
+{
+    ssize_t pos = 0;
+    const char *p;
+    int hadfname = 0;
+
+    for (p = strchr(fmt, '%'); p; p = strchr(fmt, '%')) {
+        if (p[1] == 's') {
+            pos += m_strncpy(dst + pos, n - pos, fmt, p - fmt);
+            pos += m_strcpy(dst + pos, n - pos, src);
+            fmt  = p + 2;
+            hadfname = 1;
+        } else {
+            pos += m_strncpy(dst + pos, n - pos, fmt, p + 1 - fmt);
+            fmt = p + 1 + (p[1] == '%');
+        }
+    }
+    pos += m_strcpy(dst + pos, n - pos, fmt);
+
+    if (!hadfname)
+        pos += snprintf(dst + pos, n - pos, " %s", src);
+
+    return pos;
+}
+
+ssize_t
+m_quotefile_fmt(char *dst, ssize_t n, const char *fmt, const char *src)
+{
+    char tmp[LONG_STRING];
+    mutt_quote_filename(tmp, sizeof(tmp), src);
+    return m_file_fmt(dst, n, fmt, tmp);
+}
+
+static ssize_t
+m_tempftplize(char *dst, ssize_t dlen, const char *fmt, const char *s)
+{
+    const char *p;
+
+    while ((p = strchr(fmt, '/'))) {
+        fmt = p + 1;
+    }
+
+    if (!*fmt)
+        return m_strcpy(dst, dlen, s);
+
+    for (p = strchr(fmt, '%'); p; p = strchr(p, '%')) {
+        if (p[1] == 's')
+            return m_file_fmt(dst, dlen, fmt, s);
+
+        p += 1 + (p[1] == '%');
+    }
+
+    p = strrchr(fmt, '.');
+    if (p) {
+        return snprintf(dst, dlen, "%s%s", s, p);
+    } else {
+        return snprintf(dst, dlen, "%s.%s", s, fmt);
+    }
+}
+
+int m_tempfd(char *dst, ssize_t n, const char *dir, const char *fmt)
+{
+    char raw[_POSIX_PATH_MAX], tpl[_POSIX_PATH_MAX];
+    const char *path = fmt ? tpl : raw;
+    int fd;
+
+  rand_again:
+    snprintf(raw, sizeof(raw), "%s/madmutt-%04x-%04x-%08x",
+             dir, (int)getuid(), (int)getpid(), (int)rand());
+
+    if (fmt) {
+        m_tempftplize(tpl, sizeof(tpl), fmt, raw);
+    }
+
+    fd = open(path, O_CREAT | O_EXCL | O_RDWR | O_NOFOLLOW, 0600);
+
+    if (fd < 0) {
+        if (errno == EEXIST)
+            goto rand_again;
+        return -1;
+    }
+
+    m_strcpy(dst, n, path);
+    return fd;
+}
+
+FILE *m_tempfile(char *dst, ssize_t n, const char *dir, const char *fmt)
+{
+    int fd = m_tempfd(dst, n, dir, fmt);
+    return fd < 0 ? NULL : fdopen(fd, "w+");
+}
+
+/****************************************************************************/
+/* misc                                                                     */
+/****************************************************************************/
+
+/* Decrease a file's modification time by 1 second */
+time_t m_decrease_mtime(const char *path, struct stat *st)
+{
+    struct utimbuf utim;
+    struct stat _st;
+    time_t mtime;
+
+    if (!st) {
+        if (stat(path, &_st) == -1)
+            return -1;
+        st = &_st;
+    }
+
+    if ((mtime = st->st_mtime) == time(NULL)) {
+        mtime -= 1;
+        utim.actime = mtime;
+        utim.modtime = mtime;
+        utime(path, &utim);
+    }
+
+    return mtime;
+}
+