/**************************************************************************** * apps/examples/interpreters/bas/value.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 * Gregory Nutt * * 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 #include "config.h" #include #include #include #include #include #include #ifdef HAVE_GETTEXT # include # define _(String) gettext(String) #else # define _(String) String #endif #include #include #include #include #include #include #include #ifdef HAVE_TERMCAP_H # include #endif #ifdef HAVE_CURSES_H # include #endif #include #include "fs.h" #ifdef USE_DMALLOC # include "dmalloc.h" #endif /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define LINEWIDTH 80 #define COLWIDTH 14 /**************************************************************************** * Private Data ****************************************************************************/ static struct FileStream **file; static int capacity; static int used; static struct termios origMode, rawMode; static const int open_mode[4] = { 0, O_RDONLY, O_WRONLY, O_RDWR }; #ifdef CONFIG_INTERPRETER_BAS_HAVE_SIGINT static struct sigaction old_sigint; #endif #ifdef CONFIG_INTERPRETER_BAS_HAVE_SIGQUIT static struct sigaction old_sigquit; #endif static int termchannel; const char *FS_errmsg; static char FS_errmsgbuf[80]; volatile int FS_intr; #ifdef HAVE_TGETENT static char *term; static char entrybuf[2048]; static char *cap; static char *cl; static char *cm; static char *ce; static char *cr; static char *md; static char *me; static char *AF; static char *AB; static int Co; static int NC; #endif /**************************************************************************** * 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; } if (ch == rawMode.c_cc[VERASE]) { if (f->inCapacity) { if (f->inBuf[f->inCapacity - 1] >= '\0' && f->inBuf[f->inCapacity - 1] < ' ') { FS_putChars(chn, "\b\b \b\b"); } else { FS_putChars(chn, "\b \b"); } --f->inCapacity; } } else if ((f->inCapacity + 1) < sizeof(f->inBuf)) { if (ch != '\n') { if (ch >= '\0' && ch < ' ') { FS_putChar(chn, '^'); FS_putChar(chn, ch ? (ch + 'a' - 1) : '@'); } else { FS_putChar(chn, ch); } } else if (onl) { FS_putChar(chn, '\n'); } f->inBuf[f->inCapacity++] = ch; } } while (ch != '\n'); return 0; } static int outc(int ch) { struct FileStream *f; if (opened(termchannel, 0) == -1) { return -1; } f = file[termchannel]; if (f->outSize + 1 >= f->outCapacity && FS_flush(termchannel) == -1) { return -1; } f->outBuf[f->outSize++] = ch; FS_errmsg = (const char *)0; return ch; } #ifdef HAVE_TGETENT static int mytputs(const char *str, int affcnt, int (*out) (int)) { # ifdef TPUTS_RETURNS_VOID tputs(str, affcnt, out); return 0; # else return tputs(str, affcnt, out); # endif } static int initTerminal(int chn) { static int init = 0; if (!init) { termchannel = chn; if ((term = getenv("TERM")) == (char *)0) { FS_errmsg = _("environment variable TERM is not set"); return -1; } switch (tgetent(entrybuf, term)) { case -1: { FS_errmsg = _("reading terminal description failed"); return -1; } case 0: { sprintf(FS_errmsgbuf, _("unknown terminal type %s"), term); FS_errmsg = FS_errmsgbuf; return -1; } case 1: { cl = tgetstr("cl", &cap); cm = tgetstr("cm", &cap); ce = tgetstr("ce", &cap); cr = tgetstr("cr", &cap); md = tgetstr("md", &cap); me = tgetstr("me", &cap); AF = tgetstr("AF", &cap); AB = tgetstr("AB", &cap); Co = tgetnum("Co"); if ((NC = tgetnum("NC")) == -1) { NC = 0; } return 0; } } init = 1; } return 0; } static int cls(int chn) { if (cl == (char *)0) { sprintf(FS_errmsgbuf, _("terminal type %s can not clear the screen"), term); FS_errmsg = FS_errmsgbuf; return -1; } if (mytputs(cl, 0, outc) == -1) { return -1; } return 0; } static int locate(int chn, int line, int column) { termchannel = chn; if (cm == (char *)0) { sprintf(FS_errmsgbuf, _("terminal type %s can not position the cursor"), term); FS_errmsg = FS_errmsgbuf; return -1; } if (mytputs(tgoto(cm, column - 1, line - 1), 0, outc) == -1) { return -1; } return 0; } static int colour(int chn, int foreground, int background) { if (AF && AB && Co >= 8) { static int map[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; if (foreground != -1) { if (md && me && !(NC & 32)) { if (foreground > 7 && file[chn]->outforeground <= 7) { if (mytputs(md, 0, outc) == -1) { return -1; } /* all attributes are gone now, need to set background * again */ if (background == -1) { background = file[chn]->outbackground; } } else if (foreground <= 7 && file[chn]->outforeground > 7) { if (mytputs(me, 0, outc) == -1) { return -1; } } } if (mytputs(tgoto(AF, 0, map[foreground & 7]), 0, outc) == -1) { return -1; } } if (background != -1) { if (mytputs(tgoto(AB, 0, map[background & 7]), 0, outc) == -1) { return -1; } } } return 0; } static int resetcolour(int chn) { if (me) { mytputs(me, 0, outc); } if (ce) { mytputs(ce, 0, outc); } return 0; } static void carriage_return(int chn) { if (cr) { mytputs(cr, 0, outc); } else { outc('\r'); } outc('\n'); } #else static int initTerminal(int chn) { termchannel = chn; return 0; } static int cls(int chn) { FS_errmsg = _("This installation does not support terminal handling"); return -1; } static int locate(int chn, int line, int column) { FS_errmsg = _("This installation does not support terminal handling"); return -1; } static int colour(int chn, int foreground, int background) { FS_errmsg = _("This installation does not support terminal handling"); return -1; } static int resetcolour(int chn) { return 0; } static void carriage_return(int chn) { outc('\r'); outc('\n'); } #endif #ifdef CONFIG_INTERPRETER_BAS_HAVE_SIGINT static void sigintr(int sig) { FS_intr = 1; FS_allowIntr(0); } #endif /**************************************************************************** * 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; if ((file[chn]->tty = (infd == 0 ? isatty(infd) && isatty(outfd) : 0))) { if (tcgetattr(infd, &origMode) == -1) { FS_errmsg = strerror(errno); free(file[chn]); file[chn] = (struct FileStream *)0; return -1; } rawMode = origMode; rawMode.c_lflag &= ~(ICANON | ECHO); /* IEXTEN would disable IUCLC, * breaking UC only terminals */ rawMode.c_cc[VMIN] = 1; rawMode.c_cc[VTIME] = 0; rawMode.c_oflag &= ~ONLCR; if (tcsetattr(infd, TCSADRAIN, &rawMode) == -1) { FS_errmsg = strerror(errno); free(file[chn]); file[chn] = (struct FileStream *)0; return -1; } initTerminal(chn); } 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]->tty) { tcsetattr(file[dev]->infd, TCSADRAIN, &origMode); } 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; } int FS_istty(int chn) { return (file[chn] && file[chn]->tty); } 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) { 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; } void FS_shellmode(int dev) { #if defined(CONFIG_INTERPRETER_BAS_HAVE_SIGINT) || defined(CONFIG_INTERPRETER_BAS_HAVE_SIGQUIT) struct sigaction interrupt; #endif if (file[dev]->tty) { tcsetattr(file[dev]->infd, TCSADRAIN, &origMode); } #if defined(CONFIG_INTERPRETER_BAS_HAVE_SIGINT) || defined(CONFIG_INTERPRETER_BAS_HAVE_SIGQUIT) interrupt.sa_flags = 0; sigemptyset(&interrupt.sa_mask); interrupt.sa_handler = SIG_IGN; # ifdef CONFIG_INTERPRETER_BAS_HAVE_SIGINT sigaction(SIGINT, &interrupt, &old_sigint); # endif # ifdef CONFIG_INTERPRETER_BAS_HAVE_SIGQUIT sigaction(SIGQUIT, &interrupt, &old_sigquit); # endif #endif } void FS_fsmode(int chn) { if (file[chn]->tty) { tcsetattr(file[chn]->infd, TCSADRAIN, &rawMode); } #ifdef CONFIG_INTERPRETER_BAS_HAVE_SIGINT sigaction(SIGINT, &old_sigint, (struct sigaction *)0); #endif #ifdef CONFIG_INTERPRETER_BAS_HAVE_SIGQUIT sigaction(SIGQUIT, &old_sigquit, (struct sigaction *)0); #endif } void FS_xonxoff(int chn, int on) { if (file[chn]->tty) { if (on) { rawMode.c_iflag |= (IXON | IXOFF); } else { rawMode.c_iflag &= ~(IXON | IXOFF); } tcsetattr(file[chn]->infd, TCSADRAIN, &rawMode); } } 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) { if (FS_istty(dev)) { carriage_return(dev); } else { f->outBuf[f->outSize++] = '\n'; } f->outPos = 0; } if (FS_istty(dev) && ch == '\n') { carriage_return(dev); } else { 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 USE_SELECT fd_set just_infd; struct timeval timeout; #else struct termios timedread; #endif if (opened(dev, 1) == -1) { return -1; } f = file[dev]; if (f->inSize < f->inCapacity) { return f->inBuf[f->inSize++]; } #ifdef 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 timedread = rawMode; timedread.c_cc[VMIN] = 0; timedread.c_cc[VTIME] = (ms ? ms : 100) / 100; if (tcsetattr(f->infd, TCSADRAIN, &timedread) == -1) { FS_errmsg = strerror(errno); return -1; } FS_errmsg = (const char *)0; len = read(f->infd, &c, 1); tcsetattr(f->infd, TCSADRAIN, &rawMode); if (len == -1) { FS_errmsg = strerror(errno); return -1; } return (len == 1 ? c : -1); #endif } void FS_sleep(double s) { #ifdef HAVE_NANOSLEEP struct timespec p; p.tv_sec = floor(s); p.tv_nsec = 1000000000 * (s - floor(s)); nanosleep(&p, (struct timespec *)0); #else sleep((int)s); #endif } 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) { struct stat buf; 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); if (fstat(fd, &buf) == -1) { FS_errmsg = strerror(errno); return -1; } return buf.st_size / 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; } if (s->length >= 2 && s->character[s->length - 2] == '\r') { s->character[s->length - 2] = '\n'; --s->length; } 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; } void FS_allowIntr(int on) { #ifdef CONFIG_INTERPRETER_BAS_HAVE_SIGINT struct sigaction breakact; breakact.sa_handler = on ? sigintr : SIG_IGN; sigemptyset(&breakact.sa_mask); sigaddset(&breakact.sa_mask, SIGINT); breakact.sa_flags = 0; sigaction(SIGINT, &breakact, (struct sigaction *)0); #endif }