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.
43 void mutt_nocurses_error (const char *fmt, ...)
48 vfprintf (stderr, fmt, ap);
53 void *safe_calloc (size_t nmemb, size_t size)
60 if (((size_t) -1) / nmemb <= size)
62 mutt_error _("Integer overflow -- can't allocate memory!");
67 if (!(p = calloc (nmemb, size)))
69 mutt_error _("Out of memory!");
76 void *safe_malloc (size_t siz)
82 if ((p = (void *) malloc (siz)) == 0) /* __MEM_CHECKED__ */
84 mutt_error _("Out of memory!");
91 void safe_realloc (void *ptr, size_t siz)
94 void **p = (void **)ptr;
100 free (*p); /* __MEM_CHECKED__ */
107 r = (void *) realloc (*p, siz); /* __MEM_CHECKED__ */
110 /* realloc(NULL, nbytes) doesn't seem to work under SunOS 4.1.x --- __MEM_CHECKED__ */
111 r = (void *) malloc (siz); /* __MEM_CHECKED__ */
116 mutt_error _("Out of memory!");
124 void safe_free (void *ptr)
126 void **p = (void **)ptr;
129 free (*p); /* __MEM_CHECKED__ */
134 int safe_fclose (FILE **f)
145 char *safe_strdup (const char *s)
153 p = (char *)safe_malloc (l);
158 char *safe_strcat (char *d, size_t l, const char *s)
165 l--; /* Space for the trailing '\0'. */
177 char *safe_strncat (char *d, size_t l, const char *s, size_t sl)
184 l--; /* Space for the trailing '\0'. */
188 for (; *s && l && sl; l--, sl--)
197 void mutt_str_replace (char **p, const char *s)
200 *p = safe_strdup (s);
203 void mutt_str_adjust (char **p)
205 if (!p || !*p) return;
206 safe_realloc (p, strlen (*p) + 1);
209 /* convert all characters in the string to lowercase */
210 char *mutt_strlower (char *s)
216 *p = tolower ((unsigned char) *p);
223 void mutt_unlink (const char *s)
231 /* Defend against symlink attacks */
234 flags = O_RDWR | O_NOFOLLOW;
239 if (lstat (s, &sb) == 0 && S_ISREG(sb.st_mode))
241 if ((fd = open (s, flags)) < 0)
244 if ((fstat (fd, &sb2) != 0) || !S_ISREG (sb2.st_mode)
245 || (sb.st_dev != sb2.st_dev) || (sb.st_ino != sb2.st_ino))
251 if ((f = fdopen (fd, "r+")))
254 memset (buf, 0, sizeof (buf));
255 while (sb.st_size > 0)
257 fwrite (buf, 1, MIN (sizeof (buf), sb.st_size), f);
258 sb.st_size -= MIN (sizeof (buf), sb.st_size);
265 int mutt_copy_bytes (FILE *in, FILE *out, size_t size)
272 chunk = (size > sizeof (buf)) ? sizeof (buf) : size;
273 if ((chunk = fread (buf, 1, chunk, in)) < 1)
275 if (fwrite (buf, 1, chunk, out) != chunk)
277 /* dprint (1, (debugfile, "mutt_copy_bytes(): fwrite() returned short byte count\n")); */
286 int mutt_copy_stream (FILE *fin, FILE *fout)
289 char buf[LONG_STRING];
291 while ((l = fread (buf, 1, sizeof (buf), fin)) > 0)
293 if (fwrite (buf, 1, l, fout) != l)
301 compare_stat (struct stat *osb, struct stat *nsb)
303 if (osb->st_dev != nsb->st_dev || osb->st_ino != nsb->st_ino ||
304 osb->st_rdev != nsb->st_rdev)
312 int safe_symlink(const char *oldpath, const char *newpath)
314 struct stat osb, nsb;
316 if(!oldpath || !newpath)
319 if(unlink(newpath) == -1 && errno != ENOENT)
322 if (oldpath[0] == '/')
324 if (symlink (oldpath, newpath) == -1)
329 char abs_oldpath[_POSIX_PATH_MAX];
331 if ((getcwd (abs_oldpath, sizeof abs_oldpath) == NULL) ||
332 (strlen (abs_oldpath) + 1 + strlen (oldpath) + 1 > sizeof abs_oldpath))
335 strcat (abs_oldpath, "/"); /* __STRCAT_CHECKED__ */
336 strcat (abs_oldpath, oldpath); /* __STRCAT_CHECKED__ */
337 if (symlink (abs_oldpath, newpath) == -1)
341 if(stat(oldpath, &osb) == -1 || stat(newpath, &nsb) == -1
342 || compare_stat(&osb, &nsb) == -1)
352 * This function is supposed to do nfs-safe renaming of files.
354 * Warning: We don't check whether src and target are equal.
357 int safe_rename (const char *src, const char *target)
359 struct stat ssb, tsb;
364 if (link (src, target) != 0)
368 * Coda does not allow cross-directory links, but tells
369 * us it's a cross-filesystem linking attempt.
371 * However, the Coda rename call is allegedly safe to use.
373 * With other file systems, rename should just fail when
374 * the files reside on different file systems, so it's safe
380 return rename (src, target);
386 * Stat both links and check if they are equal.
389 if (stat (src, &ssb) == -1)
394 if (stat (target, &tsb) == -1)
400 * pretend that the link failed because the target file
404 if (compare_stat (&ssb, &tsb) == -1)
411 * Unlink the original link. Should we really ignore the return
420 int safe_open (const char *path, int flags)
422 struct stat osb, nsb;
426 if ((fd = open (path, flags, 0666)) < 0)
429 /* make sure the file is not symlink */
430 if (lstat (path, &osb) < 0 || fstat (fd, &nsb) < 0 ||
431 compare_stat(&osb, &nsb) == -1)
433 /* dprint (1, (debugfile, "safe_open(): %s is a symlink!\n", path)); */
441 /* when opening files for writing, make sure the file doesn't already exist
442 * to avoid race conditions.
444 FILE *safe_fopen (const char *path, const char *mode)
446 /* first set the current umask */
451 int flags = O_CREAT | O_EXCL;
462 if ((fd = safe_open (path, flags)) < 0)
465 return (fdopen (fd, mode));
468 return (fopen (path, mode));
471 static char safe_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/";
473 void mutt_sanitize_filename (char *f, short slash)
479 if ((slash && *f == '/') || !strchr (safe_chars, *f))
484 /* these characters must be escaped in regular expressions */
486 static char rx_special_chars[] = "^.[$()|*+?{\\";
488 int mutt_rx_sanitize_string (char *dest, size_t destlen, const char *src)
490 while (*src && --destlen > 2)
492 if (strchr (rx_special_chars, *src))
508 /* Read a line from ``fp'' into the dynamically allocated ``s'',
509 * increasing ``s'' if necessary. The ending "\n" or "\r\n" is removed.
510 * If a line ends with "\", this char and the linefeed is removed,
511 * and the next line is read too.
513 char *mutt_read_line (char *s, size_t *size, FILE *fp, int *line)
520 s = safe_malloc (STRING);
526 if (fgets (s + offset, *size - offset, fp) == NULL)
531 if ((ch = strchr (s + offset, '\n')) != NULL)
535 if (ch > s && *(ch - 1) == '\r')
537 if (ch == s || *(ch - 1) != '\\')
544 c = getc (fp); /* This is kind of a hack. We want to know if the
545 char at the current point in the input stream is EOF.
546 feof() will only tell us if we've already hit EOF, not
547 if the next character is EOF. So, we need to read in
548 the next character and manually check if it is EOF. */
551 /* The last line of fp isn't \n terminated */
557 ungetc (c, fp); /* undo our dammage */
558 /* There wasn't room for the line -- increase ``s'' */
559 offset = *size - 1; /* overwrite the terminating 0 */
561 safe_realloc (&s, *size);
568 mutt_substrcpy (char *dest, const char *beg, const char *end, size_t destlen)
573 if (len > destlen - 1)
575 memcpy (dest, beg, len);
580 char *mutt_substrdup (const char *begin, const char *end)
588 len = strlen (begin);
590 p = safe_malloc (len + 1);
591 memcpy (p, begin, len);
596 /* prepare a file name to survive the shell's quoting rules.
597 * From the Unix programming FAQ by way of Liviu.
600 size_t mutt_quote_filename (char *d, size_t l, const char *f)
610 /* leave some space for the trailing characters. */
615 for(i = 0; j < l && f[i]; i++)
617 if(f[i] == '\'' || f[i] == '`')
634 /* NULL-pointer aware string comparison functions */
636 int mutt_strcmp(const char *a, const char *b)
638 return strcmp(NONULL(a), NONULL(b));
641 int mutt_strcasecmp(const char *a, const char *b)
643 return strcasecmp(NONULL(a), NONULL(b));
646 int mutt_strncmp(const char *a, const char *b, size_t l)
648 return strncmp(NONULL(a), NONULL(b), l);
651 int mutt_strncasecmp(const char *a, const char *b, size_t l)
653 return strncasecmp(NONULL(a), NONULL(b), l);
656 size_t mutt_strlen(const char *a)
658 return a ? strlen (a) : 0;
661 int mutt_strcoll(const char *a, const char *b)
663 return strcoll(NONULL(a), NONULL(b));
666 const char *mutt_stristr (const char *haystack, const char *needle)
675 while (*(p = haystack))
679 tolower ((unsigned char) *p) == tolower ((unsigned char) *q);
689 char *mutt_skip_whitespace (char *p)
695 void mutt_remove_trailing_ws (char *s)
699 for (p = s + mutt_strlen (s) - 1 ; p >= s && ISSPACE (*p) ; p--)
703 char *mutt_concat_path (char *d, const char *dir, const char *fname, size_t l)
705 const char *fmt = "%s/%s";
707 if (!*fname || (*dir && dir[strlen(dir)-1] == '/'))
710 snprintf (d, l, fmt, dir, fname);
714 const char *mutt_basename (const char *f)
716 const char *p = strrchr (f, '/');