This source file includes following definitions.
- potion_io_error
 
- potion_file_new
 
- potion_file_with_fd
 
- potion_file_close
 
- potion_file_read
 
- potion_file_write
 
- potion_file_print
 
- potion_file_string
 
- potion_lobby_read
 
- potion_file_init
 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <assert.h>
#include "p2.h"
#include "internal.h"
#include "table.h"
#ifdef __APPLE__
# include <crt_externs.h>
# undef environ
# define environ (*_NSGetEnviron())
#else
  extern char **environ;
#endif
typedef vPN(File) pn_file;
PN potion_io_error(Potion *P, const char *msg) {
  return potion_error(P, potion_str_format(P, "Error %s: %s", msg, strerror(errno)),
                      0, 0, 0);
}
PN potion_file_new(Potion *P, PN cl, PN self, PN path, PN modestr) {
  int fd;
  mode_t mode;
  if (strcmp(PN_STR_PTR(modestr), "r") == 0) {
    mode = O_RDONLY;
  } else if (strcmp(PN_STR_PTR(modestr), "r+") == 0) {
    mode = O_RDWR;
  } else if (strcmp(PN_STR_PTR(modestr), "w") == 0) {
    mode = O_WRONLY | O_TRUNC | O_CREAT;
  } else if (strcmp(PN_STR_PTR(modestr), "w+") == 0) {
    mode = O_RDWR | O_TRUNC | O_CREAT;
  } else if (strcmp(PN_STR_PTR(modestr), "a") == 0) {
    mode = O_WRONLY | O_CREAT | O_APPEND;
  } else if (strcmp(PN_STR_PTR(modestr), "a+") == 0) {
    mode = O_RDWR | O_CREAT | O_APPEND;
  } else {
    
    return PN_NIL;
  }
  if ((fd = open(PN_STR_PTR(path), mode, 0755)) == -1)
    return potion_io_error(P, "open");
  ((struct PNFile *)self)->fd = fd;
  ((struct PNFile *)self)->path = path;
  ((struct PNFile *)self)->mode = mode;
  return self;
}
PN potion_file_with_fd(Potion *P, PN cl, PN self, PN fd) {
  struct PNFile *file = (struct PNFile *)potion_object_new(P, PN_NIL, PN_VTABLE(PN_TFILE));
  file->fd = PN_INT(fd);
  file->path = PN_NIL;
#ifdef F_GETFL
  file->mode = fcntl(file->fd, F_GETFL) | O_ACCMODE;
#else
  struct stat st;
  if (fstat(file->fd, &st) == -1) perror("fstat");
  file->mode = st.st_mode;
#endif
  return (PN)file;
}
PN potion_file_close(Potion *P, PN cl, pn_file self) {
  int retval;
  while (retval = close(self->fd), retval == -1 && errno == EINTR) ;
  self->fd = -1;
  return PN_NIL;
}
PN potion_file_read(Potion *P, PN cl, pn_file self, PN n) {
  n = PN_INT(n);
  char buf[n];
  int r = read(self->fd, buf, n);
  if (r == -1) {
    return potion_io_error(P, "read");
    
    
    
  } else if (r == 0) {
    return PN_NIL;
  }
  return potion_byte_str2(P, buf, r);
}
PN potion_file_write(Potion *P, PN cl, pn_file self, PN obj) {
  long len = 0;
  char *ptr = NULL;
  
  if (!PN_IS_PTR(obj)) {
    if (!obj) return PN_NIL; 
    else if (PN_IS_NUM(obj)) {
      long tmp = PN_NUM(obj); len = sizeof(tmp); ptr = (char *)&tmp;
    }
    else if (PN_IS_BOOL(obj)) {
      char tmp = (obj == PN_TRUE) ? 1 : 0; len = 1; ptr = (char *)&tmp;
    }
    else {
      assert(0 && "Invalid primitive type");
    }
  } else {
    switch (PN_TYPE(obj)) {
      case PN_TSTRING: len = PN_STR_LEN(obj); ptr = PN_STR_PTR(obj); break;
      case PN_TBYTES:  len = potion_send(obj, PN_STR("length")); ptr = PN_STR_PTR(obj); break;
      case PN_TNUMBER: {
        double tmp = PN_DBL(obj); len = sizeof(tmp); ptr = (char *)&tmp;
        break;
      }
      default: return potion_type_error(P, obj);
    }
  }
  int r = write(self->fd, ptr, len);
  if (r == -1)
    return potion_io_error(P, "write");
  return PN_NUM(r);
}
PN potion_file_print(Potion *P, PN cl, pn_file self, PN obj) {
  return potion_file_write(P, cl, self, potion_send(obj, PN_string));
}
PN potion_file_string(Potion *P, PN cl, pn_file self) {
  int fd = self->fd, rv;
  char *buf;
  PN str;
  if (self->path != PN_NIL && fd != -1) {
    rv = asprintf(&buf, "<file %s fd: %d>", PN_STR_PTR(self->path), fd);
  } else if (fd != -1) {
    rv = asprintf(&buf, "<file fd: %d>", fd);
  } else {
    rv = asprintf(&buf, "<closed file>");
  }
  if (rv == -1) potion_allocation_error();
  str = potion_str(P, buf);
  free(buf);
  return str;
}
PN potion_lobby_read(Potion *P, PN cl, PN self) {
  char line[1024];
  if (fgets(line, 1024, stdin) != NULL)
    return potion_str(P, line);
  return PN_NIL;
}
void potion_file_init(Potion *P) {
  PN file_vt = PN_VTABLE(PN_TFILE);
  char **env = environ, *key;
  PN pe = potion_table_empty(P);
#ifdef P2
  PN PN_env = potion_str(P, "%ENV");
#else
  PN PN_env = potion_str(P, "Env");
#endif
  while (*env != NULL) {
    for (key = *env; *key != '='; key++);
    potion_table_put(P, PN_NIL, pe, PN_STRN(*env, key - *env),
      potion_str(P, key + 1));
    env++;
  }
  potion_send(P->lobby, PN_def, PN_env, pe);
  potion_method(P->lobby, "read", potion_lobby_read, 0);
  
  potion_type_constructor_is(file_vt, PN_FUNC(potion_file_new, "path=S,mode=S"));
  potion_class_method(file_vt, "fd", potion_file_with_fd, "fd=N");
  potion_method(file_vt, "string", potion_file_string, 0);
  potion_method(file_vt, "close", potion_file_close, 0);
  potion_method(file_vt, "read", potion_file_read, "n=N");
  potion_method(file_vt, "write", potion_file_write, "str=S");
  potion_method(file_vt, "print", potion_file_print, "obj=o");
}