/* 
   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