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>
43 #include "lib/debug.h"
47 static struct sysexits
55 { 0xff & EX_USAGE, "Bad usage." },
58 { 0xff & EX_DATAERR, "Data format error." },
61 { 0xff & EX_NOINPUT, "Cannot open input." },
64 { 0xff & EX_NOUSER, "User unknown." },
67 { 0xff & EX_NOHOST, "Host unknown." },
70 { 0xff & EX_UNAVAILABLE, "Service unavailable." },
73 { 0xff & EX_SOFTWARE, "Internal error." },
76 { 0xff & EX_OSERR, "Operating system error." },
79 { 0xff & EX_OSFILE, "System file missing." },
82 { 0xff & EX_CANTCREAT, "Can't create output." },
85 { 0xff & EX_IOERR, "I/O error." },
88 { 0xff & EX_TEMPFAIL, "Deferred." },
91 { 0xff & EX_PROTOCOL, "Remote protocol error." },
94 { 0xff & EX_NOPERM, "Insufficient permission." },
97 { 0xff & EX_NOPERM, "Local configuration error." },
99 { S_ERR, "Exec error." },
103 void mutt_nocurses_error (const char *fmt, ...)
108 vfprintf (stderr, fmt, ap);
110 fputc ('\n', stderr);
113 int safe_fclose (FILE ** f)
124 void mutt_unlink (const char *s)
132 /* Defend against symlink attacks */
135 flags = O_RDWR | O_NOFOLLOW;
140 if (lstat (s, &sb) == 0 && S_ISREG (sb.st_mode)) {
141 if ((fd = open (s, flags)) < 0)
144 if ((fstat (fd, &sb2) != 0) || !S_ISREG (sb2.st_mode)
145 || (sb.st_dev != sb2.st_dev) || (sb.st_ino != sb2.st_ino)) {
150 if ((f = fdopen (fd, "r+"))) {
152 memset (buf, 0, sizeof (buf));
153 while (sb.st_size > 0) {
154 fwrite (buf, 1, MIN (sizeof (buf), sb.st_size), f);
155 sb.st_size -= MIN (sizeof (buf), sb.st_size);
162 int mutt_copy_bytes (FILE * in, FILE * out, size_t size)
168 chunk = (size > sizeof (buf)) ? sizeof (buf) : size;
169 if ((chunk = fread (buf, 1, chunk, in)) < 1)
171 if (fwrite (buf, 1, chunk, out) != chunk) {
172 debug_print (1, ("fwrite() returned short byte count\n"));
181 int mutt_copy_stream (FILE * fin, FILE * fout)
184 char buf[LONG_STRING];
186 while ((l = fread (buf, 1, sizeof (buf), fin)) > 0) {
187 if (fwrite (buf, 1, l, fout) != l)
194 static int compare_stat (struct stat *osb, struct stat *nsb)
196 if (osb->st_dev != nsb->st_dev || osb->st_ino != nsb->st_ino ||
197 osb->st_rdev != nsb->st_rdev) {
204 int safe_symlink (const char *oldpath, const char *newpath)
206 struct stat osb, nsb;
208 if (!oldpath || !newpath)
211 if (unlink (newpath) == -1 && errno != ENOENT)
214 if (oldpath[0] == '/') {
215 if (symlink (oldpath, newpath) == -1)
219 char abs_oldpath[_POSIX_PATH_MAX];
221 if ((getcwd (abs_oldpath, sizeof abs_oldpath) == NULL) ||
222 (str_len (abs_oldpath) + 1 + str_len (oldpath) + 1 >
226 strcat (abs_oldpath, "/"); /* __STRCAT_CHECKED__ */
227 strcat (abs_oldpath, oldpath); /* __STRCAT_CHECKED__ */
228 if (symlink (abs_oldpath, newpath) == -1)
232 if (stat (oldpath, &osb) == -1 || stat (newpath, &nsb) == -1
233 || compare_stat (&osb, &nsb) == -1) {
242 * This function is supposed to do nfs-safe renaming of files.
244 * Warning: We don't check whether src and target are equal.
247 int safe_rename (const char *src, const char *target)
249 struct stat ssb, tsb;
254 if (link (src, target) != 0) {
257 * Coda does not allow cross-directory links, but tells
258 * us it's a cross-filesystem linking attempt.
260 * However, the Coda rename call is allegedly safe to use.
262 * With other file systems, rename should just fail when
263 * the files reside on different file systems, so it's safe
269 return rename (src, target);
275 * Stat both links and check if they are equal.
278 if (stat (src, &ssb) == -1) {
282 if (stat (target, &tsb) == -1) {
287 * pretend that the link failed because the target file
291 if (compare_stat (&ssb, &tsb) == -1) {
297 * Unlink the original link. Should we really ignore the return
306 int safe_open (const char *path, int flags)
308 struct stat osb, nsb;
312 if ((fd = open (path, flags, 0666)) < 0)
315 /* make sure the file is not symlink */
316 if (lstat (path, &osb) < 0 || fstat (fd, &nsb) < 0 ||
317 compare_stat (&osb, &nsb) == -1) {
318 debug_print (1, ("%s is a symlink!\n", path));
326 /* when opening files for writing, make sure the file doesn't already exist
327 * to avoid race conditions.
329 FILE *safe_fopen (const char *path, const char *mode)
331 /* first set the current umask */
333 if (mode[0] == 'w') {
335 int flags = O_CREAT | O_EXCL;
346 if ((fd = safe_open (path, flags)) < 0)
349 return (fdopen (fd, mode));
352 return (fopen (path, mode));
355 static char safe_chars[] =
356 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/";
358 void mutt_sanitize_filename (char *f, short slash)
364 if ((slash && *f == '/') || !strchr (safe_chars, *f))
369 /* these characters must be escaped in regular expressions */
371 static char rx_special_chars[] = "^.[$()|*+?{\\";
373 int mutt_rx_sanitize_string (char *dest, size_t destlen, const char *src)
375 while (*src && --destlen > 2) {
376 if (strchr (rx_special_chars, *src)) {
391 /* Read a line from ``fp'' into the dynamically allocated ``s'',
392 * increasing ``s'' if necessary. The ending "\n" or "\r\n" is removed.
393 * If a line ends with "\", this char and the linefeed is removed,
394 * and the next line is read too.
396 char *mutt_read_line(char *s, size_t * size, FILE * fp, int *line)
402 s = p_new(char, STRING);
407 if (fgets (s + offset, *size - offset, fp) == NULL) {
411 if ((ch = strchr (s + offset, '\n')) != NULL) {
414 if (ch > s && *(ch - 1) == '\r')
416 if (ch == s || *(ch - 1) != '\\')
423 c = getc (fp); /* This is kind of a hack. We want to know if the
424 char at the current point in the input stream is EOF.
425 feof() will only tell us if we've already hit EOF, not
426 if the next character is EOF. So, we need to read in
427 the next character and manually check if it is EOF. */
429 /* The last line of fp isn't \n terminated */
434 ungetc (c, fp); /* undo our dammage */
435 /* There wasn't room for the line -- increase ``s'' */
436 offset = *size - 1; /* overwrite the terminating 0 */
438 mem_realloc (&s, *size);
444 /* prepare a file name to survive the shell's quoting rules.
445 * From the Unix programming FAQ by way of Liviu.
448 size_t mutt_quote_filename (char *d, size_t l, const char *f)
457 /* leave some space for the trailing characters. */
462 for (i = 0; j < l && f[i]; i++) {
463 if (f[i] == '\'' || f[i] == '`') {
479 char *mutt_concat_path (char *d, const char *dir, const char *fname, size_t l)
481 const char *fmt = "%s/%s";
483 if (!*fname || (*dir && dir[str_len (dir) - 1] == '/'))
486 snprintf (d, l, fmt, dir, fname);
490 const char *mutt_basename (const char *f)
492 const char *p = strrchr (f, '/');
501 mutt_strsysexit(int e)
505 for(i = 0; sysexits_h[i].str; i++)
507 if(e == sysexits_h[i].v)
511 return sysexits_h[i].str;