Goto sanos source index

//
// file.c
//
// File I/O
//
// Copyright (C) 2002 Michael Ringgaard. 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. Neither the name of the project nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission. 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 "msvcrt.h"

#define _STDINBUFSIZ_ 256
#define _NSTREAM_     128

struct _iobuf _iob[_NSTREAM_];
char stdinbuf[_STDINBUFSIZ_];
int stdinbufpos = 0;
int stdinbuflen = 0;
struct critsect iob_lock;

#define stdin  (&_iob[0])
#define stdout (&_iob[1])
#define stderr (&_iob[2])

int vsprintf(char *buf, const char *fmt, va_list args);
int readline(char *buf, int size);

static FILE *alloc_stream() {
  FILE *stream;

  enter(&iob_lock);
  stream = _iob;
  while (stream < _iob + _NSTREAM_)  {
    if (stream->flag & _IOFREE) {
      stream->flag = 0;
      leave(&iob_lock);
      return stream;
    }

    stream++;
  }
  leave(&iob_lock);

  return NULL;
}

static void free_stream(FILE *stream) {
  stream->flag = _IOFREE;
}

int _pipe(int *phandles, unsigned int psize, int textmode) {
  TRACE("_pipe");
  return pipe(phandles);
}

int _open(const char *filename, int oflag) {
  TRACE("_open");
  //syslog(LOG_DEBUG, "_open(%s,%p)", filename, oflag);
  return open(filename, oflag, S_IREAD | S_IWRITE);
}

int _close(int handle) {
  TRACE("_close");

  if (handle == -1) {
    errno = EBADF;
    return -1;
  }

  return close(handle);
}

int _commit(int handle) {
  TRACE("_commit");
  return fsync(handle);
}

int _read(int handle, void *buffer, unsigned int count) {
  TRACE("_read");

#if 0
  if (handle == 0) {
    char *buf;
    int pos;

    // Handle console input using readline
    if (stdinbufpos == stdinbuflen) {
      int len;

      len = readline(stdinbuf, sizeof(stdinbuf) - 3);
      if (len < 0) return -1;

      stdinbuf[len++] = '\r';
      stdinbuf[len++] = '\n';
      stdinbuf[len] = 0;

      stdinbuflen = len;
      stdinbufpos = 0;
    }

    buf = buffer;
    pos = 0;
    while (pos < (int) count && stdinbufpos < stdinbuflen) {
      buf[pos++] = stdinbuf[stdinbufpos++];
    }
    return pos;
  } else {
    return read(handle, buffer, count);
  }
#else
  return read(handle, buffer, count);
#endif
}

int _write(int handle, const void *buffer, unsigned int count) {
  TRACE("_write");
  
  return write(handle, buffer, count);
}

int _setmode(int handle, int mode) {
  TRACE("_setmode");
  return setmode(handle, mode);
}

int _stat(const char *path, struct _stat *buffer) {
  struct stat64 fs;

  TRACE("_stat");
  //syslog(LOG_DEBUG, "stat on %s", path);

  if (stat64(path, &fs) < 0) return -1;

  if (buffer) {
    memset(buffer, 0, sizeof(struct _stat));
    buffer->st_atime = fs.st_atime;
    buffer->st_ctime = fs.st_ctime;
    buffer->st_mtime = fs.st_mtime;
    buffer->st_size = (int) fs.st_size;
    buffer->st_mode = fs.st_mode;
  }

  //syslog(LOG_DEBUG, "%s: mode=%d size=%d", path, buffer->st_mode, buffer->st_size);
  return 0;
}

__int64 _stati64(const char *path, struct _stati64 *buffer) {
  struct stat64 fs;

  TRACE("_stati64");
  //syslog(LOG_DEBUG, "stat on %s", path);

  if (stat64(path, &fs) < 0) return -1;

  if (buffer) {
    memset(buffer, 0, sizeof(struct _stati64));
    buffer->st_atime = fs.st_atime;
    buffer->st_ctime = fs.st_ctime;
    buffer->st_mtime = fs.st_mtime;
    buffer->st_size = fs.st_size;
    buffer->st_mode = fs.st_mode;
  }

  //syslog(LOG_DEBUG, "%s: mode=%d size=%d", path, buffer->st_mode, buffer->st_size);
  return 0;
}

int _fstat(int handle, struct _stat *buffer) {
  struct stat64 fs;

  TRACE("_fstat");
  if (fstat64(handle, &fs) < 0) return -1;

  if (buffer) {
    memset(buffer, 0, sizeof(struct _stat));
    buffer->st_atime = fs.st_atime;
    buffer->st_ctime = fs.st_ctime;
    buffer->st_mtime = fs.st_mtime;
    buffer->st_size = (int) fs.st_size;
    buffer->st_mode = fs.st_mode;
  }

  return 0;
}

__int64 _fstati64(int handle, struct _stati64 *buffer) {
  struct stat64 fs;

  TRACE("_fstati64");
  if (fstat64(handle, &fs) < 0) return -1;

  if (buffer) {
    memset(buffer, 0, sizeof(struct _stati64));
    buffer->st_atime = fs.st_atime;
    buffer->st_ctime = fs.st_ctime;
    buffer->st_mtime = fs.st_mtime;
    buffer->st_size = fs.st_size;
    buffer->st_mode = fs.st_mode;
  }

  return 0;
}

loff_t _lseek(int f, loff_t offset, int origin) {
  TRACE("_lseek");
  return lseek(f, offset, origin);
}

__int64 _lseeki64(int handle, __int64 offset, int origin) {
  TRACE("_lseeki64");
  return (int) lseek64(handle, offset, origin);
}

int _open_osfhandle(long osfhandle, int flags) {
  int rc;

  TRACE("_open_osfhandle");

  // Handles are the same as operating system handles
  // Just return the original handle after changing the mode
  rc = setmode(osfhandle, flags);
  if (rc < 0) return -1;

  return osfhandle;
}

int _dup2(int handle1, int handle2) {
  TRACE("_dup2");
  return dup2(handle1, handle2);
}

long _get_osfhandle(int filehandle) {
  TRACE("_get_osfhandle");
  return filehandle;
}

int _getdrive() {
  TRACE("_getdrive");
  // Drive C is current drive
  return 3;
}

char *_getdcwd(int drive, char *buffer, int maxlen) {
  TRACE("_getdcwd");
  return getcwd(buffer, maxlen);
}

char *_fullpath(char *abspath, const char *relpath, size_t maxlen) {
  int rc;

  TRACE("_fullpath");

  if (maxlen < 3) {
    errno = ERANGE;
    return NULL;
  }

  abspath[0] = 'C';
  abspath[1] = ':';

  rc = canonicalize(relpath, abspath + 2, maxlen - 2);
  if (rc < 0) return NULL;

  return abspath;
}

int _unlink(const char *filename) {
  TRACE("_unlink");
  return unlink(filename);
}

int remove(const char *path) {
  TRACE("_remove");
  return unlink(path);
}

int _rename(const char *oldname, const char *newname) {
  TRACE("_rename");
  return rename(oldname, newname);
}

int _access(const char *path, int mode) {
  TRACE("_access");
  return access(path, mode);
}

int _chmod(const char *filename, int pmode) {
  TRACE("_chmod");
  return chmod(filename, pmode);
}

int _mkdir(const char *dirname) {
  TRACE("_mkdir");
  return mkdir(dirname, 0666);
}

int _chdir(const char *dirname) {
  TRACE("_chdir");
  return chdir(dirname);
}

char *_getcwd(char *buffer, int maxlen) {
  TRACE("_getcwd");
  return getcwd(buffer, maxlen);
}

int _fileno(FILE *stream) {
  TRACE("_fileno");
  return stream->file;
}

FILE *_fdopen(int handle, const char *mode) {
  FILE *stream; 

  TRACE("fdopen");

  stream = alloc_stream();
  if (stream == NULL) panic("too many files open");

  stream->file = handle;
  return stream;
}

FILE *fopen(const char *filename, const char *mode) {
  FILE *stream; 
  int oflag;
  handle_t handle;

  TRACE("fopen");
  //syslog(LOG_DEBUG, "fopen(%s,%s)", filename, mode);

  switch (*mode) {
    case 'r':
      oflag = O_RDONLY;
      break;

    case 'w':
      oflag = O_WRONLY | O_CREAT | O_TRUNC;
      break;

    case 'a':
      oflag = O_WRONLY | O_CREAT | O_APPEND;
      break;
  
    default:
      errno = EINVAL;
      return NULL;
  }

  while (*++mode) {
    switch (*mode) {
      case '+':
        oflag &= ~(O_RDONLY | O_WRONLY);
        oflag |= O_RDWR;
        break;

      case 't':
        oflag &= ~(O_TEXT | O_BINARY);
        oflag |= O_TEXT;
        break;

      case 'b':
        oflag &= ~(O_TEXT | O_BINARY);
        oflag |= O_BINARY;
        break;

      case 'c':
      case 'n':
        break;

      case 'S':
        oflag |= O_SEQUENTIAL;
        break;

      case 'R':
        oflag |= O_RANDOM;
        break;

      case 'T':
        oflag |= O_SHORT_LIVED;
        break;

      case 'D':
        oflag |= O_TEMPORARY;
        break;

      default:
        errno = EINVAL;
        return NULL;
    }
  }

  handle = open(filename, oflag, S_IREAD | S_IWRITE);
  if (handle < 0) return NULL;

  stream = alloc_stream();
  if (stream == NULL) panic("too many files open");

  stream->file = handle;
  return stream;
}

FILE *freopen(const char *path, const char *mode, FILE *stream) {
  int oflag;
  handle_t handle;

  TRACE("freopen");

  switch (*mode) {
    case 'r':
      oflag = O_RDONLY;
      break;

    case 'w':
      oflag = O_WRONLY | O_CREAT | O_TRUNC;
      break;

    case 'a':
      oflag = O_WRONLY | O_CREAT | O_APPEND;
      break;
  
    default:
      errno = EINVAL;
      return NULL;
  }

  while (*++mode) {
    switch (*mode) {
      case '+':
        oflag &= ~(O_RDONLY | O_WRONLY);
        oflag |= O_RDWR;
        break;

      case 't':
        oflag &= ~(O_TEXT | O_BINARY);
        oflag |= O_TEXT;
        break;

      case 'b':
        oflag &= ~(O_TEXT | O_BINARY);
        oflag |= O_BINARY;
        break;

      case 'c':
      case 'n':
        break;

      case 'S':
        oflag |= O_SEQUENTIAL;
        break;

      case 'R':
        oflag |= O_RANDOM;
        break;

      case 'T':
        oflag |= O_SHORT_LIVED;
        break;

      case 'D':
        oflag |= O_TEMPORARY;
        break;

      default:
        errno = EINVAL;
        return NULL;
    }
  }

  handle = open(path, oflag, S_IREAD | S_IWRITE);
  if (handle < 0) return NULL;

  close(stream->file);
  stream->file = handle;
  return stream;
}

int fclose(FILE *stream) {
  int rc;

  TRACE("fclose");
  rc = close(stream->file);
  free_stream(stream);
  return rc;
}

int fflush(FILE *stream) {
  int rc;

  TRACE("fflush");
  rc = fsync(stream->file);
  if (rc < 0) {
    stream->flag |= _IOERR;
    return -1;
  }

  return rc;
}

size_t fread(void *buffer, size_t size, size_t num, FILE *stream) {
  int rc;
  int count;

  TRACE("fread");

  if ((count = size * num) == 0) return 0;

  rc = read(stream->file, buffer, size * num);
  if (rc < 0) {
    stream->flag |= _IOERR;
    return 0;
  }

  if (rc == 0) stream->flag |= _IOEOF;

  return rc / size;
}

size_t fwrite(const void *buffer, size_t size, size_t num, FILE *stream) {
  int rc;
  int count;

  TRACE("fwrite");

  if ((count = size * num) == 0) return 0;

  rc = write(stream->file, buffer, size * num);
  if (rc < 0) {
    stream->flag |= _IOERR;
    return 0;
  }

  return rc / size;
}

int fputs(const char *string, FILE *stream) {
  int len;
  int rc;

  TRACE("fputs");

  len = strlen(string);
  rc = write(stream->file, string, len);

  return rc == len ? 0 : EOF;
}

int fseek(FILE *stream, long offset, int whence) {
  TRACE("fseek");
  return lseek(stream->file, offset, whence);
}

long ftell(FILE *stream) {
  return tell(stream->file);
}

int fgetpos(FILE *stream, fpos_t *pos) {
  int rc;

  TRACE("fgetpos");

  rc = tell(stream->file);
  if (rc < 0) return -1;

  *pos = rc;
  return 0;
}

void clearerr(FILE *stream) {
  TRACE("clearerr");
  stream->flag &= ~(_IOERR | _IOEOF);
}

int getc(FILE *stream) {
  unsigned char ch;
  int rc;

  TRACE("getc");

  rc = read(stream->file, &ch, 1);
  if (rc <= 0) {
    if (rc == 0) { 
      stream->flag |= _IOEOF;
    } else {
      stream->flag |= _IOERR;
    }
    return EOF;
  }

  return ch;
}

int fgetc(FILE *stream) {
  unsigned char ch;
  int rc;

  TRACE("fgetc");

  rc = read(stream->file, &ch, 1);
  if (rc <= 0) {
    if (rc == 0) {
      stream->flag |= _IOEOF;
    } else {
      stream->flag |= _IOERR;
    }
    return EOF;
  }

  return ch;
}

int fputc(int c, FILE *stream) {
  char ch;

  TRACE("fputc");
  ch = c;
  if (write(stream->file, &ch, 1) < 0) return -1;
  return c;
}

char *fgets(char *string, int n, FILE *stream) {
  TRACE("fgets");
  panic("fgets not implemented");
  return NULL;
}

int fprintf(FILE *stream, const char *fmt, ...) {
  va_list args;
  int n;
  char buffer[1024];

  TRACEX("fprintf");
  va_start(args, fmt);
  n = vsprintf(buffer, fmt, args);
  va_end(args);
  return write(stream->file, buffer, n);
}

int vfprintf(FILE *stream, const char *fmt, va_list args) {
  int n;
  char buffer[1024];

  TRACEX("vfprintf");
  n = vsprintf(buffer, fmt, args);
  return write(stream->file, buffer, n);
}

int putchar(int c) {
  char ch;

  TRACEX("putchar");
  ch = c;
  write(stdout->file, &ch, 1);
  return c;
}

int puts(const char *string) {
  int len;
  int rc;

  TRACE("puts");

  len = strlen(string);
  rc = write(stdout->file, string, len);
  if (rc < 0) return EOF;

  rc = write(stdout->file, "\n", 1);
  if (rc < 0) return EOF;

  return 0;
}

void _splitpath(const char *path, char *drive, char *dir, char *fname, char *ext) {
  char *p;
  char *last_slash = NULL, *dot = NULL;
  int len;

  TRACE("_splitpath");
  if (strlen(path) >= 1 && path[1] == ':') {
    if (drive)  {
      drive[0] = path[0];
      drive[1] = '\0';
    }

    path += 2;
  } else if (drive) {
    *drive = '\0';
  }

  for (last_slash = NULL, p = (char *) path; *p; p++)  {
    if (*p == '/' || *p == '\\') {
     last_slash = p + 1;
    } else if (*p == '.') {
      dot = p;
    }
  }

  if (last_slash) {
    if (dir) {
      len = last_slash - path;
      if (len > MAXPATH - 1) len = MAXPATH - 1;
      memcpy(dir, path, len);
      dir[len] = '\0';
    }

    path = last_slash;
  } else if (dir) {
    *dir = '\0';
  }

  if (dot && dot >= path) {
    if (fname) {
      len = dot - path;
      if (len > MAXPATH - 1) len = MAXPATH - 1;
      memcpy(fname, path, len);
      fname[len] = '\0';
    }

    if (ext) {
      len = p - dot;
      if (len > MAXPATH - 1) len = MAXPATH - 1;
      memcpy(ext, dot, len);
      ext[len] = '\0';
    }
  } else {
    if (fname) {
      len = p - path;
      if (len > MAXPATH - 1) len = MAXPATH - 1;
      memcpy(fname, path, len);
      fname[len] = '\0';
    }
    if (ext) {
      *ext = '\0';
    }
  }
}

int _wopen(const wchar_t *filename, int oflag) {
  char buf[MAXPATH];
  int rc;

  TRACE("_wopen");
  rc = convert_filename_from_unicode(filename, buf, MAXPATH);
  if (rc < 0) return -1;

  return _open(buf, oflag);
}

int _waccess(const wchar_t *path, int mode) {
  char buf[MAXPATH];
  int rc;

  TRACE("_waccess");
  rc = convert_filename_from_unicode(path, buf, MAXPATH);
  if (rc < 0) return -1;

  return _access(buf, mode);
}

__int64 _wstati64(const wchar_t *path, struct _stati64 *buffer) {
  char buf[MAXPATH];
  int rc;

  TRACE("_wstati64");
  rc = convert_filename_from_unicode(path, buf, MAXPATH);
  if (rc < 0) return -1;

  return _stati64(buf, buffer);
}

int _wmkdir(const wchar_t *dirname) {
  char buf[MAXPATH];
  int rc;

  TRACE("_wmkdir");
  rc = convert_filename_from_unicode(dirname, buf, MAXPATH);
  if (rc < 0) return -1;

  return _mkdir(buf);
}

int _wrename(const wchar_t *oldname, const wchar_t *newname) {
  char buf1[MAXPATH];
  char buf2[MAXPATH];
  int rc;

  TRACE("_wrename");
  rc = convert_filename_from_unicode(oldname, buf1, MAXPATH);
  if (rc < 0) return -1;

  rc = convert_filename_from_unicode(newname, buf2, MAXPATH);
  if (rc < 0) return -1;

  return _rename(buf1, buf2);
}

wchar_t *_wgetdcwd(int drive, wchar_t *buffer, int maxlen) {
  char curdir[MAXPATH];
  int len;

  TRACE("_wgetdcwd");
  if (getcwd(curdir, MAXPATH) == 0) return NULL;
  len = strlen(curdir);

  if (buffer) {
    if (len >= maxlen) {
      errno = ERANGE;
      return NULL;
    }
  } else {
    if (maxlen == 0) {
      maxlen = len + 1;
    } else if (len >= maxlen) {
      errno = ERANGE;
      return NULL;
    }

    buffer = malloc(maxlen * sizeof(wchar_t));

    if (!buffer) {
      errno = ENOMEM;
      return NULL;
    }
  }

  convert_filename_to_unicode(curdir, buffer, maxlen);
  return buffer;
}

wchar_t *_wfullpath(wchar_t *abspath, const wchar_t *relpath, size_t maxlen) {
  char buf1[MAXPATH];
  char buf2[MAXPATH];
  int rc;

  TRACE("_wfullpath");
  
  if (maxlen < 2) {
    errno = EINVAL;
    return NULL;
  }

  rc = convert_filename_from_unicode(relpath, buf1, MAXPATH);
  if (rc < 0) return NULL;

  rc = canonicalize(buf1, buf2, MAXPATH);
  if (rc < 0) return NULL;

  if (!abspath) {
    maxlen = MAXPATH;
    abspath = (wchar_t *) malloc(maxlen * sizeof(wchar_t));
    if (!abspath) {
      errno = ENOMEM;
      return NULL;
    }
  }

  abspath[0] = 'c';
  abspath[1] = ':';
  rc = convert_filename_to_unicode(buf2, abspath + 2, maxlen - 2);
  if (rc < 0) return NULL;

  return abspath;
}

void init_fileio() {
  int i;

  mkcs(&iob_lock);

  memset(_iob, 0, sizeof(struct _iobuf) * _NSTREAM_);
  for (i = 0; i < _NSTREAM_; i++) {
    _iob[i].file = NOHANDLE;
    _iob[i].flag = _IOFREE;
  }

  stdin->file = fdin;
  stdin->flag = 0;
  stdout->file = fdout;
  stdout->flag = 0;
  stderr->file = fderr;
  stderr->flag = 0;
}