2 * Copyright (C) 1996-2000,2007 Michael R. Elkins <me@mutt.org>
3 * Copyright (C) 1999-2004,2006-7 Thomas Roessler <roessler@does-not-exist.org>
5 * This program is free software; you can redistribute it
6 * and/or modify it under the terms of the GNU General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later
11 * This program is distributed in the hope that it will be
12 * useful, but WITHOUT ANY WARRANTY; without even the implied
13 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more
17 * You should have received a copy of the GNU General Public
18 * License along with this program; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
24 * This file used to contain some more functions, namely those
25 * which are now in muttlib.c. They have been removed, so we have
26 * some of our "standard" functions in external programs, too.
44 #include <sys/types.h>
47 #ifdef HAVE_SYSEXITS_H
49 #else /* Make sure EX_OK is defined <philiph@pobox.com> */
56 static struct sysexits
64 { 0xff & EX_USAGE, "Bad usage." },
67 { 0xff & EX_DATAERR, "Data format error." },
70 { 0xff & EX_NOINPUT, "Cannot open input." },
73 { 0xff & EX_NOUSER, "User unknown." },
76 { 0xff & EX_NOHOST, "Host unknown." },
79 { 0xff & EX_UNAVAILABLE, "Service unavailable." },
82 { 0xff & EX_SOFTWARE, "Internal error." },
85 { 0xff & EX_OSERR, "Operating system error." },
88 { 0xff & EX_OSFILE, "System file missing." },
91 { 0xff & EX_CANTCREAT, "Can't create output." },
94 { 0xff & EX_IOERR, "I/O error." },
97 { 0xff & EX_TEMPFAIL, "Deferred." },
100 { 0xff & EX_PROTOCOL, "Remote protocol error." },
103 { 0xff & EX_NOPERM, "Insufficient permission." },
106 { 0xff & EX_NOPERM, "Local configuration error." },
108 { S_ERR, "Exec error." },
112 void mutt_nocurses_error (const char *fmt, ...)
117 vfprintf (stderr, fmt, ap);
119 fputc ('\n', stderr);
122 void *safe_calloc (size_t nmemb, size_t size)
129 if (((size_t) -1) / nmemb <= size)
131 mutt_error _("Integer overflow -- can't allocate memory!");
136 if (!(p = calloc (nmemb, size)))
138 mutt_error _("Out of memory!");
145 void *safe_malloc (size_t siz)
151 if ((p = (void *) malloc (siz)) == 0) /* __MEM_CHECKED__ */
153 mutt_error _("Out of memory!");
160 void safe_realloc (void *ptr, size_t siz)
163 void **p = (void **)ptr;
169 free (*p); /* __MEM_CHECKED__ */
176 r = (void *) realloc (*p, siz); /* __MEM_CHECKED__ */
179 /* realloc(NULL, nbytes) doesn't seem to work under SunOS 4.1.x --- __MEM_CHECKED__ */
180 r = (void *) malloc (siz); /* __MEM_CHECKED__ */
185 mutt_error _("Out of memory!");
193 void safe_free (void *ptr) /* __SAFE_FREE_CHECKED__ */
195 void **p = (void **)ptr;
198 free (*p); /* __MEM_CHECKED__ */
203 int safe_fclose (FILE **f)
214 int safe_fsync_close (FILE **f)
220 if (fflush (*f) || fsync (fileno (*f)))
233 char *safe_strdup (const char *s)
241 p = (char *)safe_malloc (l);
246 char *safe_strcat (char *d, size_t l, const char *s)
253 l--; /* Space for the trailing '\0'. */
265 char *safe_strncat (char *d, size_t l, const char *s, size_t sl)
272 l--; /* Space for the trailing '\0'. */
276 for (; *s && l && sl; l--, sl--)
285 void mutt_str_replace (char **p, const char *s)
287 FREE (p); /* __FREE_CHECKED__ */
288 *p = safe_strdup (s);
291 void mutt_str_adjust (char **p)
293 if (!p || !*p) return;
294 safe_realloc (p, strlen (*p) + 1);
297 /* convert all characters in the string to lowercase */
298 char *mutt_strlower (char *s)
304 *p = tolower ((unsigned char) *p);
311 void mutt_unlink (const char *s)
319 /* Defend against symlink attacks */
322 flags = O_RDWR | O_NOFOLLOW;
327 if (lstat (s, &sb) == 0 && S_ISREG(sb.st_mode))
329 if ((fd = open (s, flags)) < 0)
332 if ((fstat (fd, &sb2) != 0) || !S_ISREG (sb2.st_mode)
333 || (sb.st_dev != sb2.st_dev) || (sb.st_ino != sb2.st_ino))
339 if ((f = fdopen (fd, "r+")))
342 memset (buf, 0, sizeof (buf));
343 while (sb.st_size > 0)
345 fwrite (buf, 1, MIN (sizeof (buf), sb.st_size), f);
346 sb.st_size -= MIN (sizeof (buf), sb.st_size);
353 int mutt_copy_bytes (FILE *in, FILE *out, size_t size)
360 chunk = (size > sizeof (buf)) ? sizeof (buf) : size;
361 if ((chunk = fread (buf, 1, chunk, in)) < 1)
363 if (fwrite (buf, 1, chunk, out) != chunk)
365 /* dprint (1, (debugfile, "mutt_copy_bytes(): fwrite() returned short byte count\n")); */
374 int mutt_copy_stream (FILE *fin, FILE *fout)
377 char buf[LONG_STRING];
379 while ((l = fread (buf, 1, sizeof (buf), fin)) > 0)
381 if (fwrite (buf, 1, l, fout) != l)
389 compare_stat (struct stat *osb, struct stat *nsb)
391 if (osb->st_dev != nsb->st_dev || osb->st_ino != nsb->st_ino ||
392 osb->st_rdev != nsb->st_rdev)
400 int safe_symlink(const char *oldpath, const char *newpath)
402 struct stat osb, nsb;
404 if(!oldpath || !newpath)
407 if(unlink(newpath) == -1 && errno != ENOENT)
410 if (oldpath[0] == '/')
412 if (symlink (oldpath, newpath) == -1)
417 char abs_oldpath[_POSIX_PATH_MAX];
419 if ((getcwd (abs_oldpath, sizeof abs_oldpath) == NULL) ||
420 (strlen (abs_oldpath) + 1 + strlen (oldpath) + 1 > sizeof abs_oldpath))
423 strcat (abs_oldpath, "/"); /* __STRCAT_CHECKED__ */
424 strcat (abs_oldpath, oldpath); /* __STRCAT_CHECKED__ */
425 if (symlink (abs_oldpath, newpath) == -1)
429 if(stat(oldpath, &osb) == -1 || stat(newpath, &nsb) == -1
430 || compare_stat(&osb, &nsb) == -1)
442 * This function is supposed to do nfs-safe renaming of files.
444 * Warning: We don't check whether src and target are equal.
447 int safe_rename (const char *src, const char *target)
449 struct stat ssb, tsb;
454 if (link (src, target) != 0)
458 * Coda does not allow cross-directory links, but tells
459 * us it's a cross-filesystem linking attempt.
461 * However, the Coda rename call is allegedly safe to use.
463 * With other file systems, rename should just fail when
464 * the files reside on different file systems, so it's safe
469 dprint (1, (debugfile, "safe_rename: link (%s, %s) failed: %s (%d)\n", src, target, strerror (errno), errno));
472 * FUSE may return ENOSYS. VFAT may return EPERM. FreeBSD's
473 * msdosfs may return EOPNOTSUPP. ENOTSUP can also appear.
475 if (errno == EXDEV || errno == ENOSYS || errno == EPERM
480 || errno == EOPNOTSUPP
484 dprint (1, (debugfile, "safe_rename: trying rename...\n"));
485 if (rename (src, target) == -1)
487 dprint (1, (debugfile, "safe_rename: rename (%s, %s) failed: %s (%d)\n", src, target, strerror (errno), errno));
490 dprint (1, (debugfile, "safe_rename: rename succeeded.\n"));
499 * Stat both links and check if they are equal.
502 if (lstat (src, &ssb) == -1)
504 dprint (1, (debugfile, "safe_rename: can't stat %s: %s (%d)\n",
505 src, strerror (errno), errno));
509 if (lstat (target, &tsb) == -1)
511 dprint (1, (debugfile, "safe_rename: can't stat %s: %s (%d)\n",
512 src, strerror (errno), errno));
517 * pretend that the link failed because the target file
521 if (compare_stat (&ssb, &tsb) == -1)
523 dprint (1, (debugfile, "safe_rename: stat blocks for %s and %s diverge; pretending EEXIST.\n", src, target));
529 * Unlink the original link. Should we really ignore the return
533 if (unlink (src) == -1)
535 dprint (1, (debugfile, "safe_rename: unlink (%s) failed: %s (%d)\n",
536 src, strerror (errno), errno));
544 /* Create a temporary directory next to a file name */
546 static int mutt_mkwrapdir (const char *path, char *newfile, size_t nflen,
547 char *newdir, size_t ndlen)
549 const char *basename;
550 char parent[_POSIX_PATH_MAX];
554 strfcpy (parent, NONULL (path), sizeof (parent));
556 if ((p = strrchr (parent, '/')))
563 strfcpy (parent, ".", sizeof (parent));
569 snprintf (newdir, ndlen, "%s/%s", parent, ".muttXXXXXX");
572 while ((rv = mkdir (newdir, 0700)) == -1 && errno == EEXIST);
577 snprintf (newfile, nflen, "%s/%s", newdir, NONULL(basename));
581 /* remove a directory and everything under it */
582 int mutt_rmtree (const char* path)
586 char cur[_POSIX_PATH_MAX];
590 if (!(dirp = opendir (path)))
592 dprint (1, (debugfile, "mutt_rmtree: error opening directory %s\n", path));
595 while ((de = readdir (dirp)))
597 if (!strcmp (".", de->d_name) || !strcmp ("..", de->d_name))
600 snprintf (cur, sizeof (cur), "%s/%s", path, de->d_name);
601 /* XXX make nonrecursive version */
603 if (stat(cur, &statbuf) == -1)
609 if (S_ISDIR (statbuf.st_mode))
610 rc |= mutt_rmtree (cur);
621 static int mutt_put_file_in_place (const char *path, const char *safe_file, const char *safe_dir)
625 rv = safe_rename (safe_file, path);
631 int safe_open (const char *path, int flags)
633 struct stat osb, nsb;
638 char safe_file[_POSIX_PATH_MAX];
639 char safe_dir[_POSIX_PATH_MAX];
641 if (mutt_mkwrapdir (path, safe_file, sizeof (safe_file),
642 safe_dir, sizeof (safe_dir)) == -1)
645 if ((fd = open (safe_file, flags, 0600)) < 0)
651 /* NFS and I believe cygwin do not handle movement of open files well */
653 if (mutt_put_file_in_place (path, safe_file, safe_dir) == -1)
657 if ((fd = open (path, flags & ~O_EXCL, 0600)) < 0)
660 /* make sure the file is not symlink */
661 if (lstat (path, &osb) < 0 || fstat (fd, &nsb) < 0 ||
662 compare_stat(&osb, &nsb) == -1)
664 /* dprint (1, (debugfile, "safe_open(): %s is a symlink!\n", path)); */
672 /* when opening files for writing, make sure the file doesn't already exist
673 * to avoid race conditions.
675 FILE *safe_fopen (const char *path, const char *mode)
680 int flags = O_CREAT | O_EXCL;
691 if ((fd = safe_open (path, flags)) < 0)
694 return (fdopen (fd, mode));
697 return (fopen (path, mode));
700 static char safe_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/";
702 void mutt_sanitize_filename (char *f, short slash)
708 if ((slash && *f == '/') || !strchr (safe_chars, *f))
713 /* these characters must be escaped in regular expressions */
715 static char rx_special_chars[] = "^.[$()|*+?{\\";
717 int mutt_rx_sanitize_string (char *dest, size_t destlen, const char *src)
719 while (*src && --destlen > 2)
721 if (strchr (rx_special_chars, *src))
737 /* Read a line from ``fp'' into the dynamically allocated ``s'',
738 * increasing ``s'' if necessary. The ending "\n" or "\r\n" is removed.
739 * If a line ends with "\", this char and the linefeed is removed,
740 * and the next line is read too.
742 char *mutt_read_line (char *s, size_t *size, FILE *fp, int *line)
749 s = safe_malloc (STRING);
755 if (fgets (s + offset, *size - offset, fp) == NULL)
760 if ((ch = strchr (s + offset, '\n')) != NULL)
764 if (ch > s && *(ch - 1) == '\r')
766 if (ch == s || *(ch - 1) != '\\')
773 c = getc (fp); /* This is kind of a hack. We want to know if the
774 char at the current point in the input stream is EOF.
775 feof() will only tell us if we've already hit EOF, not
776 if the next character is EOF. So, we need to read in
777 the next character and manually check if it is EOF. */
780 /* The last line of fp isn't \n terminated */
786 ungetc (c, fp); /* undo our dammage */
787 /* There wasn't room for the line -- increase ``s'' */
788 offset = *size - 1; /* overwrite the terminating 0 */
790 safe_realloc (&s, *size);
797 mutt_substrcpy (char *dest, const char *beg, const char *end, size_t destlen)
802 if (len > destlen - 1)
804 memcpy (dest, beg, len);
809 char *mutt_substrdup (const char *begin, const char *end)
817 len = strlen (begin);
819 p = safe_malloc (len + 1);
820 memcpy (p, begin, len);
825 /* prepare a file name to survive the shell's quoting rules.
826 * From the Unix programming FAQ by way of Liviu.
829 size_t mutt_quote_filename (char *d, size_t l, const char *f)
839 /* leave some space for the trailing characters. */
844 for(i = 0; j < l && f[i]; i++)
846 if(f[i] == '\'' || f[i] == '`')
863 /* NULL-pointer aware string comparison functions */
865 int mutt_strcmp(const char *a, const char *b)
867 return strcmp(NONULL(a), NONULL(b));
870 int mutt_strcasecmp(const char *a, const char *b)
872 return strcasecmp(NONULL(a), NONULL(b));
875 int mutt_strncmp(const char *a, const char *b, size_t l)
877 return strncmp(NONULL(a), NONULL(b), l);
880 int mutt_strncasecmp(const char *a, const char *b, size_t l)
882 return strncasecmp(NONULL(a), NONULL(b), l);
885 size_t mutt_strlen(const char *a)
887 return a ? strlen (a) : 0;
890 int mutt_strcoll(const char *a, const char *b)
892 return strcoll(NONULL(a), NONULL(b));
895 const char *mutt_stristr (const char *haystack, const char *needle)
904 while (*(p = haystack))
908 tolower ((unsigned char) *p) == tolower ((unsigned char) *q);
918 char *mutt_skip_whitespace (char *p)
924 void mutt_remove_trailing_ws (char *s)
928 for (p = s + mutt_strlen (s) - 1 ; p >= s && ISSPACE (*p) ; p--)
933 * Write the concatened pathname (dir + "/" + fname) into dst.
934 * The slash is ommitted when dir or fname is of 0 length.
935 * Returns NULL on error or a pointer to dst otherwise.
937 char *mutt_concatn_path (char *dst, size_t dstlen,
938 const char *dir, size_t dirlen, const char *fname, size_t fnamelen)
944 return NULL; /* probably should not mask errors like this */
947 req = dirlen + fnamelen + 1; /* +1 for the trailing nul */
948 if (dirlen && fnamelen)
949 req++; /* when both components are non-nul, we add a "/" in between */
950 if (req > dstlen) { /* check for condition where the dst length is too short */
952 * 1) assert(0) or return NULL to signal error
953 * 2) copy as much of the path as will fit
954 * It doesn't appear that the return value is actually checked anywhere mutt_concat_path()
955 * is called, so we should just copy set dst to nul and let the calling function fail later.
957 dst[0] = 0; /* safe since we bail out early if dstlen == 0 */
961 if (dirlen) { /* when dir is not empty */
962 memcpy(dst, dir, dirlen);
967 if (fnamelen) { /* when fname is not empty */
968 memcpy(dst + offset, fname, fnamelen);
975 char *mutt_concat_path (char *d, const char *dir, const char *fname, size_t l)
977 const char *fmt = "%s/%s";
979 if (!*fname || (*dir && dir[strlen(dir)-1] == '/'))
982 snprintf (d, l, fmt, dir, fname);
986 const char *mutt_basename (const char *f)
988 const char *p = strrchr (f, '/');
996 mutt_strsysexit(int e)
1000 for(i = 0; sysexits_h[i].str; i++)
1002 if(e == sysexits_h[i].v)
1006 return sysexits_h[i].str;