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 int safe_fclose (FILE ** f)
56 char *safe_strdup (const char *s)
63 l = mutt_strlen (s) + 1;
64 p = (char *) safe_malloc (l);
69 char *safe_strcat (char *d, size_t l, const char *s)
76 l--; /* Space for the trailing '\0'. */
88 char *safe_strncat (char *d, size_t l, const char *s, size_t sl)
95 l--; /* Space for the trailing '\0'. */
99 for (; *s && l && sl; l--, sl--)
108 void mutt_str_replace (char **p, const char *s)
111 *p = safe_strdup (s);
114 void mutt_str_adjust (char **p)
118 safe_realloc (p, mutt_strlen (*p) + 1);
121 /* convert all characters in the string to lowercase */
122 char *mutt_strlower (char *s)
127 *p = tolower ((unsigned char) *p);
134 void mutt_unlink (const char *s)
142 /* Defend against symlink attacks */
145 flags = O_RDWR | O_NOFOLLOW;
150 if (lstat (s, &sb) == 0 && S_ISREG (sb.st_mode)) {
151 if ((fd = open (s, flags)) < 0)
154 if ((fstat (fd, &sb2) != 0) || !S_ISREG (sb2.st_mode)
155 || (sb.st_dev != sb2.st_dev) || (sb.st_ino != sb2.st_ino)) {
160 if ((f = fdopen (fd, "r+"))) {
162 memset (buf, 0, sizeof (buf));
163 while (sb.st_size > 0) {
164 fwrite (buf, 1, MIN (sizeof (buf), sb.st_size), f);
165 sb.st_size -= MIN (sizeof (buf), sb.st_size);
172 int mutt_copy_bytes (FILE * in, FILE * out, size_t size)
178 chunk = (size > sizeof (buf)) ? sizeof (buf) : size;
179 if ((chunk = fread (buf, 1, chunk, in)) < 1)
181 if (fwrite (buf, 1, chunk, out) != chunk) {
182 /* dprint (1, (debugfile, "mutt_copy_bytes(): fwrite() returned short byte count\n")); */
191 int mutt_copy_stream (FILE * fin, FILE * fout)
194 char buf[LONG_STRING];
196 while ((l = fread (buf, 1, sizeof (buf), fin)) > 0) {
197 if (fwrite (buf, 1, l, fout) != l)
204 static int compare_stat (struct stat *osb, struct stat *nsb)
206 if (osb->st_dev != nsb->st_dev || osb->st_ino != nsb->st_ino ||
207 osb->st_rdev != nsb->st_rdev) {
214 int safe_symlink (const char *oldpath, const char *newpath)
216 struct stat osb, nsb;
218 if (!oldpath || !newpath)
221 if (unlink (newpath) == -1 && errno != ENOENT)
224 if (oldpath[0] == '/') {
225 if (symlink (oldpath, newpath) == -1)
229 char abs_oldpath[_POSIX_PATH_MAX];
231 if ((getcwd (abs_oldpath, sizeof abs_oldpath) == NULL) ||
232 (mutt_strlen (abs_oldpath) + 1 + mutt_strlen (oldpath) + 1 >
236 strcat (abs_oldpath, "/"); /* __STRCAT_CHECKED__ */
237 strcat (abs_oldpath, oldpath); /* __STRCAT_CHECKED__ */
238 if (symlink (abs_oldpath, newpath) == -1)
242 if (stat (oldpath, &osb) == -1 || stat (newpath, &nsb) == -1
243 || compare_stat (&osb, &nsb) == -1) {
252 * This function is supposed to do nfs-safe renaming of files.
254 * Warning: We don't check whether src and target are equal.
257 int safe_rename (const char *src, const char *target)
259 struct stat ssb, tsb;
264 if (link (src, target) != 0) {
267 * Coda does not allow cross-directory links, but tells
268 * us it's a cross-filesystem linking attempt.
270 * However, the Coda rename call is allegedly safe to use.
272 * With other file systems, rename should just fail when
273 * the files reside on different file systems, so it's safe
279 return rename (src, target);
285 * Stat both links and check if they are equal.
288 if (stat (src, &ssb) == -1) {
292 if (stat (target, &tsb) == -1) {
297 * pretend that the link failed because the target file
301 if (compare_stat (&ssb, &tsb) == -1) {
307 * Unlink the original link. Should we really ignore the return
316 int safe_open (const char *path, int flags)
318 struct stat osb, nsb;
322 if ((fd = open (path, flags, 0666)) < 0)
325 /* make sure the file is not symlink */
326 if (lstat (path, &osb) < 0 || fstat (fd, &nsb) < 0 ||
327 compare_stat (&osb, &nsb) == -1) {
328 /* dprint (1, (debugfile, "safe_open(): %s is a symlink!\n", path)); */
336 /* when opening files for writing, make sure the file doesn't already exist
337 * to avoid race conditions.
339 FILE *safe_fopen (const char *path, const char *mode)
341 /* first set the current umask */
343 if (mode[0] == 'w') {
345 int flags = O_CREAT | O_EXCL;
356 if ((fd = safe_open (path, flags)) < 0)
359 return (fdopen (fd, mode));
362 return (fopen (path, mode));
365 static char safe_chars[] =
366 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/";
368 void mutt_sanitize_filename (char *f, short slash)
374 if ((slash && *f == '/') || !strchr (safe_chars, *f))
379 /* these characters must be escaped in regular expressions */
381 static char rx_special_chars[] = "^.[$()|*+?{\\";
383 int mutt_rx_sanitize_string (char *dest, size_t destlen, const char *src)
385 while (*src && --destlen > 2) {
386 if (strchr (rx_special_chars, *src)) {
401 /* Read a line from ``fp'' into the dynamically allocated ``s'',
402 * increasing ``s'' if necessary. The ending "\n" or "\r\n" is removed.
403 * If a line ends with "\", this char and the linefeed is removed,
404 * and the next line is read too.
406 char *mutt_read_line (char *s, size_t * size, FILE * fp, int *line)
412 s = safe_malloc (STRING);
417 if (fgets (s + offset, *size - offset, fp) == NULL) {
421 if ((ch = strchr (s + offset, '\n')) != NULL) {
424 if (ch > s && *(ch - 1) == '\r')
426 if (ch == s || *(ch - 1) != '\\')
433 c = getc (fp); /* This is kind of a hack. We want to know if the
434 char at the current point in the input stream is EOF.
435 feof() will only tell us if we've already hit EOF, not
436 if the next character is EOF. So, we need to read in
437 the next character and manually check if it is EOF. */
439 /* The last line of fp isn't \n terminated */
444 ungetc (c, fp); /* undo our dammage */
445 /* There wasn't room for the line -- increase ``s'' */
446 offset = *size - 1; /* overwrite the terminating 0 */
448 safe_realloc (&s, *size);
454 char *mutt_substrcpy (char *dest, const char *beg, const char *end,
460 if (len > destlen - 1)
462 memcpy (dest, beg, len);
467 char *mutt_substrdup (const char *begin, const char *end)
475 len = mutt_strlen (begin);
477 p = safe_malloc (len + 1);
478 memcpy (p, begin, len);
483 /* prepare a file name to survive the shell's quoting rules.
484 * From the Unix programming FAQ by way of Liviu.
487 size_t mutt_quote_filename (char *d, size_t l, const char *f)
496 /* leave some space for the trailing characters. */
501 for (i = 0; j < l && f[i]; i++) {
502 if (f[i] == '\'' || f[i] == '`') {
518 /* NULL-pointer aware string comparison functions */
520 int mutt_strcmp (const char *a, const char *b)
522 return strcmp (NONULL (a), NONULL (b));
525 int mutt_strcasecmp (const char *a, const char *b)
527 return strcasecmp (NONULL (a), NONULL (b));
530 int mutt_strncmp (const char *a, const char *b, size_t l)
532 return strncmp (NONULL (a), NONULL (b), l);
535 int mutt_strncasecmp (const char *a, const char *b, size_t l)
537 return strncasecmp (NONULL (a), NONULL (b), l);
540 size_t mutt_strlen (const char *a)
542 return a ? strlen (a) : 0;
545 int mutt_strcoll (const char *a, const char *b)
547 return strcoll (NONULL (a), NONULL (b));
550 const char *mutt_stristr (const char *haystack, const char *needle)
559 while (*(p = haystack)) {
562 tolower ((unsigned char) *p) == tolower ((unsigned char) *q);
571 char *mutt_skip_whitespace (char *p)
577 void mutt_remove_trailing_ws (char *s)
581 for (p = s + mutt_strlen (s) - 1; p >= s && ISSPACE (*p); p--)
585 char *mutt_concat_path (char *d, const char *dir, const char *fname, size_t l)
587 const char *fmt = "%s/%s";
589 if (!*fname || (*dir && dir[mutt_strlen (dir) - 1] == '/'))
592 snprintf (d, l, fmt, dir, fname);
596 const char *mutt_basename (const char *f)
598 const char *p = strrchr (f, '/');