/*
elmo - ELectronic Mail Operator
Copyright (C) 2003, 2004 rzyjontko
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2.
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, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
----------------------------------------------------------------------
file handling
*/
/****************************************************************************
* IMPLEMENTATION HEADERS
****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <dirent.h>
#ifdef HAVE_MMAP
# include <sys/mman.h>
#endif
#include "xmalloc.h"
#include "rstring.h"
#include "file.h"
#include "misc.h"
#include "error.h"
/****************************************************************************
* IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS
****************************************************************************/
#ifndef P_tmpdir
# define P_tmpdir "/tmp"
#endif
/****************************************************************************
* IMPLEMENTATION PRIVATE CLASS PROTOTYPES / EXTERNAL CLASS REFERENCES
****************************************************************************/
/****************************************************************************
* IMPLEMENTATION PRIVATE STRUCTURES / UTILITY CLASSES
****************************************************************************/
/****************************************************************************
* IMPLEMENTATION REQUIRED EXTERNAL REFERENCES (AVOID)
****************************************************************************/
/****************************************************************************
* IMPLEMENTATION PRIVATE DATA
****************************************************************************/
/****************************************************************************
* INTERFACE DATA
****************************************************************************/
/****************************************************************************
* IMPLEMENTATION PRIVATE FUNCTION PROTOTYPES
****************************************************************************/
/****************************************************************************
* IMPLEMENTATION PRIVATE FUNCTIONS
****************************************************************************/
static int
get_part (FILE *fp, int start, int end, char **place)
{
int len;
if (fseek (fp, start, SEEK_SET)){
return 1;
}
len = end - start;
*place = xmalloc (len + 1);
if (fread (*place, 1, len, fp) != len){
xfree (*place);
*place = NULL;
return 1;
}
(*place)[len] = '\0';
return 0;
}
/****************************************************************************
* INTERFACE FUNCTIONS
****************************************************************************/
int
file_part (const char *fname, int offset_start, int offset_end,
char **place)
{
FILE *fp;
int ret;
if (place == NULL)
return 1;
*place = NULL;
fp = fopen (fname, "r");
if (fp == NULL)
return 1;
ret = get_part (fp, offset_start, offset_end, place);
fclose (fp);
return ret;
}
int
file_size (const char *fname)
{
struct stat st;
if (stat (fname, &st))
return -1;
return st.st_size;
}
int
file_whole (FILE *fp, char **place, size_t *size)
{
struct stat st;
int fd;
rewind (fp);
fd = fileno (fp);
if (fstat (fd, &st))
return 1;
*size = st.st_size;
return get_part (fp, 0, st.st_size, place);
}
char *
file_with_dir (const char *dir, const char *file)
{
size_t dlen = 0;
size_t flen = 0;
int need_sep = 1;
char *result = NULL;
if (file == NULL)
return xstrdup (dir);
if (dir == NULL)
return xstrdup (file);
dlen = strlen (dir);
flen = strlen (file);
if (*file == '/' || dir[dlen - 1] == '/')
need_sep = 0;
result = xmalloc (dlen + need_sep + flen + 1);
memcpy (result, dir, dlen);
memcpy (result + dlen + need_sep, file, flen);
if (need_sep)
result[dlen] = '/';
result[dlen + need_sep + flen] = '\0';
return result;
}
int
file_temp_fd (char **temp_name)
{
char name[] = P_tmpdir "/elmo-XXXXXX";
int fd;
while (1){
fd = mkstemp (name);
if (fd != -1)
break;
if (errno != EEXIST){
error_ (errno, "%s", name);
return -1;
}
}
if (temp_name)
*temp_name = xstrdup (name);
return fd;
}
FILE *
file_temp_file (char **temp_name)
{
int fd;
fd = file_temp_fd (temp_name);
if (fd == -1)
return NULL;
return fdopen (fd, "r+");
}
FILE *
file_open (char *fname, const char *mode, int flags, int access)
{
int fd;
char *true_name;
FILE *fp;
true_name = file_expand_tilde (fname);
fd = open (true_name, flags, access);
if (fd == -1){
if (true_name != fname)
xfree (true_name);
return NULL;
}
fp = fdopen (fd, mode);
if (true_name != fname)
xfree (true_name);
return fp;
}
#ifdef HAVE_MMAP
int
file_rename (const char *oldname, const char *newname)
{
struct stat st;
int ofd;
int nfd;
char *ofile;
char *nfile;
if (rename (oldname, newname) == 0)
return 0;
if (errno != EXDEV)
return 1;
/**
* step 0: open old file
*/
ofd = open (oldname, O_RDONLY);
if (ofd == -1)
return 1;
/**
* step 1: get old file size
*/
if (fstat (ofd, &st)){
close (ofd);
return 1;
}
/**
* step 2: open new file for writing
*/
nfd = open (newname, O_RDWR | O_CREAT, 0600);
if (nfd == -1){
close (ofd);
return 1;
}
/**
* step 3: resize new file
*/
if (ftruncate (nfd, st.st_size)){
close (ofd);
close (nfd);
return 1;
}
/**
* step 4: mmap both files
*/
ofile = mmap (NULL, st.st_size, PROT_READ, MAP_PRIVATE, ofd, 0);
if (ofile == MAP_FAILED){
close (ofd);
close (nfd);
return 1;
}
nfile = mmap (NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, nfd, 0);
if (nfile == MAP_FAILED){
close (ofd);
close (nfd);
return 1;
}
/**
* step 5: copy file contents
*/
memcpy (nfile, ofile, st.st_size);
/**
* step 6: unmap regions and close file descriptors
*/
munmap (ofile, st.st_size);
munmap (nfile, st.st_size);
close (ofd);
close (nfd);
unlink (oldname);
return 0;
}
#else
int
file_rename (const char *oldname, const char *newname)
{
struct stat st;
FILE *nfp;
FILE *ofp;
char *buf;
int ret;
if (rename (oldname, newname) == 0)
return 0;
if (errno != EXDEV)
return 1;
ofp = file_open (oldname, "r", O_RDONLY);
if (ofp == NULL)
return 1;
nfp = file_open (newname, "w", O_WRONLY | O_CREAT | O_EXCL);
if (nfp == NULL)
return 1;
buf = xmalloc (4096);
while (! feof (ofp)){
ret = fread (buf, 1, 4096, ofp);
if (fwrite (buf, 1, ret, nfp) != ret){
xfree (buf);
fclose (ofp);
fclose (nfp);
return 1;
}
}
xfree (buf);
fclose (ofp);
fclose (nfp);
unlink (oldname);
return 0;
}
#endif
char *
file_expand_tilde (char *dir)
{
char *result;
char *home = getenv ("HOME");
int hlen = (home) ? strlen (home) : 0;
int dlen = (dir) ? strlen (dir) : 0;
if (dir == NULL)
return NULL;
if (*dir != '~')
return dir;
result = xmalloc (hlen + dlen);
memcpy (result, home, hlen);
memcpy (result + hlen, dir + 1, dlen);
return result;
}
rstring_t *
file_dir_items (const char *dir, int (*select)(const struct dirent *))
{
int i;
int len;
int count;
struct stat st;
struct dirent **list;
rstring_t *result;
char *file;
if (dir == NULL)
return NULL;
count = scandir (dir, &list, select, alphasort);
if (count == -1){
return NULL;
}
result = rstring_create_size (count + 1);
for (i = 0; i < count; i++){
file = file_with_dir (dir, list[i]->d_name);
if (stat (file, &st)){
xfree (file);
continue;
}
xfree (file);
if (S_ISDIR (st.st_mode)){
len = strlen (list[i]->d_name);
file = xmalloc (len + 2);
memcpy (file, list[i]->d_name, len);
file[len] = '/';
file[len + 1] = '\0';
}
else {
file = xstrdup (list[i]->d_name);
}
rstring_add (result, file);
}
result->allocated_all = 1;
for (i = 0; i < count; i++)
free (list[i]);
if (count)
free (list);
return result;
}
/****************************************************************************
* INTERFACE CLASS BODIES
****************************************************************************/
/****************************************************************************
*
* END MODULE file.c
*
****************************************************************************/
syntax highlighted by Code2HTML, v. 0.9.1