NeoMutt  2023-05-17-56-ga67199
Teaching an old dog new tricks
DOXYGEN
file.c File Reference

File management functions. More...

#include "config.h"
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <utime.h>
#include "file.h"
#include "buffer.h"
#include "date.h"
#include "logging2.h"
#include "memory.h"
#include "message.h"
#include "path.h"
#include "pool.h"
#include "string2.h"
+ Include dependency graph for file.c:

Go to the source code of this file.

Macros

#define MAX_LOCK_ATTEMPTS   5
 
#define O_NOFOLLOW   0
 

Functions

static bool compare_stat (struct stat *st_old, struct stat *st_new)
 Compare the struct stat's of two files/dirs. More...
 
static int mkwrapdir (const char *path, struct Buffer *newfile, struct Buffer *newdir)
 Create a temporary directory next to a file name. More...
 
static int put_file_in_place (const char *path, const char *safe_file, const char *safe_dir)
 Move a file into place. More...
 
int mutt_file_fclose (FILE **fp)
 Close a FILE handle (and NULL the pointer) More...
 
int mutt_file_fsync_close (FILE **fp)
 Flush the data, before closing a file (and NULL the pointer) More...
 
void mutt_file_unlink (const char *s)
 Delete a file, carefully. More...
 
int mutt_file_copy_bytes (FILE *fp_in, FILE *fp_out, size_t size)
 Copy some content from one file to another. More...
 
int mutt_file_copy_stream (FILE *fp_in, FILE *fp_out)
 Copy the contents of one file into another. More...
 
int mutt_file_symlink (const char *oldpath, const char *newpath)
 Create a symlink. More...
 
int mutt_file_safe_rename (const char *src, const char *target)
 NFS-safe renaming of files. More...
 
int mutt_file_rmtree (const char *path)
 Recursively remove a directory. More...
 
const char * mutt_file_rotate (const char *path, int count)
 Rotate a set of numbered files. More...
 
int mutt_file_open (const char *path, uint32_t flags)
 Open a file. More...
 
DIR * mutt_file_opendir (const char *path, enum MuttOpenDirMode mode)
 Open a directory. More...
 
FILE * mutt_file_fopen (const char *path, const char *mode)
 Call fopen() safely. More...
 
void mutt_file_sanitize_filename (char *path, bool slash)
 Replace unsafe characters in a filename. More...
 
int mutt_file_sanitize_regex (struct Buffer *dest, const char *src)
 Escape any regex-magic characters in a string. More...
 
bool mutt_file_seek (FILE *fp, LOFF_T offset, int whence)
 Wrapper for fseeko with error handling. More...
 
char * mutt_file_read_line (char *line, size_t *size, FILE *fp, int *line_num, ReadLineFlags flags)
 Read a line from a file. More...
 
bool mutt_file_iter_line (struct MuttFileIter *iter, FILE *fp, ReadLineFlags flags)
 Iterate over the lines from an open file pointer. More...
 
bool mutt_file_map_lines (mutt_file_map_t func, void *user_data, FILE *fp, ReadLineFlags flags)
 Process lines of text read from a file pointer. More...
 
size_t mutt_file_quote_filename (const char *filename, char *buf, size_t buflen)
 Quote a filename to survive the shell's quoting rules. More...
 
void buf_quote_filename (struct Buffer *buf, const char *filename, bool add_outer)
 Quote a filename to survive the shell's quoting rules. More...
 
int mutt_file_mkdir (const char *path, mode_t mode)
 Recursively create directories. More...
 
time_t mutt_file_decrease_mtime (const char *fp, struct stat *st)
 Decrease a file's modification time by 1 second. More...
 
void mutt_file_set_mtime (const char *from, const char *to)
 Set the modification time of one file from another. More...
 
void mutt_file_touch_atime (int fd)
 Set the access time to current time. More...
 
int mutt_file_chmod (const char *path, mode_t mode)
 Set permissions of a file. More...
 
int mutt_file_chmod_add (const char *path, mode_t mode)
 Add permissions to a file. More...
 
int mutt_file_chmod_add_stat (const char *path, mode_t mode, struct stat *st)
 Add permissions to a file. More...
 
int mutt_file_chmod_rm (const char *path, mode_t mode)
 Remove permissions from a file. More...
 
int mutt_file_chmod_rm_stat (const char *path, mode_t mode, struct stat *st)
 Remove permissions from a file. More...
 
int mutt_file_lock (int fd, bool excl, bool timeout)
 (Try to) Lock a file using fcntl() More...
 
int mutt_file_unlock (int fd)
 Unlock a file previously locked by mutt_file_lock() More...
 
void mutt_file_unlink_empty (const char *path)
 Delete a file if it's empty. More...
 
int mutt_file_rename (const char *oldfile, const char *newfile)
 Rename a file. More...
 
char * mutt_file_read_keyword (const char *file, char *buf, size_t buflen)
 Read a keyword from a file. More...
 
int mutt_file_check_empty (const char *path)
 Is the mailbox empty. More...
 
void buf_file_expand_fmt_quote (struct Buffer *dest, const char *fmt, const char *src)
 Replace s in a string with a filename. More...
 
void mutt_file_expand_fmt (struct Buffer *dest, const char *fmt, const char *src)
 Replace s in a string with a filename. More...
 
long mutt_file_get_size (const char *path)
 Get the size of a file. More...
 
long mutt_file_get_size_fp (FILE *fp)
 Get the size of a file. More...
 
int mutt_file_timespec_compare (struct timespec *a, struct timespec *b)
 Compare to time values. More...
 
void mutt_file_get_stat_timespec (struct timespec *dest, struct stat *st, enum MuttStatType type)
 Read the stat() time into a time value. More...
 
int mutt_file_stat_timespec_compare (struct stat *st, enum MuttStatType type, struct timespec *b)
 Compare stat info with a time value. More...
 
int mutt_file_stat_compare (struct stat *st1, enum MuttStatType st1_type, struct stat *st2, enum MuttStatType st2_type)
 Compare two stat infos. More...
 
void mutt_file_resolve_symlink (struct Buffer *buf)
 Resolve a symlink in place. More...
 

Variables

static const char RxSpecialChars [] = "^.[$()|*+?{\\"
 These characters must be escaped in regular expressions. More...
 
const char FilenameSafeChars [] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/"
 Set of characters that are safe to use in filenames. More...
 

Detailed Description

File management functions.

Authors
  • Richard Russon

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file file.c.

Macro Definition Documentation

◆ MAX_LOCK_ATTEMPTS

#define MAX_LOCK_ATTEMPTS   5

Definition at line 61 of file file.c.

◆ O_NOFOLLOW

#define O_NOFOLLOW   0

Definition at line 65 of file file.c.

Function Documentation

◆ compare_stat()

static bool compare_stat ( struct stat *  st_old,
struct stat *  st_new 
)
static

Compare the struct stat's of two files/dirs.

Parameters
st_oldstruct stat of the first file/dir
st_newstruct stat of the second file/dir
Return values
trueThey match

This compares the device id (st_dev), inode number (st_ino) and special id (st_rdev) of the files/dirs.

Definition at line 77 of file file.c.

78{
79 return (st_old->st_dev == st_new->st_dev) && (st_old->st_ino == st_new->st_ino) &&
80 (st_old->st_rdev == st_new->st_rdev);
81}
+ Here is the caller graph for this function:

◆ mkwrapdir()

static int mkwrapdir ( const char *  path,
struct Buffer newfile,
struct Buffer newdir 
)
static

Create a temporary directory next to a file name.

Parameters
pathExisting filename
newfileNew filename
newdirNew directory name
Return values
0Success
-1Error

Definition at line 91 of file file.c.

92{
93 const char *basename = NULL;
94 int rc = 0;
95
96 struct Buffer parent = buf_make(PATH_MAX);
97 buf_strcpy(&parent, NONULL(path));
98
99 char *p = strrchr(parent.data, '/');
100 if (p)
101 {
102 *p = '\0';
103 basename = p + 1;
104 }
105 else
106 {
107 buf_strcpy(&parent, ".");
108 basename = path;
109 }
110
111 buf_printf(newdir, "%s/%s", buf_string(&parent), ".muttXXXXXX");
112 if (!mkdtemp(newdir->data))
113 {
114 mutt_debug(LL_DEBUG1, "mkdtemp() failed\n");
115 rc = -1;
116 goto cleanup;
117 }
118
119 buf_printf(newfile, "%s/%s", newdir->data, NONULL(basename));
120
121cleanup:
122 buf_dealloc(&parent);
123 return rc;
124}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:173
void buf_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:383
struct Buffer buf_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:70
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:401
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:90
#define mutt_debug(LEVEL,...)
Definition: logging2.h:87
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
#define PATH_MAX
Definition: mutt.h:41
#define NONULL(x)
Definition: string2.h:37
String manipulation buffer.
Definition: buffer.h:34
char * data
Pointer to data.
Definition: buffer.h:35
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ put_file_in_place()

static int put_file_in_place ( const char *  path,
const char *  safe_file,
const char *  safe_dir 
)
static

Move a file into place.

Parameters
pathDestination path
safe_fileCurrent filename
safe_dirCurrent directory name
Return values
0Success
-1Error, see errno

Definition at line 134 of file file.c.

135{
136 int rc;
137
138 rc = mutt_file_safe_rename(safe_file, path);
139 unlink(safe_file);
140 rmdir(safe_dir);
141 return rc;
142}
int mutt_file_safe_rename(const char *src, const char *target)
NFS-safe renaming of files.
Definition: file.c:344
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_file_fclose()

int mutt_file_fclose ( FILE **  fp)

Close a FILE handle (and NULL the pointer)

Parameters
[out]fpFILE handle to close
Return values
0Success
EOFError, see errno

Definition at line 150 of file file.c.

151{
152 if (!fp || !*fp)
153 return 0;
154
155 int rc = fclose(*fp);
156 *fp = NULL;
157 return rc;
158}

◆ mutt_file_fsync_close()

int mutt_file_fsync_close ( FILE **  fp)

Flush the data, before closing a file (and NULL the pointer)

Parameters
[out]fpFILE handle to close
Return values
0Success
EOFError, see errno

Definition at line 166 of file file.c.

167{
168 if (!fp || !*fp)
169 return 0;
170
171 int rc = 0;
172
173 if (fflush(*fp) || fsync(fileno(*fp)))
174 {
175 int save_errno = errno;
176 rc = -1;
178 errno = save_errno;
179 }
180 else
181 {
182 rc = mutt_file_fclose(fp);
183 }
184
185 return rc;
186}
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:150
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_file_unlink()

void mutt_file_unlink ( const char *  s)

Delete a file, carefully.

Parameters
sFilename

This won't follow symlinks.

Definition at line 194 of file file.c.

195{
196 if (!s)
197 return;
198
199 struct stat st = { 0 };
200 /* Defend against symlink attacks */
201
202 const bool is_regular_file = (lstat(s, &st) == 0) && S_ISREG(st.st_mode);
203 if (!is_regular_file)
204 return;
205
206 const int fd = open(s, O_RDWR | O_NOFOLLOW);
207 if (fd < 0)
208 return;
209
210 struct stat st2 = { 0 };
211 if ((fstat(fd, &st2) != 0) || !S_ISREG(st2.st_mode) ||
212 (st.st_dev != st2.st_dev) || (st.st_ino != st2.st_ino))
213 {
214 close(fd);
215 return;
216 }
217
218 unlink(s);
219 close(fd);
220}
#define O_NOFOLLOW
Definition: file.c:65
+ Here is the caller graph for this function:

◆ mutt_file_copy_bytes()

int mutt_file_copy_bytes ( FILE *  fp_in,
FILE *  fp_out,
size_t  size 
)

Copy some content from one file to another.

Parameters
fp_inSource file
fp_outDestination file
sizeMaximum number of bytes to copy
Return values
0Success
-1Error, see errno

Definition at line 230 of file file.c.

231{
232 if (!fp_in || !fp_out)
233 return -1;
234
235 while (size > 0)
236 {
237 char buf[2048] = { 0 };
238 size_t chunk = (size > sizeof(buf)) ? sizeof(buf) : size;
239 chunk = fread(buf, 1, chunk, fp_in);
240 if (chunk < 1)
241 break;
242 if (fwrite(buf, 1, chunk, fp_out) != chunk)
243 return -1;
244
245 size -= chunk;
246 }
247
248 if (fflush(fp_out) != 0)
249 return -1;
250 return 0;
251}
+ Here is the caller graph for this function:

◆ mutt_file_copy_stream()

int mutt_file_copy_stream ( FILE *  fp_in,
FILE *  fp_out 
)

Copy the contents of one file into another.

Parameters
fp_inSource file
fp_outDestination file
Return values
numSuccess, number of bytes copied
-1Error, see errno

Definition at line 260 of file file.c.

261{
262 if (!fp_in || !fp_out)
263 return -1;
264
265 size_t total = 0;
266 size_t l;
267 char buf[1024] = { 0 };
268
269 while ((l = fread(buf, 1, sizeof(buf), fp_in)) > 0)
270 {
271 if (fwrite(buf, 1, l, fp_out) != l)
272 return -1;
273 total += l;
274 }
275
276 if (fflush(fp_out) != 0)
277 return -1;
278 return total;
279}
+ Here is the caller graph for this function:

◆ mutt_file_symlink()

int mutt_file_symlink ( const char *  oldpath,
const char *  newpath 
)

Create a symlink.

Parameters
oldpathExisting pathname
newpathNew pathname
Return values
0Success
-1Error, see errno

Definition at line 288 of file file.c.

289{
290 struct stat st_old = { 0 };
291 struct stat st_new = { 0 };
292
293 if (!oldpath || !newpath)
294 return -1;
295
296 if ((unlink(newpath) == -1) && (errno != ENOENT))
297 return -1;
298
299 if (oldpath[0] == '/')
300 {
301 if (symlink(oldpath, newpath) == -1)
302 return -1;
303 }
304 else
305 {
306 struct Buffer abs_oldpath = buf_make(PATH_MAX);
307
308 if (!mutt_path_getcwd(&abs_oldpath))
309 {
310 buf_dealloc(&abs_oldpath);
311 return -1;
312 }
313
314 buf_addch(&abs_oldpath, '/');
315 buf_addstr(&abs_oldpath, oldpath);
316 if (symlink(buf_string(&abs_oldpath), newpath) == -1)
317 {
318 buf_dealloc(&abs_oldpath);
319 return -1;
320 }
321
322 buf_dealloc(&abs_oldpath);
323 }
324
325 if ((stat(oldpath, &st_old) == -1) || (stat(newpath, &st_new) == -1) ||
326 !compare_stat(&st_old, &st_new))
327 {
328 unlink(newpath);
329 return -1;
330 }
331
332 return 0;
333}
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:253
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:238
static bool compare_stat(struct stat *st_old, struct stat *st_new)
Compare the struct stat's of two files/dirs.
Definition: file.c:77
const char * mutt_path_getcwd(struct Buffer *cwd)
Get the current working directory.
Definition: path.c:563
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_file_safe_rename()

int mutt_file_safe_rename ( const char *  src,
const char *  target 
)

NFS-safe renaming of files.

Parameters
srcOriginal filename
targetNew filename
Return values
0Success
-1Error, see errno

Warning: We don't check whether src and target are equal.

Definition at line 344 of file file.c.

345{
346 struct stat st_src = { 0 };
347 struct stat st_target = { 0 };
348 int link_errno;
349
350 if (!src || !target)
351 return -1;
352
353 if (link(src, target) != 0)
354 {
355 link_errno = errno;
356
357 /* It is historically documented that link can return -1 if NFS
358 * dies after creating the link. In that case, we are supposed
359 * to use stat to check if the link was created.
360 *
361 * Derek Martin notes that some implementations of link() follow a
362 * source symlink. It might be more correct to use stat() on src.
363 * I am not doing so to minimize changes in behavior: the function
364 * used lstat() further below for 20 years without issue, and I
365 * believe was never intended to be used on a src symlink. */
366 if ((lstat(src, &st_src) == 0) && (lstat(target, &st_target) == 0) &&
367 (compare_stat(&st_src, &st_target) == 0))
368 {
369 mutt_debug(LL_DEBUG1, "link (%s, %s) reported failure: %s (%d) but actually succeeded\n",
370 src, target, strerror(errno), errno);
371 goto success;
372 }
373
374 errno = link_errno;
375
376 /* Coda does not allow cross-directory links, but tells
377 * us it's a cross-filesystem linking attempt.
378 *
379 * However, the Coda rename call is allegedly safe to use.
380 *
381 * With other file systems, rename should just fail when
382 * the files reside on different file systems, so it's safe
383 * to try it here. */
384 mutt_debug(LL_DEBUG1, "link (%s, %s) failed: %s (%d)\n", src, target,
385 strerror(errno), errno);
386
387 /* FUSE may return ENOSYS. VFAT may return EPERM. FreeBSD's
388 * msdosfs may return EOPNOTSUPP. ENOTSUP can also appear. */
389 if ((errno == EXDEV) || (errno == ENOSYS) || errno == EPERM
390#ifdef ENOTSUP
391 || errno == ENOTSUP
392#endif
393#ifdef EOPNOTSUPP
394 || errno == EOPNOTSUPP
395#endif
396 )
397 {
398 mutt_debug(LL_DEBUG1, "trying rename\n");
399 if (rename(src, target) == -1)
400 {
401 mutt_debug(LL_DEBUG1, "rename (%s, %s) failed: %s (%d)\n", src, target,
402 strerror(errno), errno);
403 return -1;
404 }
405 mutt_debug(LL_DEBUG1, "rename succeeded\n");
406
407 return 0;
408 }
409
410 return -1;
411 }
412
413 /* Remove the compare_stat() check, because it causes problems with maildir
414 * on filesystems that don't properly support hard links, such as sshfs. The
415 * filesystem creates the link, but the resulting file is given a different
416 * inode number by the sshfs layer. This results in an infinite loop
417 * creating links. */
418#if 0
419 /* Stat both links and check if they are equal. */
420 if (lstat(src, &st_src) == -1)
421 {
422 mutt_debug(LL_DEBUG1, "#1 can't stat %s: %s (%d)\n", src, strerror(errno), errno);
423 return -1;
424 }
425
426 if (lstat(target, &st_target) == -1)
427 {
428 mutt_debug(LL_DEBUG1, "#2 can't stat %s: %s (%d)\n", src, strerror(errno), errno);
429 return -1;
430 }
431
432 /* pretend that the link failed because the target file did already exist. */
433
434 if (!compare_stat(&st_src, &st_target))
435 {
436 mutt_debug(LL_DEBUG1, "stat blocks for %s and %s diverge; pretending EEXIST\n", src, target);
437 errno = EEXIST;
438 return -1;
439 }
440#endif
441
442success:
443 /* Unlink the original link.
444 * Should we really ignore the return value here? XXX */
445 if (unlink(src) == -1)
446 {
447 mutt_debug(LL_DEBUG1, "unlink (%s) failed: %s (%d)\n", src, strerror(errno), errno);
448 }
449
450 return 0;
451}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_file_rmtree()

int mutt_file_rmtree ( const char *  path)

Recursively remove a directory.

Parameters
pathDirectory to delete
Return values
0Success
-1Error, see errno

Definition at line 459 of file file.c.

460{
461 if (!path)
462 return -1;
463
464 struct dirent *de = NULL;
465 struct stat st = { 0 };
466 int rc = 0;
467
468 DIR *dir = mutt_file_opendir(path, MUTT_OPENDIR_NONE);
469 if (!dir)
470 {
471 mutt_debug(LL_DEBUG1, "error opening directory %s\n", path);
472 return -1;
473 }
474
475 /* We avoid using the buffer pool for this function, because it
476 * invokes recursively to an unknown depth. */
477 struct Buffer cur = buf_make(PATH_MAX);
478
479 while ((de = readdir(dir)))
480 {
481 if ((mutt_str_equal(".", de->d_name)) || (mutt_str_equal("..", de->d_name)))
482 continue;
483
484 buf_printf(&cur, "%s/%s", path, de->d_name);
485 /* XXX make nonrecursive version */
486
487 if (stat(buf_string(&cur), &st) == -1)
488 {
489 rc = 1;
490 continue;
491 }
492
493 if (S_ISDIR(st.st_mode))
494 rc |= mutt_file_rmtree(buf_string(&cur));
495 else
496 rc |= unlink(buf_string(&cur));
497 }
498 closedir(dir);
499
500 rc |= rmdir(path);
501
502 buf_dealloc(&cur);
503 return rc;
504}
DIR * mutt_file_opendir(const char *path, enum MuttOpenDirMode mode)
Open a directory.
Definition: file.c:614
int mutt_file_rmtree(const char *path)
Recursively remove a directory.
Definition: file.c:459
@ MUTT_OPENDIR_NONE
Plain opendir()
Definition: file.h:73
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:798
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_file_rotate()

const char * mutt_file_rotate ( const char *  path,
int  count 
)

Rotate a set of numbered files.

Parameters
pathTemplate filename
countMaximum number of files
Return values
ptrName of the 0'th file

Given a template 'temp', rename files numbered 0 to (count-1).

Rename:

  • ...
  • temp1 -> temp2
  • temp0 -> temp1

Definition at line 519 of file file.c.

520{
521 if (!path)
522 return NULL;
523
524 struct Buffer *old_file = buf_pool_get();
525 struct Buffer *new_file = buf_pool_get();
526
527 /* rotate the old debug logs */
528 for (count -= 2; count >= 0; count--)
529 {
530 buf_printf(old_file, "%s%d", path, count);
531 buf_printf(new_file, "%s%d", path, count + 1);
532 (void) rename(buf_string(old_file), buf_string(new_file));
533 }
534
535 path = buf_strdup(old_file);
536 buf_pool_release(&old_file);
537 buf_pool_release(&new_file);
538
539 return path;
540}
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:536
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_file_open()

int mutt_file_open ( const char *  path,
uint32_t  flags 
)

Open a file.

Parameters
pathPathname to open
flagsFlags, e.g. O_EXCL
Return values
>0Success, file handle
-1Error

Definition at line 549 of file file.c.

550{
551 if (!path)
552 return -1;
553
554 int fd;
555 struct Buffer safe_file = buf_make(0);
556 struct Buffer safe_dir = buf_make(0);
557
558 if (flags & O_EXCL)
559 {
560 buf_alloc(&safe_file, PATH_MAX);
561 buf_alloc(&safe_dir, PATH_MAX);
562
563 if (mkwrapdir(path, &safe_file, &safe_dir) == -1)
564 {
565 fd = -1;
566 goto cleanup;
567 }
568
569 fd = open(buf_string(&safe_file), flags, 0600);
570 if (fd < 0)
571 {
572 rmdir(buf_string(&safe_dir));
573 goto cleanup;
574 }
575
576 /* NFS and I believe cygwin do not handle movement of open files well */
577 close(fd);
578 if (put_file_in_place(path, buf_string(&safe_file), buf_string(&safe_dir)) == -1)
579 {
580 fd = -1;
581 goto cleanup;
582 }
583 }
584
585 fd = open(path, flags & ~O_EXCL, 0600);
586 if (fd < 0)
587 goto cleanup;
588
589 /* make sure the file is not symlink */
590 struct stat st_old = { 0 };
591 struct stat st_new = { 0 };
592 if (((lstat(path, &st_old) < 0) || (fstat(fd, &st_new) < 0)) ||
593 !compare_stat(&st_old, &st_new))
594 {
595 close(fd);
596 fd = -1;
597 goto cleanup;
598 }
599
600cleanup:
601 buf_dealloc(&safe_file);
602 buf_dealloc(&safe_dir);
603
604 return fd;
605}
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:347
static int put_file_in_place(const char *path, const char *safe_file, const char *safe_dir)
Move a file into place.
Definition: file.c:134
static int mkwrapdir(const char *path, struct Buffer *newfile, struct Buffer *newdir)
Create a temporary directory next to a file name.
Definition: file.c:91
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_file_opendir()

DIR * mutt_file_opendir ( const char *  path,
enum MuttOpenDirMode  mode 
)

Open a directory.

Parameters
pathDirectory path
modeSee MuttOpenDirMode
Return values
ptrDIR handle
NULLError, see errno

Definition at line 614 of file file.c.

615{
616 if ((mode == MUTT_OPENDIR_CREATE) && (mutt_file_mkdir(path, S_IRWXU) == -1))
617 {
618 return NULL;
619 }
620 errno = 0;
621 return opendir(path);
622}
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:952
@ MUTT_OPENDIR_CREATE
Create the directory if it doesn't exist.
Definition: file.h:74
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_file_fopen()

FILE * mutt_file_fopen ( const char *  path,
const char *  mode 
)

Call fopen() safely.

Parameters
pathFilename
modeMode e.g. "r" readonly; "w" read-write
Return values
ptrFILE handle
NULLError, see errno

When opening files for writing, make sure the file doesn't already exist to avoid race conditions.

Definition at line 634 of file file.c.

635{
636 if (!path || !mode)
637 return NULL;
638
639 if (mode[0] == 'w')
640 {
641 uint32_t flags = O_CREAT | O_EXCL | O_NOFOLLOW;
642
643 if (mode[1] == '+')
644 flags |= O_RDWR;
645 else
646 flags |= O_WRONLY;
647
648 int fd = mutt_file_open(path, flags);
649 if (fd < 0)
650 return NULL;
651
652 return fdopen(fd, mode);
653 }
654 else
655 {
656 return fopen(path, mode);
657 }
658}
int mutt_file_open(const char *path, uint32_t flags)
Open a file.
Definition: file.c:549
+ Here is the call graph for this function:

◆ mutt_file_sanitize_filename()

void mutt_file_sanitize_filename ( char *  path,
bool  slash 
)

Replace unsafe characters in a filename.

Parameters
pathFilename to make safe
slashReplace '/' characters too

Definition at line 665 of file file.c.

666{
667 if (!path)
668 return;
669
670 for (; *path; path++)
671 {
672 if ((slash && (*path == '/')) || !strchr(FilenameSafeChars, *path))
673 *path = '_';
674 }
675}
const char FilenameSafeChars[]
Set of characters that are safe to use in filenames.
Definition: file.c:59
+ Here is the caller graph for this function:

◆ mutt_file_sanitize_regex()

int mutt_file_sanitize_regex ( struct Buffer dest,
const char *  src 
)

Escape any regex-magic characters in a string.

Parameters
destBuffer for result
srcString to transform
Return values
0Success
-1Error

Definition at line 684 of file file.c.

685{
686 if (!dest || !src)
687 return -1;
688
689 buf_reset(dest);
690 while (*src != '\0')
691 {
692 if (strchr(RxSpecialChars, *src))
693 buf_addch(dest, '\\');
694 buf_addch(dest, *src++);
695 }
696
697 return 0;
698}
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:88
static const char RxSpecialChars[]
These characters must be escaped in regular expressions.
Definition: file.c:56
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_file_seek()

bool mutt_file_seek ( FILE *  fp,
LOFF_T  offset,
int  whence 
)

Wrapper for fseeko with error handling.

Parameters
[in]fpFile to seek
[in]offsetOffset
[in]whenceSeek mode
Return values
trueSeek was successful
falseSeek failed

Definition at line 708 of file file.c.

709{
710 if (!fp)
711 {
712 return false;
713 }
714
715 if (fseeko(fp, offset, whence) != 0)
716 {
717 mutt_perror(_("Failed to seek file: %s"), strerror(errno));
718 return false;
719 }
720
721 return true;
722}
#define mutt_perror(...)
Definition: logging2.h:91
#define _(a)
Definition: message.h:28
+ Here is the caller graph for this function:

◆ mutt_file_read_line()

char * mutt_file_read_line ( char *  line,
size_t *  size,
FILE *  fp,
int *  line_num,
ReadLineFlags  flags 
)

Read a line from a file.

Parameters
[out]lineBuffer allocated on the head (optional)
[in]sizeLength of buffer
[in]fpFile to read
[out]line_numCurrent line number (optional)
[in]flagsFlags, e.g. MUTT_RL_CONT
Return values
ptrThe allocated string

Read a line from "fp" into the dynamically allocated "line", increasing "line" if necessary. The ending "\n" or "\r\n" is removed. If a line ends with "\", this char and the linefeed is removed, and the next line is read too.

Definition at line 738 of file file.c.

739{
740 if (!size || !fp)
741 return NULL;
742
743 size_t offset = 0;
744 char *ch = NULL;
745
746 if (!line)
747 {
748 *size = 256;
749 line = mutt_mem_malloc(*size);
750 }
751
752 while (true)
753 {
754 if (!fgets(line + offset, *size - offset, fp))
755 {
756 FREE(&line);
757 return NULL;
758 }
759 ch = strchr(line + offset, '\n');
760 if (ch)
761 {
762 if (line_num)
763 (*line_num)++;
764 if (flags & MUTT_RL_EOL)
765 return line;
766 *ch = '\0';
767 if ((ch > line) && (*(ch - 1) == '\r'))
768 *--ch = '\0';
769 if (!(flags & MUTT_RL_CONT) || (ch == line) || (*(ch - 1) != '\\'))
770 return line;
771 offset = ch - line - 1;
772 }
773 else
774 {
775 int c;
776 c = getc(fp); /* This is kind of a hack. We want to know if the
777 char at the current point in the input stream is EOF.
778 feof() will only tell us if we've already hit EOF, not
779 if the next character is EOF. So, we need to read in
780 the next character and manually check if it is EOF. */
781 if (c == EOF)
782 {
783 /* The last line of fp isn't \n terminated */
784 if (line_num)
785 (*line_num)++;
786 return line;
787 }
788 else
789 {
790 ungetc(c, fp); /* undo our damage */
791 /* There wasn't room for the line -- increase "line" */
792 offset = *size - 1; /* overwrite the terminating 0 */
793 *size += 256;
794 mutt_mem_realloc(&line, *size);
795 }
796 }
797 }
798}
#define MUTT_RL_CONT
-continuation
Definition: file.h:40
#define MUTT_RL_EOL
don't strip \n / \r\n
Definition: file.h:41
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
#define FREE(x)
Definition: memory.h:43
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_file_iter_line()

bool mutt_file_iter_line ( struct MuttFileIter iter,
FILE *  fp,
ReadLineFlags  flags 
)

Iterate over the lines from an open file pointer.

Parameters
iterState of iteration including ptr to line
fpFile pointer to read from
flagsSame as mutt_file_read_line()
Return values
trueData read
falseOn eof

This is a slightly cleaner interface for mutt_file_read_line() which avoids the eternal C loop initialization ugliness. Use like this:

struct MuttFileIter iter = { 0 };
while (mutt_file_iter_line(&iter, fp, flags))
{
do_stuff(iter.line, iter.line_num);
}
bool mutt_file_iter_line(struct MuttFileIter *iter, FILE *fp, ReadLineFlags flags)
Iterate over the lines from an open file pointer.
Definition: file.c:819
State record for mutt_file_iter_line()
Definition: file.h:81
char * line
the line data
Definition: file.h:82
int line_num
line number
Definition: file.h:84

Definition at line 819 of file file.c.

820{
821 if (!iter)
822 return false;
823
824 char *p = mutt_file_read_line(iter->line, &iter->size, fp, &iter->line_num, flags);
825 if (!p)
826 return false;
827 iter->line = p;
828 return true;
829}
char * mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, ReadLineFlags flags)
Read a line from a file.
Definition: file.c:738
size_t size
allocated size of line data
Definition: file.h:83
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_file_map_lines()

bool mutt_file_map_lines ( mutt_file_map_t  func,
void *  user_data,
FILE *  fp,
ReadLineFlags  flags 
)

Process lines of text read from a file pointer.

Parameters
funcCallback function to call for each line, see mutt_file_map_t
user_dataArbitrary data passed to "func"
fpFile pointer to read from
flagsSame as mutt_file_read_line()
Return values
trueAll data mapped
false"func" returns false

Definition at line 840 of file file.c.

841{
842 if (!func || !fp)
843 return false;
844
845 struct MuttFileIter iter = { 0 };
846 while (mutt_file_iter_line(&iter, fp, flags))
847 {
848 if (!(*func)(iter.line, iter.line_num, user_data))
849 {
850 FREE(&iter.line);
851 return false;
852 }
853 }
854 return true;
855}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_file_quote_filename()

size_t mutt_file_quote_filename ( const char *  filename,
char *  buf,
size_t  buflen 
)

Quote a filename to survive the shell's quoting rules.

Parameters
filenameString to convert
bufBuffer for the result
buflenLength of buffer
Return values
numBytes written to the buffer

From the Unix programming FAQ by way of Liviu.

Definition at line 866 of file file.c.

867{
868 if (!buf)
869 return 0;
870
871 if (!filename)
872 {
873 *buf = '\0';
874 return 0;
875 }
876
877 size_t j = 0;
878
879 /* leave some space for the trailing characters. */
880 buflen -= 6;
881
882 buf[j++] = '\'';
883
884 for (size_t i = 0; (j < buflen) && filename[i]; i++)
885 {
886 if ((filename[i] == '\'') || (filename[i] == '`'))
887 {
888 buf[j++] = '\'';
889 buf[j++] = '\\';
890 buf[j++] = filename[i];
891 buf[j++] = '\'';
892 }
893 else
894 {
895 buf[j++] = filename[i];
896 }
897 }
898
899 buf[j++] = '\'';
900 buf[j] = '\0';
901
902 return j;
903}
+ Here is the caller graph for this function:

◆ buf_quote_filename()

void buf_quote_filename ( struct Buffer buf,
const char *  filename,
bool  add_outer 
)

Quote a filename to survive the shell's quoting rules.

Parameters
bufBuffer for the result
filenameString to convert
add_outerIf true, add 'single quotes' around the result

Definition at line 911 of file file.c.

912{
913 if (!buf || !filename)
914 return;
915
916 buf_reset(buf);
917 if (add_outer)
918 buf_addch(buf, '\'');
919
920 for (; *filename != '\0'; filename++)
921 {
922 if ((*filename == '\'') || (*filename == '`'))
923 {
924 buf_addch(buf, '\'');
925 buf_addch(buf, '\\');
926 buf_addch(buf, *filename);
927 buf_addch(buf, '\'');
928 }
929 else
930 {
931 buf_addch(buf, *filename);
932 }
933 }
934
935 if (add_outer)
936 buf_addch(buf, '\'');
937}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_file_mkdir()

int mutt_file_mkdir ( const char *  path,
mode_t  mode 
)

Recursively create directories.

Parameters
pathDirectories to create
modePermissions for final directory
Return values
0Success
-1Error (errno set)

Create a directory, creating the parents if necessary. (like mkdir -p)

Note
The permissions are only set on the final directory. The permissions of any parent directories are determined by the umask. (This is how "mkdir -p" behaves)

Definition at line 952 of file file.c.

953{
954 if (!path || (*path == '\0'))
955 {
956 errno = EINVAL;
957 return -1;
958 }
959
960 errno = 0;
961 char tmp_path[PATH_MAX] = { 0 };
962 const size_t len = strlen(path);
963
964 if (len >= sizeof(tmp_path))
965 {
966 errno = ENAMETOOLONG;
967 return -1;
968 }
969
970 struct stat st = { 0 };
971 if ((stat(path, &st) == 0) && S_ISDIR(st.st_mode))
972 return 0;
973
974 /* Create a mutable copy */
975 mutt_str_copy(tmp_path, path, sizeof(tmp_path));
976
977 for (char *p = tmp_path + 1; *p; p++)
978 {
979 if (*p != '/')
980 continue;
981
982 /* Temporarily truncate the path */
983 *p = '\0';
984
985 if ((mkdir(tmp_path, S_IRWXU | S_IRWXG | S_IRWXO) != 0) && (errno != EEXIST))
986 return -1;
987
988 *p = '/';
989 }
990
991 if ((mkdir(tmp_path, mode) != 0) && (errno != EEXIST))
992 return -1;
993
994 return 0;
995}
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:653
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_file_decrease_mtime()

time_t mutt_file_decrease_mtime ( const char *  fp,
struct stat *  st 
)

Decrease a file's modification time by 1 second.

Parameters
fpFilename
ststruct stat for the file (optional)
Return values
numUpdated Unix mtime
-1Error, see errno

If a file's mtime is NOW, then set it to 1 second in the past.

Definition at line 1006 of file file.c.

1007{
1008 if (!fp)
1009 return -1;
1010
1011 struct utimbuf utim;
1012 struct stat st2 = { 0 };
1013 time_t mtime;
1014
1015 if (!st)
1016 {
1017 if (stat(fp, &st2) == -1)
1018 return -1;
1019 st = &st2;
1020 }
1021
1022 mtime = st->st_mtime;
1023 if (mtime == mutt_date_now())
1024 {
1025 mtime -= 1;
1026 utim.actime = mtime;
1027 utim.modtime = mtime;
1028 int rc;
1029 do
1030 {
1031 rc = utime(fp, &utim);
1032 } while ((rc == -1) && (errno == EINTR));
1033
1034 if (rc == -1)
1035 return -1;
1036 }
1037
1038 return mtime;
1039}
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:446
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_file_set_mtime()

void mutt_file_set_mtime ( const char *  from,
const char *  to 
)

Set the modification time of one file from another.

Parameters
fromFilename whose mtime should be copied
toFilename to update

Definition at line 1046 of file file.c.

1047{
1048 if (!from || !to)
1049 return;
1050
1051 struct utimbuf utim;
1052 struct stat st = { 0 };
1053
1054 if (stat(from, &st) != -1)
1055 {
1056 utim.actime = st.st_mtime;
1057 utim.modtime = st.st_mtime;
1058 utime(to, &utim);
1059 }
1060}
+ Here is the caller graph for this function:

◆ mutt_file_touch_atime()

void mutt_file_touch_atime ( int  fd)

Set the access time to current time.

Parameters
fdFile descriptor of the file to alter

This is just as read() would do on !noatime. Silently ignored if futimens() isn't supported.

Definition at line 1069 of file file.c.

1070{
1071#ifdef HAVE_FUTIMENS
1072 struct timespec times[2] = { { 0, UTIME_NOW }, { 0, UTIME_OMIT } };
1073 futimens(fd, times);
1074#endif
1075}
Time value with nanosecond precision.
Definition: file.h:50
+ Here is the caller graph for this function:

◆ mutt_file_chmod()

int mutt_file_chmod ( const char *  path,
mode_t  mode 
)

Set permissions of a file.

Parameters
pathFilename
modethe permissions to set
Return values
numSame as chmod(2)

This is essentially chmod(path, mode), see chmod(2).

Definition at line 1085 of file file.c.

1086{
1087 if (!path)
1088 return -1;
1089
1090 return chmod(path, mode);
1091}

◆ mutt_file_chmod_add()

int mutt_file_chmod_add ( const char *  path,
mode_t  mode 
)

Add permissions to a file.

Parameters
pathFilename
modethe permissions to add
Return values
numSame as chmod(2)

Adds the given permissions to the file. Permissions not mentioned in mode will stay as they are. This function resembles the chmod ugoa+rwxXst command family. Example:

mutt_file_chmod_add(path, S_IWUSR | S_IWGRP | S_IWOTH);

will add write permissions to path but does not alter read and other permissions.

See also
mutt_file_chmod_add_stat()

Definition at line 1110 of file file.c.

1111{
1112 return mutt_file_chmod_add_stat(path, mode, NULL);
1113}
int mutt_file_chmod_add_stat(const char *path, mode_t mode, struct stat *st)
Add permissions to a file.
Definition: file.c:1133
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_file_chmod_add_stat()

int mutt_file_chmod_add_stat ( const char *  path,
mode_t  mode,
struct stat *  st 
)

Add permissions to a file.

Parameters
pathFilename
modethe permissions to add
ststruct stat for the file (optional)
Return values
numSame as chmod(2)

Same as mutt_file_chmod_add() but saves a system call to stat() if a non-NULL stat structure is given. Useful if the stat structure of the file was retrieved before by the calling code. Example:

struct stat st;
stat(path, &st);
// ... do something else with st ...
mutt_file_chmod_add_stat(path, S_IWUSR | S_IWGRP | S_IWOTH, st);
See also
mutt_file_chmod_add()

Definition at line 1133 of file file.c.

1134{
1135 if (!path)
1136 return -1;
1137
1138 struct stat st2 = { 0 };
1139
1140 if (!st)
1141 {
1142 if (stat(path, &st2) == -1)
1143 return -1;
1144 st = &st2;
1145 }
1146 return chmod(path, st->st_mode | mode);
1147}
+ Here is the caller graph for this function:

◆ mutt_file_chmod_rm()

int mutt_file_chmod_rm ( const char *  path,
mode_t  mode 
)

Remove permissions from a file.

Parameters
pathFilename
modethe permissions to remove
Return values
numSame as chmod(2)

Removes the given permissions from the file. Permissions not mentioned in mode will stay as they are. This function resembles the chmod ugoa-rwxXst command family. Example:

mutt_file_chmod_rm(path, S_IWUSR | S_IWGRP | S_IWOTH);

will remove write permissions from path but does not alter read and other permissions.

See also
mutt_file_chmod_rm_stat()

Definition at line 1166 of file file.c.

1167{
1168 return mutt_file_chmod_rm_stat(path, mode, NULL);
1169}
int mutt_file_chmod_rm_stat(const char *path, mode_t mode, struct stat *st)
Remove permissions from a file.
Definition: file.c:1189
+ Here is the call graph for this function:

◆ mutt_file_chmod_rm_stat()

int mutt_file_chmod_rm_stat ( const char *  path,
mode_t  mode,
struct stat *  st 
)

Remove permissions from a file.

Parameters
pathFilename
modethe permissions to remove
ststruct stat for the file (optional)
Return values
numSame as chmod(2)

Same as mutt_file_chmod_rm() but saves a system call to stat() if a non-NULL stat structure is given. Useful if the stat structure of the file was retrieved before by the calling code. Example:

struct stat st;
stat(path, &st);
// ... do something else with st ...
mutt_file_chmod_rm_stat(path, S_IWUSR | S_IWGRP | S_IWOTH, st);
See also
mutt_file_chmod_rm()

Definition at line 1189 of file file.c.

1190{
1191 if (!path)
1192 return -1;
1193
1194 struct stat st2 = { 0 };
1195
1196 if (!st)
1197 {
1198 if (stat(path, &st2) == -1)
1199 return -1;
1200 st = &st2;
1201 }
1202 return chmod(path, st->st_mode & ~mode);
1203}
+ Here is the caller graph for this function:

◆ mutt_file_lock()

int mutt_file_lock ( int  fd,
bool  excl,
bool  timeout 
)

(Try to) Lock a file using fcntl()

Parameters
fdFile descriptor to file
exclIf true, try to lock exclusively
timeoutIf true, Retry MAX_LOCK_ATTEMPTS times
Return values
0Success
-1Failure

Use fcntl() to lock a file.

Use mutt_file_unlock() to unlock the file.

Definition at line 1218 of file file.c.

1219{
1220 struct stat st = { 0 }, prev_sb = { 0 };
1221 int count = 0;
1222 int attempt = 0;
1223
1224 struct flock lck;
1225 memset(&lck, 0, sizeof(struct flock));
1226 lck.l_type = excl ? F_WRLCK : F_RDLCK;
1227 lck.l_whence = SEEK_SET;
1228
1229 while (fcntl(fd, F_SETLK, &lck) == -1)
1230 {
1231 mutt_debug(LL_DEBUG1, "fcntl errno %d\n", errno);
1232 if ((errno != EAGAIN) && (errno != EACCES))
1233 {
1234 mutt_perror("fcntl");
1235 return -1;
1236 }
1237
1238 if (fstat(fd, &st) != 0)
1239 st.st_size = 0;
1240
1241 if (count == 0)
1242 prev_sb = st;
1243
1244 /* only unlock file if it is unchanged */
1245 if ((prev_sb.st_size == st.st_size) && (++count >= (timeout ? MAX_LOCK_ATTEMPTS : 0)))
1246 {
1247 if (timeout)
1248 mutt_error(_("Timeout exceeded while attempting fcntl lock"));
1249 return -1;
1250 }
1251
1252 prev_sb = st;
1253
1254 mutt_message(_("Waiting for fcntl lock... %d"), ++attempt);
1255 sleep(1);
1256 }
1257
1258 return 0;
1259}
#define MAX_LOCK_ATTEMPTS
Definition: file.c:61
#define mutt_error(...)
Definition: logging2.h:90
#define mutt_message(...)
Definition: logging2.h:89
+ Here is the caller graph for this function:

◆ mutt_file_unlock()

int mutt_file_unlock ( int  fd)

Unlock a file previously locked by mutt_file_lock()

Parameters
fdFile descriptor to file
Return values
0Always

Definition at line 1266 of file file.c.

1267{
1268 struct flock unlockit;
1269
1270 memset(&unlockit, 0, sizeof(struct flock));
1271 unlockit.l_type = F_UNLCK;
1272 unlockit.l_whence = SEEK_SET;
1273 (void) fcntl(fd, F_SETLK, &unlockit);
1274
1275 return 0;
1276}
+ Here is the caller graph for this function:

◆ mutt_file_unlink_empty()

void mutt_file_unlink_empty ( const char *  path)

Delete a file if it's empty.

Parameters
pathFile to delete

Definition at line 1354 of file file.c.

1355{
1356 if (!path)
1357 return;
1358
1359 struct stat st = { 0 };
1360
1361 int fd = open(path, O_RDWR);
1362 if (fd == -1)
1363 return;
1364
1365 if (mutt_file_lock(fd, true, true) == -1)
1366 {
1367 close(fd);
1368 return;
1369 }
1370
1371 if ((fstat(fd, &st) == 0) && (st.st_size == 0))
1372 unlink(path);
1373
1374 mutt_file_unlock(fd);
1375 close(fd);
1376}
int mutt_file_lock(int fd, bool excl, bool timeout)
(Try to) Lock a file using fcntl()
Definition: file.c:1218
int mutt_file_unlock(int fd)
Unlock a file previously locked by mutt_file_lock()
Definition: file.c:1266
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_file_rename()

int mutt_file_rename ( const char *  oldfile,
const char *  newfile 
)

Rename a file.

Parameters
oldfileOld filename
newfileNew filename
Return values
0Success
1Old file doesn't exist
2New file already exists
3Some other error
Note
on access(2) use No dangling symlink problems here due to mutt_file_fopen().

Definition at line 1390 of file file.c.

1391{
1392 if (!oldfile || !newfile)
1393 return -1;
1394 if (access(oldfile, F_OK) != 0)
1395 return 1;
1396 if (access(newfile, F_OK) == 0)
1397 return 2;
1398
1399 FILE *fp_old = fopen(oldfile, "r");
1400 if (!fp_old)
1401 return 3;
1402 FILE *fp_new = mutt_file_fopen(newfile, "w");
1403 if (!fp_new)
1404 {
1405 mutt_file_fclose(&fp_old);
1406 return 3;
1407 }
1408 mutt_file_copy_stream(fp_old, fp_new);
1409 mutt_file_fclose(&fp_new);
1410 mutt_file_fclose(&fp_old);
1411 mutt_file_unlink(oldfile);
1412 return 0;
1413}
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:260
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:634
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:194
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_file_read_keyword()

char * mutt_file_read_keyword ( const char *  file,
char *  buf,
size_t  buflen 
)

Read a keyword from a file.

Parameters
fileFile to read
bufBuffer to store the keyword
buflenLength of the buf
Return values
ptrStart of the keyword

Read one line from the start of a file. Skip any leading whitespace and extract the first token.

Definition at line 1425 of file file.c.

1426{
1427 FILE *fp = mutt_file_fopen(file, "r");
1428 if (!fp)
1429 return NULL;
1430
1431 buf = fgets(buf, buflen, fp);
1432 mutt_file_fclose(&fp);
1433
1434 if (!buf)
1435 return NULL;
1436
1437 SKIPWS(buf);
1438 char *start = buf;
1439
1440 while ((*buf != '\0') && !isspace(*buf))
1441 buf++;
1442
1443 *buf = '\0';
1444
1445 return start;
1446}
#define SKIPWS(ch)
Definition: string2.h:45
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_file_check_empty()

int mutt_file_check_empty ( const char *  path)

Is the mailbox empty.

Parameters
pathPath to mailbox
Return values
1Mailbox is empty
0Mailbox is not empty
-1Error

Definition at line 1455 of file file.c.

1456{
1457 if (!path)
1458 return -1;
1459
1460 struct stat st = { 0 };
1461 if (stat(path, &st) == -1)
1462 return -1;
1463
1464 return st.st_size == 0;
1465}
+ Here is the caller graph for this function:

◆ buf_file_expand_fmt_quote()

void buf_file_expand_fmt_quote ( struct Buffer dest,
const char *  fmt,
const char *  src 
)

Replace s in a string with a filename.

Parameters
destBuffer for the result
fmtprintf-like format string
srcFilename to substitute

This function also quotes the file to prevent shell problems.

Definition at line 1475 of file file.c.

1476{
1477 struct Buffer tmp = buf_make(PATH_MAX);
1478
1479 buf_quote_filename(&tmp, src, true);
1480 mutt_file_expand_fmt(dest, fmt, buf_string(&tmp));
1481 buf_dealloc(&tmp);
1482}
void buf_quote_filename(struct Buffer *buf, const char *filename, bool add_outer)
Quote a filename to survive the shell's quoting rules.
Definition: file.c:911
void mutt_file_expand_fmt(struct Buffer *dest, const char *fmt, const char *src)
Replace s in a string with a filename.
Definition: file.c:1490
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_file_expand_fmt()

void mutt_file_expand_fmt ( struct Buffer dest,
const char *  fmt,
const char *  src 
)

Replace s in a string with a filename.

Parameters
destBuffer for the result
fmtprintf-like format string
srcFilename to substitute

Definition at line 1490 of file file.c.

1491{
1492 if (!dest || !fmt || !src)
1493 return;
1494
1495 const char *p = NULL;
1496 bool found = false;
1497
1498 buf_reset(dest);
1499
1500 for (p = fmt; *p; p++)
1501 {
1502 if (*p == '%')
1503 {
1504 switch (p[1])
1505 {
1506 case '%':
1507 buf_addch(dest, *p++);
1508 break;
1509 case 's':
1510 found = true;
1511 buf_addstr(dest, src);
1512 p++;
1513 break;
1514 default:
1515 buf_addch(dest, *p);
1516 break;
1517 }
1518 }
1519 else
1520 {
1521 buf_addch(dest, *p);
1522 }
1523 }
1524
1525 if (!found)
1526 {
1527 buf_addch(dest, ' ');
1528 buf_addstr(dest, src);
1529 }
1530}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_file_get_size()

long mutt_file_get_size ( const char *  path)

Get the size of a file.

Parameters
pathFile to measure
Return values
numSize in bytes
0Error

Definition at line 1538 of file file.c.

1539{
1540 if (!path)
1541 return 0;
1542
1543 struct stat st = { 0 };
1544 if (stat(path, &st) != 0)
1545 return 0;
1546
1547 return st.st_size;
1548}
+ Here is the caller graph for this function:

◆ mutt_file_get_size_fp()

long mutt_file_get_size_fp ( FILE *  fp)

Get the size of a file.

Parameters
fpFILE* to measure
Return values
numSize in bytes
0Error

Definition at line 1556 of file file.c.

1557{
1558 if (!fp)
1559 return 0;
1560
1561 struct stat st = { 0 };
1562 if (fstat(fileno(fp), &st) != 0)
1563 return 0;
1564
1565 return st.st_size;
1566}
+ Here is the caller graph for this function:

◆ mutt_file_timespec_compare()

int mutt_file_timespec_compare ( struct timespec a,
struct timespec b 
)

Compare to time values.

Parameters
aFirst time value
bSecond time value
Return values
-1a precedes b
0a and b are identical
1b precedes a

Definition at line 1576 of file file.c.

1577{
1578 if (!a || !b)
1579 return 0;
1580 if (a->tv_sec < b->tv_sec)
1581 return -1;
1582 if (a->tv_sec > b->tv_sec)
1583 return 1;
1584
1585 if (a->tv_nsec < b->tv_nsec)
1586 return -1;
1587 if (a->tv_nsec > b->tv_nsec)
1588 return 1;
1589 return 0;
1590}
long tv_nsec
Number of nanosecond, on top.
Definition: file.h:52
time_t tv_sec
Number of seconds since the epoch.
Definition: file.h:51
+ Here is the caller graph for this function:

◆ mutt_file_get_stat_timespec()

void mutt_file_get_stat_timespec ( struct timespec dest,
struct stat *  st,
enum MuttStatType  type 
)

Read the stat() time into a time value.

Parameters
destTime value to populate
ststat info
typeType of stat info to read, e.g. MUTT_STAT_ATIME

Definition at line 1598 of file file.c.

1599{
1600 if (!dest || !st)
1601 return;
1602
1603 dest->tv_sec = 0;
1604 dest->tv_nsec = 0;
1605
1606 switch (type)
1607 {
1608 case MUTT_STAT_ATIME:
1609 dest->tv_sec = st->st_atime;
1610#ifdef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
1611 dest->tv_nsec = st->st_atim.tv_nsec;
1612#endif
1613 break;
1614 case MUTT_STAT_MTIME:
1615 dest->tv_sec = st->st_mtime;
1616#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
1617 dest->tv_nsec = st->st_mtim.tv_nsec;
1618#endif
1619 break;
1620 case MUTT_STAT_CTIME:
1621 dest->tv_sec = st->st_ctime;
1622#ifdef HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC
1623 dest->tv_nsec = st->st_ctim.tv_nsec;
1624#endif
1625 break;
1626 }
1627}
@ MUTT_STAT_CTIME
File/dir's ctime - creation time.
Definition: file.h:65
@ MUTT_STAT_ATIME
File/dir's atime - last accessed time.
Definition: file.h:63
@ MUTT_STAT_MTIME
File/dir's mtime - last modified time.
Definition: file.h:64
+ Here is the caller graph for this function:

◆ mutt_file_stat_timespec_compare()

int mutt_file_stat_timespec_compare ( struct stat *  st,
enum MuttStatType  type,
struct timespec b 
)

Compare stat info with a time value.

Parameters
ststat info
typeType of stat info, e.g. MUTT_STAT_ATIME
bTime value
Return values
-1a precedes b
0a and b are identical
1b precedes a

Definition at line 1638 of file file.c.

1640{
1641 if (!st || !b)
1642 return 0;
1643
1644 struct timespec a = { 0 };
1645
1646 mutt_file_get_stat_timespec(&a, st, type);
1647 return mutt_file_timespec_compare(&a, b);
1648}
void mutt_file_get_stat_timespec(struct timespec *dest, struct stat *st, enum MuttStatType type)
Read the stat() time into a time value.
Definition: file.c:1598
int mutt_file_timespec_compare(struct timespec *a, struct timespec *b)
Compare to time values.
Definition: file.c:1576
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_file_stat_compare()

int mutt_file_stat_compare ( struct stat *  st1,
enum MuttStatType  st1_type,
struct stat *  st2,
enum MuttStatType  st2_type 
)

Compare two stat infos.

Parameters
st1First stat info
st1_typeType of first stat info, e.g. MUTT_STAT_ATIME
st2Second stat info
st2_typeType of second stat info, e.g. MUTT_STAT_ATIME
Return values
-1a precedes b
0a and b are identical
1b precedes a

Definition at line 1660 of file file.c.

1662{
1663 if (!st1 || !st2)
1664 return 0;
1665
1666 struct timespec a = { 0 };
1667 struct timespec b = { 0 };
1668
1669 mutt_file_get_stat_timespec(&a, st1, st1_type);
1670 mutt_file_get_stat_timespec(&b, st2, st2_type);
1671 return mutt_file_timespec_compare(&a, &b);
1672}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_file_resolve_symlink()

void mutt_file_resolve_symlink ( struct Buffer buf)

Resolve a symlink in place.

Parameters
bufInput/output path

Definition at line 1678 of file file.c.

1679{
1680 struct stat st = { 0 };
1681 int rc = lstat(buf_string(buf), &st);
1682 if ((rc != -1) && S_ISLNK(st.st_mode))
1683 {
1684 char path[PATH_MAX] = { 0 };
1685 if (realpath(buf_string(buf), path))
1686 {
1687 buf_strcpy(buf, path);
1688 }
1689 }
1690}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Variable Documentation

◆ RxSpecialChars

const char RxSpecialChars[] = "^.[$()|*+?{\\"
static

These characters must be escaped in regular expressions.

Definition at line 56 of file file.c.

◆ FilenameSafeChars

const char FilenameSafeChars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/"

Set of characters that are safe to use in filenames.

Definition at line 59 of file file.c.