2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4 * Copyright (C) 1999-2000 Thomas Roessler <roessler@does-not-exist.org>
6 * This file is part of mutt-ng, see http://www.muttng.org/.
7 * It's licensed under the GNU General Public License,
8 * please see the file GPL in the top level source directory.
12 * This file used to contain some more functions, namely those
13 * which are now in muttlib.c. They have been removed, so we have
14 * some of our "standard" functions in external programs, too.
35 void mutt_nocurses_error (const char *fmt, ...)
40 vfprintf (stderr, fmt, ap);
45 void *safe_calloc (size_t nmemb, size_t size)
52 if (((size_t) - 1) / nmemb <= size) {
53 mutt_error _("Integer overflow -- can't allocate memory!");
59 if (!(p = calloc (nmemb, size))) {
60 mutt_error _("Out of memory!");
68 void *safe_malloc (size_t siz)
74 if ((p = (void *) malloc (siz)) == 0) { /* __MEM_CHECKED__ */
75 mutt_error _("Out of memory!");
83 void safe_realloc (void *ptr, size_t siz)
86 void **p = (void **) ptr;
90 free (*p); /* __MEM_CHECKED__ */
97 r = (void *) realloc (*p, siz); /* __MEM_CHECKED__ */
99 /* realloc(NULL, nbytes) doesn't seem to work under SunOS 4.1.x --- __MEM_CHECKED__ */
100 r = (void *) malloc (siz); /* __MEM_CHECKED__ */
104 mutt_error _("Out of memory!");
113 void safe_free (void *ptr)
115 void **p = (void **) ptr;
118 free (*p); /* __MEM_CHECKED__ */
123 int safe_fclose (FILE ** f)
134 char *safe_strdup (const char *s)
141 l = mutt_strlen (s) + 1;
142 p = (char *) safe_malloc (l);
147 char *safe_strcat (char *d, size_t l, const char *s)
154 l--; /* Space for the trailing '\0'. */
166 char *safe_strncat (char *d, size_t l, const char *s, size_t sl)
173 l--; /* Space for the trailing '\0'. */
177 for (; *s && l && sl; l--, sl--)
186 void mutt_str_replace (char **p, const char *s)
189 *p = safe_strdup (s);
192 void mutt_str_adjust (char **p)
196 safe_realloc (p, mutt_strlen (*p) + 1);
199 /* convert all characters in the string to lowercase */
200 char *mutt_strlower (char *s)
205 *p = tolower ((unsigned char) *p);
212 void mutt_unlink (const char *s)
220 /* Defend against symlink attacks */
223 flags = O_RDWR | O_NOFOLLOW;
228 if (lstat (s, &sb) == 0 && S_ISREG (sb.st_mode)) {
229 if ((fd = open (s, flags)) < 0)
232 if ((fstat (fd, &sb2) != 0) || !S_ISREG (sb2.st_mode)
233 || (sb.st_dev != sb2.st_dev) || (sb.st_ino != sb2.st_ino)) {
238 if ((f = fdopen (fd, "r+"))) {
240 memset (buf, 0, sizeof (buf));
241 while (sb.st_size > 0) {
242 fwrite (buf, 1, MIN (sizeof (buf), sb.st_size), f);
243 sb.st_size -= MIN (sizeof (buf), sb.st_size);
250 int mutt_copy_bytes (FILE * in, FILE * out, size_t size)
256 chunk = (size > sizeof (buf)) ? sizeof (buf) : size;
257 if ((chunk = fread (buf, 1, chunk, in)) < 1)
259 if (fwrite (buf, 1, chunk, out) != chunk) {
260 /* dprint (1, (debugfile, "mutt_copy_bytes(): fwrite() returned short byte count\n")); */
269 int mutt_copy_stream (FILE * fin, FILE * fout)
272 char buf[LONG_STRING];
274 while ((l = fread (buf, 1, sizeof (buf), fin)) > 0) {
275 if (fwrite (buf, 1, l, fout) != l)
282 static int compare_stat (struct stat *osb, struct stat *nsb)
284 if (osb->st_dev != nsb->st_dev || osb->st_ino != nsb->st_ino ||
285 osb->st_rdev != nsb->st_rdev) {
292 int safe_symlink (const char *oldpath, const char *newpath)
294 struct stat osb, nsb;
296 if (!oldpath || !newpath)
299 if (unlink (newpath) == -1 && errno != ENOENT)
302 if (oldpath[0] == '/') {
303 if (symlink (oldpath, newpath) == -1)
307 char abs_oldpath[_POSIX_PATH_MAX];
309 if ((getcwd (abs_oldpath, sizeof abs_oldpath) == NULL) ||
310 (mutt_strlen (abs_oldpath) + 1 + mutt_strlen (oldpath) + 1 >
314 strcat (abs_oldpath, "/"); /* __STRCAT_CHECKED__ */
315 strcat (abs_oldpath, oldpath); /* __STRCAT_CHECKED__ */
316 if (symlink (abs_oldpath, newpath) == -1)
320 if (stat (oldpath, &osb) == -1 || stat (newpath, &nsb) == -1
321 || compare_stat (&osb, &nsb) == -1) {
330 * This function is supposed to do nfs-safe renaming of files.
332 * Warning: We don't check whether src and target are equal.
335 int safe_rename (const char *src, const char *target)
337 struct stat ssb, tsb;
342 if (link (src, target) != 0) {
345 * Coda does not allow cross-directory links, but tells
346 * us it's a cross-filesystem linking attempt.
348 * However, the Coda rename call is allegedly safe to use.
350 * With other file systems, rename should just fail when
351 * the files reside on different file systems, so it's safe
357 return rename (src, target);
363 * Stat both links and check if they are equal.
366 if (stat (src, &ssb) == -1) {
370 if (stat (target, &tsb) == -1) {
375 * pretend that the link failed because the target file
379 if (compare_stat (&ssb, &tsb) == -1) {
385 * Unlink the original link. Should we really ignore the return
394 int safe_open (const char *path, int flags)
396 struct stat osb, nsb;
400 if ((fd = open (path, flags, 0666)) < 0)
403 /* make sure the file is not symlink */
404 if (lstat (path, &osb) < 0 || fstat (fd, &nsb) < 0 ||
405 compare_stat (&osb, &nsb) == -1) {
406 /* dprint (1, (debugfile, "safe_open(): %s is a symlink!\n", path)); */
414 /* when opening files for writing, make sure the file doesn't already exist
415 * to avoid race conditions.
417 FILE *safe_fopen (const char *path, const char *mode)
419 /* first set the current umask */
421 if (mode[0] == 'w') {
423 int flags = O_CREAT | O_EXCL;
434 if ((fd = safe_open (path, flags)) < 0)
437 return (fdopen (fd, mode));
440 return (fopen (path, mode));
443 static char safe_chars[] =
444 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/";
446 void mutt_sanitize_filename (char *f, short slash)
452 if ((slash && *f == '/') || !strchr (safe_chars, *f))
457 /* these characters must be escaped in regular expressions */
459 static char rx_special_chars[] = "^.[$()|*+?{\\";
461 int mutt_rx_sanitize_string (char *dest, size_t destlen, const char *src)
463 while (*src && --destlen > 2) {
464 if (strchr (rx_special_chars, *src)) {
479 /* Read a line from ``fp'' into the dynamically allocated ``s'',
480 * increasing ``s'' if necessary. The ending "\n" or "\r\n" is removed.
481 * If a line ends with "\", this char and the linefeed is removed,
482 * and the next line is read too.
484 char *mutt_read_line (char *s, size_t * size, FILE * fp, int *line)
490 s = safe_malloc (STRING);
495 if (fgets (s + offset, *size - offset, fp) == NULL) {
499 if ((ch = strchr (s + offset, '\n')) != NULL) {
502 if (ch > s && *(ch - 1) == '\r')
504 if (ch == s || *(ch - 1) != '\\')
511 c = getc (fp); /* This is kind of a hack. We want to know if the
512 char at the current point in the input stream is EOF.
513 feof() will only tell us if we've already hit EOF, not
514 if the next character is EOF. So, we need to read in
515 the next character and manually check if it is EOF. */
517 /* The last line of fp isn't \n terminated */
522 ungetc (c, fp); /* undo our dammage */
523 /* There wasn't room for the line -- increase ``s'' */
524 offset = *size - 1; /* overwrite the terminating 0 */
526 safe_realloc (&s, *size);
532 char *mutt_substrcpy (char *dest, const char *beg, const char *end,
538 if (len > destlen - 1)
540 memcpy (dest, beg, len);
545 char *mutt_substrdup (const char *begin, const char *end)
553 len = mutt_strlen (begin);
555 p = safe_malloc (len + 1);
556 memcpy (p, begin, len);
561 /* prepare a file name to survive the shell's quoting rules.
562 * From the Unix programming FAQ by way of Liviu.
565 size_t mutt_quote_filename (char *d, size_t l, const char *f)
574 /* leave some space for the trailing characters. */
579 for (i = 0; j < l && f[i]; i++) {
580 if (f[i] == '\'' || f[i] == '`') {
596 /* NULL-pointer aware string comparison functions */
598 int mutt_strcmp (const char *a, const char *b)
600 return strcmp (NONULL (a), NONULL (b));
603 int mutt_strcasecmp (const char *a, const char *b)
605 return strcasecmp (NONULL (a), NONULL (b));
608 int mutt_strncmp (const char *a, const char *b, size_t l)
610 return strncmp (NONULL (a), NONULL (b), l);
613 int mutt_strncasecmp (const char *a, const char *b, size_t l)
615 return strncasecmp (NONULL (a), NONULL (b), l);
618 size_t mutt_strlen (const char *a)
620 return a ? strlen (a) : 0;
623 int mutt_strcoll (const char *a, const char *b)
625 return strcoll (NONULL (a), NONULL (b));
628 const char *mutt_stristr (const char *haystack, const char *needle)
637 while (*(p = haystack)) {
640 tolower ((unsigned char) *p) == tolower ((unsigned char) *q);
649 char *mutt_skip_whitespace (char *p)
655 void mutt_remove_trailing_ws (char *s)
659 for (p = s + mutt_strlen (s) - 1; p >= s && ISSPACE (*p); p--)
663 char *mutt_concat_path (char *d, const char *dir, const char *fname, size_t l)
665 const char *fmt = "%s/%s";
667 if (!*fname || (*dir && dir[mutt_strlen (dir) - 1] == '/'))
670 snprintf (d, l, fmt, dir, fname);
674 const char *mutt_basename (const char *f)
676 const char *p = strrchr (f, '/');