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)))
232 char *safe_strdup (const char *s)
240 p = (char *)safe_malloc (l);
245 char *safe_strcat (char *d, size_t l, const char *s)
252 l--; /* Space for the trailing '\0'. */
264 char *safe_strncat (char *d, size_t l, const char *s, size_t sl)
271 l--; /* Space for the trailing '\0'. */
275 for (; *s && l && sl; l--, sl--)
284 void mutt_str_replace (char **p, const char *s)
286 FREE (p); /* __FREE_CHECKED__ */
287 *p = safe_strdup (s);
290 void mutt_str_adjust (char **p)
292 if (!p || !*p) return;
293 safe_realloc (p, strlen (*p) + 1);
296 /* convert all characters in the string to lowercase */
297 char *mutt_strlower (char *s)
303 *p = tolower ((unsigned char) *p);
310 void mutt_unlink (const char *s)
318 /* Defend against symlink attacks */
321 flags = O_RDWR | O_NOFOLLOW;
326 if (lstat (s, &sb) == 0 && S_ISREG(sb.st_mode))
328 if ((fd = open (s, flags)) < 0)
331 if ((fstat (fd, &sb2) != 0) || !S_ISREG (sb2.st_mode)
332 || (sb.st_dev != sb2.st_dev) || (sb.st_ino != sb2.st_ino))
338 if ((f = fdopen (fd, "r+")))
341 memset (buf, 0, sizeof (buf));
342 while (sb.st_size > 0)
344 fwrite (buf, 1, MIN (sizeof (buf), sb.st_size), f);
345 sb.st_size -= MIN (sizeof (buf), sb.st_size);
352 int mutt_copy_bytes (FILE *in, FILE *out, size_t size)
359 chunk = (size > sizeof (buf)) ? sizeof (buf) : size;
360 if ((chunk = fread (buf, 1, chunk, in)) < 1)
362 if (fwrite (buf, 1, chunk, out) != chunk)
364 /* dprint (1, (debugfile, "mutt_copy_bytes(): fwrite() returned short byte count\n")); */
373 int mutt_copy_stream (FILE *fin, FILE *fout)
376 char buf[LONG_STRING];
378 while ((l = fread (buf, 1, sizeof (buf), fin)) > 0)
380 if (fwrite (buf, 1, l, fout) != l)
388 compare_stat (struct stat *osb, struct stat *nsb)
390 if (osb->st_dev != nsb->st_dev || osb->st_ino != nsb->st_ino ||
391 osb->st_rdev != nsb->st_rdev)
399 int safe_symlink(const char *oldpath, const char *newpath)
401 struct stat osb, nsb;
403 if(!oldpath || !newpath)
406 if(unlink(newpath) == -1 && errno != ENOENT)
409 if (oldpath[0] == '/')
411 if (symlink (oldpath, newpath) == -1)
416 char abs_oldpath[_POSIX_PATH_MAX];
418 if ((getcwd (abs_oldpath, sizeof abs_oldpath) == NULL) ||
419 (strlen (abs_oldpath) + 1 + strlen (oldpath) + 1 > sizeof abs_oldpath))
422 strcat (abs_oldpath, "/"); /* __STRCAT_CHECKED__ */
423 strcat (abs_oldpath, oldpath); /* __STRCAT_CHECKED__ */
424 if (symlink (abs_oldpath, newpath) == -1)
428 if(stat(oldpath, &osb) == -1 || stat(newpath, &nsb) == -1
429 || compare_stat(&osb, &nsb) == -1)
441 * This function is supposed to do nfs-safe renaming of files.
443 * Warning: We don't check whether src and target are equal.
446 int safe_rename (const char *src, const char *target)
448 struct stat ssb, tsb;
453 if (link (src, target) != 0)
457 * Coda does not allow cross-directory links, but tells
458 * us it's a cross-filesystem linking attempt.
460 * However, the Coda rename call is allegedly safe to use.
462 * With other file systems, rename should just fail when
463 * the files reside on different file systems, so it's safe
468 dprint (1, (debugfile, "safe_rename: link (%s, %s) failed: %s (%d)\n", src, target, strerror (errno), errno));
471 * FUSE may return ENOSYS. VFAT may return EPERM. FreeBSD's
472 * msdosfs may return EOPNOTSUPP. ENOTSUP can also appear.
474 if (errno == EXDEV || errno == ENOSYS || errno == EPERM
479 || errno == EOPNOTSUPP
483 dprint (1, (debugfile, "safe_rename: trying rename...\n"));
484 if (rename (src, target) == -1)
486 dprint (1, (debugfile, "safe_rename: rename (%s, %s) failed: %s (%d)\n", src, target, strerror (errno), errno));
489 dprint (1, (debugfile, "safe_rename: rename succeeded.\n"));
498 * Stat both links and check if they are equal.
501 if (lstat (src, &ssb) == -1)
503 dprint (1, (debugfile, "safe_rename: can't stat %s: %s (%d)\n",
504 src, strerror (errno), errno));
508 if (lstat (target, &tsb) == -1)
510 dprint (1, (debugfile, "safe_rename: can't stat %s: %s (%d)\n",
511 src, strerror (errno), errno));
516 * pretend that the link failed because the target file
520 if (compare_stat (&ssb, &tsb) == -1)
522 dprint (1, (debugfile, "safe_rename: stat blocks for %s and %s diverge; pretending EEXIST.\n", src, target));
528 * Unlink the original link. Should we really ignore the return
532 if (unlink (src) == -1)
534 dprint (1, (debugfile, "safe_rename: unlink (%s) failed: %s (%d)\n",
535 src, strerror (errno), errno));
543 /* Create a temporary directory next to a file name */
545 static int mutt_mkwrapdir (const char *path, char *newfile, size_t nflen,
546 char *newdir, size_t ndlen)
548 const char *basename;
549 char parent[_POSIX_PATH_MAX];
553 strfcpy (parent, NONULL (path), sizeof (parent));
555 if ((p = strrchr (parent, '/')))
562 strfcpy (parent, ".", sizeof (parent));
568 snprintf (newdir, ndlen, "%s/%s", parent, ".muttXXXXXX");
571 while ((rv = mkdir (newdir, 0700)) == -1 && errno == EEXIST);
576 snprintf (newfile, nflen, "%s/%s", newdir, NONULL(basename));
580 /* remove a directory and everything under it */
581 int mutt_rmtree (const char* path)
585 char cur[_POSIX_PATH_MAX];
589 if (!(dirp = opendir (path)))
591 dprint (1, (debugfile, "mutt_rmtree: error opening directory %s\n", path));
594 while ((de = readdir (dirp)))
596 if (!strcmp (".", de->d_name) || !strcmp ("..", de->d_name))
599 snprintf (cur, sizeof (cur), "%s/%s", path, de->d_name);
600 /* XXX make nonrecursive version */
602 if (stat(cur, &statbuf) == -1)
608 if (S_ISDIR (statbuf.st_mode))
609 rc |= mutt_rmtree (cur);
620 static int mutt_put_file_in_place (const char *path, const char *safe_file, const char *safe_dir)
624 rv = safe_rename (safe_file, path);
630 int safe_open (const char *path, int flags)
632 struct stat osb, nsb;
637 char safe_file[_POSIX_PATH_MAX];
638 char safe_dir[_POSIX_PATH_MAX];
640 if (mutt_mkwrapdir (path, safe_file, sizeof (safe_file),
641 safe_dir, sizeof (safe_dir)) == -1)
644 if ((fd = open (safe_file, flags, 0600)) < 0)
650 /* NFS and I believe cygwin do not handle movement of open files well */
652 if (mutt_put_file_in_place (path, safe_file, safe_dir) == -1)
656 if ((fd = open (path, flags & ~O_EXCL, 0600)) < 0)
659 /* make sure the file is not symlink */
660 if (lstat (path, &osb) < 0 || fstat (fd, &nsb) < 0 ||
661 compare_stat(&osb, &nsb) == -1)
663 /* dprint (1, (debugfile, "safe_open(): %s is a symlink!\n", path)); */
671 /* when opening files for writing, make sure the file doesn't already exist
672 * to avoid race conditions.
674 FILE *safe_fopen (const char *path, const char *mode)
679 int flags = O_CREAT | O_EXCL;
690 if ((fd = safe_open (path, flags)) < 0)
693 return (fdopen (fd, mode));
696 return (fopen (path, mode));
699 static char safe_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/";
701 void mutt_sanitize_filename (char *f, short slash)
707 if ((slash && *f == '/') || !strchr (safe_chars, *f))
712 /* these characters must be escaped in regular expressions */
714 static char rx_special_chars[] = "^.[$()|*+?{\\";
716 int mutt_rx_sanitize_string (char *dest, size_t destlen, const char *src)
718 while (*src && --destlen > 2)
720 if (strchr (rx_special_chars, *src))
736 /* Read a line from ``fp'' into the dynamically allocated ``s'',
737 * increasing ``s'' if necessary. The ending "\n" or "\r\n" is removed.
738 * If a line ends with "\", this char and the linefeed is removed,
739 * and the next line is read too.
741 char *mutt_read_line (char *s, size_t *size, FILE *fp, int *line, int flags)
748 s = safe_malloc (STRING);
754 if (fgets (s + offset, *size - offset, fp) == NULL)
759 if ((ch = strchr (s + offset, '\n')) != NULL)
765 if (ch > s && *(ch - 1) == '\r')
767 if (!(flags & M_CONT) || ch == s || *(ch - 1) != '\\')
774 c = getc (fp); /* This is kind of a hack. We want to know if the
775 char at the current point in the input stream is EOF.
776 feof() will only tell us if we've already hit EOF, not
777 if the next character is EOF. So, we need to read in
778 the next character and manually check if it is EOF. */
781 /* The last line of fp isn't \n terminated */
787 ungetc (c, fp); /* undo our dammage */
788 /* There wasn't room for the line -- increase ``s'' */
789 offset = *size - 1; /* overwrite the terminating 0 */
791 safe_realloc (&s, *size);
798 mutt_substrcpy (char *dest, const char *beg, const char *end, size_t destlen)
803 if (len > destlen - 1)
805 memcpy (dest, beg, len);
810 char *mutt_substrdup (const char *begin, const char *end)
818 len = strlen (begin);
820 p = safe_malloc (len + 1);
821 memcpy (p, begin, len);
826 /* prepare a file name to survive the shell's quoting rules.
827 * From the Unix programming FAQ by way of Liviu.
830 size_t mutt_quote_filename (char *d, size_t l, const char *f)
840 /* leave some space for the trailing characters. */
845 for(i = 0; j < l && f[i]; i++)
847 if(f[i] == '\'' || f[i] == '`')
864 /* NULL-pointer aware string comparison functions */
866 int mutt_strcmp(const char *a, const char *b)
868 return strcmp(NONULL(a), NONULL(b));
871 int mutt_strcasecmp(const char *a, const char *b)
873 return strcasecmp(NONULL(a), NONULL(b));
876 int mutt_strncmp(const char *a, const char *b, size_t l)
878 return strncmp(NONULL(a), NONULL(b), l);
881 int mutt_strncasecmp(const char *a, const char *b, size_t l)
883 return strncasecmp(NONULL(a), NONULL(b), l);
886 size_t mutt_strlen(const char *a)
888 return a ? strlen (a) : 0;
891 int mutt_strcoll(const char *a, const char *b)
893 return strcoll(NONULL(a), NONULL(b));
896 const char *mutt_stristr (const char *haystack, const char *needle)
905 while (*(p = haystack))
909 tolower ((unsigned char) *p) == tolower ((unsigned char) *q);
919 char *mutt_skip_whitespace (char *p)
925 void mutt_remove_trailing_ws (char *s)
929 for (p = s + mutt_strlen (s) - 1 ; p >= s && ISSPACE (*p) ; p--)
934 * Write the concatened pathname (dir + "/" + fname) into dst.
935 * The slash is ommitted when dir or fname is of 0 length.
936 * Returns NULL on error or a pointer to dst otherwise.
938 char *mutt_concatn_path (char *dst, size_t dstlen,
939 const char *dir, size_t dirlen, const char *fname, size_t fnamelen)
945 return NULL; /* probably should not mask errors like this */
948 req = dirlen + fnamelen + 1; /* +1 for the trailing nul */
949 if (dirlen && fnamelen)
950 req++; /* when both components are non-nul, we add a "/" in between */
951 if (req > dstlen) { /* check for condition where the dst length is too short */
953 * 1) assert(0) or return NULL to signal error
954 * 2) copy as much of the path as will fit
955 * It doesn't appear that the return value is actually checked anywhere mutt_concat_path()
956 * is called, so we should just copy set dst to nul and let the calling function fail later.
958 dst[0] = 0; /* safe since we bail out early if dstlen == 0 */
962 if (dirlen) { /* when dir is not empty */
963 memcpy(dst, dir, dirlen);
968 if (fnamelen) { /* when fname is not empty */
969 memcpy(dst + offset, fname, fnamelen);
976 char *mutt_concat_path (char *d, const char *dir, const char *fname, size_t l)
978 const char *fmt = "%s/%s";
980 if (!*fname || (*dir && dir[strlen(dir)-1] == '/'))
983 snprintf (d, l, fmt, dir, fname);
987 const char *mutt_basename (const char *f)
989 const char *p = strrchr (f, '/');
997 mutt_strsysexit(int e)
1001 for(i = 0; sysexits_h[i].str; i++)
1003 if(e == sysexits_h[i].v)
1007 return sysexits_h[i].str;
1010 int mutt_atos (const char *str, short *dst)
1015 short *t = dst ? dst : &tmp;
1019 if ((rc = mutt_atol (str, &res)) < 0)
1021 if ((short) res != res)
1028 int mutt_atoi (const char *str, int *dst)
1033 int *t = dst ? dst : &tmp;
1037 if ((rc = mutt_atol (str, &res)) < 0)
1039 if ((int) res != res)
1046 int mutt_atol (const char *str, long *dst)
1049 long *res = dst ? dst : &r;
1059 *res = strtol (str, &e, 10);
1060 if (e && *e != '\0')