/******************************************************************************
**
** file.c
**
** This file is part of the ABYSS Web server project.
**
** Copyright (C) 2000 by Moez Mahfoudh <mmoez@bigfoot.com>.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
** 1. Redistributions of source code must retain the above copyright
**    notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
**    notice, this list of conditions and the following disclaimer in the
**    documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
**    derived from this software without specific prior written permission.
** 
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
** SUCH DAMAGE.
**
******************************************************************************/

#include "xmlrpc_config.h"
#include "mallocvar.h"

#define _CRT_SECURE_NO_WARNINGS
    /* Tell msvcrt not to warn about functions that are often misused and
       cause security exposures.
    */

#if !defined(_FILE_OFFSET_BITS)
#define _FILE_OFFSET_BITS 64
#endif
    /* Tell GNU libc to make off_t 64 bits and all the POSIX file functions
       the versions that handle 64 bit file offsets.
    */
#if !defined(_LARGE_FILES)
#define _LARGE_FILES
#endif
    /* Same as above, but for AIX */

#include <string.h>

#if MSVCRT
  #include <io.h>
  typedef __int64 readwriterc_t;
#else
  #include <unistd.h>
  #include <fcntl.h>
  #include <dirent.h>
  #include <sys/stat.h>
  typedef ssize_t readwriterc_t;
#endif

#include "bool.h"
#include "xmlrpc-c/string_int.h"
#include "xmlrpc-c/abyss.h"
#include "file.h"

bool const win32 =
#ifdef WIN32
TRUE;
#else
FALSE;
#endif

struct TFileFind {
#ifdef WIN32
  #if MSVCRT
      intptr_t handle;
  #else
      HANDLE handle;
  #endif
#else
    char path[NAME_MAX+1];
    DIR * handle;
#endif
};

    

/*********************************************************************
** File
*********************************************************************/

static void
createFileImage(TFile **     const filePP,
                const char * const name,
                uint32_t     const attrib,
                bool         const createFile,
                bool *       const succeededP) {

    TFile * fileP;

    MALLOCVAR(fileP);
    if (fileP == NULL)
        *succeededP = FALSE;
    else {
        int rc;

        if (createFile)
            /* Unix prefers S_IWUSR, but some Windows knows only S_IWRITE */
            rc = open(name, attrib | O_CREAT, S_IWRITE | S_IREAD);
        else
            rc = open(name, attrib);

        if (rc < 0)
            *succeededP = FALSE;
        else {
            fileP->fd = rc;
            *succeededP = TRUE;
        }
        if (!*succeededP)
            free(fileP);
    }
    *filePP = fileP;
}



bool
FileOpen(TFile **     const filePP,
         const char * const name,
         uint32_t     const attrib) {

    bool succeeded;

    createFileImage(filePP, name, attrib, FALSE, &succeeded);

    return succeeded;
}



bool
FileOpenCreate(TFile **     const filePP,
               const char * const name,
               uint32_t     const attrib) {

    bool succeeded;

    createFileImage(filePP, name, attrib, TRUE, &succeeded);

    return succeeded;
}



bool
FileWrite(const TFile * const fileP,
          const void *  const buffer,
          uint32_t      const len) {

    readwriterc_t rc;

    rc = write(fileP->fd, buffer, len);

    return (rc > 0 && (uint32_t)rc == len);
}



int32_t
FileRead(const TFile * const fileP,
         void *        const buffer,
         uint32_t      const len) {

    return read(fileP->fd, buffer, len);
}



bool
FileSeek(const TFile * const fileP,
         uint64_t      const pos,
         uint32_t      const attrib) {

    int64_t rc;
#if MSVCRT
    rc =  _lseeki64(fileP->fd, pos, attrib);
#else
    rc =  lseek(fileP->fd, pos, attrib);
#endif
    return (rc >= 0);
}



uint64_t
FileSize(const TFile * const fileP) {

#if MSVCRT
    return (_filelength(fileP->fd));
#else
    struct stat fs;

    fstat(fileP->fd, &fs);
    return (fs.st_size);
#endif  
}



bool
FileClose(TFile * const fileP) {

    int rc;

    rc = close(fileP->fd);

    if (rc >= 0)
        free(fileP);

    return (rc >= 0);
}



bool
FileStat(const char * const filename,
         TFileStat *  const filestat) {

    int rc;

#if MSVCRT
    rc = _stati64(filename,filestat);
#else
    rc = stat(filename,filestat);
#endif
    return (rc >= 0);
}



static void
fileFindFirstWin(TFileFind *  const filefindP ATTR_UNUSED,
                 const char * const path,
                 TFileInfo *  const fileinfo  ATTR_UNUSED,
                 bool *       const retP      ATTR_UNUSED) {
    const char * search;

    xmlrpc_asprintf(&search, "%s\\*", path);

#if MSVCRT
    filefindP->handle = _findfirsti64(search, fileinfo);
    *retP = filefindP->handle != -1;
#else
#ifdef WIN32
    filefindP->handle = FindFirstFile(search, &fileinfo->data);
    *retP = filefindP->handle != INVALID_HANDLE_VALUE;
    if (*retP) {
        LARGE_INTEGER li;
        li.LowPart = fileinfo->data.nFileSizeLow;
        li.HighPart = fileinfo->data.nFileSizeHigh;
        strcpy( fileinfo->name, fileinfo->data.cFileName );
        fileinfo->attrib = fileinfo->data.dwFileAttributes;
        fileinfo->size   = li.QuadPart;
        fileinfo->time_write = fileinfo->data.ftLastWriteTime.dwLowDateTime;
    }
#endif
#endif
    xmlrpc_strfree(search);
}



static void
fileFindFirstPosix(TFileFind *  const filefindP,
                   const char * const path,
                   TFileInfo *  const fileinfo,
                   bool *       const retP) {
    
#if !MSVCRT
    strncpy(filefindP->path, path, NAME_MAX);
    filefindP->path[NAME_MAX] = '\0';
    filefindP->handle = opendir(path);
    if (filefindP->handle)
        *retP = FileFindNext(filefindP, fileinfo);
    else
        *retP = FALSE;
#endif
}
    


bool
FileFindFirst(TFileFind ** const filefindPP,
              const char * const path,
              TFileInfo *  const fileinfo) {

    bool succeeded;

    TFileFind * filefindP;

    MALLOCVAR(filefindP);

    if (filefindP == NULL)
        succeeded = FALSE;
    else {
        if (win32)
            fileFindFirstWin(filefindP, path, fileinfo, &succeeded);
        else
            fileFindFirstPosix(filefindP, path, fileinfo, &succeeded);
        if (!succeeded)
            free(filefindP);
    }
    *filefindPP = filefindP;

    return succeeded;
}



static void
fileFindNextWin(TFileFind * const filefindP ATTR_UNUSED,
                TFileInfo * const fileinfo  ATTR_UNUSED,
                bool *      const retvalP   ATTR_UNUSED) {

#if MSVCRT
    *retvalP = _findnexti64(filefindP->handle, fileinfo) != -1;
#else
#ifdef WIN32
    bool found;
    found = FindNextFile(filefindP->handle, &fileinfo->data);
    if (found) {
        LARGE_INTEGER li;
        li.LowPart = fileinfo->data.nFileSizeLow;
        li.HighPart = fileinfo->data.nFileSizeHigh;
        strcpy(fileinfo->name, fileinfo->data.cFileName);
        fileinfo->attrib = fileinfo->data.dwFileAttributes;
        fileinfo->size   = li.QuadPart;
        fileinfo->time_write = fileinfo->data.ftLastWriteTime.dwLowDateTime;
    }
    *retvalP = found;
#endif
#endif
}



static void
fileFindNextPosix(TFileFind * const filefindP,
                  TFileInfo * const fileinfoP,
                  bool *      const retvalP) {

#ifndef WIN32
    struct dirent * deP;

    deP = readdir(filefindP->handle);
    if (deP) {
        char z[NAME_MAX+1];
        struct stat fs;

        strcpy(fileinfoP->name, deP->d_name);
        strcpy(z, filefindP->path);
        strncat(z, "/",NAME_MAX);
        strncat(z, fileinfoP->name, NAME_MAX);
        z[NAME_MAX] = '\0';
        
        stat(z, &fs);

        if (fs.st_mode & S_IFDIR)
            fileinfoP->attrib = A_SUBDIR;
        else
            fileinfoP->attrib = 0;

        fileinfoP->size       = fs.st_size;
        fileinfoP->time_write = fs.st_mtime;
        
        *retvalP = TRUE;
    } else
        *retvalP = FALSE;
#endif
}



bool
FileFindNext(TFileFind * const filefindP,
             TFileInfo * const fileinfo) {

    bool retval;

    if (win32)
        fileFindNextWin(filefindP, fileinfo, &retval);
    else
        fileFindNextPosix(filefindP, fileinfo, &retval);

    return retval;
}



void
FileFindClose(TFileFind * const filefindP) {
#ifdef WIN32
#if MSVCRT
    _findclose(filefindP->handle);
#else
   FindClose(filefindP->handle);
#endif
#else
    closedir(filefindP->handle);
#endif
    free(filefindP);
}
