summaryrefslogtreecommitdiff
path: root/apps/interpreters/bas/fs.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/interpreters/bas/fs.c')
-rw-r--r--apps/interpreters/bas/fs.c1823
1 files changed, 1823 insertions, 0 deletions
diff --git a/apps/interpreters/bas/fs.c b/apps/interpreters/bas/fs.c
new file mode 100644
index 000000000..cea05db56
--- /dev/null
+++ b/apps/interpreters/bas/fs.c
@@ -0,0 +1,1823 @@
+/****************************************************************************
+ * apps/interpreters/bas/fs.c
+ * BASIC file system interface.
+ *
+ * Copyright (c) 1999-2014 Michael Haardt
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Adapted to NuttX and re-released under a 3-clause BSD license:
+ *
+ * Copyright (C) 2014 Gregory Nutt. All rights reserved.
+ * Authors: Alan Carvalho de Assis <Alan Carvalho de Assis>
+ * Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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 NuttX 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <math.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "fs.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define LINEWIDTH 80
+#define COLWIDTH 14
+
+#define _(String) String
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct FileStream **file;
+static int capacity;
+static int used;
+static const int open_mode[4] = { 0, O_RDONLY, O_WRONLY, O_RDWR };
+
+const char *FS_errmsg;
+static char FS_errmsgbuf[80];
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static int size(int dev)
+{
+ if (dev >= capacity)
+ {
+ int i;
+ struct FileStream **n;
+
+ n = (struct FileStream **)
+ realloc(file, (dev + 1) * sizeof(struct FileStream *));
+ if (n == (struct FileStream **)0)
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+
+ file = n;
+ for (i = capacity; i <= dev; ++i)
+ {
+ file[i] = (struct FileStream *)0;
+ }
+
+ capacity = dev + 1;
+ }
+
+ return 0;
+}
+
+static int opened(int dev, int mode)
+{
+ int fd = -1;
+
+ if (dev < 0 || dev >= capacity || file[dev] == (struct FileStream *)0)
+ {
+ snprintf(FS_errmsgbuf, sizeof(FS_errmsgbuf), _("channel #%d not open"),
+ dev);
+ FS_errmsg = FS_errmsgbuf;
+ return -1;
+ }
+
+ if (mode == -1)
+ {
+ return 0;
+ }
+
+ switch (mode)
+ {
+ case 0:
+ {
+ fd = file[dev]->outfd;
+ if (fd == -1)
+ {
+ snprintf(FS_errmsgbuf, sizeof(FS_errmsgbuf),
+ _("channel #%d not opened for writing"), dev);
+ }
+ break;
+ }
+
+ case 1:
+ {
+ fd = file[dev]->infd;
+ if (fd == -1)
+ {
+ snprintf(FS_errmsgbuf, sizeof(FS_errmsgbuf),
+ _("channel #%d not opened for reading"), dev);
+ }
+ break;
+ }
+
+ case 2:
+ {
+ fd = file[dev]->randomfd;
+ if (fd == -1)
+ {
+ snprintf(FS_errmsgbuf, sizeof(FS_errmsgbuf),
+ _("channel #%d not opened for random access"), dev);
+ }
+ break;
+ }
+
+ case 3:
+ {
+ fd = file[dev]->binaryfd;
+ if (fd == -1)
+ {
+ snprintf(FS_errmsgbuf, sizeof(FS_errmsgbuf),
+ _("channel #%d not opened for binary access"), dev);
+ }
+ break;
+ }
+
+ case 4:
+ {
+ fd =
+ (file[dev]->randomfd !=
+ -1 ? file[dev]->randomfd : file[dev]->binaryfd);
+ if (fd == -1)
+ {
+ snprintf(FS_errmsgbuf, sizeof(FS_errmsgbuf),
+ _("channel #%d not opened for random or binary access"),
+ dev);
+ }
+ break;
+ }
+
+ default:
+ assert(0);
+ }
+
+ if (fd == -1)
+ {
+ FS_errmsg = FS_errmsgbuf;
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+static int refill(int dev)
+{
+ struct FileStream *f;
+ ssize_t len;
+
+ f = file[dev];
+ f->inSize = 0;
+ len = read(f->infd, f->inBuf, sizeof(f->inBuf));
+ if (len <= 0)
+ {
+ f->inCapacity = 0;
+ FS_errmsg = (len == -1 ? strerror(errno) : (const char *)0);
+ return -1;
+ }
+ else
+ {
+ f->inCapacity = len;
+ return 0;
+ }
+}
+
+static int edit(int chn, int onl)
+{
+ struct FileStream *f = file[chn];
+ char *buf = f->inBuf;
+ char ch;
+ int r;
+
+ for (buf = f->inBuf; buf < (f->inBuf + f->inCapacity); ++buf)
+ {
+ if (*buf >= '\0' && *buf < ' ')
+ {
+ FS_putChar(chn, '^');
+ FS_putChar(chn, *buf ? (*buf + 'a' - 1) : '@');
+ }
+ else
+ {
+ FS_putChar(chn, *buf);
+ }
+ }
+ do
+ {
+ FS_flush(chn);
+ if ((r = read(f->infd, &ch, 1)) == -1)
+ {
+ f->inCapacity = 0;
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+ else if (r == 0 || (f->inCapacity == 0 && ch == 4))
+ {
+ FS_errmsg = (char *)0;
+ return -1;
+ }
+
+#ifdef CONFIG_INTERPREPTER_BAS_VT100
+ /* REVISIT: Use VT100 commands to erase */
+#warning Missing Logic
+#else
+ if ((f->inCapacity + 1) < sizeof(f->inBuf))
+ {
+ /* Ignore carriage returns that may accompany a CRLF sequence.
+ * REVISIT: Some environments may have other line termination rules
+ */
+
+ if (ch != '\r')
+ {
+ /* Is this a new line character */
+
+ if (ch != '\n')
+ {
+ /* No.. escape control characters other than newline and
+ * carriage return
+ */
+
+ if (ch >= '\0' && ch < ' ')
+ {
+ FS_putChar(chn, '^');
+ FS_putChar(chn, ch ? (ch + 'a' - 1) : '@');
+ }
+
+ /* Output normal, printable characters */
+
+ else
+ {
+ FS_putChar(chn, ch);
+ }
+ }
+
+ /* Echo the newline (or not) */
+
+ else if (onl)
+ {
+ FS_putChar(chn, '\n');
+ }
+
+ f->inBuf[f->inCapacity++] = ch;
+ }
+ }
+#endif
+ }
+ while (ch != '\n');
+
+ return 0;
+}
+
+static int cls(int chn)
+{
+#ifdef CONFIG_INTERPREPTER_BAS_VT100
+ /* REVISIT: Use VT100 commands to clear the screen */
+#warning Missing Logic
+#endif
+ FS_errmsg = _("Clear screen operation not implemented");
+ return -1;
+}
+
+static int locate(int chn, int line, int column)
+{
+#ifdef CONFIG_INTERPREPTER_BAS_VT100
+ /* REVISIT: Use VT100 commands to set the cursor position */
+#warning Missing Logic
+#endif
+ FS_errmsg = _("Set cursor position operation not implement");
+ return -1;
+}
+
+static int colour(int chn, int foreground, int background)
+{
+#ifdef CONFIG_INTERPREPTER_BAS_VT100
+ /* REVISIT: Use VT100 commands to color */
+#warning Missing Logic
+#endif
+ FS_errmsg = _("Set color operation no implemented");
+ return -1;
+}
+
+static int resetcolour(int chn)
+{
+#ifdef CONFIG_INTERPREPTER_BAS_VT100
+ /* REVISIT: Use VT100 commands to reset color */
+#warning Missing Logic
+#endif
+ return 0;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int FS_opendev(int chn, int infd, int outfd)
+{
+ if (size(chn) == -1)
+ {
+ return -1;
+ }
+
+ if (file[chn] != (struct FileStream *)0)
+ {
+ FS_errmsg = _("channel already open");
+ return -1;
+ }
+
+ file[chn] = malloc(sizeof(struct FileStream));
+ file[chn]->dev = 1;
+#ifdef CONFIG_SERIAL_TERMIOS
+ file[chn]->tty = (infd == 0 ? isatty(infd) && isatty(outfd) : 0);
+#else
+ file[chn]->tty = 1;
+#endif
+ file[chn]->recLength = 1;
+ file[chn]->infd = infd;
+ file[chn]->inSize = 0;
+ file[chn]->inCapacity = 0;
+ file[chn]->outfd = outfd;
+ file[chn]->outPos = 0;
+ file[chn]->outLineWidth = LINEWIDTH;
+ file[chn]->outColWidth = COLWIDTH;
+ file[chn]->outCapacity = sizeof(file[chn]->outBuf);
+ file[chn]->outSize = 0;
+ file[chn]->outforeground = -1;
+ file[chn]->outbackground = -1;
+ file[chn]->randomfd = -1;
+ file[chn]->binaryfd = -1;
+ FS_errmsg = (const char *)0;
+ ++used;
+ return 0;
+}
+
+int FS_openin(const char *name)
+{
+ int chn, fd;
+
+ if ((fd = open(name, O_RDONLY)) == -1)
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+
+ for (chn = 0; chn < capacity; ++chn)
+ {
+ if (file[chn] == (struct FileStream *)0)
+ {
+ break;
+ }
+ }
+
+ if (size(chn) == -1)
+ {
+ return -1;
+ }
+
+ file[chn] = malloc(sizeof(struct FileStream));
+ file[chn]->recLength = 1;
+ file[chn]->dev = 0;
+ file[chn]->tty = 0;
+ file[chn]->infd = fd;
+ file[chn]->inSize = 0;
+ file[chn]->inCapacity = 0;
+ file[chn]->outfd = -1;
+ file[chn]->randomfd = -1;
+ file[chn]->binaryfd = -1;
+ FS_errmsg = (const char *)0;
+ ++used;
+ return chn;
+}
+
+int FS_openinChn(int chn, const char *name, int mode)
+{
+ int fd;
+ mode_t fl;
+
+ if (size(chn) == -1)
+ {
+ return -1;
+ }
+
+ if (file[chn] != (struct FileStream *)0)
+ {
+ FS_errmsg = _("channel already open");
+ return -1;
+ }
+
+ fl = open_mode[mode];
+
+ /* Serial devices on Linux should be opened non-blocking, otherwise the
+ * open() may block already. Named pipes can not be opened non-blocking in
+ * write-only mode, so first try non-blocking, then blocking. */
+
+ if ((fd = open(name, fl | O_NONBLOCK)) == -1)
+ {
+ if (errno != ENXIO || (fd = open(name, fl)) == -1)
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+ }
+ else if (fcntl(fd, F_SETFL, (long)fl) == -1)
+ {
+ FS_errmsg = strerror(errno);
+ close(fd);
+ return -1;
+ }
+
+ file[chn] = malloc(sizeof(struct FileStream));
+ file[chn]->recLength = 1;
+ file[chn]->dev = 0;
+ file[chn]->tty = 0;
+ file[chn]->infd = fd;
+ file[chn]->inSize = 0;
+ file[chn]->inCapacity = 0;
+ file[chn]->outfd = -1;
+ file[chn]->randomfd = -1;
+ file[chn]->binaryfd = -1;
+ FS_errmsg = (const char *)0;
+ ++used;
+ return chn;
+}
+
+int FS_openout(const char *name)
+{
+ int chn, fd;
+
+ if ((fd = open(name, O_WRONLY | O_TRUNC | O_CREAT, 0666)) == -1)
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+
+ for (chn = 0; chn < capacity; ++chn)
+ {
+ if (file[chn] == (struct FileStream *)0)
+ {
+ break;
+ }
+ }
+
+ if (size(chn) == -1)
+ {
+ return -1;
+ }
+
+ file[chn] = malloc(sizeof(struct FileStream));
+ file[chn]->recLength = 1;
+ file[chn]->dev = 0;
+ file[chn]->tty = 0;
+ file[chn]->infd = -1;
+ file[chn]->outfd = fd;
+ file[chn]->outPos = 0;
+ file[chn]->outLineWidth = LINEWIDTH;
+ file[chn]->outColWidth = COLWIDTH;
+ file[chn]->outSize = 0;
+ file[chn]->outCapacity = sizeof(file[chn]->outBuf);
+ file[chn]->randomfd = -1;
+ file[chn]->binaryfd = -1;
+ FS_errmsg = (const char *)0;
+ ++used;
+ return chn;
+}
+
+int FS_openoutChn(int chn, const char *name, int mode, int append)
+{
+ int fd;
+ mode_t fl;
+
+ if (size(chn) == -1)
+ {
+ return -1;
+ }
+
+ if (file[chn] != (struct FileStream *)0)
+ {
+ FS_errmsg = _("channel already open");
+ return -1;
+ }
+
+ fl = open_mode[mode] | (append ? O_APPEND : 0);
+
+ /* Serial devices on Linux should be opened non-blocking, otherwise the */
+ /* open() may block already. Named pipes can not be opened non-blocking */
+ /* in write-only mode, so first try non-blocking, then blocking. */
+
+ fd = open(name, fl | O_CREAT | (append ? 0 : O_TRUNC) | O_NONBLOCK, 0666);
+ if (fd == -1)
+ {
+ if (errno != ENXIO ||
+ (fd = open(name, fl | O_CREAT | (append ? 0 : O_TRUNC), 0666)) == -1)
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+ }
+ else if (fcntl(fd, F_SETFL, (long)fl) == -1)
+ {
+ FS_errmsg = strerror(errno);
+ close(fd);
+ return -1;
+ }
+
+ file[chn] = malloc(sizeof(struct FileStream));
+ file[chn]->recLength = 1;
+ file[chn]->dev = 0;
+ file[chn]->tty = 0;
+ file[chn]->infd = -1;
+ file[chn]->outfd = fd;
+ file[chn]->outPos = 0;
+ file[chn]->outLineWidth = LINEWIDTH;
+ file[chn]->outColWidth = COLWIDTH;
+ file[chn]->outSize = 0;
+ file[chn]->outCapacity = sizeof(file[chn]->outBuf);
+ file[chn]->randomfd = -1;
+ file[chn]->binaryfd = -1;
+ FS_errmsg = (const char *)0;
+ ++used;
+ return chn;
+}
+
+int FS_openrandomChn(int chn, const char *name, int mode, int recLength)
+{
+ int fd;
+
+ assert(chn >= 0);
+ assert(name != (const char *)0);
+ assert(recLength > 0);
+ if (size(chn) == -1)
+ {
+ return -1;
+ }
+
+ if (file[chn] != (struct FileStream *)0)
+ {
+ FS_errmsg = _("channel already open");
+ return -1;
+ }
+
+ if ((fd = open(name, open_mode[mode] | O_CREAT, 0666)) == -1)
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+
+ file[chn] = malloc(sizeof(struct FileStream));
+ file[chn]->recLength = recLength;
+ file[chn]->dev = 0;
+ file[chn]->tty = 0;
+ file[chn]->infd = -1;
+ file[chn]->outfd = -1;
+ file[chn]->randomfd = fd;
+ file[chn]->recBuf = malloc(recLength);
+ memset(file[chn]->recBuf, 0, recLength);
+ StringField_new(&file[chn]->field);
+ file[chn]->binaryfd = -1;
+ FS_errmsg = (const char *)0;
+ ++used;
+ return chn;
+}
+
+int FS_openbinaryChn(int chn, const char *name, int mode)
+{
+ int fd;
+
+ assert(chn >= 0);
+ assert(name != (const char *)0);
+ if (size(chn) == -1)
+ {
+ return -1;
+ }
+
+ if (file[chn] != (struct FileStream *)0)
+ {
+ FS_errmsg = _("channel already open");
+ return -1;
+ }
+
+ if ((fd = open(name, open_mode[mode] | O_CREAT, 0666)) == -1)
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+ file[chn] = malloc(sizeof(struct FileStream));
+ file[chn]->recLength = 1;
+ file[chn]->dev = 0;
+ file[chn]->tty = 0;
+ file[chn]->infd = -1;
+ file[chn]->outfd = -1;
+ file[chn]->randomfd = -1;
+ file[chn]->binaryfd = fd;
+ FS_errmsg = (const char *)0;
+ ++used;
+ return chn;
+}
+
+int FS_freechn(void)
+{
+ int i;
+
+ for (i = 0; i < capacity && file[i]; ++i);
+ if (size(i) == -1)
+ {
+ return -1;
+ }
+
+ return i;
+}
+
+int FS_flush(int dev)
+{
+ ssize_t written;
+ size_t offset;
+
+ if (file[dev] == (struct FileStream *)0)
+ {
+ FS_errmsg = _("channel not open");
+ return -1;
+ }
+
+ offset = 0;
+ while (offset < file[dev]->outSize)
+ {
+ written =
+ write(file[dev]->outfd, file[dev]->outBuf + offset,
+ file[dev]->outSize - offset);
+ if (written == -1)
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+ else
+ {
+ offset += written;
+ }
+ }
+
+ file[dev]->outSize = 0;
+ FS_errmsg = (const char *)0;
+ return 0;
+}
+
+int FS_close(int dev)
+{
+ if (file[dev] == (struct FileStream *)0)
+ {
+ FS_errmsg = _("channel not open");
+ return -1;
+ }
+
+ if (file[dev]->outfd >= 0)
+ {
+ if (file[dev]->tty &&
+ (file[dev]->outforeground != -1 || file[dev]->outbackground != -1))
+ {
+ resetcolour(dev);
+ }
+
+ FS_flush(dev);
+ close(file[dev]->outfd);
+ }
+
+ if (file[dev]->randomfd >= 0)
+ {
+ StringField_destroy(&file[dev]->field);
+ free(file[dev]->recBuf);
+ close(file[dev]->randomfd);
+ }
+
+ if (file[dev]->binaryfd >= 0)
+ {
+ close(file[dev]->binaryfd);
+ }
+
+ if (file[dev]->infd >= 0)
+ {
+ close(file[dev]->infd);
+ }
+
+ free(file[dev]);
+ file[dev] = (struct FileStream *)0;
+ FS_errmsg = (const char *)0;
+ if (--used == 0)
+ {
+ free(file);
+ capacity = 0;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_SERIAL_TERMIOS
+int FS_istty(int chn)
+{
+ return (file[chn] && file[chn]->tty);
+}
+#endif
+
+int FS_lock(int chn, off_t offset, off_t length, int mode, int w)
+{
+ int fd;
+ struct flock recordLock;
+
+ if (file[chn] == (struct FileStream *)0)
+ {
+ FS_errmsg = _("channel not open");
+ return -1;
+ }
+
+ if ((fd = file[chn]->infd) == -1)
+ {
+ if ((fd = file[chn]->outfd) == -1)
+ {
+ if ((fd = file[chn]->randomfd) == -1)
+ {
+ if ((fd = file[chn]->binaryfd) == -1)
+ assert(0);
+ }
+ }
+ }
+
+ recordLock.l_whence = SEEK_SET;
+ recordLock.l_start = offset;
+ recordLock.l_len = length;
+ switch (mode)
+ {
+ case FS_LOCK_SHARED:
+ recordLock.l_type = F_RDLCK;
+ break;
+
+ case FS_LOCK_EXCLUSIVE:
+ recordLock.l_type = F_WRLCK;
+ break;
+
+ case FS_LOCK_NONE:
+ recordLock.l_type = F_UNLCK;
+ break;
+
+ default:
+ assert(0);
+ }
+
+ if (fcntl(fd, w ? F_SETLKW : F_SETLK, &recordLock) == -1)
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+int FS_truncate(int chn)
+{
+#ifdef CONFIG_INTERPRETER_BAS_HAVE_FTRUNCATE
+ int fd;
+ off_t o;
+
+ if (file[chn] == (struct FileStream *)0)
+ {
+ FS_errmsg = _("channel not open");
+ return -1;
+ }
+
+ if ((fd = file[chn]->infd) == -1)
+ {
+ if ((fd = file[chn]->outfd) == -1)
+ {
+ if ((fd = file[chn]->randomfd) == -1)
+ {
+ if ((fd = file[chn]->binaryfd) == -1)
+ {
+ assert(0);
+ }
+ }
+ }
+ }
+
+ if ((o = lseek(fd, 0, SEEK_CUR)) == (off_t) - 1 || ftruncate(fd, o + 1) == -1)
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+
+ return 0;
+#else
+ FS_errmsg = strerror(ENOSYS);
+ return -1;
+#endif
+}
+
+void FS_shellmode(int dev)
+{
+}
+
+void FS_fsmode(int chn)
+{
+}
+
+void FS_xonxoff(int chn, int on)
+{
+ /* Not implemented */
+}
+
+int FS_put(int chn)
+{
+ ssize_t offset, written;
+
+ if (opened(chn, 2) == -1)
+ {
+ return -1;
+ }
+
+ offset = 0;
+ while (offset < file[chn]->recLength)
+ {
+ written =
+ write(file[chn]->randomfd, file[chn]->recBuf + offset,
+ file[chn]->recLength - offset);
+ if (written == -1)
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+ else
+ {
+ offset += written;
+ }
+ }
+
+ FS_errmsg = (const char *)0;
+ return 0;
+}
+
+int FS_putChar(int dev, char ch)
+{
+ struct FileStream *f;
+
+ if (opened(dev, 0) == -1)
+ {
+ return -1;
+ }
+
+ f = file[dev];
+ if (ch == '\n')
+ {
+ f->outPos = 0;
+ }
+
+ if (ch == '\b' && f->outPos)
+ {
+ --f->outPos;
+ }
+
+ if (f->outSize + 2 >= f->outCapacity && FS_flush(dev) == -1)
+ {
+ return -1;
+ }
+
+ if (f->outLineWidth && f->outPos == f->outLineWidth)
+ {
+ f->outBuf[f->outSize++] = '\n';
+ f->outPos = 0;
+ }
+
+ f->outBuf[f->outSize++] = ch;
+
+ if (ch != '\n' && ch != '\b')
+ {
+ ++f->outPos;
+ }
+
+ FS_errmsg = (const char *)0;
+ return 0;
+}
+
+int FS_putChars(int dev, const char *chars)
+{
+ while (*chars)
+ {
+ if (FS_putChar(dev, *chars++) == -1)
+ {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int FS_putString(int dev, const struct String *s)
+{
+ size_t len = s->length;
+ const char *c = s->character;
+
+ while (len)
+ {
+ if (FS_putChar(dev, *c++) == -1)
+ {
+ return -1;
+ }
+ else
+ {
+ --len;
+ }
+ }
+
+ return 0;
+}
+
+int FS_putItem(int dev, const struct String *s)
+{
+ struct FileStream *f;
+
+ if (opened(dev, 0) == -1)
+ {
+ return -1;
+ }
+
+ f = file[dev];
+ if (f->outPos && f->outPos + s->length > f->outLineWidth)
+ {
+ FS_nextline(dev);
+ }
+
+ return FS_putString(dev, s);
+}
+
+int FS_putbinaryString(int chn, const struct String *s)
+{
+ if (opened(chn, 3) == -1)
+ {
+ return -1;
+ }
+
+ if (s->length &&
+ write(file[chn]->binaryfd, s->character, s->length) != s->length)
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+int FS_putbinaryInteger(int chn, long int x)
+{
+ char s[sizeof(long int)];
+ int i;
+
+ if (opened(chn, 3) == -1)
+ {
+ return -1;
+ }
+
+ for (i = 0; i < sizeof(x); ++i, x >>= 8)
+ {
+ s[i] = (x & 0xff);
+ }
+
+ if (write(file[chn]->binaryfd, s, sizeof(s)) != sizeof(s))
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+int FS_putbinaryReal(int chn, double x)
+{
+ if (opened(chn, 3) == -1)
+ {
+ return -1;
+ }
+
+ if (write(file[chn]->binaryfd, &x, sizeof(x)) != sizeof(x))
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+int FS_getbinaryString(int chn, struct String *s)
+{
+ ssize_t len;
+
+ if (opened(chn, 3) == -1)
+ {
+ return -1;
+ }
+
+ if (s->length &&
+ (len = read(file[chn]->binaryfd, s->character, s->length)) != s->length)
+ {
+ if (len == -1)
+ {
+ FS_errmsg = strerror(errno);
+ }
+ else
+ {
+ FS_errmsg = _("End of file");
+ }
+
+ return -1;
+ }
+
+ return 0;
+}
+
+int FS_getbinaryInteger(int chn, long int *x)
+{
+ char s[sizeof(long int)];
+ int i;
+ ssize_t len;
+
+ if (opened(chn, 3) == -1)
+ {
+ return -1;
+ }
+
+ if ((len = read(file[chn]->binaryfd, s, sizeof(s))) != sizeof(s))
+ {
+ if (len == -1)
+ {
+ FS_errmsg = strerror(errno);
+ }
+ else
+ {
+ FS_errmsg = _("End of file");
+ }
+
+ return -1;
+ }
+
+ *x = (s[sizeof(x) - 1] < 0) ? -1 : 0;
+ for (i = sizeof(s) - 1; i >= 0; --i)
+ {
+ *x = (*x << 8) | (s[i] & 0xff);
+ }
+
+ return 0;
+}
+
+int FS_getbinaryReal(int chn, double *x)
+{
+ ssize_t len;
+
+ if (opened(chn, 3) == -1)
+ {
+ return -1;
+ }
+
+ if ((len = read(file[chn]->binaryfd, x, sizeof(*x))) != sizeof(*x))
+ {
+ if (len == -1)
+ {
+ FS_errmsg = strerror(errno);
+ }
+ else
+ {
+ FS_errmsg = _("End of file");
+ }
+
+ return -1;
+ }
+
+ return 0;
+}
+
+int FS_nextcol(int dev)
+{
+ struct FileStream *f;
+
+ if (opened(dev, 0) == -1)
+ {
+ return -1;
+ }
+
+ f = file[dev];
+ if (f->outPos % f->outColWidth
+ && f->outLineWidth
+ && ((f->outPos / f->outColWidth + 2) * f->outColWidth) > f->outLineWidth)
+ {
+ return FS_putChar(dev, '\n');
+ }
+
+ if (!(f->outPos % f->outColWidth) && FS_putChar(dev, ' ') == -1)
+ {
+ return -1;
+ }
+
+ while (f->outPos % f->outColWidth)
+ {
+ if (FS_putChar(dev, ' ') == -1)
+ {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int FS_nextline(int dev)
+{
+ struct FileStream *f;
+
+ if (opened(dev, 0) == -1)
+ {
+ return -1;
+ }
+
+ f = file[dev];
+ if (f->outPos && FS_putChar(dev, '\n') == -1)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+int FS_tab(int dev, int position)
+{
+ struct FileStream *f = file[dev];
+
+ if (f->outLineWidth && position >= f->outLineWidth)
+ {
+ position = f->outLineWidth - 1;
+ }
+
+ while (f->outPos < (position - 1))
+ {
+ if (FS_putChar(dev, ' ') == -1)
+ {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int FS_width(int dev, int width)
+{
+ if (opened(dev, 0) == -1)
+ {
+ return -1;
+ }
+
+ if (width < 0)
+ {
+ FS_errmsg = _("negative width");
+ return -1;
+ }
+
+ file[dev]->outLineWidth = width;
+ return 0;
+}
+
+int FS_zone(int dev, int zone)
+{
+ if (opened(dev, 0) == -1)
+ {
+ return -1;
+ }
+
+ if (zone <= 0)
+ {
+ FS_errmsg = _("non-positive zone width");
+ return -1;
+ }
+
+ file[dev]->outColWidth = zone;
+ return 0;
+}
+
+int FS_cls(int chn)
+{
+ struct FileStream *f;
+
+ if (opened(chn, 0) == -1)
+ {
+ return -1;
+ }
+
+ f = file[chn];
+ if (!f->tty)
+ {
+ FS_errmsg = _("not a terminal");
+ return -1;
+ }
+
+ if (cls(chn) == -1)
+ {
+ return -1;
+ }
+
+ if (FS_flush(chn) == -1)
+ {
+ return -1;
+ }
+
+ f->outPos = 0;
+ return 0;
+}
+
+int FS_locate(int chn, int line, int column)
+{
+ struct FileStream *f;
+
+ if (opened(chn, 0) == -1)
+ {
+ return -1;
+ }
+
+ f = file[chn];
+ if (!f->tty)
+ {
+ FS_errmsg = _("not a terminal");
+ return -1;
+ }
+
+ if (locate(chn, line, column) == -1)
+ {
+ return -1;
+ }
+
+ if (FS_flush(chn) == -1)
+ {
+ return -1;
+ }
+
+ f->outPos = column - 1;
+ return 0;
+}
+
+int FS_colour(int chn, int foreground, int background)
+{
+ struct FileStream *f;
+
+ if (opened(chn, 0) == -1)
+ {
+ return -1;
+ }
+
+ f = file[chn];
+ if (!f->tty)
+ {
+ FS_errmsg = _("not a terminal");
+ return -1;
+ }
+
+ if (colour(chn, foreground, background) == -1)
+ {
+ return -1;
+ }
+
+ f->outforeground = foreground;
+ f->outbackground = background;
+ return 0;
+}
+
+int FS_getChar(int dev)
+{
+ struct FileStream *f;
+
+ if (opened(dev, 1) == -1)
+ {
+ return -1;
+ }
+
+ f = file[dev];
+ if (f->inSize == f->inCapacity && refill(dev) == -1)
+ {
+ return -1;
+ }
+
+ FS_errmsg = (const char *)0;
+ if (f->inSize + 1 == f->inCapacity)
+ {
+ char ch = f->inBuf[f->inSize];
+
+ f->inSize = f->inCapacity = 0;
+ return ch;
+ }
+ else
+ {
+ return f->inBuf[f->inSize++];
+ }
+}
+
+int FS_get(int chn)
+{
+ ssize_t offset, rd;
+
+ if (opened(chn, 2) == -1)
+ {
+ return -1;
+ }
+
+ offset = 0;
+ while (offset < file[chn]->recLength)
+ {
+ rd =
+ read(file[chn]->randomfd, file[chn]->recBuf + offset,
+ file[chn]->recLength - offset);
+ if (rd == -1)
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+ else
+ {
+ offset += rd;
+ }
+ }
+
+ FS_errmsg = (const char *)0;
+ return 0;
+}
+
+int FS_inkeyChar(int dev, int ms)
+{
+ struct FileStream *f;
+ char c;
+ ssize_t len;
+#ifdef CONFIG_INTERPRETER_BAS_USE_SELECT
+ fd_set just_infd;
+ struct timeval timeout;
+#endif
+
+ if (opened(dev, 1) == -1)
+ {
+ return -1;
+ }
+
+ f = file[dev];
+ if (f->inSize < f->inCapacity)
+ {
+ return f->inBuf[f->inSize++];
+ }
+
+#ifdef CONFIG_INTERPRETER_BAS_USE_SELECT
+ FD_ZERO(&just_infd);
+ FD_SET(f->infd, &just_infd);
+ timeout.tv_sec = ms / 1000;
+ timeout.tv_usec = (ms % 1000) * 1000;
+ switch (select(f->infd + 1, &just_infd, (fd_set *) 0, (fd_set *) 0, &timeout))
+ {
+ case 1:
+ {
+ FS_errmsg = (const char *)0;
+ len = read(f->infd, &c, 1);
+ return (len == 1 ? c : -1);
+ }
+
+ case 0:
+ {
+ FS_errmsg = (const char *)0;
+ return -1;
+ }
+
+ case -1:
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+
+ default:
+ assert(0);
+ }
+
+ return 0;
+
+#else
+ FS_errmsg = (const char *)0;
+ len = read(f->infd, &c, 1);
+
+ if (len == -1)
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+
+ return (len == 1 ? c : -1);
+#endif
+}
+
+void FS_sleep(double s)
+{
+ struct timespec p;
+
+ p.tv_sec = floor(s);
+ p.tv_nsec = 1000000000 * (s - floor(s));
+
+ nanosleep(&p, (struct timespec *)0);
+}
+
+int FS_eof(int chn)
+{
+ struct FileStream *f;
+
+ if (opened(chn, 1) == -1)
+ {
+ return -1;
+ }
+
+ f = file[chn];
+ if (f->inSize == f->inCapacity && refill(chn) == -1)
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+long int FS_loc(int chn)
+{
+ int fd;
+ off_t cur, offset = 0;
+
+ if (opened(chn, -1) == -1)
+ {
+ return -1;
+ }
+
+ if (file[chn]->infd != -1)
+ {
+ fd = file[chn]->infd;
+ offset = -file[chn]->inCapacity + file[chn]->inSize;
+ }
+ else if (file[chn]->outfd != -1)
+ {
+ fd = file[chn]->outfd;
+ offset = file[chn]->outSize;
+ }
+ else if (file[chn]->randomfd != -1)
+ {
+ fd = file[chn]->randomfd;
+ }
+ else
+ {
+ fd = file[chn]->binaryfd;
+ }
+
+ assert(fd != -1);
+ if ((cur = lseek(fd, 0, SEEK_CUR)) == -1)
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+
+ return (cur + offset) / file[chn]->recLength;
+}
+
+long int FS_lof(int chn)
+{
+ off_t curpos;
+ off_t endpos;
+ int fd;
+
+ if (opened(chn, -1) == -1)
+ {
+ return -1;
+ }
+
+ if (file[chn]->infd != -1)
+ {
+ fd = file[chn]->infd;
+ }
+ else if (file[chn]->outfd != -1)
+ {
+ fd = file[chn]->outfd;
+ }
+ else if (file[chn]->randomfd != -1)
+ {
+ fd = file[chn]->randomfd;
+ }
+ else
+ {
+ fd = file[chn]->binaryfd;
+ }
+
+ assert(fd != -1);
+
+ /* Get the size of the file */
+ /* Save the current file position */
+
+ curpos = lseek(fd, 0, SEEK_CUR);
+ if (curpos == (off_t)-1)
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+
+ /* Get the position at the end of the file */
+
+ endpos = lseek(fd, 0, SEEK_END);
+ if (endpos == (off_t)-1)
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+
+ /* Restore the file position */
+
+ curpos = lseek(fd, curpos, SEEK_SET);
+ if (curpos == (off_t)-1)
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+
+ return (long int)(endpos / file[chn]->recLength);
+}
+
+long int FS_recLength(int chn)
+{
+ if (opened(chn, 2) == -1)
+ {
+ return -1;
+ }
+
+ return file[chn]->recLength;
+}
+
+void FS_field(int chn, struct String *s, long int position, long int length)
+{
+ assert(file[chn]);
+ String_joinField(s, &file[chn]->field, file[chn]->recBuf + position, length);
+}
+
+int FS_seek(int chn, long int record)
+{
+ if (opened(chn, 2) != -1)
+ {
+ if (lseek
+ (file[chn]->randomfd, (off_t) record * file[chn]->recLength,
+ SEEK_SET) != -1)
+ {
+ return 0;
+ }
+
+ FS_errmsg = strerror(errno);
+ }
+ else if (opened(chn, 4) != -1)
+ {
+ if (lseek(file[chn]->binaryfd, (off_t) record, SEEK_SET) != -1)
+ {
+ return 0;
+ }
+
+ FS_errmsg = strerror(errno);
+ }
+
+ return -1;
+}
+
+int FS_appendToString(int chn, struct String *s, int onl)
+{
+ size_t new;
+ char *n;
+ struct FileStream *f = file[chn];
+ int c;
+
+ if (f->tty && f->inSize == f->inCapacity)
+ {
+ if (edit(chn, onl) == -1)
+ {
+ return (FS_errmsg ? -1 : 0);
+ }
+ }
+
+ do
+ {
+ n = f->inBuf + f->inSize;
+ while (1)
+ {
+ if (n == f->inBuf + f->inCapacity)
+ {
+ break;
+ }
+
+ c = *n++;
+ if (c == '\n')
+ {
+ break;
+ }
+ }
+
+ new = n - (f->inBuf + f->inSize);
+ if (new)
+ {
+ size_t offset = s->length;
+
+ if (String_size(s, offset + new) == -1)
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+
+ memcpy(s->character + offset, f->inBuf + f->inSize, new);
+ f->inSize += new;
+ if (*(n - 1) == '\n')
+ {
+ if (f->inSize == f->inCapacity)
+ {
+ f->inSize = f->inCapacity = 0;
+ }
+
+ return 0;
+ }
+ }
+
+ if ((c = FS_getChar(chn)) >= 0)
+ {
+ String_appendChar(s, c);
+ }
+
+ if (c == '\n')
+ {
+ if (s->length >= 2 && s->character[s->length - 2] == '\r')
+ {
+ s->character[s->length - 2] = '\n';
+ --s->length;
+ }
+
+ return 0;
+ }
+ }
+ while (c != -1);
+
+ return (FS_errmsg ? -1 : 0);
+}
+
+void FS_closefiles(void)
+{
+ int i;
+
+ for (i = 0; i < capacity; ++i)
+ {
+ if (file[i] && !file[i]->dev)
+ {
+ FS_close(i);
+ }
+ }
+}
+
+int FS_charpos(int chn)
+{
+ if (file[chn] == (struct FileStream *)0)
+ {
+ FS_errmsg = _("channel not open");
+ return -1;
+ }
+
+ return (file[chn]->outPos);
+}
+
+int FS_copy(const char *from, const char *to)
+{
+ int infd, outfd;
+ char buf[4096];
+ ssize_t inlen, outlen = -1;
+
+ if ((infd = open(from, O_RDONLY)) == -1)
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+
+ if ((outfd = open(to, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1)
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+
+ while ((inlen = read(infd, &buf, sizeof(buf))) > 0)
+ {
+ ssize_t off = 0;
+
+ while (inlen && (outlen = write(outfd, &buf + off, inlen)) > 0)
+ {
+ off += outlen;
+ inlen -= outlen;
+ }
+
+ if (outlen == -1)
+ {
+ FS_errmsg = strerror(errno);
+ close(infd);
+ close(outfd);
+ return -1;
+ }
+ }
+
+ if (inlen == -1)
+ {
+ FS_errmsg = strerror(errno);
+ close(infd);
+ close(outfd);
+ return -1;
+ }
+
+ if (close(infd) == -1)
+ {
+ FS_errmsg = strerror(errno);
+ close(outfd);
+ return -1;
+ }
+
+ if (close(outfd) == -1)
+ {
+ FS_errmsg = strerror(errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+int FS_portInput(int address)
+{
+ FS_errmsg = _("Direct port access not available");
+ return -1;
+}
+
+int FS_memInput(int address)
+{
+ FS_errmsg = _("Direct memory access not available");
+ return -1;
+}
+
+int FS_portOutput(int address, int value)
+{
+ FS_errmsg = _("Direct port access not available");
+ return -1;
+}
+
+int FS_memOutput(int address, int value)
+{
+ FS_errmsg = _("Direct memory access not available");
+ return -1;
+}