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)
766 if (ch > s && *(ch - 1) == '\r')
768 if (!(flags & M_CONT) || ch == s || *(ch - 1) != '\\')
775 c = getc (fp); /* This is kind of a hack. We want to know if the
776 char at the current point in the input stream is EOF.
777 feof() will only tell us if we've already hit EOF, not
778 if the next character is EOF. So, we need to read in
779 the next character and manually check if it is EOF. */
782 /* The last line of fp isn't \n terminated */
789 ungetc (c, fp); /* undo our dammage */
790 /* There wasn't room for the line -- increase ``s'' */
791 offset = *size - 1; /* overwrite the terminating 0 */
793 safe_realloc (&s, *size);
800 mutt_substrcpy (char *dest, const char *beg, const char *end, size_t destlen)
805 if (len > destlen - 1)
807 memcpy (dest, beg, len);
812 char *mutt_substrdup (const char *begin, const char *end)
820 len = strlen (begin);
822 p = safe_malloc (len + 1);
823 memcpy (p, begin, len);
828 /* prepare a file name to survive the shell's quoting rules.
829 * From the Unix programming FAQ by way of Liviu.
832 size_t mutt_quote_filename (char *d, size_t l, const char *f)
842 /* leave some space for the trailing characters. */
847 for(i = 0; j < l && f[i]; i++)
849 if(f[i] == '\'' || f[i] == '`')
866 /* NULL-pointer aware string comparison functions */
868 int mutt_strcmp(const char *a, const char *b)
870 return strcmp(NONULL(a), NONULL(b));
873 int mutt_strcasecmp(const char *a, const char *b)
875 return strcasecmp(NONULL(a), NONULL(b));
878 int mutt_strncmp(const char *a, const char *b, size_t l)
880 return strncmp(NONULL(a), NONULL(b), l);
883 int mutt_strncasecmp(const char *a, const char *b, size_t l)
885 return strncasecmp(NONULL(a), NONULL(b), l);
888 size_t mutt_strlen(const char *a)
890 return a ? strlen (a) : 0;
893 int mutt_strcoll(const char *a, const char *b)
895 return strcoll(NONULL(a), NONULL(b));
898 const char *mutt_stristr (const char *haystack, const char *needle)
907 while (*(p = haystack))
911 tolower ((unsigned char) *p) == tolower ((unsigned char) *q);
921 char *mutt_skip_whitespace (char *p)
927 void mutt_remove_trailing_ws (char *s)
931 for (p = s + mutt_strlen (s) - 1 ; p >= s && ISSPACE (*p) ; p--)
936 * Write the concatened pathname (dir + "/" + fname) into dst.
937 * The slash is ommitted when dir or fname is of 0 length.
938 * Returns NULL on error or a pointer to dst otherwise.
940 char *mutt_concatn_path (char *dst, size_t dstlen,
941 const char *dir, size_t dirlen, const char *fname, size_t fnamelen)
947 return NULL; /* probably should not mask errors like this */
950 req = dirlen + fnamelen + 1; /* +1 for the trailing nul */
951 if (dirlen && fnamelen)
952 req++; /* when both components are non-nul, we add a "/" in between */
953 if (req > dstlen) { /* check for condition where the dst length is too short */
955 * 1) assert(0) or return NULL to signal error
956 * 2) copy as much of the path as will fit
957 * It doesn't appear that the return value is actually checked anywhere mutt_concat_path()
958 * is called, so we should just copy set dst to nul and let the calling function fail later.
960 dst[0] = 0; /* safe since we bail out early if dstlen == 0 */
964 if (dirlen) { /* when dir is not empty */
965 memcpy(dst, dir, dirlen);
970 if (fnamelen) { /* when fname is not empty */
971 memcpy(dst + offset, fname, fnamelen);
978 char *mutt_concat_path (char *d, const char *dir, const char *fname, size_t l)
980 const char *fmt = "%s/%s";
982 if (!*fname || (*dir && dir[strlen(dir)-1] == '/'))
985 snprintf (d, l, fmt, dir, fname);
989 const char *mutt_basename (const char *f)
991 const char *p = strrchr (f, '/');
999 mutt_strsysexit(int e)
1003 for(i = 0; sysexits_h[i].str; i++)
1005 if(e == sysexits_h[i].v)
1009 return sysexits_h[i].str;
1012 void mutt_debug (FILE *fp, const char *fmt, ...)
1015 time_t now = time (NULL);
1016 static char buf[23] = "";
1017 static time_t last = 0;
1021 strftime (buf, sizeof (buf), "%Y-%m-%d %H:%M:%S", localtime (&now));
1024 fprintf (fp, "[%s] ", buf);
1026 vfprintf (fp, fmt, ap);
1030 int mutt_atos (const char *str, short *dst)
1035 short *t = dst ? dst : &tmp;
1039 if ((rc = mutt_atol (str, &res)) < 0)
1041 if ((short) res != res)
1048 int mutt_atoi (const char *str, int *dst)
1053 int *t = dst ? dst : &tmp;
1057 if ((rc = mutt_atol (str, &res)) < 0)
1059 if ((int) res != res)
1066 int mutt_atol (const char *str, long *dst)
1069 long *res = dst ? dst : &r;
1079 *res = strtol (str, &e, 10);
1080 if ((*res == LONG_MAX && errno == ERANGE) ||