/****************************************************************************
**  SIONLIB     http://www.fz-juelich.de/jsc/sionlib                       **
*****************************************************************************
**  Copyright (c) 2008-2018                                                **
**  Forschungszentrum Juelich, Juelich Supercomputing Centre               **
**                                                                         **
**  See the file COPYRIGHT in the package base directory for details       **
****************************************************************************/

#define _XOPEN_SOURCE 700

#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>

#include "sion_const.h"
#include "sion_debug.h"
#include "sion_error_handler.h"
#include "sion_file.h"

_sion_fileptr *_lsion_file_open(const char *fname, unsigned int flags, unsigned int addflags)
{
  if (flags & SION_FILE_FLAG_ANSI) {
    _sion_fileptr *sion_fileptr = _lsion_file_alloc_and_init_sion_fileptr();
    sion_fileptr->flags |= SION_FILE_FLAG_ANSI;

    if (flags & SION_FILE_FLAG_WRITE) {
      sion_fileptr->flags |= SION_FILE_FLAG_WRITE;

      if (flags & SION_FILE_FLAG_CREATE) {
        sion_fileptr->flags |= SION_FILE_FLAG_CREATE;
        sion_fileptr->fileptr = _lsion_file_open_ansi_write_create(fname, addflags);
      } else {
        sion_fileptr->fileptr = _lsion_file_open_ansi_write_existing(fname, addflags);
      }
    } else {
      sion_fileptr->flags |= SION_FILE_FLAG_READ;
      sion_fileptr->fileptr = _lsion_file_open_ansi_read(fname, addflags);
    }

    /* could not open ANSI file */
    if (!sion_fileptr->fileptr) {
      free(sion_fileptr);
      _lsion_errorprint(SION_NOT_SUCCESS, _SION_ERROR_WARN, "error: could not open file (ANSI), %s %d %d, aborting ...\n", fname,
        flags, addflags);
      return NULL;
    }

    return sion_fileptr;
  } else if (flags & SION_FILE_FLAG_POSIX) {
    _sion_fileptr *sion_fileptr = _lsion_file_alloc_and_init_sion_fileptr();
    sion_fileptr->flags |= SION_FILE_FLAG_POSIX;

    if (flags & SION_FILE_FLAG_WRITE) {
      sion_fileptr->flags |= SION_FILE_FLAG_WRITE;
      if (flags & SION_FILE_FLAG_CREATE) {
        sion_fileptr->flags |= SION_FILE_FLAG_CREATE;
        sion_fileptr->fd = _lsion_file_open_posix_write_create(fname, addflags);
      } else {
        sion_fileptr->fd = _lsion_file_open_posix_write_existing(fname, addflags);
      }
    } else {
      sion_fileptr->flags |= SION_FILE_FLAG_READ;
      sion_fileptr->fd = _lsion_file_open_posix_read(fname, addflags);
    }

    /* could not open POSIX file */
    if (sion_fileptr->fd < 0) {
      free(sion_fileptr);
      _lsion_errorprint(SION_NOT_SUCCESS, _SION_ERROR_WARN, "error: could not open file (POSIX), %s %d %d, aborting ...\n", fname,
        flags, addflags);
      return NULL;
    }

    return sion_fileptr;
  } else {
    /* unknown mode? */
    _lsion_errorprint(
      SION_NOT_SUCCESS, _SION_ERROR_WARN, "internal error: unknown open type, %s %d %d, aborting ...\n", fname, flags, addflags);
    return NULL;
  }
}

#define DFUNCTION "_lsion_file_close"
int _lsion_file_close(_sion_fileptr *sion_fileptr)
{
  if (NULL == sion_fileptr) {
    return SION_NOT_SUCCESS;
  }

  int rc;
  if (sion_fileptr->flags & SION_FILE_FLAG_ANSI) {
    rc = _lsion_file_close_ansi(sion_fileptr->fileptr);
    sion_fileptr->fileptr = NULL;
  } else if (sion_fileptr->flags & SION_FILE_FLAG_POSIX) {
    rc = _lsion_file_close_posix(sion_fileptr->fd);
  } else {
    rc = SION_NOT_SUCCESS;
  }

  DPRINTFP((32, DFUNCTION, -1, "free now fileptr=%x %d rc=%d\n", sion_fileptr, sion_fileptr->flags, rc));
  free(sion_fileptr);

  return rc;
}
#undef DFUNCTION

int64_t _lsion_file_write(const void *data, int64_t bytes, _sion_fileptr *sion_fileptr)
{
  if (NULL == sion_fileptr) {
    return SION_SIZE_NOT_VALID;
  }

  if (sion_fileptr->flags & SION_FILE_FLAG_ANSI) {
    return _lsion_file_write_ansi(data, bytes, sion_fileptr->fileptr);
  } else if (sion_fileptr->flags & SION_FILE_FLAG_POSIX) {
    return _lsion_file_write_posix(data, bytes, sion_fileptr->fd);
  } else {
    return _lsion_errorprint(
      SION_SIZE_NOT_VALID, _SION_ERROR_RETURN, "_lsion_file_write: cannot find valid file flag (flags=%d)\n", sion_fileptr->flags);
  }
}

int64_t _lsion_file_read(void *data, int64_t bytes, _sion_fileptr *sion_fileptr)
{
  if (NULL == sion_fileptr) {
    return SION_SIZE_NOT_VALID;
  }

  if (sion_fileptr->flags & SION_FILE_FLAG_ANSI) {
    return _lsion_file_read_ansi(data, bytes, sion_fileptr->fileptr);
  } else if (sion_fileptr->flags & SION_FILE_FLAG_POSIX) {
    return _lsion_file_read_posix(data, bytes, sion_fileptr->fd);
  } else {
    return _lsion_errorprint(
      SION_SIZE_NOT_VALID, _SION_ERROR_RETURN, "_lsion_file_read: cannot find valid file flag (flags=%d)\n", sion_fileptr->flags);
  }
}

long _lsion_file_get_opt_blksize(_sion_fileptr *sion_fileptr)
{
  if (NULL == sion_fileptr) {
    return SION_SIZE_NOT_VALID;
  }

  if (sion_fileptr->flags & SION_FILE_FLAG_ANSI) {
    return _lsion_file_get_opt_blksize_ansi(sion_fileptr->fileptr);
  } else if (sion_fileptr->flags & SION_FILE_FLAG_POSIX) {
    return _lsion_file_get_opt_blksize_posix(sion_fileptr->fd);
  } else {
    return _lsion_errorprint(SION_SIZE_NOT_VALID, _SION_ERROR_RETURN,
      "_lsion_file_get_opt_blksize: cannot find valid file flag (flags=%d)\n", sion_fileptr->flags);
  }
}

int _lsion_file_stat_file(const char *fname)
{
#if defined(_SION_LINUX) || defined(_SION_DARWIN) || defined(_SION_AIX) || defined(_SION_BGP) || defined(_SION_BGQ)
  struct stat sbuf;
  return 0 == stat(fname, &sbuf);
#endif
}

int64_t _lsion_file_set_position(_sion_fileptr *sion_fileptr, int64_t startpointer)
{
  if (NULL == sion_fileptr) {
    return SION_SIZE_NOT_VALID;
  }

  if (sion_fileptr->flags & SION_FILE_FLAG_ANSI) {
    return _lsion_file_set_position_ansi(sion_fileptr->fileptr, startpointer);
  } else if (sion_fileptr->flags & SION_FILE_FLAG_POSIX) {
    return _lsion_file_set_position_posix(sion_fileptr->fd, startpointer);
  } else {
    return _lsion_errorprint(SION_SIZE_NOT_VALID, _SION_ERROR_RETURN,
      "_lsion_file_set_position: cannot find valid file flag (flags=%d)\n", sion_fileptr->flags);
  }
}

int64_t _lsion_file_get_position(_sion_fileptr *sion_fileptr)
{
  if (NULL == sion_fileptr) {
    return SION_SIZE_NOT_VALID;
  }

  if (sion_fileptr->flags & SION_FILE_FLAG_ANSI) {
    return _lsion_file_get_position_ansi(sion_fileptr->fileptr);
  } else if (sion_fileptr->flags & SION_FILE_FLAG_POSIX) {
    return _lsion_file_get_position_posix(sion_fileptr->fd);
  } else {
    return _lsion_errorprint(SION_SIZE_NOT_VALID, _SION_ERROR_RETURN,
      "_lsion_file_get_position: cannot find valid file flag (flags=%d)\n", sion_fileptr->flags);
  }
}

int _lsion_file_flush(_sion_fileptr *sion_fileptr)
{
  if (NULL == sion_fileptr) {
    return SION_NOT_SUCCESS;
  }

  if (sion_fileptr->flags & SION_FILE_FLAG_ANSI) {
    return _lsion_file_flush_ansi(sion_fileptr->fileptr);
  } else if (sion_fileptr->flags & SION_FILE_FLAG_POSIX) {
    return _lsion_file_flush_posix(sion_fileptr->fd);
  } else {
    return _lsion_errorprint(
      SION_NOT_SUCCESS, _SION_ERROR_RETURN, "_lsion_file_flush: cannot find valid file flag (flags=%d)\n", sion_fileptr->flags);
  }
}

int _lsion_file_set_buffer(_sion_fileptr *sion_fileptr, char *buffer, int32_t buffer_size)
{
  if (sion_fileptr == NULL) {
    return SION_NOT_SUCCESS;
  }

  if (sion_fileptr->flags & SION_FILE_FLAG_ANSI) {
    return _lsion_file_set_buffer_ansi(sion_fileptr->fileptr, buffer, buffer_size);
  } else if (sion_fileptr->flags & SION_FILE_FLAG_POSIX) {
    return _lsion_file_set_buffer_posix(sion_fileptr->fd, buffer, buffer_size);
  } else {
    return _lsion_errorprint(SION_NOT_SUCCESS, _SION_ERROR_RETURN,
      "_lsion_file_set_buffer: cannot find valid file flag (flags=%d)\n", sion_fileptr->flags);
  }
}

int _lsion_file_get_fd(_sion_fileptr *sion_fileptr)
{
  if (sion_fileptr == NULL) {
    return SION_ID_UNDEF;
  }

  if (sion_fileptr->flags & SION_FILE_FLAG_POSIX) {
    return sion_fileptr->fd;
  } else if (sion_fileptr->flags & SION_FILE_FLAG_ANSI) {
    return fileno(sion_fileptr->fileptr);
  } else {
    return _lsion_errorprint(
      SION_ID_UNDEF, _SION_ERROR_RETURN, "_lsion_file_get_fd: cannot find valid file flag (flags=%d)\n", sion_fileptr->flags);
  }
}

/* ********************************************************************************************** */
/* *** INTERFACE Functions                                                                        */
/* ********************************************************************************************** */

FILE *_lsion_file_open_ansi_write_create(const char *fname, unsigned int addflags)
{
#if defined(_SION_LINUX) || defined(_SION_DARWIN)
  return fopen(fname, "w");
#elif defined(_SION_AIX) || defined(_SION_BGP) || defined(_SION_BGQ)
  int fd = open64(fname, O_CREAT | O_RDWR, 0664);
  return fdopen(fd, "w");
#else
#error "No platform selected."
  return NULL;
#endif
}

FILE *_lsion_file_open_ansi_write_existing(const char *fname, unsigned int addflags)
{
#if defined(_SION_LINUX) || defined(_SION_DARWIN)
  /* changed from w to r+ to prevent time truncation of empty existing file */
  return fopen(fname, "r+");
#elif defined(_SION_AIX)
  int fd = open64(fname, O_CREAT | O_RDWR, 0664);
  return fdopen(fd, "r+");
#elif defined(_SION_BGP) || defined(_SION_BGQ)
  int fd = open64(fname, O_RDWR, 0664);
  return fdopen(fd, "r+");
#else
#error "No platform selected."
  return NULL;
#endif
}

FILE *_lsion_file_open_ansi_read(const char *fname, unsigned int addflags)
{
#if defined(_SION_LINUX) || defined(_SION_DARWIN)
  return fopen(fname, "r");
#elif defined(_SION_AIX) || defined(_SION_BGP) || defined(_SION_BGQ)
  int fd = open64(fname, O_RDONLY, 0664);
  return fdopen(fd, "r");
#else
#error "No platform selected."
  return NULL;
#endif
}

int _lsion_file_close_ansi(FILE *fileptr)
{
#if defined(_SION_LINUX) || defined(_SION_DARWIN) || defined(_SION_AIX) || defined(_SION_BGP) || defined(_SION_BGQ)
  return (0 == fclose(fileptr)) ? SION_SUCCESS : SION_NOT_SUCCESS;
#else
#error "No platform selected."
  return SION_NOT_SUCCESS;
#endif
}

long _lsion_file_get_opt_blksize_ansi(FILE *fileptr)
{
#if defined(_SION_LINUX) || defined(_SION_DARWIN) || defined(_SION_AIX) || defined(_SION_BGP) || defined(_SION_BGQ)
  int fd = fileno(fileptr);
  struct stat sbuf;
  if (fstat(fd, &sbuf) == 0) {
    return sbuf.st_blksize;
  } else {
    return SION_SIZE_NOT_VALID;
  }
#else
#error "No platform selected."
  return SION_SIZE_NOT_VALID;
#endif
}

int64_t _lsion_file_set_position_ansi(FILE *fileptr, int64_t startpointer)
{
  int64_t newpos;

  DPRINTFP((32, "_sion_set_position_ansi", -1, "enter (to %lld) (to %ld)\n", (long long)startpointer, (long)startpointer));

#if defined(_SION_BGP) || defined(_SION_BGQ)
  off_t offset = (off_t)startpointer;
  if (offset != startpointer) {
    DPRINTFP((32, "_sion_set_position_ansi", -1,
      "_sion_set_position_ansi: cannot set position to %ld (%zu),%lld (%zu)  offset conversion error\n", offset, sizeof(offset),
      startpointer, sizeof(startpointer)));
    return _lsion_errorprint(SION_SIZE_NOT_VALID, _SION_ERROR_RETURN,
      "_sion_set_position_ansi: cannot set position to %ld (%zu),%lld (%zu)  offset conversion error\n", offset, sizeof(offset),
      startpointer, sizeof(startpointer));
  }
  if (!fflush(fileptr)) {
    return _lsion_errorprint(SION_SIZE_NOT_VALID, _SION_ERROR_RETURN, "_sion_set_position_ansi: cannot flush file\n");
  }
  int fd = fileno(fileptr);
  off_t result;
  newpos = result = lseek(fd, offset, SEEK_SET);
#elif defined(_SION_LINUX) || defined(_SION_DARWIN)
  off_t offset = (off_t)startpointer;
  if (offset != startpointer) {
    DPRINTFP((32, "_sion_set_position_ansi", -1,
      "_sion_set_position_ansi: cannot set position to %ld (%zu),%lld (%zu)  offset conversion error\n", offset, sizeof(offset),
      startpointer, sizeof(startpointer)));
    return _lsion_errorprint(SION_SIZE_NOT_VALID, _SION_ERROR_RETURN,
      "_sion_set_position_ansi: cannot set position to %ld (%zu),%lld (%zu)  offset conversion error\n", offset, sizeof(offset),
      startpointer, sizeof(startpointer));
  }
  off_t result;
  newpos = result = fseeko(fileptr, offset, SEEK_SET);
  DPRINTFP((1024, "_sion_set_position_ansi", -1, "set position=%lld  (LINUX)\n", (long long)result));
#elif defined(_SION_AIX)
  off64_t offset = (off_t)startpointer;
  off64_t result;
  if (offset != startpointer) {
    return _lsion_errorprint(SION_SIZE_NOT_VALID, _SION_ERROR_RETURN,
      "_sion_set_position_ansi: cannot set position to %ld (%zu),%lld (%zu)  offset conversion error\n", offset, sizeof(offset),
      startpointer, sizeof(startpointer));
  }
  result = fseeko64(fileptr, offset, SEEK_SET);
  newpos = result = ftello64(fileptr);
#else
#error "No platform selected."
  newpos = SION_SIZE_NOT_VALID;
#endif
  DPRINTFP((32, "_sion_set_position_ansi", -1, "leave (%lld --> %lld)\n", (long long)startpointer, (long long)newpos));

  return newpos;
}

int64_t _lsion_file_get_position_ansi(FILE *fileptr)
{
  int64_t result;

#if defined(_SION_LINUX) || defined(_SION_DARWIN)
  off_t resulto;
  resulto = ftello(fileptr);
  result = (int64_t)resulto;
#if defined(_SION_LINUX)
  DPRINTFP((4096, "_sion_get_position", -1, "get position=%ld  (LINUX)\n", (long)result));
#elif defined(_SION_DARWIN)
  DPRINTFP((4096, "_sion_get_position", -1, "get position=%ld  (DARWIN)\n", (long)result));
#endif
#elif defined(_SION_AIX)
  result = ftell(fileptr);
  DPRINTFP((4096, "_sion_get_position", -1, "got position=%lld (AIX)\n", result));
#elif defined(_SION_BGP)
  off_t resulto;
  int fd = fileno(fileptr);
  resulto = lseek(fd, 0, SEEK_CUR);
  result = (int64_t)resulto;
  DPRINTFP((4096, "_sion_get_position", -1, "got position=%lld (BGP)\n", result));
#elif defined(_SION_BGQ)
  off_t resulto;
  int fd = fileno(fileptr);
  resulto = lseek(fd, 0, SEEK_CUR);
  result = (int64_t)resulto;
  DPRINTFP((4096, "_sion_get_position", -1, "got position=%lld (BGQ)\n", result));
#else
#error "No platform selected."
  result = SION_SIZE_NOT_VALID;
#endif

  return result;
}

int _lsion_file_flush_ansi(FILE *fileptr)
{
  return (0 == fflush(fileptr)) ? SION_SUCCESS : SION_NOT_SUCCESS;
}

int _lsion_file_set_buffer_ansi(FILE *fileptr, char *buffer, int32_t buffer_size)
{
  DPRINTFP((32, "_lsion_file_set_buffer", -1, "set buffer of fileptr\n"));
  return (0 == setvbuf(fileptr, buffer, _IOFBF, (size_t)buffer_size)) ? SION_SUCCESS : SION_NOT_SUCCESS;
}

int64_t _lsion_file_write_ansi(const void *data, int64_t bytes, FILE *fileptr)
{
  return (bytes == fwrite(data, 1, bytes, fileptr)) ? bytes : -1;
}

int64_t _lsion_file_read_ansi(void *data, int64_t bytes, FILE *fileptr)
{
  if (fread(data, 1, bytes, fileptr) < bytes) {
    if (feof(fileptr)) {
      return bytes;
    } else {
      return -1;
    }
  } else {
    return bytes;
  }
}

int _lsion_file_open_posix_write_create(const char *fname, unsigned int addflags)
{
  int fd;

#if defined(_SION_LINUX) || defined(_SION_DARWIN)
  do {
    fd = open(fname, O_CREAT | O_RDWR, 0664);
  } while (-1 == fd && EINTR == errno);
#elif defined(_SION_AIX) || defined(_SION_BGP) || defined(_SION_BGQ)
  do {
    fd = open64(fname, O_CREAT | O_RDWR, 0664);
  } while (-1 == fd && EINTR == errno);
#else
#error "No platform selected."
  fd = -1;
#endif

  return fd;
}

int _lsion_file_open_posix_write_existing(const char *fname, unsigned int addflags)
{
  int fd;

#if defined(_SION_LINUX) || defined(_SION_DARWIN)
  do {
    fd = open(fname, O_RDWR, 0664);
  } while (-1 == fd && EINTR == errno);
#elif defined(_SION_AIX) || defined(_SION_BGP) || defined(_SION_BGQ)
  do {
    fd = open64(fname, O_RDWR, 0664);
  } while (-1 == fd && EINTR == errno);
#else
#error "No platform selected."
  fd = -1;
#endif

  return fd;
}

int _lsion_file_open_posix_read(const char *fname, unsigned int addflags)
{
  int fd;

#if defined(_SION_LINUX) || defined(_SION_DARWIN)
  do {
    fd = open(fname, O_RDONLY, 0664);
  } while (-1 == fd && EINTR == errno);
#elif defined(_SION_AIX) || defined(_SION_BGP) || defined(_SION_BGQ)
  do {
    fd = open64(fname, O_RDONLY, 0664);
  } while (-1 == fd && EINTR == errno);
#else
#error "No platform selected."
  fd = -1;
#endif

  return fd;
}

int _lsion_file_close_posix(int fd)
{
#if defined(_SION_LINUX) || defined(_SION_DARWIN) || defined(_SION_AIX) || defined(_SION_BGP) || defined(_SION_BGQ)
  return (0 == close(fd)) ? SION_SUCCESS : SION_NOT_SUCCESS;
#else
#error "No platform selected."
  return SION_NOT_SUCCESS;
#endif
}

long _lsion_file_get_opt_blksize_posix(int fd)
{
#if defined(_SION_LINUX) || defined(_SION_DARWIN) || defined(_SION_AIX) || defined(_SION_BGP) || defined(_SION_BGQ)
  struct stat sbuf;
  if (fstat(fd, &sbuf) == 0) {
    return sbuf.st_blksize;
  } else {
    return SION_SIZE_NOT_VALID;
  }
#else
#error "No platform selected."
  return SION_SIZE_NOT_VALID;
#endif
}

int64_t _lsion_file_set_position_posix(int fd, int64_t startpointer)
{
  int64_t newpos = SION_SIZE_NOT_VALID;

  DPRINTFP((32, "_sion_set_position_posix", -1, "enter (to %lld)\n", (long long)startpointer));

#if defined(_SION_LINUX) || defined(_SION_DARWIN) || defined(_SION_BGP) || defined(_SION_BGQ)
  off_t offset = (off_t)startpointer;
  if (offset != startpointer) {
    return _lsion_errorprint(SION_SIZE_NOT_VALID, _SION_ERROR_RETURN,
      "_sion_set_position_posix: cannot set position to %ld (%zu),%lld (%zu)  offset conversion error\n", offset, sizeof(offset),
      startpointer, sizeof(startpointer));
  }
  off_t result;
  newpos = result = lseek(fd, offset, SEEK_SET);
#if defined(_SION_LINUX) || defined(_SION_DARWIN)
  DPRINTFP((4096, "_sion_set_position_posix", -1, "set position=%lld  (LINUX)\n", (long long)result));
#endif
#elif defined(_SION_AIX)
  off64_t offset = (off_t)startpointer;
  off64_t result;
  if (offset != startpointer) {
    return _lsion_errorprint(SION_SIZE_NOT_VALID, _SION_ERROR_RETURN,
      "_sion_set_position_posix: cannot set position to %ld (%zu),%lld (%zu)  offset conversion error\n", offset, sizeof(offset),
      startpointer, sizeof(startpointer));
  }
  newpos = result = lseek(fd, offset, SEEK_SET);
#else
#error "No platform selected."
  newpos = SION_SIZE_NOT_VALID;
#endif
  DPRINTFP((32, "_sion_set_position_posix", -1, "leave (to %lld)\n", (long long)startpointer));

  return newpos;
}

int64_t _lsion_file_get_position_posix(int fd)
{
  int64_t result = SION_SIZE_NOT_VALID;

  off_t resulto;
  resulto = lseek(fd, 0, SEEK_CUR);
  result = (int64_t)resulto;

#if defined(_SION_LINUX)
  DPRINTFP((4096, "_sion_get_position", -1, "get position=%ld  (LINUX)\n", (long)result));
#elif defined(_SION_DARWIN)
  DPRINTFP((4096, "_sion_get_position", -1, "get position=%ld  (DARWIN)\n", (long)result));
#elif defined(_SION_AIX)
  DPRINTFP((4096, "_sion_get_position", -1, "get position=%ld  (AIX)\n", (long)result));
#elif defined(_SION_BGP)
  DPRINTFP((4096, "_sion_get_position", -1, "get position=%ld  (BGP)\n", (long)result));
#elif defined(_SION_BGQ)
  DPRINTFP((4096, "_sion_get_position", -1, "get position=%ld  (BGQ)\n", (long)result));
#endif

  return result;
}

int _lsion_file_flush_posix(int fd)
{
  int frc;

#if defined(_SION_BGQ)
  /* BGQ 20.09.13: bad fsync performance since V1R2M1, try without */
  frc = 0;
#else
  do {
    frc = fsync(fd);
  } while (frc != 0 && EINTR == errno);
#endif

  return (0 == frc) ? SION_SUCCESS : SION_NOT_SUCCESS;
}

int _lsion_file_set_buffer_posix(int fd, char *buffer, int32_t buffer_size)
{
  DPRINTFP((32, "_lsion_file_set_buffer", -1, "set buffer of fileptr\n"));
  return SION_SUCCESS;
}

int64_t _lsion_file_write_posix(const void *data, int64_t bytes, int fd)
{
  ssize_t n = 0, k;
  while (1) {
    k = write(fd, data, bytes);
    if (k == -1) {
      if (errno != EINTR) {
        // I/O error, return -1
        return -1;
      } // else, interrupted before starting, retry
    } else if (k == bytes) {
      // requested amount has been written
      return n + k;
    } else {
      // k < bytes, presumably interrupted or no space left, retry
      // - retry after interruption should continue
      // - retry with no space left should lead to error
      bytes -= k;
      n += k;
      data = (char *)data + k;
    }
  }
}

int64_t _lsion_file_read_posix(void *data, int64_t bytes, int fd)
{
  ssize_t n = 0, k;
  while (1) {
    k = read(fd, data, bytes);
    if (k == -1) {
      if (errno != EINTR) {
        // I/O error, return -1
        return -1;
      } // else, interrupted before starting, retry
    } else if (k == 0) {
      // presumably EOF, return number of bytes read up to here
      return n;
    } else if (k == bytes) {
      // requested amount has been read
      return n + k;
    } else {
      // k < bytes, presumably interrupted or EOF, retry
      // - retry after interruption should continue
      // - retry at EOF should lead to k == 0 next
      bytes -= k;
      n += k;
      data = (char *)data + k;
    }
  }
}

/*!\brief Create and return _sion_fileptr
 *
 * @return _sion_fileptr
 */
_sion_fileptr *_lsion_file_alloc_and_init_sion_fileptr(void)
{
  _sion_fileptr *sion_fileptr;

  sion_fileptr = malloc(sizeof(_sion_fileptr));
  if (sion_fileptr == NULL) {
    _lsion_errorprint(SION_NOT_SUCCESS, _SION_ERROR_RETURN,
      "cannot allocate _sion_fileptr structure of size %lu (_sion_fileptr), aborting ...\n",
      (unsigned long)sizeof(_sion_fileptr));
    return NULL;
  }
  sion_fileptr->fileptr = NULL;
  sion_fileptr->fd = -1;
  sion_fileptr->flags = 0;

  return sion_fileptr;
}

/*!
 * @return File pointer description
 */
char *_lsion_get_fileptr_desc(_sion_fileptr *sion_fileptr)
{
  int flags;
  if (!sion_fileptr) {
    return "<undefined>";
  }
  flags = sion_fileptr->flags;

  if (flags & SION_FILE_FLAG_ANSI) {
    if (flags & SION_FILE_FLAG_WRITE) {
      if (flags & SION_FILE_FLAG_CREATE) {
        return "<ANSI,WRITE,CREATE>";
      } else {
        return "<ANSI,WRITE>";
      }
    } else {
      return "<ANSI,READ>";
    }
  } else {
    if (flags & SION_FILE_FLAG_POSIX) {
      if (flags & SION_FILE_FLAG_WRITE) {
        if (flags & SION_FILE_FLAG_CREATE) {
          return "<POSIX,WRITE,CREATE>";
        } else {
          return "<POSIX,WRITE>";
        }
      } else {
        return "<POSIX,READ>";
      }
    }
  }
  return "<unknown>";
}
