// ---------------------------------------------------------------------------
// - csio.cxx                                                                -
// - standard system library - c i/o function implementation                 -
// ---------------------------------------------------------------------------
// - This program is free software;  you can redistribute it  and/or  modify -
// - it provided that this copyright notice is kept intact.                  -
// -                                                                         -
// - 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.  In no event shall -
// - the copyright holder be liable for any  direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software.     -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2008 amaury darsch                                   -
// ---------------------------------------------------------------------------

#include "cstr.hpp"
#include "ccnv.hpp"
#include "cerr.hpp"
#include "csio.hpp"
#include "csio.hxx"

namespace afnix {

  // the selector handle structure
  struct s_select {
    // input set
    fd_set d_iset;
    // output set
    fd_set d_oset;
    // max sid
    int    d_smax;
    // create a new handle
    s_select (void) {
      FD_ZERO (&d_iset);
      FD_ZERO (&d_oset);
      d_smax = 0;
    }
  };

  // return the default input stream associated with this process

  int c_stdin (void) {
    return STDIN_FILENO;
  }

  // return the default output stream associated with this process

  int c_stdout (void) {
    return STDOUT_FILENO;
  }

  // return the default error stream associated with this process

  int c_stderr (void) {
    return STDERR_FILENO;
  }

  // check if the name corresponds to a file name and can be accessed.
  // it does not necessarily means that it can be opened.

  bool c_isfile (const char* name) {
    int         status;
    struct stat buffer;
  
    if (c_strlen (name) == 0) return false;
  
    status = stat (name,&buffer);
    if ((status == 0) && S_ISREG (buffer.st_mode)) return true;
    return false;
  }

  // check if the name corresponds to a directory name and can be accessed.
  // it does not necessarily means that it can be opened.

  bool c_isdir (const char* name) {
    int         status;
    struct stat buffer;
  
    if (c_strlen (name) == 0) return false;
  
    status = stat (name,&buffer);
    if ((status == 0) && S_ISDIR (buffer.st_mode)) return true;
    return false;
  }

  // open a file for reading. a file descriptor is returned on success or
  // -1 in case of error.

  int c_openr (const char* name) {
    int fd = open (name, O_RDONLY);
    return (fd == -1) ? c_errmap (errno) : fd;
  }

  // open a file for writing. a file descriptor is returned on success or
  // -1 in case of error.

  int c_openw (const char* name, const bool tflag, const bool aflag) {
    // compute flag and mode
    int    flag = O_WRONLY | O_CREAT;
    mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
    if (tflag == true) flag |= O_TRUNC;
    if (aflag == true) flag |= O_APPEND;
    // try to open the file
    int fd = open (name, flag, mode);
    return (fd == -1) ? c_errmap (errno) : fd;
  }

  // open a file for read write. A file descriptor is returned on success or
  // -1 in case of error.

  int c_openrw (const char* name, const bool tflag, const bool aflag) {
    // compute flag and mode
    int    flag = O_RDWR  | O_CREAT;
    mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
    if (tflag == true) flag |= O_TRUNC;
    if (aflag == true) flag |= O_APPEND;
    // try to open the file
    int fd = open (name, flag, mode);
    return (fd == -1) ? c_errmap (errno) : fd;
  }

  // open a session file for read write by user and read by other.


  int c_opensd (const char* name) {
    // compute flag and mode
    int    flag = O_RDWR  | O_CREAT | O_EXCL;
    mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
    // try to open the file
    int fd = open (name, flag, mode);
    return (fd == -1) ? c_errmap (errno) : fd;
  }

  // open a temporary file
  // wait for a character or timeout

  bool c_rdwait (const int sid, const long tout) {
    // chck for good descriptor
    if (sid < 0) return false;
    // initialize the set
    fd_set  set;
    FD_ZERO (&set);
    FD_SET  (sid, &set);
     
    // initialize the timeout
    struct timeval timeout;
    timeout.tv_sec  = tout / 1000;
    timeout.tv_usec = (tout % 1000) * 1000;

    // wait on select
    int status = 0;
    if (tout == -1)
      status = select (sid+1, &set, NULL, NULL, NULL);
    else
      status = select (sid+1, &set, NULL, NULL, &timeout);
    return (status == 1) ? true : false;
  }

  // read n bytes in a buffer. The buffer must be pre-allocated. The number
  // of bytes read is returned

  t_long c_read (const int sid, char* buffer, const t_long count) {  
    // standard checks
    if ((sid < 0) || (buffer == 0)) return AFNIX_ERR_IARG;
    // read n bytes and return error code
    t_long result = read (sid, buffer, count);
    return (result == -1) ? c_errmap (errno) : result;
  }

  // write n bytes from a buffer. The buffer must be pre-allocated. The number
  // of bytes written is returned

  t_long c_write (const int sid, const char* buffer, const t_long count) {
    // standard checks
    if ((sid < 0) || (buffer == 0)) return AFNIX_ERR_IARG;
    if (count == 0) return 0;  
    // write n bytes and return
    t_long result = write (sid, buffer, count);
    return (result == -1) ? c_errmap (errno) : result;
  }

  // close this stream.

  bool c_close (const int sid) {
    if (sid < 0) return AFNIX_ERR_IARG;
    return (close (sid) == -1) ? false : true;
  }

  // return the size of a file

  t_long c_fsize (const int sid) {
    struct stat buffer;
    // check that we have a regular file
    int status = fstat (sid,&buffer);
    if ((status != 0) || (!S_ISREG (buffer.st_mode))) return AFNIX_ERR_IARG;
    // return the file size
    return buffer.st_size;
  }

  // set a file at a certain position

  bool c_lseek (const int sid, const t_long pos) {
    off_t status = lseek (sid, (off_t) pos, SEEK_SET);
    if (status == (off_t) -1) return false;
    return true;
  }

  // lock completly a file

  bool c_flock (const int sid, const bool wlk) {
    // standard check
    if (sid == -1) return false;
    // make a full range locking
    struct flock fl;
    fl.l_type   = wlk ? F_WRLCK : F_RDLCK;
    fl.l_start  = 0;
    fl.l_whence = SEEK_SET;
    fl.l_len    = 0;
    // lock or wait
    return (fcntl (sid, F_SETLKW, &fl) == -1) ? false : true;
  }

  // unlock a file

  bool c_funlock (const int sid) {
    // standard check
    if (sid == -1) return false;
    // unlock the file
    struct flock fl;
    fl.l_type   = F_UNLCK;
    fl.l_start  = 0;
    fl.l_whence = SEEK_SET;
    fl.l_len    = 0;
    // lock or wait
    return (fcntl (sid, F_SETLK, &fl) == -1) ? false : true;
  }

  // remove the file given its name.

  bool c_rm (const char* name) {
    if (name == nilp) return false;
    return (unlink (name) == 0) ? true : false;
  }

  // create a new selector handle

  void* c_shnew (void) {
    return new s_select;
  }

  // free a selector handle

  void c_shfree (void* handle) {
    s_select* sh = (s_select*) handle;
    delete sh;
  }

  // add an input descriptor to the select handle

  void c_shiadd (void* handle, const int sid) {
    if ((handle == nilp) || (sid < 0)) return;
    s_select* sh = (s_select*) handle;
    FD_SET (sid, &(sh->d_iset));
    if (sid > sh->d_smax) sh->d_smax = sid;
  } 

  // add an output descriptor to the select handle

  void c_shoadd (void* handle, const int sid) {
    if ((handle == nilp) || (sid < 0)) return;
    s_select* sh = (s_select*) handle;
    FD_SET (sid, &(sh->d_oset));
    if (sid > sh->d_smax) sh->d_smax = sid;
  }

  // wait for a descriptor to be ready

  long c_shwait (void* handle, const long tout) {
    // check for valid call
    if (handle == nilp) return 0;
    s_select* sh = (s_select*) handle;
    // initialize the timeout
    struct timeval timeout;
    timeout.tv_sec  = tout / 1000;
    timeout.tv_usec = (tout % 1000) * 1000;
    // now call select
    int result = 0;
    int nsh     = sh->d_smax + 1;
    if (tout < 0) 
      result = select (nsh, &(sh->d_iset), &(sh->d_oset), NULL, NULL);
    else
      result = select (nsh, &(sh->d_iset), &(sh->d_oset), NULL, &timeout);
    // check for error and remap
    if (result == 1) result = c_errmap (errno);
    return result;
  }

  // return true if an input descriptor is set

  bool c_shitst (void* handle, const int sid) {
    // check for valid call
    if ((handle == nilp) || (sid < 0)) return false;
    s_select* sh = (s_select*) handle;
    return (FD_ISSET (sid, &(sh->d_iset)) == 0) ? false : true;
  }

  // return true if an output descriptor is set

  bool c_shotst (void* handle, const int sid) {
    // check for valid call
    if ((handle == nilp) || (sid < 0)) return false;
    s_select* sh = (s_select*) handle;
    return (FD_ISSET (sid, &(sh->d_oset)) == 0) ? false : true;
  }
}
