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) {
65 mutt_error _("Integer overflow -- can't allocate memory!");
71 if (!(p = calloc (nmemb, size))) {
72 mutt_error _("Out of memory!");
80 void *safe_malloc (size_t siz)
86 if ((p = (void *) malloc (siz)) == 0) { /* __MEM_CHECKED__ */
87 mutt_error _("Out of memory!");
95 void safe_realloc (void *ptr, size_t siz)
98 void **p = (void **) ptr;
102 free (*p); /* __MEM_CHECKED__ */
109 r = (void *) realloc (*p, siz); /* __MEM_CHECKED__ */
111 /* realloc(NULL, nbytes) doesn't seem to work under SunOS 4.1.x --- __MEM_CHECKED__ */
112 r = (void *) malloc (siz); /* __MEM_CHECKED__ */
116 mutt_error _("Out of memory!");
125 void safe_free (void *ptr)
127 void **p = (void **) ptr;
130 free (*p); /* __MEM_CHECKED__ */
135 int safe_fclose (FILE ** f)
146 char *safe_strdup (const char *s)
154 p = (char *) safe_malloc (l);
159 char *safe_strcat (char *d, size_t l, const char *s)
166 l--; /* Space for the trailing '\0'. */
178 char *safe_strncat (char *d, size_t l, const char *s, size_t sl)
185 l--; /* Space for the trailing '\0'. */
189 for (; *s && l && sl; l--, sl--)
198 void mutt_str_replace (char **p, const char *s)
201 *p = safe_strdup (s);
204 void mutt_str_adjust (char **p)
208 safe_realloc (p, strlen (*p) + 1);
211 /* convert all characters in the string to lowercase */
212 char *mutt_strlower (char *s)
217 *p = tolower ((unsigned char) *p);
224 void mutt_unlink (const char *s)
232 /* Defend against symlink attacks */
235 flags = O_RDWR | O_NOFOLLOW;
240 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)) {
250 if ((f = fdopen (fd, "r+"))) {
252 memset (buf, 0, sizeof (buf));
253 while (sb.st_size > 0) {
254 fwrite (buf, 1, MIN (sizeof (buf), sb.st_size), f);
255 sb.st_size -= MIN (sizeof (buf), sb.st_size);
262 int mutt_copy_bytes (FILE * in, FILE * out, size_t size)
268 chunk = (size > sizeof (buf)) ? sizeof (buf) : size;
269 if ((chunk = fread (buf, 1, chunk, in)) < 1)
271 if (fwrite (buf, 1, chunk, out) != chunk) {
272 /* dprint (1, (debugfile, "mutt_copy_bytes(): fwrite() returned short byte count\n")); */
281 int mutt_copy_stream (FILE * fin, FILE * fout)
284 char buf[LONG_STRING];
286 while ((l = fread (buf, 1, sizeof (buf), fin)) > 0) {
287 if (fwrite (buf, 1, l, fout) != l)
294 static int compare_stat (struct stat *osb, struct stat *nsb)
296 if (osb->st_dev != nsb->st_dev || osb->st_ino != nsb->st_ino ||
297 osb->st_rdev != nsb->st_rdev) {
304 int safe_symlink (const char *oldpath, const char *newpath)
306 struct stat osb, nsb;
308 if (!oldpath || !newpath)
311 if (unlink (newpath) == -1 && errno != ENOENT)
314 if (oldpath[0] == '/') {
315 if (symlink (oldpath, newpath) == -1)
319 char abs_oldpath[_POSIX_PATH_MAX];
321 if ((getcwd (abs_oldpath, sizeof abs_oldpath) == NULL) ||
322 (strlen (abs_oldpath) + 1 + strlen (oldpath) + 1 >
326 strcat (abs_oldpath, "/"); /* __STRCAT_CHECKED__ */
327 strcat (abs_oldpath, oldpath); /* __STRCAT_CHECKED__ */
328 if (symlink (abs_oldpath, newpath) == -1)
332 if (stat (oldpath, &osb) == -1 || stat (newpath, &nsb) == -1
333 || compare_stat (&osb, &nsb) == -1) {
342 * This function is supposed to do nfs-safe renaming of files.
344 * Warning: We don't check whether src and target are equal.
347 int safe_rename (const char *src, const char *target)
349 struct stat ssb, tsb;
354 if (link (src, target) != 0) {
357 * Coda does not allow cross-directory links, but tells
358 * us it's a cross-filesystem linking attempt.
360 * However, the Coda rename call is allegedly safe to use.
362 * With other file systems, rename should just fail when
363 * the files reside on different file systems, so it's safe
369 return rename (src, target);
375 * Stat both links and check if they are equal.
378 if (stat (src, &ssb) == -1) {
382 if (stat (target, &tsb) == -1) {
387 * pretend that the link failed because the target file
391 if (compare_stat (&ssb, &tsb) == -1) {
397 * Unlink the original link. Should we really ignore the return
406 int safe_open (const char *path, int flags)
408 struct stat osb, nsb;
412 if ((fd = open (path, flags, 0666)) < 0)
415 /* make sure the file is not symlink */
416 if (lstat (path, &osb) < 0 || fstat (fd, &nsb) < 0 ||
417 compare_stat (&osb, &nsb) == -1) {
418 /* dprint (1, (debugfile, "safe_open(): %s is a symlink!\n", path)); */
426 /* when opening files for writing, make sure the file doesn't already exist
427 * to avoid race conditions.
429 FILE *safe_fopen (const char *path, const char *mode)
431 /* first set the current umask */
433 if (mode[0] == 'w') {
435 int flags = O_CREAT | O_EXCL;
446 if ((fd = safe_open (path, flags)) < 0)
449 return (fdopen (fd, mode));
452 return (fopen (path, mode));
455 static char safe_chars[] =
456 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/";
458 void mutt_sanitize_filename (char *f, short slash)
464 if ((slash && *f == '/') || !strchr (safe_chars, *f))
469 /* these characters must be escaped in regular expressions */
471 static char rx_special_chars[] = "^.[$()|*+?{\\";
473 int mutt_rx_sanitize_string (char *dest, size_t destlen, const char *src)
475 while (*src && --destlen > 2) {
476 if (strchr (rx_special_chars, *src)) {
491 /* Read a line from ``fp'' into the dynamically allocated ``s'',
492 * increasing ``s'' if necessary. The ending "\n" or "\r\n" is removed.
493 * If a line ends with "\", this char and the linefeed is removed,
494 * and the next line is read too.
496 char *mutt_read_line (char *s, size_t * size, FILE * fp, int *line)
502 s = safe_malloc (STRING);
507 if (fgets (s + offset, *size - offset, fp) == NULL) {
511 if ((ch = strchr (s + offset, '\n')) != NULL) {
514 if (ch > s && *(ch - 1) == '\r')
516 if (ch == s || *(ch - 1) != '\\')
523 c = getc (fp); /* This is kind of a hack. We want to know if the
524 char at the current point in the input stream is EOF.
525 feof() will only tell us if we've already hit EOF, not
526 if the next character is EOF. So, we need to read in
527 the next character and manually check if it is EOF. */
529 /* The last line of fp isn't \n terminated */
534 ungetc (c, fp); /* undo our dammage */
535 /* There wasn't room for the line -- increase ``s'' */
536 offset = *size - 1; /* overwrite the terminating 0 */
538 safe_realloc (&s, *size);
544 char *mutt_substrcpy (char *dest, const char *beg, const char *end,
550 if (len > destlen - 1)
552 memcpy (dest, beg, len);
557 char *mutt_substrdup (const char *begin, const char *end)
565 len = strlen (begin);
567 p = safe_malloc (len + 1);
568 memcpy (p, begin, len);
573 /* prepare a file name to survive the shell's quoting rules.
574 * From the Unix programming FAQ by way of Liviu.
577 size_t mutt_quote_filename (char *d, size_t l, const char *f)
586 /* leave some space for the trailing characters. */
591 for (i = 0; j < l && f[i]; i++) {
592 if (f[i] == '\'' || f[i] == '`') {
608 /* NULL-pointer aware string comparison functions */
610 int mutt_strcmp (const char *a, const char *b)
612 return strcmp (NONULL (a), NONULL (b));
615 int mutt_strcasecmp (const char *a, const char *b)
617 return strcasecmp (NONULL (a), NONULL (b));
620 int mutt_strncmp (const char *a, const char *b, size_t l)
622 return strncmp (NONULL (a), NONULL (b), l);
625 int mutt_strncasecmp (const char *a, const char *b, size_t l)
627 return strncasecmp (NONULL (a), NONULL (b), l);
630 size_t mutt_strlen (const char *a)
632 return a ? strlen (a) : 0;
635 int mutt_strcoll (const char *a, const char *b)
637 return strcoll (NONULL (a), NONULL (b));
640 const char *mutt_stristr (const char *haystack, const char *needle)
649 while (*(p = haystack)) {
652 tolower ((unsigned char) *p) == tolower ((unsigned char) *q);
661 char *mutt_skip_whitespace (char *p)
667 void mutt_remove_trailing_ws (char *s)
671 for (p = s + mutt_strlen (s) - 1; p >= s && ISSPACE (*p); p--)
675 char *mutt_concat_path (char *d, const char *dir, const char *fname, size_t l)
677 const char *fmt = "%s/%s";
679 if (!*fname || (*dir && dir[strlen (dir) - 1] == '/'))
682 snprintf (d, l, fmt, dir, fname);
686 const char *mutt_basename (const char *f)
688 const char *p = strrchr (f, '/');