2 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
3 * Copyright (C) 1999-2000 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., 59 Temple Place - Suite 330,
20 * Boston, MA 02111, 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.
47 void mutt_nocurses_error (const char *fmt, ...)
52 vfprintf (stderr, fmt, ap);
57 void *safe_calloc (size_t nmemb, size_t size)
64 if (((size_t) -1) / nmemb <= size)
66 mutt_error _("Integer overflow -- can't allocate memory!");
71 if (!(p = calloc (nmemb, size)))
73 mutt_error _("Out of memory!");
80 void *safe_malloc (size_t siz)
86 if ((p = (void *) malloc (siz)) == 0) /* __MEM_CHECKED__ */
88 mutt_error _("Out of memory!");
95 void safe_realloc (void *ptr, size_t siz)
98 void **p = (void **)ptr;
104 free (*p); /* __MEM_CHECKED__ */
111 r = (void *) realloc (*p, siz); /* __MEM_CHECKED__ */
114 /* realloc(NULL, nbytes) doesn't seem to work under SunOS 4.1.x --- __MEM_CHECKED__ */
115 r = (void *) malloc (siz); /* __MEM_CHECKED__ */
120 mutt_error _("Out of memory!");
128 void safe_free (void *ptr)
130 void **p = (void **)ptr;
133 free (*p); /* __MEM_CHECKED__ */
138 int safe_fclose (FILE **f)
149 char *safe_strdup (const char *s)
157 p = (char *)safe_malloc (l);
162 char *safe_strcat (char *d, size_t l, const char *s)
169 l--; /* Space for the trailing '\0'. */
181 char *safe_strncat (char *d, size_t l, const char *s, size_t sl)
188 l--; /* Space for the trailing '\0'. */
192 for (; *s && l && sl; l--, sl--)
201 void mutt_str_replace (char **p, const char *s)
204 *p = safe_strdup (s);
207 void mutt_str_adjust (char **p)
209 if (!p || !*p) return;
210 safe_realloc (p, strlen (*p) + 1);
213 /* convert all characters in the string to lowercase */
214 char *mutt_strlower (char *s)
220 *p = tolower ((unsigned char) *p);
227 void mutt_unlink (const char *s)
235 /* Defend against symlink attacks */
238 flags = O_RDWR | O_NOFOLLOW;
243 if (lstat (s, &sb) == 0 && S_ISREG(sb.st_mode))
245 if ((fd = open (s, flags)) < 0)
248 if ((fstat (fd, &sb2) != 0) || !S_ISREG (sb2.st_mode)
249 || (sb.st_dev != sb2.st_dev) || (sb.st_ino != sb2.st_ino))
255 if ((f = fdopen (fd, "r+")))
258 memset (buf, 0, sizeof (buf));
259 while (sb.st_size > 0)
261 fwrite (buf, 1, MIN (sizeof (buf), sb.st_size), f);
262 sb.st_size -= MIN (sizeof (buf), sb.st_size);
269 int mutt_copy_bytes (FILE *in, FILE *out, size_t size)
276 chunk = (size > sizeof (buf)) ? sizeof (buf) : size;
277 if ((chunk = fread (buf, 1, chunk, in)) < 1)
279 if (fwrite (buf, 1, chunk, out) != chunk)
281 /* dprint (1, (debugfile, "mutt_copy_bytes(): fwrite() returned short byte count\n")); */
290 int mutt_copy_stream (FILE *fin, FILE *fout)
293 char buf[LONG_STRING];
295 while ((l = fread (buf, 1, sizeof (buf), fin)) > 0)
297 if (fwrite (buf, 1, l, fout) != l)
305 compare_stat (struct stat *osb, struct stat *nsb)
307 if (osb->st_dev != nsb->st_dev || osb->st_ino != nsb->st_ino ||
308 osb->st_rdev != nsb->st_rdev)
316 int safe_symlink(const char *oldpath, const char *newpath)
318 struct stat osb, nsb;
320 if(!oldpath || !newpath)
323 if(unlink(newpath) == -1 && errno != ENOENT)
326 if (oldpath[0] == '/')
328 if (symlink (oldpath, newpath) == -1)
333 char abs_oldpath[_POSIX_PATH_MAX];
335 if ((getcwd (abs_oldpath, sizeof abs_oldpath) == NULL) ||
336 (strlen (abs_oldpath) + 1 + strlen (oldpath) + 1 > sizeof abs_oldpath))
339 strcat (abs_oldpath, "/"); /* __STRCAT_CHECKED__ */
340 strcat (abs_oldpath, oldpath); /* __STRCAT_CHECKED__ */
341 if (symlink (abs_oldpath, newpath) == -1)
345 if(stat(oldpath, &osb) == -1 || stat(newpath, &nsb) == -1
346 || compare_stat(&osb, &nsb) == -1)
356 * This function is supposed to do nfs-safe renaming of files.
358 * Warning: We don't check whether src and target are equal.
361 int safe_rename (const char *src, const char *target)
363 struct stat ssb, tsb;
368 if (link (src, target) != 0)
372 * Coda does not allow cross-directory links, but tells
373 * us it's a cross-filesystem linking attempt.
375 * However, the Coda rename call is allegedly safe to use.
377 * With other file systems, rename should just fail when
378 * the files reside on different file systems, so it's safe
384 return rename (src, target);
390 * Stat both links and check if they are equal.
393 if (stat (src, &ssb) == -1)
398 if (stat (target, &tsb) == -1)
404 * pretend that the link failed because the target file
408 if (compare_stat (&ssb, &tsb) == -1)
415 * Unlink the original link. Should we really ignore the return
424 int safe_open (const char *path, int flags)
426 struct stat osb, nsb;
430 if ((fd = open (path, flags, 0666)) < 0)
433 /* make sure the file is not symlink */
434 if (lstat (path, &osb) < 0 || fstat (fd, &nsb) < 0 ||
435 compare_stat(&osb, &nsb) == -1)
437 /* dprint (1, (debugfile, "safe_open(): %s is a symlink!\n", path)); */
445 /* when opening files for writing, make sure the file doesn't already exist
446 * to avoid race conditions.
448 FILE *safe_fopen (const char *path, const char *mode)
450 /* first set the current umask */
455 int flags = O_CREAT | O_EXCL;
466 if ((fd = safe_open (path, flags)) < 0)
469 return (fdopen (fd, mode));
472 return (fopen (path, mode));
475 static char safe_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/";
477 void mutt_sanitize_filename (char *f, short slash)
483 if ((slash && *f == '/') || !strchr (safe_chars, *f))
488 /* these characters must be escaped in regular expressions */
490 static char rx_special_chars[] = "^.[$()|*+?{\\";
492 int mutt_rx_sanitize_string (char *dest, size_t destlen, const char *src)
494 while (*src && --destlen > 2)
496 if (strchr (rx_special_chars, *src))
512 /* Read a line from ``fp'' into the dynamically allocated ``s'',
513 * increasing ``s'' if necessary. The ending "\n" or "\r\n" is removed.
514 * If a line ends with "\", this char and the linefeed is removed,
515 * and the next line is read too.
517 char *mutt_read_line (char *s, size_t *size, FILE *fp, int *line)
524 s = safe_malloc (STRING);
530 if (fgets (s + offset, *size - offset, fp) == NULL)
535 if ((ch = strchr (s + offset, '\n')) != NULL)
539 if (ch > s && *(ch - 1) == '\r')
541 if (ch == s || *(ch - 1) != '\\')
548 c = getc (fp); /* This is kind of a hack. We want to know if the
549 char at the current point in the input stream is EOF.
550 feof() will only tell us if we've already hit EOF, not
551 if the next character is EOF. So, we need to read in
552 the next character and manually check if it is EOF. */
555 /* The last line of fp isn't \n terminated */
561 ungetc (c, fp); /* undo our dammage */
562 /* There wasn't room for the line -- increase ``s'' */
563 offset = *size - 1; /* overwrite the terminating 0 */
565 safe_realloc (&s, *size);
572 mutt_substrcpy (char *dest, const char *beg, const char *end, size_t destlen)
577 if (len > destlen - 1)
579 memcpy (dest, beg, len);
584 char *mutt_substrdup (const char *begin, const char *end)
592 len = strlen (begin);
594 p = safe_malloc (len + 1);
595 memcpy (p, begin, len);
600 /* prepare a file name to survive the shell's quoting rules.
601 * From the Unix programming FAQ by way of Liviu.
604 size_t mutt_quote_filename (char *d, size_t l, const char *f)
614 /* leave some space for the trailing characters. */
619 for(i = 0; j < l && f[i]; i++)
621 if(f[i] == '\'' || f[i] == '`')
638 /* NULL-pointer aware string comparison functions */
640 int mutt_strcmp(const char *a, const char *b)
642 return strcmp(NONULL(a), NONULL(b));
645 int mutt_strcasecmp(const char *a, const char *b)
647 return strcasecmp(NONULL(a), NONULL(b));
650 int mutt_strncmp(const char *a, const char *b, size_t l)
652 return strncmp(NONULL(a), NONULL(b), l);
655 int mutt_strncasecmp(const char *a, const char *b, size_t l)
657 return strncasecmp(NONULL(a), NONULL(b), l);
660 size_t mutt_strlen(const char *a)
662 return a ? strlen (a) : 0;
665 int mutt_strcoll(const char *a, const char *b)
667 return strcoll(NONULL(a), NONULL(b));
670 const char *mutt_stristr (const char *haystack, const char *needle)
679 while (*(p = haystack))
683 tolower ((unsigned char) *p) == tolower ((unsigned char) *q);
693 char *mutt_skip_whitespace (char *p)
699 void mutt_remove_trailing_ws (char *s)
703 for (p = s + mutt_strlen (s) - 1 ; p >= s && ISSPACE (*p) ; p--)
707 char *mutt_concat_path (char *d, const char *dir, const char *fname, size_t l)
709 const char *fmt = "%s/%s";
711 if (!*fname || (*dir && dir[strlen(dir)-1] == '/'))
714 snprintf (d, l, fmt, dir, fname);
718 const char *mutt_basename (const char *f)
720 const char *p = strrchr (f, '/');