aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/drivers/unix
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /erts/emulator/drivers/unix
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'erts/emulator/drivers/unix')
-rw-r--r--erts/emulator/drivers/unix/bin_drv.c224
-rw-r--r--erts/emulator/drivers/unix/mem_drv.c145
-rw-r--r--erts/emulator/drivers/unix/multi_drv.c105
-rw-r--r--erts/emulator/drivers/unix/sig_drv.c81
-rw-r--r--erts/emulator/drivers/unix/ttsl_drv.c1299
-rw-r--r--erts/emulator/drivers/unix/unix_efile.c1505
6 files changed, 3359 insertions, 0 deletions
diff --git a/erts/emulator/drivers/unix/bin_drv.c b/erts/emulator/drivers/unix/bin_drv.c
new file mode 100644
index 0000000000..1827187d57
--- /dev/null
+++ b/erts/emulator/drivers/unix/bin_drv.c
@@ -0,0 +1,224 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/* Purpose: Binary file driver interface , used for code loading */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "erl_driver.h"
+#include <errno.h>
+
+#ifdef NO_UMASK
+#define FILE_MODE 0644
+#else
+#define FILE_MODE 0666
+#endif
+
+static int this_port;
+
+static long bin_start();
+static int bin_init(),bin_stop(),bin_erlang_read();
+static int read_fill(), write_fill();
+
+const struct erl_drv_entry bin_driver_entry = {
+ bin_init,
+ bin_start,
+ bin_stop,
+ bin_erlang_read,
+ NULL,
+ NULL,
+ "binary_filer"
+};
+
+
+
+static int bin_init()
+{
+ this_port = -1;
+ return 0;
+}
+
+
+static long bin_start(port,buf)
+int port;
+char *buf;
+{
+
+ if (this_port != -1)
+ return(-1);
+ this_port = port;
+ return(port);
+}
+
+
+static int bin_stop()
+{
+ this_port = -1;
+}
+
+
+static int bin_erlang_read(port,buf,count)
+long port;
+char *buf;
+int count;
+
+{
+ struct stat statbuf;
+ int i,size,fd,rval;
+ char *t,*rbuf;
+
+ buf[count] = '\0';
+ switch (*buf) {
+ case 'c' :
+ if (chdir(buf+1) == 0) /* sucsess */
+ driver_output(this_port,"y",1);
+ else
+ driver_output(this_port,"n",1);
+ return 0;
+ case 'f':
+#ifdef MSDOS
+ if ((fd = open(buf+1,O_RDONLY|O_BINARY)) < 0) {
+#else
+ if ((fd = open(buf+1,O_RDONLY, 0)) < 0) {
+#endif
+ driver_output(this_port,"n",1);
+ return 0;
+ }
+ if (stat(buf+1,&statbuf) < 0) {
+ driver_output(this_port,"n",1);
+ close(fd);
+ return 0;
+ }
+ if (S_ISREG(statbuf.st_mode))
+ size = statbuf.st_size;
+ else
+ size = BUFSIZ;
+
+ if ((rbuf = (char*) driver_alloc(1 + size)) == NULL) {
+ fprintf(stderr,"Out of memory\n");
+ close(fd);
+ driver_output(this_port,"n",1);
+ return 0;
+ }
+ if (S_ISREG(statbuf.st_mode)) {
+ if (read_fill(fd,1+rbuf,size) != size) {
+ driver_free(rbuf);
+ close(fd);
+ driver_output(this_port,"n",1);
+ return 0;;
+ }
+ }
+ /* The idea here is that if it's a regular file read the entire
+ * entire file and if it's a device file or a tty try to read
+ * until errno != EINTR
+ */
+
+ else {
+ while (1) {
+ rval = read(fd,1+rbuf,size);
+ if (rval < 0 && errno == EINTR)
+ continue;
+ if (rval < 0) {
+ driver_free(rbuf);
+ close(fd);
+ driver_output(this_port,"n",1);
+ return 0;
+ }
+ size = rval;
+ break;
+ }
+ }
+ rbuf[0] = 'y';
+ driver_output(this_port,rbuf,1+size);
+ driver_free(rbuf);
+ close(fd);
+ return 0;
+ case 'w':
+#ifdef MSDOS
+ if ((fd = open(buf+1, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
+ FILE_MODE)) < 0) {
+#else
+ if ((fd = open(buf+1, O_WRONLY | O_CREAT | O_TRUNC, FILE_MODE)) < 0) {
+#endif
+ driver_output(this_port,"n",1);
+ return 0;
+ }
+ t = buf+1; i = 1;
+ while(*t && i++ < count) t++;
+ t++;
+ /* t now points to the contents of what we shall write */
+ size = count - 1 - i;
+
+ /* gotta write_fill if we are writing to a slow device
+ such as /dev/audio */
+
+ if (write_fill (fd,t,size) != size) {
+ driver_output(this_port,"n",1);
+ close(fd);
+ return 0;
+ }
+ driver_output(this_port,"y",1);
+ close(fd);
+ return 0;
+ default:
+ driver_failure(this_port,-1);
+ return 0;
+ }
+}
+
+
+static int write_fill(fd, buf, len)
+char *buf;
+{
+ int i, done = 0;
+
+ do {
+ if ((i = write(fd, buf+done, len-done)) < 0) {
+ if (errno != EINTR)
+ return (i);
+ i = 0;
+ }
+ done += i;
+ } while (done < len);
+ return (len);
+}
+
+
+static int read_fill(fd, buf, len)
+char *buf;
+{
+ int i, got = 0;
+
+ do {
+ if ((i = read(fd, buf+got, len-got)) <= 0) {
+ if (i == 0 || errno != EINTR)
+ return (i);
+ i = 0;
+ }
+ got += i;
+ } while (got < len);
+ return (len);
+}
+
diff --git a/erts/emulator/drivers/unix/mem_drv.c b/erts/emulator/drivers/unix/mem_drv.c
new file mode 100644
index 0000000000..1417ca1121
--- /dev/null
+++ b/erts/emulator/drivers/unix/mem_drv.c
@@ -0,0 +1,145 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/* Purpose: Access to elib memory statistics */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include "erl_driver.h"
+#include "elib_stat.h"
+
+#define MAP_BUF_SIZE 1000 /* Max map size */
+#define HISTO_BUF_SIZE 100 /* Max histogram buckets */
+
+static ErlDrvData mem_start(ErlDrvPort);
+static int mem_init(void);
+static void mem_stop(ErlDrvData);
+static void mem_command(ErlDrvData, char*, int);
+
+const struct driver_entry mem_driver_entry = {
+ mem_init,
+ mem_start,
+ mem_stop,
+ mem_command,
+ NULL,
+ NULL,
+ "mem_drv"
+};
+
+static int mem_init(void)
+{
+ return 0;
+}
+
+static ErlDrvData mem_start(ErlDrvPort port, char* buf)
+{
+ return (ErlDrvData)port;
+}
+
+static void mem_stop(ErlDrvData port)
+{
+}
+
+void putint32(p, v)
+byte* p; int v;
+{
+ p[0] = (v >> 24) & 0xff;
+ p[1] = (v >> 16) & 0xff;
+ p[2] = (v >> 8) & 0xff;
+ p[3] = (v) & 0xff;
+}
+
+int getint16(p)
+byte* p;
+{
+ return (p[0] << 8) | p[1];
+}
+
+/*
+** Command:
+** m L1 L0 -> a heap map of length L1*256 + L0 is returned
+** s -> X3 X2 X1 X0 Y3 Y2 Y1 Y0 Z3 Z2 Z1 Z0
+** X == Total heap size bytes
+** Y == Total free bytes
+** Z == Size of largest free block in bytes
+**
+** h L1 L0 B0 -> Generate a logarithm historgram base B with L buckets
+** l L1 L0 S0 -> Generate a linear histogram with step S with L buckets
+*/
+unsigned char outbuf[HISTO_BUF_SIZE*2*4];
+
+static void mem_command(ErlDrvData port, char* buf, int count)
+{
+ if ((count == 1) && buf[0] == 's') {
+ struct elib_stat info;
+ char v[3*4];
+
+ elib_stat(&info);
+
+ putint32(v, info.mem_total*4);
+ putint32(v+4, info.mem_free*4);
+ putint32(v+8, info.max_free*4);
+ driver_output((ErlDrvPort)port, v, 12);
+ return;
+ }
+ else if ((count == 3) && buf[0] == 'm') {
+ char w[MAP_BUF_SIZE];
+ int n = getint16(buf+1);
+
+ if (n > MAP_BUF_SIZE)
+ n = MAP_BUF_SIZE;
+ elib_heap_map(w, n);
+ driver_output((ErlDrvPort)port, w, n);
+ return;
+ }
+ else if ((count == 4) && (buf[0] == 'h' || buf[0] == 'l')) {
+ unsigned long vf[HISTO_BUF_SIZE];
+ unsigned long va[HISTO_BUF_SIZE];
+ int n = getint16(buf+1);
+ int base = (unsigned char) buf[3];
+
+ if (n >= HISTO_BUF_SIZE)
+ n = HISTO_BUF_SIZE;
+ if (buf[0] == 'l')
+ base = -base;
+ if (elib_histo(vf, va, n, base) < 0) {
+ driver_failure((ErlDrvPort)port, -1);
+ return;
+ }
+ else {
+ char* p = outbuf;
+ int i;
+
+ for (i = 0; i < n; i++) {
+ putint32(p, vf[i]);
+ p += 4;
+ }
+ for (i = 0; i < n; i++) {
+ putint32(p, va[i]);
+ p += 4;
+ }
+ driver_output((ErlDrvPort)port, outbuf, n*8);
+ }
+ return;
+ }
+ driver_failure((ErlDrvPort)port, -1);
+}
diff --git a/erts/emulator/drivers/unix/multi_drv.c b/erts/emulator/drivers/unix/multi_drv.c
new file mode 100644
index 0000000000..822c96730c
--- /dev/null
+++ b/erts/emulator/drivers/unix/multi_drv.c
@@ -0,0 +1,105 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/* Purpose: Multidriver interface
+ This is an example of a driver which allows multiple instances of itself.
+ I.e have one erlang process execute open_port(multi......) and
+ at the same time have an other erlang process open an other port
+ running multi there as well.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "erl_driver.h"
+#include "sys.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define MAXCHANNEL 20
+
+static char buf[BUFSIZ];
+
+static ErlDrvData multi_start(ErlDrvPort, char*);
+static int multi_init(void);
+static void multi_stop(ErlDrvData),multi_erlang_read(ErlDrvData, char*, int);
+
+struct driver_entry multi_driver_entry = {
+ multi_init,
+ multi_start,
+ multi_stop,
+ multi_erlang_read,
+ NULL,
+ NULL,
+ "multi"
+};
+
+
+struct channel {
+ ErlDrvPort portno;
+ int channel;
+};
+
+struct channel channels[MAXCHANNEL]; /* Max MAXCHANNEL instances */
+
+static int multi_init(void)
+{
+ memzero(channels,MAXCHANNEL * sizeof(struct channel));
+ return 0;
+}
+
+static ErlDrvData multi_start(ErlDrvPort port, char* buf)
+{
+ int chan;
+ chan = get_new_channel();
+ channels[port].portno = port;
+ channels[port].channel = chan;
+ fprintf(stderr,"Opening channel %d port is %d\n",chan,port);
+ return (ErlDrvData)port;
+}
+
+
+static int multi_stop(ErlDrvData port)
+{
+ fprintf(stderr,"Closing channel %d\n",channels[port].channel);
+ remove_channel(channels[(int)port].channel);
+}
+
+
+static int multi_erlang_read(ErlDrvData port, char* buf, int count)
+{
+ fprintf(stderr,"Writing %d bytes to channel %d\n",
+ count,
+ channels[(int)port].channel);
+}
+
+
+/* These two funs are fake */
+
+int get_new_channel()
+{
+ static int ch = 1;
+ return(ch++);
+}
+
+void remove_channel(int ch)
+{
+}
diff --git a/erts/emulator/drivers/unix/sig_drv.c b/erts/emulator/drivers/unix/sig_drv.c
new file mode 100644
index 0000000000..aab5d63a40
--- /dev/null
+++ b/erts/emulator/drivers/unix/sig_drv.c
@@ -0,0 +1,81 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/* Purpose: demonstrate how to include interupt handlers in erlang */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include "erl_driver.h"
+#include <signal.h>
+#include <stdio.h>
+
+static ErlDrvData sig_start(ErlDrvPort, char*);
+static int sig_init(void);
+static void sig_stop(ErlDrvData), doio(ErlDrvData, ErlDrvEvent);
+
+ErlDrvEntry sig_driver_entry = {
+ sig_init,
+ sig_start,
+ sig_stop,
+ NULL,
+ doio,
+ NULL,
+ "sig_test"
+};
+
+static ErlDrvPort this_port;
+
+static int sig_init(void)
+{
+ this_port = (ErlDrvPort)-1;
+ return 0;
+}
+
+static sigc(int ino)
+{
+ driver_interrupt(this_port, ino);
+}
+
+static ErlDrvData sig_start(ErlDrvPort port, char* buf)
+{
+ if (this_port != (ErlDrvPort)-1)
+ return ERL_DRV_ERROR_GENERAL;
+ this_port = port;
+ signal(SIGUSR1, sigc);
+ return (ErlDrvData)port;
+}
+
+static void sig_stop(ErlDrvData port)
+{
+ this_port = (ErlDrvPort)-1;
+ signal(SIGUSR1, SIG_DFL);
+}
+
+doio(ErlDrvData port, ErlDrvEvent ino)
+{
+ /* First go get the io, unless we already did that */
+ /* In the sighandler */
+
+ /* Then send it to erlang */
+
+ driver_output(this_port, "y", 1);
+}
diff --git a/erts/emulator/drivers/unix/ttsl_drv.c b/erts/emulator/drivers/unix/ttsl_drv.c
new file mode 100644
index 0000000000..4c2514669b
--- /dev/null
+++ b/erts/emulator/drivers/unix/ttsl_drv.c
@@ -0,0 +1,1299 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * Tty driver that reads one character at the time and provides a
+ * smart line for output.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "erl_driver.h"
+
+static int ttysl_init(void);
+static ErlDrvData ttysl_start(ErlDrvPort, char*);
+
+#ifdef HAVE_TERMCAP /* else make an empty driver that can not be opened */
+
+#include "sys.h"
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <unistd.h>
+#include <termios.h>
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#if !defined(HAVE_SETLOCALE) || !defined(HAVE_NL_LANGINFO) || !defined(HAVE_LANGINFO_H)
+#define PRIMITIVE_UTF8_CHECK 1
+#else
+#include <langinfo.h>
+#endif
+
+#define TRUE 1
+#define FALSE 0
+
+/* Termcap functions. */
+int tgetent(char* bp, char *name);
+int tgetnum(char* cap);
+int tgetflag(char* cap);
+char *tgetstr(char* cap, char** buf);
+char *tgoto(char* cm, int col, int line);
+int tputs(char* cp, int affcnt, int (*outc)(int c));
+
+/* Terminal capabilites in which we are interested. */
+static char *capbuf;
+static char *up, *down, *left, *right;
+static int cols, xn;
+static volatile int cols_needs_update = FALSE;
+
+/* The various opcodes. */
+#define OP_PUTC 0
+#define OP_MOVE 1
+#define OP_INSC 2
+#define OP_DELC 3
+#define OP_BEEP 4
+/* Control op */
+#define CTRL_OP_GET_WINSIZE 100
+#define CTRL_OP_GET_UNICODE_STATE 101
+#define CTRL_OP_SET_UNICODE_STATE 102
+
+
+
+static int lbuf_size = BUFSIZ;
+static Uint32 *lbuf; /* The current line buffer */
+static int llen; /* The current line length */
+static int lpos; /* The current "cursor position" in the line buffer */
+
+/*
+ * Tags used in line buffer to show that these bytes represent special characters,
+ * Max unicode is 0x0010ffff, so we have lots of place for meta tags...
+ */
+#define CONTROL_TAG 0x10000000U /* Control character, value in first position */
+#define ESCAPED_TAG 0x01000000U /* Escaped character, value in first position */
+#define TAG_MASK 0xFF000000U
+
+#define MAXSIZE (1 << 16)
+
+#define COL(_l) ((_l) % cols)
+#define LINE(_l) ((_l) / cols)
+
+#define NL '\n'
+
+/* Main interface functions. */
+static void ttysl_stop(ErlDrvData);
+static void ttysl_from_erlang(ErlDrvData, char*, int);
+static void ttysl_from_tty(ErlDrvData, ErlDrvEvent);
+static void ttysl_stop_select(ErlDrvEvent, void*);
+static Sint16 get_sint16(char*);
+
+static ErlDrvPort ttysl_port;
+static int ttysl_fd;
+static FILE *ttysl_out;
+
+/* Functions that work on the line buffer. */
+static int start_lbuf(void);
+static int stop_lbuf(void);
+static int put_chars(byte*,int);
+static int move_rel(int);
+static int ins_chars(byte *,int);
+static int del_chars(int);
+static int step_over_chars(int);
+static int insert_buf(byte*,int);
+static int write_buf(Uint32 *,int);
+static int outc(int c);
+static int move_cursor(int,int);
+
+/* Termcap functions. */
+static int start_termcap(void);
+static int stop_termcap(void);
+static int move_left(int);
+static int move_right(int);
+static int move_up(int);
+static int move_down(int);
+static void update_cols(void);
+
+/* Terminal setting functions. */
+static int tty_init(int,int,int,int);
+static int tty_set(int);
+static int tty_reset(int);
+static int ttysl_control(ErlDrvData, unsigned int, char *, int, char **, int);
+static RETSIGTYPE suspend(int);
+static RETSIGTYPE cont(int);
+static RETSIGTYPE winch(int);
+
+/*#define LOG_DEBUG*/
+
+#ifdef LOG_DEBUG
+FILE *debuglog = NULL;
+
+#define DEBUGLOG(X) \
+do { \
+ if (debuglog != NULL) { \
+ my_debug_printf X; \
+ } \
+} while (0)
+
+static void my_debug_printf(char *fmt, ...)
+{
+ char buffer[1024];
+ va_list args;
+
+ va_start(args, fmt);
+ erts_vsnprintf(buffer,1024,fmt,args);
+ va_end(args);
+ erts_fprintf(debuglog,"%s\n",buffer);
+ //erts_printf("Debuglog = %s\n",buffer);
+}
+
+#else
+
+#define DEBUGLOG(X)
+
+#endif
+
+static int utf8_mode = 0;
+static byte utf8buf[4]; /* for incomplete input */
+static int utf8buf_size; /* size of incomplete input */
+
+# define IF_IMPL(x) x
+#else
+# define IF_IMPL(x) NULL
+#endif /* HAVE_TERMCAP */
+
+/* Define the driver table entry. */
+struct erl_drv_entry ttsl_driver_entry = {
+ ttysl_init,
+ ttysl_start,
+ IF_IMPL(ttysl_stop),
+ IF_IMPL(ttysl_from_erlang),
+ IF_IMPL(ttysl_from_tty),
+ NULL,
+ "tty_sl",
+ NULL,
+ NULL,
+ IF_IMPL(ttysl_control),
+ NULL, /* timeout */
+ NULL, /* outputv */
+ NULL, /* ready_async */
+ NULL, /* flush */
+ NULL, /* call */
+ NULL, /* event */
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ 0, /* ERL_DRV_FLAGs */
+ NULL,
+ NULL, /* process_exit */
+ IF_IMPL(ttysl_stop_select)
+};
+
+
+static int ttysl_init(void)
+{
+#ifdef HAVE_TERMCAP
+ ttysl_port = (ErlDrvPort)-1;
+ ttysl_fd = -1;
+ lbuf = NULL; /* For line buffer handling */
+ capbuf = NULL; /* For termcap handling */
+#endif
+#ifdef LOG_DEBUG
+ {
+ char *dl;
+ if ((dl = getenv("TTYSL_DEBUG_LOG")) != NULL && *dl) {
+ debuglog = fopen(dl,"w+");
+ if (debuglog != NULL)
+ setbuf(debuglog,NULL);
+ }
+ DEBUGLOG(("Debuglog = %s(0x%ld)\n",dl,(long) debuglog));
+ }
+#endif
+ return 0;
+}
+
+static ErlDrvData ttysl_start(ErlDrvPort port, char* buf)
+{
+#ifndef HAVE_TERMCAP
+ return ERL_DRV_ERROR_GENERAL;
+#else
+ char *s, *t, c, *l;
+ int canon, echo, sig; /* Terminal characteristics */
+ int flag;
+ extern int using_oldshell; /* set this to let the rest of erts know */
+
+ utf8buf_size = 0;
+ if (ttysl_port != (ErlDrvPort)-1)
+ return ERL_DRV_ERROR_GENERAL;
+
+ if (!isatty(0) || !isatty(1))
+ return ERL_DRV_ERROR_GENERAL;
+
+ /* Set the terminal modes to default leave as is. */
+ canon = echo = sig = 0;
+
+ /* Parse the input parameters. */
+ for (s = strchr(buf, ' '); s; s = t) {
+ s++;
+ /* Find end of this argument (start of next) and insert NUL. */
+ if ((t = strchr(s, ' '))) {
+ c = *t;
+ *t = '\0';
+ }
+ if ((flag = ((*s == '+') ? 1 : ((*s == '-') ? -1 : 0)))) {
+ if (s[1] == 'c') canon = flag;
+ if (s[1] == 'e') echo = flag;
+ if (s[1] == 's') sig = flag;
+ }
+ else if ((ttysl_fd = open(s, O_RDWR, 0)) < 0)
+ return ERL_DRV_ERROR_GENERAL;
+ }
+ if (ttysl_fd < 0)
+ ttysl_fd = 0;
+
+ if (tty_init(ttysl_fd, canon, echo, sig) < 0 ||
+ tty_set(ttysl_fd) < 0) {
+ ttysl_port = (ErlDrvPort)-1;
+ tty_reset(ttysl_fd);
+ return ERL_DRV_ERROR_GENERAL;
+ }
+
+ /* Set up smart line and termcap stuff. */
+ if (!start_lbuf() || !start_termcap()) {
+ stop_lbuf(); /* Must free this */
+ tty_reset(ttysl_fd);
+ return ERL_DRV_ERROR_GENERAL;
+ }
+
+ /* Open the terminal and set the terminal */
+ ttysl_out = fdopen(ttysl_fd, "w");
+
+#ifdef PRIMITIVE_UTF8_CHECK
+ setlocale(LC_CTYPE, ""); /* Set international environment,
+ ignore result */
+ if (((l = getenv("LC_ALL")) && *l) ||
+ ((l = getenv("LC_CTYPE")) && *l) ||
+ ((l = getenv("LANG")) && *l)) {
+ if (strstr(l, "UTF-8"))
+ utf8_mode = 1;
+ }
+
+#else
+ l = setlocale(LC_CTYPE, ""); /* Set international environment */
+ if (l != NULL) {
+ utf8_mode = (strcmp(nl_langinfo(CODESET), "UTF-8") == 0);
+ DEBUGLOG(("setlocale: %s\n",l));
+ }
+#endif
+ DEBUGLOG(("utf8_mode is %s\n",(utf8_mode) ? "on" : "off"));
+ sys_sigset(SIGCONT, cont);
+ sys_sigset(SIGWINCH, winch);
+
+ driver_select(port, (ErlDrvEvent)(Uint)ttysl_fd, ERL_DRV_READ|ERL_DRV_USE, 1);
+ ttysl_port = port;
+
+ /* we need to know this when we enter the break handler */
+ using_oldshell = 0;
+
+ return (ErlDrvData)ttysl_port; /* Nothing important to return */
+#endif /* HAVE_TERMCAP */
+}
+
+#ifdef HAVE_TERMCAP
+
+#define DEF_HEIGHT 24
+#define DEF_WIDTH 80
+static void ttysl_get_window_size(Uint32 *width, Uint32 *height)
+{
+#ifdef TIOCGWINSZ
+ struct winsize ws;
+ if (ioctl(ttysl_fd,TIOCGWINSZ,&ws) == 0) {
+ *width = (Uint32) ws.ws_col;
+ *height = (Uint32) ws.ws_row;
+ if (*width <= 0)
+ *width = DEF_WIDTH;
+ if (*height <= 0)
+ *height = DEF_HEIGHT;
+ return;
+ }
+#endif
+ *width = DEF_WIDTH;
+ *height = DEF_HEIGHT;
+}
+
+static int ttysl_control(ErlDrvData drv_data,
+ unsigned int command,
+ char *buf, int len,
+ char **rbuf, int rlen)
+{
+ char resbuff[2*sizeof(Uint32)];
+ int res_size;
+ switch (command) {
+ case CTRL_OP_GET_WINSIZE:
+ {
+ Uint32 w,h;
+ ttysl_get_window_size(&w,&h);
+ memcpy(resbuff,&w,sizeof(Uint32));
+ memcpy(resbuff+sizeof(Uint32),&h,sizeof(Uint32));
+ res_size = 2*sizeof(Uint32);
+ }
+ break;
+ case CTRL_OP_GET_UNICODE_STATE:
+ *resbuff = (utf8_mode) ? 1 : 0;
+ res_size = 1;
+ break;
+ case CTRL_OP_SET_UNICODE_STATE:
+ if (len > 0) {
+ int m = (int) *buf;
+ *resbuff = (utf8_mode) ? 1 : 0;
+ res_size = 1;
+ utf8_mode = (m) ? 1 : 0;
+ } else {
+ return 0;
+ }
+ break;
+ default:
+ return 0;
+ }
+ if (rlen < res_size) {
+ *rbuf = driver_alloc(res_size);
+ }
+ memcpy(*rbuf,resbuff,res_size);
+ return res_size;
+}
+
+
+static void ttysl_stop(ErlDrvData ttysl_data)
+{
+ if (ttysl_port != (ErlDrvPort)-1) {
+ stop_lbuf();
+ stop_termcap();
+ tty_reset(ttysl_fd);
+ driver_select(ttysl_port, (ErlDrvEvent)(Uint)ttysl_fd, ERL_DRV_READ|ERL_DRV_USE, 0);
+ sys_sigset(SIGCONT, SIG_DFL);
+ sys_sigset(SIGWINCH, SIG_DFL);
+ }
+ ttysl_port = (ErlDrvPort)-1;
+ ttysl_fd = -1;
+ /* return TRUE; */
+}
+
+static int put_utf8(int ch, byte *target, int sz, int *pos)
+{
+ Uint x = (Uint) ch;
+ if (x < 0x80) {
+ if (*pos >= sz) {
+ return -1;
+ }
+ target[(*pos)++] = (byte) x;
+ }
+ else if (x < 0x800) {
+ if (((*pos) + 1) >= sz) {
+ return -1;
+ }
+ target[(*pos)++] = (((byte) (x >> 6)) |
+ ((byte) 0xC0));
+ target[(*pos)++] = (((byte) (x & 0x3F)) |
+ ((byte) 0x80));
+ } else if (x < 0x10000) {
+ if ((x >= 0xD800 && x <= 0xDFFF) ||
+ (x == 0xFFFE) ||
+ (x == 0xFFFF)) { /* Invalid unicode range */
+ return -1;
+ }
+ if (((*pos) + 2) >= sz) {
+ return -1;
+ }
+
+ target[(*pos)++] = (((byte) (x >> 12)) |
+ ((byte) 0xE0));
+ target[(*pos)++] = ((((byte) (x >> 6)) & 0x3F) |
+ ((byte) 0x80));
+ target[(*pos)++] = (((byte) (x & 0x3F)) |
+ ((byte) 0x80));
+ } else if (x < 0x110000) { /* Standard imposed max */
+ if (((*pos) + 3) >= sz) {
+ return -1;
+ }
+ target[(*pos)++] = (((byte) (x >> 18)) |
+ ((byte) 0xF0));
+ target[(*pos)++] = ((((byte) (x >> 12)) & 0x3F) |
+ ((byte) 0x80));
+ target[(*pos)++] = ((((byte) (x >> 6)) & 0x3F) |
+ ((byte) 0x80));
+ target[(*pos)++] = (((byte) (x & 0x3F)) |
+ ((byte) 0x80));
+ } else {
+ return -1;
+ }
+ return 0;
+}
+
+
+static int pick_utf8(byte *s, int sz, int *pos)
+{
+ int size = sz - (*pos);
+ byte *source;
+ Uint unipoint;
+
+ if (size > 0) {
+ source = s + (*pos);
+ if (((*source) & ((byte) 0x80)) == 0) {
+ unipoint = (int) *source;
+ ++(*pos);
+ return (int) unipoint;
+ } else if (((*source) & ((byte) 0xE0)) == 0xC0) {
+ if (size < 2) {
+ return -2;
+ }
+ if (((source[1] & ((byte) 0xC0)) != 0x80) ||
+ ((*source) < 0xC2) /* overlong */) {
+ return -1;
+ }
+ (*pos) += 2;
+ unipoint =
+ (((Uint) ((*source) & ((byte) 0x1F))) << 6) |
+ ((Uint) (source[1] & ((byte) 0x3F)));
+ return (int) unipoint;
+ } else if (((*source) & ((byte) 0xF0)) == 0xE0) {
+ if (size < 3) {
+ return -2;
+ }
+ if (((source[1] & ((byte) 0xC0)) != 0x80) ||
+ ((source[2] & ((byte) 0xC0)) != 0x80) ||
+ (((*source) == 0xE0) && (source[1] < 0xA0)) /* overlong */ ) {
+ return -1;
+ }
+ if ((((*source) & ((byte) 0xF)) == 0xD) &&
+ ((source[1] & 0x20) != 0)) {
+ return -1;
+ }
+ if (((*source) == 0xEF) && (source[1] == 0xBF) &&
+ ((source[2] == 0xBE) || (source[2] == 0xBF))) {
+ return -1;
+ }
+ (*pos) += 3;
+ unipoint =
+ (((Uint) ((*source) & ((byte) 0xF))) << 12) |
+ (((Uint) (source[1] & ((byte) 0x3F))) << 6) |
+ ((Uint) (source[2] & ((byte) 0x3F)));
+ return (int) unipoint;
+ } else if (((*source) & ((byte) 0xF8)) == 0xF0) {
+ if (size < 4) {
+ return -2 ;
+ }
+ if (((source[1] & ((byte) 0xC0)) != 0x80) ||
+ ((source[2] & ((byte) 0xC0)) != 0x80) ||
+ ((source[3] & ((byte) 0xC0)) != 0x80) ||
+ (((*source) == 0xF0) && (source[1] < 0x90)) /* overlong */) {
+ return -1;
+ }
+ if ((((*source) & ((byte)0x7)) > 0x4U) ||
+ ((((*source) & ((byte)0x7)) == 0x4U) &&
+ ((source[1] & ((byte)0x3F)) > 0xFU))) {
+ return -1;
+ }
+ (*pos) += 4;
+ unipoint =
+ (((Uint) ((*source) & ((byte) 0x7))) << 18) |
+ (((Uint) (source[1] & ((byte) 0x3F))) << 12) |
+ (((Uint) (source[2] & ((byte) 0x3F))) << 6) |
+ ((Uint) (source[3] & ((byte) 0x3F)));
+ return (int) unipoint;
+ } else {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+}
+
+static int octal_or_hex_positions(Uint c)
+{
+ int x = 0;
+ Uint ch = c;
+ if (!ch) {
+ return 1;
+ }
+ while(ch) {
+ ++x;
+ ch >>= 3;
+ }
+ if (x <= 3) {
+ return 3;
+ }
+ /* \x{H ...} format when larger than \777 */
+ x = 0;
+ ch = c;
+ while(ch) {
+ ++x;
+ ch >>= 4;
+ }
+ return x+3;
+}
+
+static void octal_or_hex_format(Uint ch, byte *buf, int *pos)
+{
+ static byte hex_chars[] = { '0','1','2','3','4','5','6','7','8','9',
+ 'A','B','C','D','E','F'};
+ int num = octal_or_hex_positions(ch);
+ if (num != 3) {
+ buf[(*pos)++] = 'x';
+ buf[(*pos)++] = '{';
+ num -= 3;
+ while(num--) {
+ buf[(*pos)++] = hex_chars[((ch >> (4*num)) & 0xFU)];
+ }
+ buf[(*pos)++] = '}';
+ } else {
+ while(num--) {
+ buf[(*pos)++] = ((byte) ((ch >> (3*num)) & 0x7U) + '0');
+ }
+ }
+}
+
+/*
+ * Check that there is enough room in all buffers to copy all pad chars
+ * and stiff we need If not, realloc lbuf.
+ */
+static int check_buf_size(byte *s, int n)
+{
+ int pos = 0;
+ int ch;
+ int size = 10;
+
+ while(pos < n) {
+ /* Indata is always UTF-8 */
+ if ((ch = pick_utf8(s,n,&pos)) < 0) {
+ /* XXX temporary allow invalid chars */
+ ch = (int) s[pos];
+ DEBUGLOG(("Invalid UTF8:%d",ch));
+ ++pos;
+ }
+ if (utf8_mode) { /* That is, terminal is UTF8 compliant */
+ if (ch >= 128 || isprint(ch)) {
+ DEBUGLOG(("Printable(UTF-8:%d):%d",(pos - opos),ch));
+ size++; /* Buffer contains wide characters... */
+ } else if (ch == '\t') {
+ size += 8;
+ } else {
+ DEBUGLOG(("Magic(UTF-8:%d):%d",(pos - opos),ch));
+ size += 2;
+ }
+ } else {
+ if (ch <= 255 && isprint(ch)) {
+ DEBUGLOG(("Printable:%d",ch));
+ size++;
+ } else if (ch == '\t')
+ size += 8;
+ else if (ch >= 128) {
+ DEBUGLOG(("Non printable:%d",ch));
+ size += (octal_or_hex_positions(ch) + 1);
+ }
+ else {
+ DEBUGLOG(("Magic:%d",ch));
+ size += 2;
+ }
+ }
+ }
+
+ if (size + lpos >= lbuf_size) {
+
+ lbuf_size = size + lpos + BUFSIZ;
+ if ((lbuf = driver_realloc(lbuf, lbuf_size * sizeof(Uint32))) == NULL) {
+ driver_failure(ttysl_port, -1);
+ return(0);
+ }
+ }
+ return(1);
+}
+
+
+static void ttysl_from_erlang(ErlDrvData ttysl_data, char* buf, int count)
+{
+ if (lpos > MAXSIZE)
+ put_chars((byte*)"\n", 1);
+
+ switch (buf[0]) {
+ case OP_PUTC:
+ DEBUGLOG(("OP: Putc(%d)",count-1));
+ if (check_buf_size((byte*)buf+1, count-1) == 0)
+ return;
+ put_chars((byte*)buf+1, count-1);
+ break;
+ case OP_MOVE:
+ move_rel(get_sint16(buf+1));
+ break;
+ case OP_INSC:
+ if (check_buf_size((byte*)buf+1, count-1) == 0)
+ return;
+ ins_chars((byte*)buf+1, count-1);
+ break;
+ case OP_DELC:
+ del_chars(get_sint16(buf+1));
+ break;
+ case OP_BEEP:
+ outc('\007');
+ break;
+ default:
+ /* Unknown op, just ignore. */
+ break;
+ }
+ fflush(ttysl_out);
+ return; /* TRUE; */
+}
+
+static void ttysl_from_tty(ErlDrvData ttysl_data, ErlDrvEvent fd)
+{
+ byte b[1024];
+ ssize_t i;
+ int ch = 0, pos = 0;
+ int left = 1024;
+ byte *p = b;
+ byte t[1024];
+ int tpos;
+
+ if (utf8buf_size > 0) {
+ memcpy(b,utf8buf,utf8buf_size);
+ left -= utf8buf_size;
+ p += utf8buf_size;
+ utf8buf_size = 0;
+ }
+
+ if ((i = read((int)(Sint)fd, (char *) p, left)) >= 0) {
+ if (p != b) {
+ i += (p - b);
+ }
+ if (utf8_mode) { /* Hopefully an UTF8 terminal */
+ while(pos < i && (ch = pick_utf8(b,i,&pos)) >= 0)
+ ;
+ if (ch == -2 && i - pos <= 4) {
+ /* bytes left to care for */
+ utf8buf_size = i -pos;
+ memcpy(utf8buf,b+pos,utf8buf_size);
+ } else if (ch == -1) {
+ DEBUGLOG(("Giving up on UTF8 mode, invalid character"));
+ utf8_mode = 0;
+ goto latin_terminal;
+ }
+ driver_output(ttysl_port, (char *) b, pos);
+ } else {
+ latin_terminal:
+ tpos = 0;
+ while (pos < i) {
+ while (tpos < 1020 && pos < i) { /* Max 4 bytes for UTF8 */
+ put_utf8((int) b[pos++], t, 1024, &tpos);
+ }
+ driver_output(ttysl_port, (char *) t, tpos);
+ tpos = 0;
+ }
+ }
+ } else {
+ driver_failure(ttysl_port, -1);
+ }
+}
+
+static void ttysl_stop_select(ErlDrvEvent e, void* _)
+{
+ int fd = (int)(long)e;
+ if (fd != 0) {
+ close(fd);
+ }
+}
+
+/* Procedures for putting and getting integers to/from strings. */
+static Sint16 get_sint16(char *s)
+{
+ return ((*s << 8) | ((byte*)s)[1]);
+}
+
+static int start_lbuf(void)
+{
+ if (!lbuf && !(lbuf = ( Uint32*) driver_alloc(lbuf_size * sizeof(Uint32))))
+ return FALSE;
+ llen = 0;
+ lpos = 0;
+ return TRUE;
+}
+
+static int stop_lbuf(void)
+{
+ if (lbuf) {
+ driver_free(lbuf);
+ lbuf = NULL;
+ }
+ return TRUE;
+}
+
+/* Put l bytes (in UTF8) from s into the buffer and output them. */
+static int put_chars(byte *s, int l)
+{
+ int n;
+
+ n = insert_buf(s, l);
+ if (n > 0)
+ write_buf(lbuf + lpos - n, n);
+ if (lpos > llen)
+ llen = lpos;
+ return TRUE;
+}
+
+/*
+ * Move the current postition forwards or backwards within the current
+ * line. We know about padding.
+ */
+static int move_rel(int n)
+{
+ int npos; /* The new position */
+
+ /* Step forwards or backwards over the buffer. */
+ npos = step_over_chars(n);
+
+ /* Calculate move, updates pointers and move the cursor. */
+ move_cursor(lpos, npos);
+ lpos = npos;
+ return TRUE;
+}
+
+/* Insert characters into the buffer at the current position. */
+static int ins_chars(byte *s, int l)
+{
+ int n, tl;
+ Uint32 *tbuf = NULL; /* Suppress warning about use-before-set */
+
+ /* Move tail of buffer to make space. */
+ if ((tl = llen - lpos) > 0) {
+ if ((tbuf = driver_alloc(tl * sizeof(Uint32))) == NULL)
+ return FALSE;
+ memcpy(tbuf, lbuf + lpos, tl * sizeof(Uint32));
+ }
+ n = insert_buf(s, l);
+ if (tl > 0) {
+ memcpy(lbuf + lpos, tbuf, tl * sizeof(Uint32));
+ driver_free(tbuf);
+ }
+ llen += n;
+ write_buf(lbuf + (lpos - n), llen - (lpos - n));
+ move_cursor(llen, lpos);
+ return TRUE;
+}
+
+/*
+ * Delete characters in the buffer. Can delete characters before (n < 0)
+ * and after (n > 0) the current position. Cursor left at beginning of
+ * deleted block.
+ */
+static int del_chars(int n)
+{
+ int i, l, r;
+ int pos;
+
+ update_cols();
+
+ /* Step forward or backwards over n logical characters. */
+ pos = step_over_chars(n);
+
+ if (pos > lpos) {
+ l = pos - lpos; /* Buffer characters to delete */
+ r = llen - lpos - l; /* Characters after deleted */
+ /* Fix up buffer and buffer pointers. */
+ if (r > 0)
+ memcpy(lbuf + lpos, lbuf + pos, r * sizeof(Uint32));
+ llen -= l;
+ /* Write out characters after, blank the tail and jump back to lpos. */
+ write_buf(lbuf + lpos, r);
+ for (i = l ; i > 0; --i)
+ outc(' ');
+ if (COL(llen+l) == 0 && xn)
+ {
+ outc(' ');
+ move_left(1);
+ }
+ move_cursor(llen + l, lpos);
+ }
+ else if (pos < lpos) {
+ l = lpos - pos; /* Buffer characters */
+ r = llen - lpos; /* Characters after deleted */
+ move_cursor(lpos, lpos-l); /* Move back */
+ /* Fix up buffer and buffer pointers. */
+ if (r > 0)
+ memcpy(lbuf + pos, lbuf + lpos, r * sizeof(Uint32));
+ lpos -= l;
+ llen -= l;
+ /* Write out characters after, blank the tail and jump back to lpos. */
+ write_buf(lbuf + lpos, r);
+ for (i = l ; i > 0; --i)
+ outc(' ');
+ if (COL(llen+l) == 0 && xn)
+ {
+ outc(' ');
+ move_left(1);
+ }
+ move_cursor(llen + l, lpos);
+ }
+ return TRUE;
+}
+
+/* Step over n logical characters, check for overflow. */
+static int step_over_chars(int n)
+{
+ Uint32 *c, *beg, *end;
+
+ beg = lbuf;
+ end = lbuf + llen;
+ c = lbuf + lpos;
+ for ( ; n > 0 && c < end; --n) {
+ c++;
+ while (c < end && (*c & TAG_MASK) && ((*c & ~TAG_MASK) == 0))
+ c++;
+ }
+ for ( ; n < 0 && c > beg; n++) {
+ --c;
+ while (c > beg && (*c & TAG_MASK) && ((*c & ~TAG_MASK) == 0))
+ --c;
+ }
+ return c - lbuf;
+}
+
+/*
+ * Insert n characters into the buffer at lpos.
+ * Know about pad characters and treat \n specially.
+ */
+
+static int insert_buf(byte *s, int n)
+{
+ int pos = 0;
+ int buffpos = lpos;
+ int ch;
+
+ while (pos < n) {
+ if ((ch = pick_utf8(s,n,&pos)) < 0) {
+ /* XXX temporary allow invalid chars */
+ ch = (int) s[pos];
+ DEBUGLOG(("insert_buf: Invalid UTF8:%d",ch));
+ ++pos;
+ }
+ if ((utf8_mode && (ch >= 128 || isprint(ch))) || (ch <= 255 && isprint(ch))) {
+ DEBUGLOG(("insert_buf: Printable(UTF-8):%d",ch));
+ lbuf[lpos++] = (Uint32) ch;
+ } else if (ch >= 128) { /* not utf8 mode */
+ int nc = octal_or_hex_positions(ch);
+ lbuf[lpos++] = ((Uint32) ch) | ESCAPED_TAG;
+ while (nc--) {
+ lbuf[lpos++] = ESCAPED_TAG;
+ }
+ } else if (ch == '\t') {
+ do {
+ lbuf[lpos++] = (CONTROL_TAG | ((Uint32) ch));
+ ch = 0;
+ } while (lpos % 8);
+ } else if (ch == '\n' || ch == '\r') {
+ write_buf(lbuf + buffpos, lpos - buffpos);
+ outc('\r');
+ if (ch == '\n')
+ outc('\n');
+ if (llen > lpos) {
+ memcpy(lbuf, lbuf + lpos, llen - lpos);
+ }
+ llen -= lpos;
+ lpos = buffpos = 0;
+ } else {
+ DEBUGLOG(("insert_buf: Magic(UTF-8):%d",ch));
+ lbuf[lpos++] = ch | CONTROL_TAG;
+ lbuf[lpos++] = CONTROL_TAG;
+ }
+ }
+ return lpos - buffpos; /* characters "written" into
+ current buffer (may be less due to newline) */
+}
+
+
+
+/*
+ * Write n characters in line buffer starting at s. Be smart about
+ * non-printables. Know about pad characters and that \n can never
+ * occur normally.
+ */
+
+static int write_buf(Uint32 *s, int n)
+{
+ byte ubuf[4];
+ int ubytes = 0, i;
+ byte lastput = ' ';
+
+ update_cols();
+
+ while (n > 0) {
+ if (!(*s & TAG_MASK) ) {
+ if (utf8_mode) {
+ ubytes = 0;
+ if (put_utf8((int) *s, ubuf, 4, &ubytes) == 0) {
+ for (i = 0; i < ubytes; ++i) {
+ outc(ubuf[i]);
+ }
+ lastput = 0; /* Means the last written character was multibyte UTF8 */
+ }
+ } else {
+ outc((byte) *s);
+ lastput = (byte) *s;
+ }
+ --n;
+ ++s;
+ }
+ else if (*s == (CONTROL_TAG | ((Uint32) '\t'))) {
+ outc(lastput = ' ');
+ --n; s++;
+ while (n > 0 && *s == CONTROL_TAG) {
+ outc(lastput = ' ');
+ --n; s++;
+ }
+ } else if (*s & CONTROL_TAG) {
+ outc('^');
+ outc(lastput = ((byte) ((*s == 0177) ? '?' : *s | 0x40)));
+ n -= 2;
+ s += 2;
+ } else if (*s & ESCAPED_TAG) {
+ Uint32 ch = *s & ~(TAG_MASK);
+ byte *octbuff;
+ byte octtmp[256];
+ int octbytes;
+ DEBUGLOG(("Escaped: %d", ch));
+ octbytes = octal_or_hex_positions(ch);
+ if (octbytes > 256) {
+ octbuff = driver_alloc(octbytes);
+ } else {
+ octbuff = octtmp;
+ }
+ octbytes = 0;
+ octal_or_hex_format(ch, octbuff, &octbytes);
+ DEBUGLOG(("octbytes: %d", octbytes));
+ outc('\\');
+ for (i = 0; i < octbytes; ++i) {
+ outc(lastput = octbuff[i]);
+ DEBUGLOG(("outc: %d", (int) lastput));
+ }
+ n -= octbytes+1;
+ s += octbytes+1;
+ if (octbuff != octtmp) {
+ driver_free(octbuff);
+ }
+ } else {
+ DEBUGLOG(("Very unexpected character %d",(int) *s));
+ ++n;
+ --s;
+ }
+ }
+ /* Check landed in first column of new line and have 'xn' bug. */
+ n = s - lbuf;
+ if (COL(n) == 0 && xn && n != 0) {
+ if (n >= llen) {
+ outc(' ');
+ } else if (lastput == 0) { /* A multibyte UTF8 character */
+ for (i = 0; i < ubytes; ++i) {
+ outc(ubuf[i]);
+ }
+ } else {
+ outc(lastput);
+ }
+ move_left(1);
+ }
+ return TRUE;
+}
+
+
+/* The basic procedure for outputting one character. */
+static int outc(int c)
+{
+ return (int)putc(c, ttysl_out);
+}
+
+static int move_cursor(int from, int to)
+{
+ int dc, dl;
+
+ update_cols();
+
+ dc = COL(to) - COL(from);
+ dl = LINE(to) - LINE(from);
+ if (dl > 0)
+ move_down(dl);
+ else if (dl < 0)
+ move_up(-dl);
+ if (dc > 0)
+ move_right(dc);
+ else if (dc < 0)
+ move_left(-dc);
+ return TRUE;
+}
+
+static int start_termcap(void)
+{
+ int eres;
+ size_t envsz = 1024;
+ char *env = NULL;
+ char *c;
+
+ capbuf = driver_alloc(1024);
+ if (!capbuf)
+ goto false;
+ eres = erl_drv_getenv("TERM", capbuf, &envsz);
+ if (eres == 0)
+ env = capbuf;
+ else if (eres < 0)
+ goto false;
+ else /* if (eres > 1) */ {
+ char *envbuf = driver_alloc(envsz);
+ if (!envbuf)
+ goto false;
+ while (1) {
+ char *newenvbuf;
+ eres = erl_drv_getenv("TERM", envbuf, &envsz);
+ if (eres == 0)
+ break;
+ newenvbuf = driver_realloc(envbuf, envsz);
+ if (eres < 0 || !newenvbuf) {
+ env = newenvbuf ? newenvbuf : envbuf;
+ goto false;
+ }
+ envbuf = newenvbuf;
+ }
+ env = envbuf;
+ }
+ if (tgetent((char*)lbuf, env) <= 0)
+ goto false;
+ if (env != capbuf) {
+ env = NULL;
+ driver_free(env);
+ }
+ c = capbuf;
+ cols = tgetnum("co");
+ if (cols <= 0)
+ cols = DEF_WIDTH;
+ xn = tgetflag("xn");
+ up = tgetstr("up", &c);
+ if (!(down = tgetstr("do", &c)))
+ down = "\n";
+ if (!(left = tgetflag("bs") ? "\b" : tgetstr("bc", &c)))
+ left = "\b"; /* Can't happen - but does on Solaris 2 */
+ right = tgetstr("nd", &c);
+ if (up && down && left && right)
+ return TRUE;
+ false:
+ if (env && env != capbuf)
+ driver_free(env);
+ if (capbuf)
+ driver_free(capbuf);
+ capbuf = NULL;
+ return FALSE;
+}
+
+static int stop_termcap(void)
+{
+ if (capbuf) driver_free(capbuf);
+ capbuf = NULL;
+ return TRUE;
+}
+
+static int move_left(int n)
+{
+ while (n-- > 0)
+ tputs(left, 1, outc);
+ return TRUE;
+}
+
+static int move_right(int n)
+{
+ while (n-- > 0)
+ tputs(right, 1, outc);
+ return TRUE;
+}
+
+static int move_up(int n)
+{
+ while (n-- > 0)
+ tputs(up, 1, outc);
+ return TRUE;
+}
+
+static int move_down(int n)
+{
+ while (n-- > 0)
+ tputs(down, 1, outc);
+ return TRUE;
+}
+
+
+/*
+ * Updates cols if terminal has resized (SIGWINCH). Should be called
+ * at the start of any function that uses the COL or LINE macros. If
+ * the terminal is resized after calling this function but before use
+ * of the macros, then we may write to the wrong screen location.
+ *
+ * We cannot call this from the SIGWINCH handler because it uses
+ * ioctl() which is not a safe function as listed in the signal(7)
+ * man page.
+ */
+static void update_cols(void)
+{
+ Uint32 width, height;
+
+ if (cols_needs_update) {
+ cols_needs_update = FALSE;
+ ttysl_get_window_size(&width, &height);
+ cols = width;
+ }
+}
+
+
+/*
+ * Put a terminal device into non-canonical mode with ECHO off.
+ * Before doing so we first save the terminal's current mode,
+ * assuming the caller will call the tty_reset() function
+ * (also in this file) when it's done with raw mode.
+ */
+
+static struct termios tty_smode, tty_rmode;
+
+static int tty_init(int fd, int canon, int echo, int sig)
+{
+ if (tcgetattr(fd, &tty_rmode) < 0)
+ return -1;
+ tty_smode = tty_rmode;
+
+ /* Default characteristics for all usage including termcap output. */
+ tty_smode.c_iflag &= ~ISTRIP;
+
+ /* Turn canonical (line mode) on off. */
+ if (canon > 0) {
+ tty_smode.c_iflag |= ICRNL;
+ tty_smode.c_lflag |= ICANON;
+ tty_smode.c_oflag |= OPOST;
+ tty_smode.c_cc[VEOF] = tty_rmode.c_cc[VEOF];
+#ifdef VDSUSP
+ tty_smode.c_cc[VDSUSP] = tty_rmode.c_cc[VDSUSP];
+#endif
+ }
+ if (canon < 0) {
+ tty_smode.c_iflag &= ~ICRNL;
+ tty_smode.c_lflag &= ~ICANON;
+ tty_smode.c_oflag &= ~OPOST;
+ /* Must get these really right or funny effects can occur. */
+ tty_smode.c_cc[VMIN] = 1;
+ tty_smode.c_cc[VTIME] = 0;
+#ifdef VDSUSP
+ tty_smode.c_cc[VDSUSP] = 0;
+#endif
+ }
+
+ /* Turn echo on or off. */
+ if (echo > 0)
+ tty_smode.c_lflag |= ECHO;
+ if (echo < 0)
+ tty_smode.c_lflag &= ~ECHO;
+
+ /* Set extra characteristics for "RAW" mode, no signals. */
+ if (sig > 0) {
+ /* Ignore IMAXBEL as not POSIX. */
+#ifndef QNX
+ tty_smode.c_iflag |= (BRKINT|IGNPAR|ICRNL|IXON|IXANY);
+#else
+ tty_smode.c_iflag |= (BRKINT|IGNPAR|ICRNL|IXON);
+#endif
+ tty_smode.c_lflag |= (ISIG|IEXTEN);
+ }
+ if (sig < 0) {
+ /* Ignore IMAXBEL as not POSIX. */
+#ifndef QNX
+ tty_smode.c_iflag &= ~(BRKINT|IGNPAR|ICRNL|IXON|IXANY);
+#else
+ tty_smode.c_iflag &= ~(BRKINT|IGNPAR|ICRNL|IXON);
+#endif
+ tty_smode.c_lflag &= ~(ISIG|IEXTEN);
+ }
+ return 0;
+}
+
+/*
+ * Set/restore a terminal's mode to whatever it was on the most
+ * recent call to the tty_init() function above.
+ */
+
+static int tty_set(int fd)
+{
+ DEBUGF(("Setting tty...\n"));
+
+ if (tcsetattr(fd, TCSANOW, &tty_smode) < 0)
+ return(-1);
+ return(0);
+}
+
+static int tty_reset(int fd) /* of terminal device */
+{
+ DEBUGF(("Resetting tty...\n"));
+
+ if (tcsetattr(fd, TCSANOW, &tty_rmode) < 0)
+ return(-1);
+
+ return(0);
+}
+
+/*
+ * Signal handler to cope with signals so that we can reset the tty
+ * to the orignal settings
+ */
+
+static RETSIGTYPE suspend(int sig)
+{
+ if (tty_reset(ttysl_fd) < 0) {
+ fprintf(stderr,"Can't reset tty \n");
+ exit(1);
+ }
+
+ sys_sigset(sig, SIG_DFL); /* Set signal handler to default */
+ sys_sigrelease(sig); /* Allow 'sig' to come through */
+ kill(getpid(), sig); /* Send ourselves the signal */
+ sys_sigblock(sig); /* Reset to old mask */
+ sys_sigset(sig, suspend); /* Reset signal handler */
+
+ if (tty_set(ttysl_fd) < 0) {
+ fprintf(stderr,"Can't set tty raw \n");
+ exit(1);
+ }
+}
+
+static RETSIGTYPE cont(int sig)
+{
+ if (tty_set(ttysl_fd) < 0) {
+ fprintf(stderr,"Can't set tty raw\n");
+ exit(1);
+ }
+}
+
+static RETSIGTYPE winch(int sig)
+{
+ cols_needs_update = TRUE;
+}
+#endif /* HAVE_TERMCAP */
diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c
new file mode 100644
index 0000000000..d395b68691
--- /dev/null
+++ b/erts/emulator/drivers/unix/unix_efile.c
@@ -0,0 +1,1505 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * Purpose: Provides file and directory operations for Unix.
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include "sys.h"
+#include "erl_driver.h"
+#include "erl_efile.h"
+#include <utime.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SYS_UIO_H
+#include <sys/types.h>
+#include <sys/uio.h>
+#endif
+
+#ifdef _OSE_
+#include "efs.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#ifdef _OSE_SFK_
+#include <string.h>
+#endif
+#endif /* _OSE_ */
+
+#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
+#define DARWIN 1
+#endif
+
+#ifdef DARWIN
+#include <fcntl.h>
+#endif /* DARWIN */
+
+#ifdef VXWORKS
+#include <ioLib.h>
+#include <dosFsLib.h>
+#include <nfsLib.h>
+#include <sys/stat.h>
+/*
+** Not nice to include usrLib.h as MANY normal variable names get reported
+** as shadowing globals, like 'i' for example.
+** Instead we declare the only function we use here
+*/
+/*
+ * #include <usrLib.h>
+ */
+extern STATUS copy(char *, char *);
+#include <errno.h>
+#endif
+
+#ifdef SUNOS4
+# define getcwd(buf, size) getwd(buf)
+#endif
+
+/* Find a definition of MAXIOV, that is used in the code later. */
+#if defined IOV_MAX
+#define MAXIOV IOV_MAX
+#elif defined UIO_MAXIOV
+#define MAXIOV UIO_MAXIOV
+#else
+#define MAXIOV 16
+#endif
+
+
+/*
+ * Macros for testing file types.
+ */
+
+#ifdef _OSE_
+
+#define ISDIR(st) S_ISDIR(((st).st_mode))
+#define ISREG(st) S_ISREG(((st).st_mode))
+#define ISDEV(st) (S_ISCHR(((st).st_mode)) || S_ISBLK(((st).st_mode)))
+#define ISLNK(st) S_ISLNK(((st).st_mode))
+#ifdef NO_UMASK
+#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
+#define DIR_MODE (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
+#else
+#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
+#define DIR_MODE (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | \
+ S_IWOTH | S_IXOTH)
+#endif
+
+#else /* !_OSE_ */
+
+#define ISDIR(st) (((st).st_mode & S_IFMT) == S_IFDIR)
+#define ISREG(st) (((st).st_mode & S_IFMT) == S_IFREG)
+#define ISDEV(st) \
+ (((st).st_mode&S_IFMT) == S_IFCHR || ((st).st_mode&S_IFMT) == S_IFBLK)
+#define ISLNK(st) (((st).st_mode & S_IFLNK) == S_IFLNK)
+#ifdef NO_UMASK
+#define FILE_MODE 0644
+#define DIR_MODE 0755
+#else
+#define FILE_MODE 0666
+#define DIR_MODE 0777
+#endif
+
+#endif /* _OSE_ */
+
+#ifdef VXWORKS /* Currently only used on vxworks */
+
+#define EF_ALLOC(S) driver_alloc((S))
+#define EF_REALLOC(P, S) driver_realloc((P), (S))
+#define EF_SAFE_ALLOC(S) ef_safe_alloc((S))
+#define EF_SAFE_REALLOC(P, S) ef_safe_realloc((P), (S))
+#define EF_FREE(P) do { if((P)) driver_free((P)); } while(0)
+
+extern void erl_exit(int n, char *fmt, _DOTS_);
+
+static void *ef_safe_alloc(Uint s)
+{
+ void *p = EF_ALLOC(s);
+ if (!p) erl_exit(1,
+ "unix efile drv: Can't allocate %d bytes of memory\n",
+ s);
+ return p;
+}
+
+#if 0 /* Currently not used */
+
+static void *ef_safe_realloc(void *op, Uint s)
+{
+ void *p = EF_REALLOC(op, s);
+ if (!p) erl_exit(1,
+ "unix efile drv: Can't reallocate %d bytes of memory\n",
+ s);
+ return p;
+}
+
+#endif /* #if 0 */
+#endif /* #ifdef VXWORKS */
+
+#define IS_DOT_OR_DOTDOT(s) \
+ (s[0] == '.' && (s[1] == '\0' || (s[1] == '.' && s[2] == '\0')))
+
+#ifdef VXWORKS
+static FUNCTION(int, vxworks_to_posix, (int vx_errno));
+#endif
+
+/*
+** VxWorks (not) strikes again. Too long RESULTING paths
+** may give the infamous bus error. Have to check ALL
+** filenames and pathnames. No wonder the emulator is slow on
+** these cards...
+*/
+#ifdef VXWORKS
+#define CHECK_PATHLEN(Name, ErrInfo) \
+ if (path_size(Name) > PATH_MAX) { \
+ errno = ENAMETOOLONG; \
+ return check_error(-1, ErrInfo); \
+ }
+#else
+#define CHECK_PATHLEN(X,Y) /* Nothing */
+#endif
+
+static FUNCTION(int, check_error, (int result, Efile_error* errInfo));
+
+static int
+check_error(int result, Efile_error *errInfo)
+{
+ if (result < 0) {
+#ifdef VXWORKS
+ errInfo->posix_errno = errInfo->os_errno = vxworks_to_posix(errno);
+#else
+ errInfo->posix_errno = errInfo->os_errno = errno;
+#endif
+ return 0;
+ }
+ return 1;
+}
+
+#ifdef VXWORKS
+
+/*
+ * VxWorks has different error codes for different file systems.
+ * We map those to POSIX ones.
+ */
+static int
+vxworks_to_posix(int vx_errno)
+{
+ DEBUGF(("[vxworks_to_posix] vx_errno: %08x\n", vx_errno));
+ switch (vx_errno) {
+ /* dosFsLib mapping */
+#ifdef S_dosFsLib_FILE_ALREADY_EXISTS
+ case S_dosFsLib_FILE_ALREADY_EXISTS: return EEXIST;
+#else
+ case S_dosFsLib_FILE_EXISTS: return EEXIST;
+#endif
+#ifdef S_dosFsLib_BAD_DISK
+ case S_dosFsLib_BAD_DISK: return EIO;
+#endif
+#ifdef S_dosFsLib_CANT_CHANGE_ROOT
+ case S_dosFsLib_CANT_CHANGE_ROOT: return EINVAL;
+#endif
+#ifdef S_dosFsLib_NO_BLOCK_DEVICE
+ case S_dosFsLib_NO_BLOCK_DEVICE: return ENOTBLK;
+#endif
+#ifdef S_dosFsLib_BAD_SEEK
+ case S_dosFsLib_BAD_SEEK: return ESPIPE;
+#endif
+ case S_dosFsLib_VOLUME_NOT_AVAILABLE: return ENXIO;
+ case S_dosFsLib_DISK_FULL: return ENOSPC;
+ case S_dosFsLib_FILE_NOT_FOUND: return ENOENT;
+ case S_dosFsLib_NO_FREE_FILE_DESCRIPTORS: return ENFILE;
+ case S_dosFsLib_INVALID_NUMBER_OF_BYTES: return EINVAL;
+ case S_dosFsLib_ILLEGAL_NAME: return EINVAL;
+ case S_dosFsLib_CANT_DEL_ROOT: return EACCES;
+ case S_dosFsLib_NOT_FILE: return EISDIR;
+ case S_dosFsLib_NOT_DIRECTORY: return ENOTDIR;
+ case S_dosFsLib_NOT_SAME_VOLUME: return EXDEV;
+ case S_dosFsLib_READ_ONLY: return EACCES;
+ case S_dosFsLib_ROOT_DIR_FULL: return ENOSPC;
+ case S_dosFsLib_DIR_NOT_EMPTY: return EEXIST;
+ case S_dosFsLib_NO_LABEL: return ENXIO;
+ case S_dosFsLib_INVALID_PARAMETER: return EINVAL;
+ case S_dosFsLib_NO_CONTIG_SPACE: return ENOSPC;
+ case S_dosFsLib_FD_OBSOLETE: return EBADF;
+ case S_dosFsLib_DELETED: return EINVAL;
+ case S_dosFsLib_INTERNAL_ERROR: return EIO;
+ case S_dosFsLib_WRITE_ONLY: return EACCES;
+ /* nfsLib mapping - is needed since Windriver has used */
+ /* inconsistent error codes (errno.h/nfsLib.h). */
+ case S_nfsLib_NFS_OK: return 0;
+ case S_nfsLib_NFSERR_PERM: return EPERM;
+ case S_nfsLib_NFSERR_NOENT: return ENOENT;
+ case S_nfsLib_NFSERR_IO: return EIO;
+ case S_nfsLib_NFSERR_NXIO: return ENXIO;
+#ifdef S_nfsLib_NFSERR_ACCES
+ case S_nfsLib_NFSERR_ACCES: return EACCES;
+#else
+ case S_nfsLib_NFSERR_ACCESS: return EACCES;
+#endif
+ case S_nfsLib_NFSERR_EXIST: return EEXIST;
+ case S_nfsLib_NFSERR_NODEV: return ENODEV;
+ case S_nfsLib_NFSERR_NOTDIR: return ENOTDIR;
+ case S_nfsLib_NFSERR_ISDIR: return EISDIR;
+ case S_nfsLib_NFSERR_FBIG: return EFBIG;
+ case S_nfsLib_NFSERR_NOSPC: return ENOSPC;
+ case S_nfsLib_NFSERR_ROFS: return EROFS;
+ case S_nfsLib_NFSERR_NAMETOOLONG: return ENAMETOOLONG;
+ case S_nfsLib_NFSERR_NOTEMPTY: return EEXIST;
+ case S_nfsLib_NFSERR_DQUOT: return ENOSPC;
+ case S_nfsLib_NFSERR_STALE: return EINVAL;
+ case S_nfsLib_NFSERR_WFLUSH: return ENXIO;
+ /* And sometimes (...) the error codes are from ioLib (as in the */
+ /* case of the (for nfsLib) unimplemented rename function) */
+ case S_ioLib_DISK_NOT_PRESENT: return EIO;
+#if S_ioLib_DISK_NOT_PRESENT != S_ioLib_NO_DRIVER
+ case S_ioLib_NO_DRIVER: return ENXIO;
+#endif
+ case S_ioLib_UNKNOWN_REQUEST: return ENOSYS;
+ case S_ioLib_DEVICE_TIMEOUT: return EIO;
+#ifdef S_ioLib_UNFORMATED
+ /* Added (VxWorks 5.2 -> 5.3.1) */
+ #if S_ioLib_UNFORMATED != S_ioLib_DEVICE_TIMEOUT
+ case S_ioLib_UNFORMATED: return EIO;
+ #endif
+#endif
+#if S_ioLib_DEVICE_TIMEOUT != S_ioLib_DEVICE_ERROR
+ case S_ioLib_DEVICE_ERROR: return ENXIO;
+#endif
+ case S_ioLib_WRITE_PROTECTED: return EACCES;
+ case S_ioLib_NO_FILENAME: return EINVAL;
+ case S_ioLib_CANCELLED: return EINTR;
+ case S_ioLib_NO_DEVICE_NAME_IN_PATH: return EINVAL;
+ case S_ioLib_NAME_TOO_LONG: return ENAMETOOLONG;
+#ifdef S_objLib_OBJ_UNAVAILABLE
+ case S_objLib_OBJ_UNAVAILABLE: return ENOENT;
+#endif
+
+ /* Temporary workaround for a weird error in passFs
+ (VxWorks Simsparc only). File operation fails because of
+ ENOENT, but errno is not set. */
+#ifdef SIMSPARCSOLARIS
+ case 0: return ENOENT;
+#endif
+
+ }
+ /* If the error code matches none of the above, assume */
+ /* it is a POSIX one already. The upper bits (>=16) are */
+ /* cleared since VxWorks uses those bits to indicate in */
+ /* what module the error occured. */
+ return vx_errno & 0xffff;
+}
+
+static int
+vxworks_enotsup(Efile_error *errInfo)
+{
+ errInfo->posix_errno = errInfo->os_errno = ENOTSUP;
+ return 0;
+}
+
+static int
+count_path_length(char *pathname, char *pathname2)
+{
+ static int stack[PATH_MAX / 2 + 1];
+ int sp = 0;
+ char *tmp;
+ char *cpy = NULL;
+ int i;
+ int sum;
+ for(i = 0;i < 2;++i) {
+ if (!i) {
+ cpy = EF_SAFE_ALLOC(strlen(pathname)+1);
+ strcpy(cpy, pathname);
+ } else if (pathname2 != NULL) {
+ EF_FREE(cpy);
+ cpy = EF_SAFE_ALLOC(strlen(pathname2)+1);
+ strcpy(cpy, pathname2);
+ } else
+ break;
+
+ for (tmp = strtok(cpy,"/"); tmp != NULL; tmp = strtok(NULL,"/")) {
+ if (!strcmp(tmp,"..") && sp > 0)
+ --sp;
+ else if (strcmp(tmp,"."))
+ stack[sp++] = strlen(tmp);
+ }
+ }
+ if (cpy != NULL)
+ EF_FREE(cpy);
+ sum = 0;
+ for(i = 0;i < sp; ++i)
+ sum += stack[i]+1;
+ return (sum) ? sum : 1;
+}
+
+static int
+path_size(char *pathname)
+{
+ static char currdir[PATH_MAX+2];
+ if (*pathname == '/')
+ return count_path_length(pathname,NULL);
+ ioDefPathGet(currdir);
+ strcat(currdir,"/");
+ return count_path_length(currdir,pathname);
+}
+
+#endif /* VXWORKS */
+
+#ifdef _OSE_
+static int
+ose_enotsup(Efile_error *errInfo)
+{
+ errInfo->posix_errno = errInfo->os_errno = ENOTSUP;
+ return 0;
+}
+#endif /* _OSE_ */
+
+int
+efile_mkdir(Efile_error* errInfo, /* Where to return error codes. */
+ char* name) /* Name of directory to create. */
+{
+ CHECK_PATHLEN(name,errInfo);
+#ifdef NO_MKDIR_MODE
+#ifdef VXWORKS
+ /* This is a VxWorks/nfs workaround for erl_tar to create
+ * non-existant directories. (of some reason (...) VxWorks
+ * returns, the *non-module-prefixed*, 0xd code when
+ * trying to create a directory in a directory that doesn't exist).
+ * (see efile_openfile)
+ */
+ if (mkdir(name) < 0) {
+ struct stat sb;
+ if (name[0] == '\0') {
+ /* Return the correct error code enoent */
+ errno = S_nfsLib_NFSERR_NOENT;
+ } else if (stat(name, &sb) == OK) {
+ errno = S_nfsLib_NFSERR_EXIST;
+ } else if((strchr(name, '/') != NULL) && (errno == 0xd)) {
+ /* Return the correct error code enoent */
+ errno = S_nfsLib_NFSERR_NOENT;
+ }
+ return check_error(-1, errInfo);
+ } else return 1;
+#else
+ return check_error(mkdir(name), errInfo);
+#endif
+#else
+ return check_error(mkdir(name, DIR_MODE), errInfo);
+#endif
+}
+
+int
+efile_rmdir(Efile_error* errInfo, /* Where to return error codes. */
+ char* name) /* Name of directory to delete. */
+{
+ CHECK_PATHLEN(name, errInfo);
+ if (rmdir(name) == 0) {
+ return 1;
+ }
+#ifdef VXWORKS
+ if (name[0] == '\0') {
+ /* Return the correct error code enoent */
+ errno = S_nfsLib_NFSERR_NOENT;
+ }
+#else
+ if (errno == ENOTEMPTY) {
+ errno = EEXIST;
+ }
+ if (errno == EEXIST) {
+ int saved_errno = errno;
+ struct stat file_stat;
+ struct stat cwd_stat;
+
+ /*
+ * The error code might be wrong if this is the current directory.
+ */
+
+ if (stat(name, &file_stat) == 0 && stat(".", &cwd_stat) == 0 &&
+ file_stat.st_ino == cwd_stat.st_ino &&
+ file_stat.st_dev == cwd_stat.st_dev) {
+ saved_errno = EINVAL;
+ }
+ errno = saved_errno;
+ }
+#endif
+ return check_error(-1, errInfo);
+}
+
+int
+efile_delete_file(Efile_error* errInfo, /* Where to return error codes. */
+ char* name) /* Name of file to delete. */
+{
+ CHECK_PATHLEN(name,errInfo);
+#ifdef _OSE_
+ if (remove(name) == 0) {
+ return 1;
+ }
+#else
+ if (unlink(name) == 0) {
+ return 1;
+ }
+ if (errno == EISDIR) { /* Linux sets the wrong error code. */
+ errno = EPERM;
+ }
+#endif
+ return check_error(-1, errInfo);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Changes the name of an existing file or directory, from src to dst.
+ * If src and dst refer to the same file or directory, does nothing
+ * and returns success. Otherwise if dst already exists, it will be
+ * deleted and replaced by src subject to the following conditions:
+ * If src is a directory, dst may be an empty directory.
+ * If src is a file, dst may be a file.
+ * In any other situation where dst already exists, the rename will
+ * fail.
+ *
+ * Results:
+ * If the directory was successfully created, returns 1.
+ * Otherwise the return value is 0 and errno is set to
+ * indicate the error. Some possible values for errno are:
+ *
+ * EACCES: src or dst parent directory can't be read and/or written.
+ * EEXIST: dst is a non-empty directory.
+ * EINVAL: src is a root directory or dst is a subdirectory of src.
+ * EISDIR: dst is a directory, but src is not.
+ * ENOENT: src doesn't exist, or src or dst is "".
+ * ENOTDIR: src is a directory, but dst is not.
+ * EXDEV: src and dst are on different filesystems.
+ *
+ * Side effects:
+ * The implementation of rename may allow cross-filesystem renames,
+ * but the caller should be prepared to emulate it with copy and
+ * delete if errno is EXDEV.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+efile_rename(Efile_error* errInfo, /* Where to return error codes. */
+ char* src, /* Original name. */
+ char* dst) /* New name. */
+{
+ CHECK_PATHLEN(src,errInfo);
+ CHECK_PATHLEN(dst,errInfo);
+#ifdef VXWORKS
+
+ /* First check if src == dst, if so, just return. */
+ /* VxWorks dos file system destroys the file otherwise, */
+ /* VxWorks nfs file system rename doesn't work at all. */
+ if(strcmp(src, dst) == 0)
+ return 1;
+#endif
+ if (rename(src, dst) == 0) {
+ return 1;
+ }
+#ifdef VXWORKS
+ /* nfs for VxWorks doesn't support rename. We try to emulate it */
+ /* (by first copying src to dst and then deleting src). */
+ if(errno == S_ioLib_UNKNOWN_REQUEST && /* error code returned
+ by ioLib (!) */
+ copy(src, dst) == OK &&
+ unlink(src) == OK)
+ return 1;
+#endif
+ if (errno == ENOTEMPTY) {
+ errno = EEXIST;
+ }
+#if defined (sparc) && !defined(VXWORKS) && !defined(_OSE_)
+ /*
+ * SunOS 4.1.4 reports overwriting a non-empty directory with a
+ * directory as EINVAL instead of EEXIST (first rule out the correct
+ * EINVAL result code for moving a directory into itself). Must be
+ * conditionally compiled because realpath() is only defined on SunOS.
+ */
+
+ if (errno == EINVAL) {
+ char srcPath[MAXPATHLEN], dstPath[MAXPATHLEN];
+ DIR *dirPtr;
+ struct dirent *dirEntPtr;
+
+#ifdef PURIFY
+ memset(srcPath, '\0', sizeof(srcPath));
+ memset(dstPath, '\0', sizeof(dstPath));
+#endif
+
+ if ((realpath(src, srcPath) != NULL)
+ && (realpath(dst, dstPath) != NULL)
+ && (strncmp(srcPath, dstPath, strlen(srcPath)) != 0)) {
+ dirPtr = opendir(dst);
+ if (dirPtr != NULL) {
+ while ((dirEntPtr = readdir(dirPtr)) != NULL) {
+ if ((strcmp(dirEntPtr->d_name, ".") != 0) &&
+ (strcmp(dirEntPtr->d_name, "..") != 0)) {
+ errno = EEXIST;
+ closedir(dirPtr);
+ return check_error(-1, errInfo);
+ }
+ }
+ closedir(dirPtr);
+ }
+ }
+ errno = EINVAL;
+ }
+#endif /* sparc */
+
+ if (strcmp(src, "/") == 0) {
+ /*
+ * Alpha reports renaming / as EBUSY and Linux reports it as EACCES,
+ * instead of EINVAL.
+ */
+
+ errno = EINVAL;
+ }
+
+ /*
+ * DEC Alpha OSF1 V3.0 returns EACCES when attempting to move a
+ * file across filesystems and the parent directory of that file is
+ * not writable. Most other systems return EXDEV. Does nothing to
+ * correct this behavior.
+ */
+
+ return check_error(-1, errInfo);
+}
+
+int
+efile_chdir(Efile_error* errInfo, /* Where to return error codes. */
+ char* name) /* Name of directory to make current. */
+{
+ CHECK_PATHLEN(name, errInfo);
+ return check_error(chdir(name), errInfo);
+}
+
+
+int
+efile_getdcwd(Efile_error* errInfo, /* Where to return error codes. */
+ int drive, /* 0 - current, 1 - A, 2 - B etc. */
+ char* buffer, /* Where to return the current
+ directory. */
+ size_t size) /* Size of buffer. */
+{
+ if (drive == 0) {
+ if (getcwd(buffer, size) == NULL)
+ return check_error(-1, errInfo);
+
+#ifdef SIMSPARCSOLARIS
+ /* We get "host:" prepended to the dirname - remove!. */
+ {
+ int i = 0;
+ int j = 0;
+ while ((buffer[i] != ':') && (buffer[i] != '\0')) i++;
+ if (buffer[i] == ':') {
+ i++;
+ while ((buffer[j++] = buffer[i++]) != '\0');
+ }
+ }
+#endif
+ return 1;
+ }
+
+ /*
+ * Drives other than 0 is not supported on Unix.
+ */
+
+ errno = ENOTSUP;
+ return check_error(-1, errInfo);
+}
+
+int
+efile_readdir(Efile_error* errInfo, /* Where to return error codes. */
+ char* name, /* Name of directory to open. */
+ EFILE_DIR_HANDLE* p_dir_handle, /* Pointer to directory
+ handle of
+ open directory.*/
+ char* buffer, /* Pointer to buffer for
+ one filename. */
+ size_t size) /* Size of buffer. */
+{
+ DIR *dp; /* Pointer to directory structure. */
+ struct dirent* dirp; /* Pointer to directory entry. */
+
+ /*
+ * If this is the first call, we must open the directory.
+ */
+
+ CHECK_PATHLEN(name, errInfo);
+
+ if (*p_dir_handle == NULL) {
+ dp = opendir(name);
+ if (dp == NULL)
+ return check_error(-1, errInfo);
+ *p_dir_handle = (EFILE_DIR_HANDLE) dp;
+ }
+
+ /*
+ * Retrieve the name of the next file using the directory handle.
+ */
+
+ dp = *((DIR **)((void *)p_dir_handle));
+ for (;;) {
+ dirp = readdir(dp);
+ if (dirp == NULL) {
+ closedir(dp);
+ return 0;
+ }
+ if (IS_DOT_OR_DOTDOT(dirp->d_name))
+ continue;
+ buffer[0] = '\0';
+ strncat(buffer, dirp->d_name, size-1);
+ return 1;
+ }
+}
+
+int
+efile_openfile(Efile_error* errInfo, /* Where to return error codes. */
+ char* name, /* Name of directory to open. */
+ int flags, /* Flags to user for opening. */
+ int* pfd, /* Where to store the file
+ descriptor. */
+ Sint64 *pSize) /* Where to store the size of the
+ file. */
+{
+ struct stat statbuf;
+ int fd;
+ int mode; /* Open mode. */
+#ifdef VXWORKS
+ char pathbuff[PATH_MAX+2];
+ char sbuff[PATH_MAX*2];
+ char *totbuff = sbuff;
+ int nameneed;
+#endif
+
+
+ CHECK_PATHLEN(name, errInfo);
+
+#ifdef VXWORKS
+ /* Have to check that it's not a directory. */
+ if (stat(name,&statbuf) != ERROR && ISDIR(statbuf)) {
+ errno = EISDIR;
+ return check_error(-1, errInfo);
+ }
+#endif
+
+ if (stat(name, &statbuf) >= 0 && !ISREG(statbuf)) {
+#if !defined(VXWORKS) && !defined(OSE)
+ /*
+ * For UNIX only, here is some ugly code to allow
+ * /dev/null to be opened as a file.
+ *
+ * Assumption: The i-node number for /dev/null cannot be zero.
+ */
+ static ino_t dev_null_ino = 0;
+
+ if (dev_null_ino == 0) {
+ struct stat nullstatbuf;
+
+ if (stat("/dev/null", &nullstatbuf) >= 0) {
+ dev_null_ino = nullstatbuf.st_ino;
+ }
+ }
+ if (!(dev_null_ino && statbuf.st_ino == dev_null_ino)) {
+#endif
+ errno = EISDIR;
+ return check_error(-1, errInfo);
+#if !defined(VXWORKS) && !defined(OSE)
+ }
+#endif
+ }
+
+ switch (flags & (EFILE_MODE_READ|EFILE_MODE_WRITE)) {
+ case EFILE_MODE_READ:
+ mode = O_RDONLY;
+ break;
+ case EFILE_MODE_WRITE:
+ if (flags & EFILE_NO_TRUNCATE)
+ mode = O_WRONLY | O_CREAT;
+ else
+ mode = O_WRONLY | O_CREAT | O_TRUNC;
+ break;
+ case EFILE_MODE_READ_WRITE:
+ mode = O_RDWR | O_CREAT;
+ break;
+ default:
+ errno = EINVAL;
+ return check_error(-1, errInfo);
+ }
+
+
+ if (flags & EFILE_MODE_APPEND) {
+ mode &= ~O_TRUNC;
+#ifndef VXWORKS
+ mode |= O_APPEND; /* Dont make VxWorks think things it shouldn't */
+#endif
+ }
+
+
+#ifdef VXWORKS
+ if (*name != '/') {
+ /* Make sure it is an absolute pathname, because ftruncate needs it */
+ ioDefPathGet(pathbuff);
+ strcat(pathbuff,"/");
+ nameneed = strlen(pathbuff) + strlen(name) + 1;
+ if (nameneed > PATH_MAX*2)
+ totbuff = EF_SAFE_ALLOC(nameneed);
+ strcpy(totbuff,pathbuff);
+ strcat(totbuff,name);
+ fd = open(totbuff, mode, FILE_MODE);
+ if (totbuff != sbuff)
+ EF_FREE(totbuff);
+ } else {
+ fd = open(name, mode, FILE_MODE);
+ }
+#else
+ fd = open(name, mode, FILE_MODE);
+#endif
+
+#ifdef VXWORKS
+
+ /* This is a VxWorks/nfs workaround for erl_tar to create
+ * non-existant directories. (of some reason (...) VxWorks
+ * returns, the *non-module-prefixed*, 0xd code when
+ * trying to write a file in a directory that doesn't exist).
+ * (see efile_mkdir)
+ */
+ if ((fd < 0) && (strchr(name, '/') != NULL) && (errno == 0xd)) {
+ /* Return the correct error code enoent */
+ errno = S_nfsLib_NFSERR_NOENT;
+ return check_error(-1, errInfo);
+ }
+#endif
+
+ if (!check_error(fd, errInfo))
+ return 0;
+
+ *pfd = fd;
+ if (pSize) {
+ *pSize = statbuf.st_size;
+ }
+ return 1;
+}
+
+int
+efile_may_openfile(Efile_error* errInfo, char *name) {
+ struct stat statbuf; /* Information about the file */
+ int result;
+
+ result = stat(name, &statbuf);
+ if (!check_error(result, errInfo))
+ return 0;
+ if (!ISREG(statbuf)) {
+ errno = EISDIR;
+ return check_error(-1, errInfo);
+ }
+ return 1;
+}
+
+void
+efile_closefile(int fd)
+{
+ close(fd);
+}
+
+int
+efile_fsync(Efile_error *errInfo, /* Where to return error codes. */
+ int fd) /* File descriptor for file to sync. */
+{
+#ifdef NO_FSYNC
+#ifdef VXWORKS
+ return check_error(ioctl(fd, FIOSYNC, 0), errInfo);
+#else
+ undefined fsync
+#endif /* VXWORKS */
+#else
+#if defined(DARWIN) && defined(F_FULLFSYNC)
+ return check_error(fcntl(fd, F_FULLFSYNC), errInfo);
+#else
+ return check_error(fsync(fd), errInfo);
+#endif /* DARWIN */
+#endif /* NO_FSYNC */
+}
+
+int
+efile_fileinfo(Efile_error* errInfo, Efile_info* pInfo,
+ char* name, int info_for_link)
+{
+ struct stat statbuf; /* Information about the file */
+ struct tm *timep; /* Broken-apart filetime. */
+ int result;
+
+#ifdef VXWORKS
+ if (*name == '\0') {
+ errInfo->posix_errno = errInfo->os_errno = ENOENT;
+ return 0;
+ }
+#endif
+
+ CHECK_PATHLEN(name, errInfo);
+
+ if (info_for_link) {
+#if (defined(VXWORKS) || defined(_OSE_))
+ result = stat(name, &statbuf);
+#else
+ result = lstat(name, &statbuf);
+#endif
+ } else {
+ result = stat(name, &statbuf);
+ }
+ if (!check_error(result, errInfo)) {
+ return 0;
+ }
+
+#if SIZEOF_OFF_T == 4
+ pInfo->size_high = 0;
+#else
+ pInfo->size_high = (Uint32)(statbuf.st_size >> 32);
+#endif
+ pInfo->size_low = (Uint32)statbuf.st_size;
+
+#ifdef NO_ACCESS
+ /* Just look at read/write access for owner. */
+#ifdef VXWORKS
+
+ pInfo->access = FA_NONE;
+ if(statbuf.st_mode & S_IRUSR)
+ pInfo->access |= FA_READ;
+ if(statbuf.st_mode & S_IWUSR)
+ pInfo->access |= FA_WRITE;
+
+#else
+
+ pInfo->access = ((statbuf.st_mode >> 6) & 07) >> 1;
+
+#endif /* VXWORKS */
+#else
+ pInfo->access = FA_NONE;
+ if (access(name, R_OK) == 0)
+ pInfo->access |= FA_READ;
+ if (access(name, W_OK) == 0)
+ pInfo->access |= FA_WRITE;
+
+#endif
+
+ if (ISDEV(statbuf))
+ pInfo->type = FT_DEVICE;
+ else if (ISDIR(statbuf))
+ pInfo->type = FT_DIRECTORY;
+ else if (ISREG(statbuf))
+ pInfo->type = FT_REGULAR;
+ else if (ISLNK(statbuf))
+ pInfo->type = FT_SYMLINK;
+ else
+ pInfo->type = FT_OTHER;
+
+#if defined(HAVE_LOCALTIME_R) || defined(VXWORKS)
+ {
+ /* Use the reentrant version of localtime() */
+ static struct tm local_tm;
+#define localtime(a) (localtime_r((a), &local_tm), &local_tm)
+#endif
+
+
+#define GET_TIME(dst, src) \
+ timep = localtime(&statbuf.src); \
+ (dst).year = timep->tm_year+1900; \
+ (dst).month = timep->tm_mon+1; \
+ (dst).day = timep->tm_mday; \
+ (dst).hour = timep->tm_hour; \
+ (dst).minute = timep->tm_min; \
+ (dst).second = timep->tm_sec
+
+ GET_TIME(pInfo->accessTime, st_atime);
+ GET_TIME(pInfo->modifyTime, st_mtime);
+ GET_TIME(pInfo->cTime, st_ctime);
+
+#undef GET_TIME
+
+#if defined(HAVE_LOCALTIME_R) || defined(VXWORKS)
+ }
+#endif
+
+ pInfo->mode = statbuf.st_mode;
+ pInfo->links = statbuf.st_nlink;
+ pInfo->major_device = statbuf.st_dev;
+#ifdef _OSE_
+ pInfo->minor_device = 0;
+#else
+ pInfo->minor_device = statbuf.st_rdev;
+#endif
+ pInfo->inode = statbuf.st_ino;
+ pInfo->uid = statbuf.st_uid;
+ pInfo->gid = statbuf.st_gid;
+
+ return 1;
+}
+
+int
+efile_write_info(Efile_error *errInfo, Efile_info *pInfo, char *name)
+{
+ CHECK_PATHLEN(name, errInfo);
+
+#ifdef VXWORKS
+
+ if (pInfo->mode != -1) {
+ int fd;
+ struct stat statbuf;
+
+ fd = open(name, O_RDONLY, 0);
+ if (!check_error(fd, errInfo))
+ return 0;
+ if (fstat(fd, &statbuf) < 0) {
+ close(fd);
+ return check_error(-1, errInfo);
+ }
+ if (pInfo->mode & S_IWUSR) {
+ /* clear read only bit */
+ statbuf.st_attrib &= ~DOS_ATTR_RDONLY;
+ } else {
+ /* set read only bit */
+ statbuf.st_attrib |= DOS_ATTR_RDONLY;
+ }
+ /* This should work for dos files but not for nfs ditos, so don't
+ * report errors (to avoid problems when running e.g. erl_tar)
+ */
+ ioctl(fd, FIOATTRIBSET, statbuf.st_attrib);
+ close(fd);
+ }
+#else
+ /*
+ * On some systems chown will always fail for a non-root user unless
+ * POSIX_CHOWN_RESTRICTED is not set. Others will succeed as long as
+ * you don't try to chown a file to someone besides youself.
+ */
+
+#ifndef _OSE_
+ if (chown(name, pInfo->uid, pInfo->gid) && errno != EPERM) {
+ return check_error(-1, errInfo);
+ }
+#endif
+
+ if (pInfo->mode != -1) {
+ mode_t newMode = pInfo->mode & (S_ISUID | S_ISGID |
+ S_IRWXU | S_IRWXG | S_IRWXO);
+ if (chmod(name, newMode)) {
+ newMode &= ~(S_ISUID | S_ISGID);
+ if (chmod(name, newMode)) {
+ return check_error(-1, errInfo);
+ }
+ }
+ }
+
+#endif /* !VXWORKS */
+
+#ifndef _OSE_
+
+ if (pInfo->accessTime.year != -1 && pInfo->modifyTime.year != -1) {
+ struct utimbuf tval;
+ struct tm timebuf;
+
+#define MKTIME(tb, ts) \
+ timebuf.tm_year = ts.year-1900; \
+ timebuf.tm_mon = ts.month-1; \
+ timebuf.tm_mday = ts.day; \
+ timebuf.tm_hour = ts.hour; \
+ timebuf.tm_min = ts.minute; \
+ timebuf.tm_sec = ts.second; \
+ timebuf.tm_isdst = -1; \
+ if ((tb = mktime(&timebuf)) == (time_t) -1) { \
+ errno = EINVAL; \
+ return check_error(-1, errInfo); \
+ }
+
+ MKTIME(tval.actime, pInfo->accessTime);
+ MKTIME(tval.modtime, pInfo->modifyTime);
+#undef MKTIME
+
+#ifdef VXWORKS
+ /* VxWorks' utime doesn't work when the file is a nfs mounted
+ * one, don't report error if utime fails.
+ */
+ utime(name, &tval);
+ return 1;
+#else
+ return check_error(utime(name, &tval), errInfo);
+#endif
+ }
+#endif /* !_OSE_ */
+ return 1;
+}
+
+
+int
+efile_write(Efile_error* errInfo, /* Where to return error codes. */
+ int flags, /* Flags given when file was
+ opened. */
+ int fd, /* File descriptor to write to. */
+ char* buf, /* Buffer to write. */
+ size_t count) /* Number of bytes to write. */
+{
+ ssize_t written; /* Bytes written in last operation. */
+
+#ifdef VXWORKS
+ if (flags & EFILE_MODE_APPEND) {
+ lseek(fd, 0, SEEK_END); /* Naive append emulation on VXWORKS */
+ }
+#endif
+ while (count > 0) {
+ if ((written = write(fd, buf, count)) < 0) {
+ if (errno != EINTR)
+ return check_error(-1, errInfo);
+ else
+ written = 0;
+ }
+ ASSERT(written <= count);
+ buf += written;
+ count -= written;
+ }
+ return 1;
+}
+
+int
+efile_writev(Efile_error* errInfo, /* Where to return error codes */
+ int flags, /* Flags given when file was
+ * opened */
+ int fd, /* File descriptor to write to */
+ SysIOVec* iov, /* Vector of buffer structs.
+ * The structs are unchanged
+ * after the call */
+ int iovcnt, /* Number of structs in vector */
+ size_t size) /* Number of bytes to write */
+{
+ int cnt = 0; /* Buffers so far written */
+ int p = 0; /* Position in next buffer */
+
+ ASSERT(iovcnt >= 0);
+
+#ifdef VXWORKS
+ if (flags & EFILE_MODE_APPEND) {
+ lseek(fd, 0, SEEK_END); /* Naive append emulation on VXWORKS */
+ }
+#endif
+
+ while (cnt < iovcnt) {
+#ifdef HAVE_WRITEV
+ int w; /* Bytes written in this call */
+ int b = iovcnt - cnt; /* Buffers to write */
+ if (b > MAXIOV)
+ b = MAXIOV;
+ if (iov[cnt].iov_base && iov[cnt].iov_len > 0) {
+ if (b == 1) {
+ /* Degenerated io vector */
+ do {
+ w = write(fd, iov[cnt].iov_base + p, iov[cnt].iov_len - p);
+ } while (w < 0 && errno == EINTR);
+ } else {
+ /* Non-empty vector first.
+ * Adjust pos in first buffer in case of
+ * previous incomplete writev */
+ iov[cnt].iov_base += p;
+ iov[cnt].iov_len -= p;
+ do {
+ w = writev(fd, &iov[cnt], b);
+ } while (w < 0 && errno == EINTR);
+ iov[cnt].iov_base -= p;
+ iov[cnt].iov_len += p;
+ }
+ if (w < 0)
+ return check_error(-1, errInfo);
+ } else {
+ /* Empty vector first - skip */
+ cnt++;
+ continue;
+ }
+ ASSERT(w >= 0);
+ /* Move forward to next vector to write */
+ for (; cnt < iovcnt; cnt++) {
+ if (iov[cnt].iov_base && iov[cnt].iov_len > 0) {
+ if (w < iov[cnt].iov_len)
+ break;
+ else
+ w -= iov[cnt].iov_len;
+ }
+ }
+ ASSERT(w >= 0);
+ p = w > 0 ? w : 0; /* Skip p bytes next writev */
+#else /* #ifdef HAVE_WRITEV */
+ if (iov[cnt].iov_base && iov[cnt].iov_len > 0) {
+ /* Non-empty vector */
+ int w; /* Bytes written in this call */
+ while (p < iov[cnt].iov_len) {
+ do {
+ w = write(fd, iov[cnt].iov_base + p, iov[cnt].iov_len - p);
+ } while (w < 0 && errno == EINTR);
+ if (w < 0)
+ return check_error(-1, errInfo);
+ p += w;
+ }
+ }
+ cnt++;
+ p = 0;
+#endif /* #ifdef HAVE_WRITEV */
+ } /* while (cnt< iovcnt) */
+ size = 0; /* Avoid compiler warning */
+ return 1;
+}
+
+int
+efile_read(Efile_error* errInfo, /* Where to return error codes. */
+ int flags, /* Flags given when file was opened. */
+ int fd, /* File descriptor to read from. */
+ char* buf, /* Buffer to read into. */
+ size_t count, /* Number of bytes to read. */
+ size_t *pBytesRead) /* Where to return number of
+ bytes read. */
+{
+ ssize_t n;
+
+ for (;;) {
+ if ((n = read(fd, buf, count)) >= 0)
+ break;
+ else if (errno != EINTR)
+ return check_error(-1, errInfo);
+ }
+ *pBytesRead = (size_t) n;
+ return 1;
+}
+
+
+/* pread() and pwrite() */
+/* Some unix systems, notably Solaris has these syscalls */
+/* It is especially nice for i.e. the dets module to have support */
+/* for this, even if the underlying OS dosn't support it, it is */
+/* reasonably easy to work around by first calling seek, and then */
+/* calling read(). */
+/* This later strategy however changes the file pointer, which pread() */
+/* does not do. We choose to ignore this and say that the location */
+/* of the file pointer is undefined after a call to any of the p functions*/
+
+
+int
+efile_pread(Efile_error* errInfo, /* Where to return error codes. */
+ int fd, /* File descriptor to read from. */
+ Sint64 offset, /* Offset in bytes from BOF. */
+ char* buf, /* Buffer to read into. */
+ size_t count, /* Number of bytes to read. */
+ size_t *pBytesRead) /* Where to return
+ number of bytes read. */
+{
+#if defined(HAVE_PREAD) && defined(HAVE_PWRITE)
+ ssize_t n;
+ off_t off = (off_t) offset;
+ if (off != offset) {
+ errno = EINVAL;
+ return check_error(-1, errInfo);
+ }
+ for (;;) {
+ if ((n = pread(fd, buf, count, offset)) >= 0)
+ break;
+ else if (errno != EINTR)
+ return check_error(-1, errInfo);
+ }
+ *pBytesRead = (size_t) n;
+ return 1;
+#else
+ {
+ int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL);
+ if (res) {
+ return efile_read(errInfo, 0, fd, buf, count, pBytesRead);
+ } else {
+ return res;
+ }
+ }
+#endif
+}
+
+
+
+int
+efile_pwrite(Efile_error* errInfo, /* Where to return error codes. */
+ int fd, /* File descriptor to write to. */
+ char* buf, /* Buffer to write. */
+ size_t count, /* Number of bytes to write. */
+ Sint64 offset) /* where to write it */
+{
+#if defined(HAVE_PREAD) && defined(HAVE_PWRITE)
+ ssize_t written; /* Bytes written in last operation. */
+ off_t off = (off_t) offset;
+ if (off != offset) {
+ errno = EINVAL;
+ return check_error(-1, errInfo);
+ }
+
+ while (count > 0) {
+ if ((written = pwrite(fd, buf, count, offset)) < 0) {
+ if (errno != EINTR)
+ return check_error(-1, errInfo);
+ else
+ written = 0;
+ }
+ ASSERT(written <= count);
+ buf += written;
+ count -= written;
+ offset += written;
+ }
+ return 1;
+#else /* For unix systems that don't support pread() and pwrite() */
+ {
+ int res = efile_seek(errInfo, fd, offset, EFILE_SEEK_SET, NULL);
+
+ if (res) {
+ return efile_write(errInfo, 0, fd, buf, count);
+ } else {
+ return res;
+ }
+ }
+#endif
+}
+
+
+int
+efile_seek(Efile_error* errInfo, /* Where to return error codes. */
+ int fd, /* File descriptor to do the seek on. */
+ Sint64 offset, /* Offset in bytes from the given
+ origin. */
+ int origin, /* Origin of seek (SEEK_SET, SEEK_CUR,
+ SEEK_END). */
+ Sint64 *new_location) /* Resulting new location in file. */
+{
+ off_t off, result;
+
+ switch (origin) {
+ case EFILE_SEEK_SET: origin = SEEK_SET; break;
+ case EFILE_SEEK_CUR: origin = SEEK_CUR; break;
+ case EFILE_SEEK_END: origin = SEEK_END; break;
+ default:
+ errno = EINVAL;
+ return check_error(-1, errInfo);
+ }
+ off = (off_t) offset;
+ if (off != offset) {
+ errno = EINVAL;
+ return check_error(-1, errInfo);
+ }
+
+ errno = 0;
+ result = lseek(fd, off, origin);
+
+ /*
+ * Note that the man page for lseek (on SunOs 5) says:
+ *
+ * "if fildes is a remote file descriptor and offset is
+ * negative, lseek() returns the file pointer even if it is
+ * negative."
+ */
+
+ if (result < 0 && errno == 0)
+ errno = EINVAL;
+ if (result < 0)
+ return check_error(-1, errInfo);
+ if (new_location) {
+ *new_location = result;
+ }
+ return 1;
+}
+
+
+int
+efile_truncate_file(Efile_error* errInfo, int *fd, int flags)
+{
+#ifdef VXWORKS
+ off_t offset;
+ char namebuf[PATH_MAX+1];
+ char namebuf2[PATH_MAX+10];
+ int new;
+ int dummy;
+ int i;
+ int left;
+ static char buff[1024];
+ struct stat st;
+ Efile_error tmperr;
+
+ if ((offset = lseek(*fd, 0, 1)) < 0) {
+ return check_error((int) offset,errInfo);
+ }
+ if (ftruncate(*fd, offset) < 0) {
+ if (vxworks_to_posix(errno) != EINVAL) {
+ return check_error(-1, errInfo);
+ }
+ /*
+ ** Kludge
+ */
+ if(ioctl(*fd,FIOGETNAME,(int) namebuf) < 0) {
+ return check_error(-1, errInfo);
+ }
+ for(i=0;i<1000;++i) {
+ sprintf(namebuf2,"%s%d",namebuf,i);
+ CHECK_PATHLEN(namebuf2,errInfo);
+ if (stat(namebuf2,&st) < 0) {
+ break;
+ }
+ }
+ if (i > 1000) {
+ errno = EINVAL;
+ return check_error(-1, errInfo);
+ }
+ if (close(*fd) < 0) {
+ return check_error(-1, errInfo);
+ }
+ if (efile_rename(&tmperr,namebuf,namebuf2) < 0) {
+ i = check_error(-1,&tmperr);
+ if (!efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE,
+ fd,&dummy)) {
+ *fd = -1;
+ } else {
+ *errInfo = tmperr;
+ }
+ return i;
+ }
+ if ((*fd = open(namebuf2, O_RDONLY, 0)) < 0) {
+ i = check_error(-1,errInfo);
+ efile_rename(&tmperr,namebuf2,namebuf); /* at least try */
+ if (!efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE,
+ fd,&dummy)) {
+ *fd = -1;
+ } else {
+ lseek(*fd,offset,SEEK_SET);
+ }
+ return i;
+ }
+ /* Point of no return... */
+
+ if ((new = open(namebuf,O_RDWR | O_CREAT, FILE_MODE)) < 0) {
+ close(*fd);
+ *fd = -1;
+ return 0;
+ }
+ left = offset;
+
+ while (left) {
+ if ((i = read(*fd,buff,(left > 1024) ? 1024 : left)) < 0) {
+ i = check_error(-1,errInfo);
+ close(new);
+ close(*fd);
+ unlink(namebuf);
+ efile_rename(&tmperr,namebuf2,namebuf); /* at least try */
+ if (!efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE,
+ fd,&dummy)) {
+ *fd = -1;
+ } else {
+ lseek(*fd,offset,SEEK_SET);
+ }
+ return i;
+ }
+ left -= i;
+ if (write(new,buff,i) < 0) {
+ i = check_error(-1,errInfo);
+ close(new);
+ close(*fd);
+ unlink(namebuf);
+ rename(namebuf2,namebuf); /* at least try */
+ if (!efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE,
+ fd,&dummy)) {
+ *fd = -1;
+ } else {
+ lseek(*fd,offset,SEEK_SET);
+ }
+ return i;
+ }
+ }
+ close(*fd);
+ unlink(namebuf2);
+ close(new);
+ i = efile_openfile(errInfo,namebuf,flags | EFILE_NO_TRUNCATE,fd,
+ &dummy);
+ if (i) {
+ lseek(*fd,offset,SEEK_SET);
+ }
+ return i;
+ }
+ return 1;
+#else
+#ifndef NO_FTRUNCATE
+ off_t offset;
+
+ return check_error((offset = lseek(*fd, 0, 1)) >= 0 &&
+ ftruncate(*fd, offset) == 0 ? 1 : -1,
+ errInfo);
+#else
+ return 1;
+#endif
+#endif
+}
+
+int
+efile_readlink(Efile_error* errInfo, char* name, char* buffer, size_t size)
+{
+#ifdef _OSE_
+ return ose_enotsup(errInfo);
+#else
+#ifdef VXWORKS
+ return vxworks_enotsup(errInfo);
+#else
+ int len;
+ ASSERT(size > 0);
+ len = readlink(name, buffer, size-1);
+ if (len == -1) {
+ return check_error(-1, errInfo);
+ }
+ buffer[len] = '\0';
+ return 1;
+#endif
+#endif
+}
+
+int
+efile_altname(Efile_error* errInfo, char* name, char* buffer, size_t size)
+{
+ errno = ENOTSUP;
+ return check_error(-1, errInfo);
+}
+
+int
+efile_link(Efile_error* errInfo, char* old, char* new)
+{
+#ifdef _OSE_
+ return ose_enotsup(errInfo);
+#else
+#ifdef VXWORKS
+ return vxworks_enotsup(errInfo);
+#else
+ return check_error(link(old, new), errInfo);
+#endif
+#endif
+}
+
+int
+efile_symlink(Efile_error* errInfo, char* old, char* new)
+{
+#ifdef _OSE_
+ return ose_enotsup(errInfo);
+#else
+#ifdef VXWORKS
+ return vxworks_enotsup(errInfo);
+#else
+ return check_error(symlink(old, new), errInfo);
+#endif
+#endif
+}