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.
38 void mutt_nocurses_error (const char *fmt, ...)
43 vfprintf (stderr, fmt, ap);
48 int safe_fclose (FILE ** f)
59 char *safe_strdup (const char *s)
66 l = mutt_strlen (s) + 1;
67 p = (char *) safe_malloc (l);
72 char *safe_strcat (char *d, size_t l, const char *s)
79 l--; /* Space for the trailing '\0'. */
91 char *safe_strncat (char *d, size_t l, const char *s, size_t sl)
98 l--; /* Space for the trailing '\0'. */
102 for (; *s && l && sl; l--, sl--)
111 void mutt_str_replace (char **p, const char *s)
114 *p = safe_strdup (s);
117 void mutt_str_adjust (char **p)
121 safe_realloc (p, mutt_strlen (*p) + 1);
124 /* convert all characters in the string to lowercase */
125 char *mutt_strlower (char *s)
130 *p = tolower ((unsigned char) *p);
137 void mutt_unlink (const char *s)
145 /* Defend against symlink attacks */
148 flags = O_RDWR | O_NOFOLLOW;
153 if (lstat (s, &sb) == 0 && S_ISREG (sb.st_mode)) {
154 if ((fd = open (s, flags)) < 0)
157 if ((fstat (fd, &sb2) != 0) || !S_ISREG (sb2.st_mode)
158 || (sb.st_dev != sb2.st_dev) || (sb.st_ino != sb2.st_ino)) {
163 if ((f = fdopen (fd, "r+"))) {
165 memset (buf, 0, sizeof (buf));
166 while (sb.st_size > 0) {
167 fwrite (buf, 1, MIN (sizeof (buf), sb.st_size), f);
168 sb.st_size -= MIN (sizeof (buf), sb.st_size);
175 int mutt_copy_bytes (FILE * in, FILE * out, size_t size)
181 chunk = (size > sizeof (buf)) ? sizeof (buf) : size;
182 if ((chunk = fread (buf, 1, chunk, in)) < 1)
184 if (fwrite (buf, 1, chunk, out) != chunk) {
185 /* dprint (1, (debugfile, "mutt_copy_bytes(): fwrite() returned short byte count\n")); */
194 int mutt_copy_stream (FILE * fin, FILE * fout)
197 char buf[LONG_STRING];
199 while ((l = fread (buf, 1, sizeof (buf), fin)) > 0) {
200 if (fwrite (buf, 1, l, fout) != l)
207 static int compare_stat (struct stat *osb, struct stat *nsb)
209 if (osb->st_dev != nsb->st_dev || osb->st_ino != nsb->st_ino ||
210 osb->st_rdev != nsb->st_rdev) {
217 int safe_symlink (const char *oldpath, const char *newpath)
219 struct stat osb, nsb;
221 if (!oldpath || !newpath)
224 if (unlink (newpath) == -1 && errno != ENOENT)
227 if (oldpath[0] == '/') {
228 if (symlink (oldpath, newpath) == -1)
232 char abs_oldpath[_POSIX_PATH_MAX];
234 if ((getcwd (abs_oldpath, sizeof abs_oldpath) == NULL) ||
235 (mutt_strlen (abs_oldpath) + 1 + mutt_strlen (oldpath) + 1 >
239 strcat (abs_oldpath, "/"); /* __STRCAT_CHECKED__ */
240 strcat (abs_oldpath, oldpath); /* __STRCAT_CHECKED__ */
241 if (symlink (abs_oldpath, newpath) == -1)
245 if (stat (oldpath, &osb) == -1 || stat (newpath, &nsb) == -1
246 || compare_stat (&osb, &nsb) == -1) {
255 * This function is supposed to do nfs-safe renaming of files.
257 * Warning: We don't check whether src and target are equal.
260 int safe_rename (const char *src, const char *target)
262 struct stat ssb, tsb;
267 if (link (src, target) != 0) {
270 * Coda does not allow cross-directory links, but tells
271 * us it's a cross-filesystem linking attempt.
273 * However, the Coda rename call is allegedly safe to use.
275 * With other file systems, rename should just fail when
276 * the files reside on different file systems, so it's safe
282 return rename (src, target);
288 * Stat both links and check if they are equal.
291 if (stat (src, &ssb) == -1) {
295 if (stat (target, &tsb) == -1) {
300 * pretend that the link failed because the target file
304 if (compare_stat (&ssb, &tsb) == -1) {
310 * Unlink the original link. Should we really ignore the return
319 int safe_open (const char *path, int flags)
321 struct stat osb, nsb;
325 if ((fd = open (path, flags, 0666)) < 0)
328 /* make sure the file is not symlink */
329 if (lstat (path, &osb) < 0 || fstat (fd, &nsb) < 0 ||
330 compare_stat (&osb, &nsb) == -1) {
331 /* dprint (1, (debugfile, "safe_open(): %s is a symlink!\n", path)); */
339 /* when opening files for writing, make sure the file doesn't already exist
340 * to avoid race conditions.
342 FILE *safe_fopen (const char *path, const char *mode)
344 /* first set the current umask */
346 if (mode[0] == 'w') {
348 int flags = O_CREAT | O_EXCL;
359 if ((fd = safe_open (path, flags)) < 0)
362 return (fdopen (fd, mode));
365 return (fopen (path, mode));
368 static char safe_chars[] =
369 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/";
371 void mutt_sanitize_filename (char *f, short slash)
377 if ((slash && *f == '/') || !strchr (safe_chars, *f))
382 /* these characters must be escaped in regular expressions */
384 static char rx_special_chars[] = "^.[$()|*+?{\\";
386 int mutt_rx_sanitize_string (char *dest, size_t destlen, const char *src)
388 while (*src && --destlen > 2) {
389 if (strchr (rx_special_chars, *src)) {
404 /* Read a line from ``fp'' into the dynamically allocated ``s'',
405 * increasing ``s'' if necessary. The ending "\n" or "\r\n" is removed.
406 * If a line ends with "\", this char and the linefeed is removed,
407 * and the next line is read too.
409 char *mutt_read_line (char *s, size_t * size, FILE * fp, int *line)
415 s = safe_malloc (STRING);
420 if (fgets (s + offset, *size - offset, fp) == NULL) {
424 if ((ch = strchr (s + offset, '\n')) != NULL) {
427 if (ch > s && *(ch - 1) == '\r')
429 if (ch == s || *(ch - 1) != '\\')
436 c = getc (fp); /* This is kind of a hack. We want to know if the
437 char at the current point in the input stream is EOF.
438 feof() will only tell us if we've already hit EOF, not
439 if the next character is EOF. So, we need to read in
440 the next character and manually check if it is EOF. */
442 /* The last line of fp isn't \n terminated */
447 ungetc (c, fp); /* undo our dammage */
448 /* There wasn't room for the line -- increase ``s'' */
449 offset = *size - 1; /* overwrite the terminating 0 */
451 safe_realloc (&s, *size);
457 char *mutt_substrcpy (char *dest, const char *beg, const char *end,
463 if (len > destlen - 1)
465 memcpy (dest, beg, len);
470 char *mutt_substrdup (const char *begin, const char *end)
478 len = mutt_strlen (begin);
480 p = safe_malloc (len + 1);
481 memcpy (p, begin, len);
486 /* prepare a file name to survive the shell's quoting rules.
487 * From the Unix programming FAQ by way of Liviu.
490 size_t mutt_quote_filename (char *d, size_t l, const char *f)
499 /* leave some space for the trailing characters. */
504 for (i = 0; j < l && f[i]; i++) {
505 if (f[i] == '\'' || f[i] == '`') {
521 /* NULL-pointer aware string comparison functions */
523 int mutt_strcmp (const char *a, const char *b)
525 return strcmp (NONULL (a), NONULL (b));
528 int mutt_strcasecmp (const char *a, const char *b)
530 return strcasecmp (NONULL (a), NONULL (b));
533 int mutt_strncmp (const char *a, const char *b, size_t l)
535 return strncmp (NONULL (a), NONULL (b), l);
538 int mutt_strncasecmp (const char *a, const char *b, size_t l)
540 return strncasecmp (NONULL (a), NONULL (b), l);
543 size_t mutt_strlen (const char *a)
545 return a ? strlen (a) : 0;
548 int mutt_strcoll (const char *a, const char *b)
550 return strcoll (NONULL (a), NONULL (b));
553 const char *mutt_stristr (const char *haystack, const char *needle)
562 while (*(p = haystack)) {
565 tolower ((unsigned char) *p) == tolower ((unsigned char) *q);
574 char *mutt_skip_whitespace (char *p)
580 void mutt_remove_trailing_ws (char *s)
584 for (p = s + mutt_strlen (s) - 1; p >= s && ISSPACE (*p); p--)
588 char *mutt_concat_path (char *d, const char *dir, const char *fname, size_t l)
590 const char *fmt = "%s/%s";
592 if (!*fname || (*dir && dir[mutt_strlen (dir) - 1] == '/'))
595 snprintf (d, l, fmt, dir, fname);
599 const char *mutt_basename (const char *f)
601 const char *p = strrchr (f, '/');