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> */
41 #include "lib/debug.h"
45 static struct sysexits
53 { 0xff & EX_USAGE, "Bad usage." },
56 { 0xff & EX_DATAERR, "Data format error." },
59 { 0xff & EX_NOINPUT, "Cannot open input." },
62 { 0xff & EX_NOUSER, "User unknown." },
65 { 0xff & EX_NOHOST, "Host unknown." },
68 { 0xff & EX_UNAVAILABLE, "Service unavailable." },
71 { 0xff & EX_SOFTWARE, "Internal error." },
74 { 0xff & EX_OSERR, "Operating system error." },
77 { 0xff & EX_OSFILE, "System file missing." },
80 { 0xff & EX_CANTCREAT, "Can't create output." },
83 { 0xff & EX_IOERR, "I/O error." },
86 { 0xff & EX_TEMPFAIL, "Deferred." },
89 { 0xff & EX_PROTOCOL, "Remote protocol error." },
92 { 0xff & EX_NOPERM, "Insufficient permission." },
95 { 0xff & EX_NOPERM, "Local configuration error." },
97 { S_ERR, "Exec error." },
101 void mutt_nocurses_error (const char *fmt, ...)
106 vfprintf (stderr, fmt, ap);
108 fputc ('\n', stderr);
111 int safe_fclose (FILE ** f)
122 void mutt_unlink (const char *s)
130 /* Defend against symlink attacks */
133 flags = O_RDWR | O_NOFOLLOW;
138 if (lstat (s, &sb) == 0 && S_ISREG (sb.st_mode)) {
139 if ((fd = open (s, flags)) < 0)
142 if ((fstat (fd, &sb2) != 0) || !S_ISREG (sb2.st_mode)
143 || (sb.st_dev != sb2.st_dev) || (sb.st_ino != sb2.st_ino)) {
148 if ((f = fdopen (fd, "r+"))) {
150 memset (buf, 0, sizeof (buf));
151 while (sb.st_size > 0) {
152 fwrite (buf, 1, MIN (sizeof (buf), sb.st_size), f);
153 sb.st_size -= MIN (sizeof (buf), sb.st_size);
160 int mutt_copy_bytes (FILE * in, FILE * out, size_t size)
166 chunk = (size > sizeof (buf)) ? sizeof (buf) : size;
167 if ((chunk = fread (buf, 1, chunk, in)) < 1)
169 if (fwrite (buf, 1, chunk, out) != chunk) {
170 debug_print (1, ("fwrite() returned short byte count\n"));
179 int mutt_copy_stream (FILE * fin, FILE * fout)
182 char buf[LONG_STRING];
184 while ((l = fread (buf, 1, sizeof (buf), fin)) > 0) {
185 if (fwrite (buf, 1, l, fout) != l)
192 static int compare_stat (struct stat *osb, struct stat *nsb)
194 if (osb->st_dev != nsb->st_dev || osb->st_ino != nsb->st_ino ||
195 osb->st_rdev != nsb->st_rdev) {
202 int safe_symlink (const char *oldpath, const char *newpath)
204 struct stat osb, nsb;
206 if (!oldpath || !newpath)
209 if (unlink (newpath) == -1 && errno != ENOENT)
212 if (oldpath[0] == '/') {
213 if (symlink (oldpath, newpath) == -1)
217 char abs_oldpath[_POSIX_PATH_MAX];
219 if ((getcwd (abs_oldpath, sizeof abs_oldpath) == NULL) ||
220 (str_len (abs_oldpath) + 1 + str_len (oldpath) + 1 >
224 strcat (abs_oldpath, "/"); /* __STRCAT_CHECKED__ */
225 strcat (abs_oldpath, oldpath); /* __STRCAT_CHECKED__ */
226 if (symlink (abs_oldpath, newpath) == -1)
230 if (stat (oldpath, &osb) == -1 || stat (newpath, &nsb) == -1
231 || compare_stat (&osb, &nsb) == -1) {
240 * This function is supposed to do nfs-safe renaming of files.
242 * Warning: We don't check whether src and target are equal.
245 int safe_rename (const char *src, const char *target)
247 struct stat ssb, tsb;
252 if (link (src, target) != 0) {
255 * Coda does not allow cross-directory links, but tells
256 * us it's a cross-filesystem linking attempt.
258 * However, the Coda rename call is allegedly safe to use.
260 * With other file systems, rename should just fail when
261 * the files reside on different file systems, so it's safe
267 return rename (src, target);
273 * Stat both links and check if they are equal.
276 if (stat (src, &ssb) == -1) {
280 if (stat (target, &tsb) == -1) {
285 * pretend that the link failed because the target file
289 if (compare_stat (&ssb, &tsb) == -1) {
295 * Unlink the original link. Should we really ignore the return
304 int safe_open (const char *path, int flags)
306 struct stat osb, nsb;
310 if ((fd = open (path, flags, 0666)) < 0)
313 /* make sure the file is not symlink */
314 if (lstat (path, &osb) < 0 || fstat (fd, &nsb) < 0 ||
315 compare_stat (&osb, &nsb) == -1) {
316 debug_print (1, ("%s is a symlink!\n", path));
324 /* when opening files for writing, make sure the file doesn't already exist
325 * to avoid race conditions.
327 FILE *safe_fopen (const char *path, const char *mode)
329 /* first set the current umask */
331 if (mode[0] == 'w') {
333 int flags = O_CREAT | O_EXCL;
344 if ((fd = safe_open (path, flags)) < 0)
347 return (fdopen (fd, mode));
350 return (fopen (path, mode));
353 static char safe_chars[] =
354 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/";
356 void mutt_sanitize_filename (char *f, short slash)
362 if ((slash && *f == '/') || !strchr (safe_chars, *f))
367 /* these characters must be escaped in regular expressions */
369 static char rx_special_chars[] = "^.[$()|*+?{\\";
371 int mutt_rx_sanitize_string (char *dest, size_t destlen, const char *src)
373 while (*src && --destlen > 2) {
374 if (strchr (rx_special_chars, *src)) {
389 /* Read a line from ``fp'' into the dynamically allocated ``s'',
390 * increasing ``s'' if necessary. The ending "\n" or "\r\n" is removed.
391 * If a line ends with "\", this char and the linefeed is removed,
392 * and the next line is read too.
394 char *mutt_read_line (char *s, size_t * size, FILE * fp, int *line)
400 s = mem_malloc (STRING);
405 if (fgets (s + offset, *size - offset, fp) == NULL) {
409 if ((ch = strchr (s + offset, '\n')) != NULL) {
412 if (ch > s && *(ch - 1) == '\r')
414 if (ch == s || *(ch - 1) != '\\')
421 c = getc (fp); /* This is kind of a hack. We want to know if the
422 char at the current point in the input stream is EOF.
423 feof() will only tell us if we've already hit EOF, not
424 if the next character is EOF. So, we need to read in
425 the next character and manually check if it is EOF. */
427 /* The last line of fp isn't \n terminated */
432 ungetc (c, fp); /* undo our dammage */
433 /* There wasn't room for the line -- increase ``s'' */
434 offset = *size - 1; /* overwrite the terminating 0 */
436 mem_realloc (&s, *size);
442 /* prepare a file name to survive the shell's quoting rules.
443 * From the Unix programming FAQ by way of Liviu.
446 size_t mutt_quote_filename (char *d, size_t l, const char *f)
455 /* leave some space for the trailing characters. */
460 for (i = 0; j < l && f[i]; i++) {
461 if (f[i] == '\'' || f[i] == '`') {
477 char *mutt_concat_path (char *d, const char *dir, const char *fname, size_t l)
479 const char *fmt = "%s/%s";
481 if (!*fname || (*dir && dir[str_len (dir) - 1] == '/'))
484 snprintf (d, l, fmt, dir, fname);
488 const char *mutt_basename (const char *f)
490 const char *p = strrchr (f, '/');
499 mutt_strsysexit(int e)
503 for(i = 0; sysexits_h[i].str; i++)
505 if(e == sysexits_h[i].v)
509 return sysexits_h[i].str;