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.
31 #ifdef HAVE_SYSEXITS_H
33 #else /* Make sure EX_OK is defined <philiph@pobox.com> */
37 #include <lib-lib/mem.h>
42 #include "lib/debug.h"
46 static struct sysexits
54 { 0xff & EX_USAGE, "Bad usage." },
57 { 0xff & EX_DATAERR, "Data format error." },
60 { 0xff & EX_NOINPUT, "Cannot open input." },
63 { 0xff & EX_NOUSER, "User unknown." },
66 { 0xff & EX_NOHOST, "Host unknown." },
69 { 0xff & EX_UNAVAILABLE, "Service unavailable." },
72 { 0xff & EX_SOFTWARE, "Internal error." },
75 { 0xff & EX_OSERR, "Operating system error." },
78 { 0xff & EX_OSFILE, "System file missing." },
81 { 0xff & EX_CANTCREAT, "Can't create output." },
84 { 0xff & EX_IOERR, "I/O error." },
87 { 0xff & EX_TEMPFAIL, "Deferred." },
90 { 0xff & EX_PROTOCOL, "Remote protocol error." },
93 { 0xff & EX_NOPERM, "Insufficient permission." },
96 { 0xff & EX_NOPERM, "Local configuration error." },
98 { S_ERR, "Exec error." },
102 void mutt_nocurses_error (const char *fmt, ...)
107 vfprintf (stderr, fmt, ap);
109 fputc ('\n', stderr);
112 int safe_fclose (FILE ** f)
123 void mutt_unlink (const char *s)
131 /* Defend against symlink attacks */
134 flags = O_RDWR | O_NOFOLLOW;
139 if (lstat (s, &sb) == 0 && S_ISREG (sb.st_mode)) {
140 if ((fd = open (s, flags)) < 0)
143 if ((fstat (fd, &sb2) != 0) || !S_ISREG (sb2.st_mode)
144 || (sb.st_dev != sb2.st_dev) || (sb.st_ino != sb2.st_ino)) {
149 if ((f = fdopen (fd, "r+"))) {
151 memset (buf, 0, sizeof (buf));
152 while (sb.st_size > 0) {
153 fwrite (buf, 1, MIN (sizeof (buf), sb.st_size), f);
154 sb.st_size -= MIN (sizeof (buf), sb.st_size);
161 int mutt_copy_bytes (FILE * in, FILE * out, size_t size)
167 chunk = (size > sizeof (buf)) ? sizeof (buf) : size;
168 if ((chunk = fread (buf, 1, chunk, in)) < 1)
170 if (fwrite (buf, 1, chunk, out) != chunk) {
171 debug_print (1, ("fwrite() returned short byte count\n"));
180 int mutt_copy_stream (FILE * fin, FILE * fout)
183 char buf[LONG_STRING];
185 while ((l = fread (buf, 1, sizeof (buf), fin)) > 0) {
186 if (fwrite (buf, 1, l, fout) != l)
193 static int compare_stat (struct stat *osb, struct stat *nsb)
195 if (osb->st_dev != nsb->st_dev || osb->st_ino != nsb->st_ino ||
196 osb->st_rdev != nsb->st_rdev) {
203 int safe_symlink (const char *oldpath, const char *newpath)
205 struct stat osb, nsb;
207 if (!oldpath || !newpath)
210 if (unlink (newpath) == -1 && errno != ENOENT)
213 if (oldpath[0] == '/') {
214 if (symlink (oldpath, newpath) == -1)
218 char abs_oldpath[_POSIX_PATH_MAX];
220 if ((getcwd (abs_oldpath, sizeof abs_oldpath) == NULL) ||
221 (str_len (abs_oldpath) + 1 + str_len (oldpath) + 1 >
225 strcat (abs_oldpath, "/"); /* __STRCAT_CHECKED__ */
226 strcat (abs_oldpath, oldpath); /* __STRCAT_CHECKED__ */
227 if (symlink (abs_oldpath, newpath) == -1)
231 if (stat (oldpath, &osb) == -1 || stat (newpath, &nsb) == -1
232 || compare_stat (&osb, &nsb) == -1) {
241 * This function is supposed to do nfs-safe renaming of files.
243 * Warning: We don't check whether src and target are equal.
246 int safe_rename (const char *src, const char *target)
248 struct stat ssb, tsb;
253 if (link (src, target) != 0) {
256 * Coda does not allow cross-directory links, but tells
257 * us it's a cross-filesystem linking attempt.
259 * However, the Coda rename call is allegedly safe to use.
261 * With other file systems, rename should just fail when
262 * the files reside on different file systems, so it's safe
268 return rename (src, target);
274 * Stat both links and check if they are equal.
277 if (stat (src, &ssb) == -1) {
281 if (stat (target, &tsb) == -1) {
286 * pretend that the link failed because the target file
290 if (compare_stat (&ssb, &tsb) == -1) {
296 * Unlink the original link. Should we really ignore the return
305 int safe_open (const char *path, int flags)
307 struct stat osb, nsb;
311 if ((fd = open (path, flags, 0666)) < 0)
314 /* make sure the file is not symlink */
315 if (lstat (path, &osb) < 0 || fstat (fd, &nsb) < 0 ||
316 compare_stat (&osb, &nsb) == -1) {
317 debug_print (1, ("%s is a symlink!\n", path));
325 /* when opening files for writing, make sure the file doesn't already exist
326 * to avoid race conditions.
328 FILE *safe_fopen (const char *path, const char *mode)
330 /* first set the current umask */
332 if (mode[0] == 'w') {
334 int flags = O_CREAT | O_EXCL;
345 if ((fd = safe_open (path, flags)) < 0)
348 return (fdopen (fd, mode));
351 return (fopen (path, mode));
354 static char safe_chars[] =
355 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/";
357 void mutt_sanitize_filename (char *f, short slash)
363 if ((slash && *f == '/') || !strchr (safe_chars, *f))
368 /* these characters must be escaped in regular expressions */
370 static char rx_special_chars[] = "^.[$()|*+?{\\";
372 int mutt_rx_sanitize_string (char *dest, size_t destlen, const char *src)
374 while (*src && --destlen > 2) {
375 if (strchr (rx_special_chars, *src)) {
390 /* Read a line from ``fp'' into the dynamically allocated ``s'',
391 * increasing ``s'' if necessary. The ending "\n" or "\r\n" is removed.
392 * If a line ends with "\", this char and the linefeed is removed,
393 * and the next line is read too.
395 char *mutt_read_line(char *s, size_t * size, FILE * fp, int *line)
401 s = p_new(char, STRING);
406 if (fgets (s + offset, *size - offset, fp) == NULL) {
410 if ((ch = strchr (s + offset, '\n')) != NULL) {
413 if (ch > s && *(ch - 1) == '\r')
415 if (ch == s || *(ch - 1) != '\\')
422 c = getc (fp); /* This is kind of a hack. We want to know if the
423 char at the current point in the input stream is EOF.
424 feof() will only tell us if we've already hit EOF, not
425 if the next character is EOF. So, we need to read in
426 the next character and manually check if it is EOF. */
428 /* The last line of fp isn't \n terminated */
433 ungetc (c, fp); /* undo our dammage */
434 /* There wasn't room for the line -- increase ``s'' */
435 offset = *size - 1; /* overwrite the terminating 0 */
437 p_realloc(&s, *size);
443 /* prepare a file name to survive the shell's quoting rules.
444 * From the Unix programming FAQ by way of Liviu.
447 size_t mutt_quote_filename (char *d, size_t l, const char *f)
456 /* leave some space for the trailing characters. */
461 for (i = 0; j < l && f[i]; i++) {
462 if (f[i] == '\'' || f[i] == '`') {
478 char *mutt_concat_path (char *d, const char *dir, const char *fname, size_t l)
480 const char *fmt = "%s/%s";
482 if (!*fname || (*dir && dir[str_len (dir) - 1] == '/'))
485 snprintf (d, l, fmt, dir, fname);
489 const char *mutt_basename (const char *f)
491 const char *p = strrchr (f, '/');
500 mutt_strsysexit(int e)
504 for(i = 0; sysexits_h[i].str; i++)
506 if(e == sysexits_h[i].v)
510 return sysexits_h[i].str;