diff --git a/src/frontend/Makefile.am b/src/frontend/Makefile.am index 14ae122ef..8781b8610 100644 --- a/src/frontend/Makefile.am +++ b/src/frontend/Makefile.am @@ -34,6 +34,8 @@ libfte_la_SOURCES = \ com_dump.h \ com_echo.c \ com_echo.h \ + com_fileio.c \ + com_fileio.h \ com_ghelp.c \ com_ghelp.h \ com_gnuplot.h \ diff --git a/src/frontend/com_fileio.c b/src/frontend/com_fileio.c new file mode 100644 index 000000000..078baa2c3 --- /dev/null +++ b/src/frontend/com_fileio.c @@ -0,0 +1,181 @@ +/* Commands for opening and reading arbitrary files. */ + +#include +#include +#include + +#include "ngspice/ngspice.h" + +#include "ngspice/bool.h" +#include "ngspice/wordlist.h" + +#include "com_strcmp.h" +#include "variable.h" + +/* Track open files with these structures, indexed by the underlying + * descriptor. Not many should be needed. + */ + +#define MAX_OPEN_FILES 20 +#define MAX_TEXT_LINE 8192 + +static struct { + FILE *fp; + char *name; +} Open_Files[MAX_OPEN_FILES]; + +/* Check whether error messages should be suppressed. That is useful when + * opening a file to see if it exists. + */ + +static int verbose(void) +{ + return !cp_getvar("silent_fileio", CP_BOOL, NULL, 0); +} + +/* fopen handle file_name [mode] + * + * For example: fopen handle result.txt r + * + * The underlying file descriptor (or -1) is returned in the variable "handle". + */ + +void com_fopen(wordlist *wl) +{ + char *var, *file_name, *mode; + FILE *fp; + int fd; + + var = wl->wl_word; + wl = wl->wl_next; + file_name = cp_unquote(wl->wl_word); + wl = wl->wl_next; + mode = wl ? cp_unquote(wl->wl_word) : "r"; + fp = fopen(file_name, mode); + if (fp) { + fd = fileno(fp); + if (fd < MAX_OPEN_FILES) { + if (Open_Files[fd].fp) // Not expected! + fclose(Open_Files[fd].fp); + if (Open_Files[fd].name) + tfree(Open_Files[fd].name); + Open_Files[fd].fp = fp; + Open_Files[fd].name = copy(file_name); + } else { + fclose(fp); + fprintf(stderr, + "com_fopen() cannot open %s: too many open files\n", + file_name); + fd = -1; + } + } else { + fd = -1; + if (verbose()) { + fprintf(stderr, "com_fopen() cannot open %s: %s\n", + file_name, strerror(errno)); + } + } + tfree(file_name); + if (wl) + tfree(mode); + cp_vset(var, CP_NUM, &fd); +} + +/* Command looks like: + * fread result handle [length] + * where handle is a small positive integer, result names a variable + * and length is the name of a variable used to return the length of the line. + * The returned length is -1 at EOF, -2 on failure. + */ + +void com_fread(wordlist *wl) +{ + char *handle, *result, *lvar; + int fd, length; + char buf[MAX_TEXT_LINE]; + + result = cp_unquote(wl->wl_word); + wl = wl->wl_next; + handle = cp_unquote(wl->wl_word); + fd = atoi(handle); + tfree(handle); + wl = wl->wl_next; + if (wl) + lvar = cp_unquote(wl->wl_word); + else + lvar = NULL; + + if (fd >= 0 && fd < MAX_OPEN_FILES) { + if (!Open_Files[fd].fp) { + /* Allow stdin, for example. */ + + Open_Files[fd].fp = fdopen(fd, "r"); + if (!Open_Files[fd].fp && verbose()) { + fprintf(stderr, "com_fread() cannot open handle %d\n", fd); + goto err; + } + } + + if (fgets(buf, sizeof buf, Open_Files[fd].fp)) { + length = strlen(buf); + if (length > 0 && buf[length - 1] == '\n') { + --length; + if (length > 0 && buf[length - 1] == '\r') { + /* Windows CRLF line termination. */ + + --length; + } + buf[length] = '\0'; + } else if (verbose()) { + fprintf(stderr, + "com_fread() found line in %s " + "too long for buffer\n", + Open_Files[fd].name); + } + } else { + if (feof(Open_Files[fd].fp)) { + length = -1; + } else if (verbose()) { + fprintf(stderr, + "com_fread() error reading %s: %s\n", + Open_Files[fd].name, strerror(errno)); + length = -2; + } + *buf = '\0'; + } + } else if (verbose()) { + fprintf(stderr, + "com_fread(): file handle %d is not in accepted range.\n", + fd); + err: + length = -1; + *buf = '\0'; + } + cp_vset(result, CP_STRING, buf); + tfree(result); + if (lvar) { + cp_vset(lvar, CP_NUM, &length); + tfree(lvar); + } +} + +void com_fclose(wordlist *wl) +{ + char *handle; + int fd; + + handle = cp_unquote(wl->wl_word); + fd = atoi(handle); + tfree(handle); + if (fd <= 2 || fd >= MAX_OPEN_FILES) + return; + if (Open_Files[fd].fp) { + fclose(Open_Files[fd].fp); + Open_Files[fd].fp = NULL; + } + if (Open_Files[fd].name) + tfree(Open_Files[fd].name); +} + + + diff --git a/src/frontend/com_fileio.h b/src/frontend/com_fileio.h new file mode 100644 index 000000000..ddb160555 --- /dev/null +++ b/src/frontend/com_fileio.h @@ -0,0 +1,9 @@ +#ifndef ngspice_COM_FILEIO_H +#define ngspice_COM_FILEIO_H + + +extern void com_fopen(wordlist *wl); +extern void com_fread(wordlist *wl); +extern void com_fclose(wordlist *wl); + +#endif diff --git a/src/frontend/com_strcmp.c b/src/frontend/com_strcmp.c index 014a8025a..4663d59b3 100644 --- a/src/frontend/com_strcmp.c +++ b/src/frontend/com_strcmp.c @@ -23,3 +23,54 @@ com_strcmp(wordlist *wl) tfree(s2); cp_vset(var, CP_NUM, &i); } + +/* These must be more evil still. */ + +void com_strstr(wordlist *wl) +{ + char *var, *s1, *s2; + int i; + + s1 = cp_unquote(wl->wl_next->wl_word); + s2 = cp_unquote(wl->wl_next->wl_next->wl_word); + if (*s2) { + var = strstr(s1, s2); // Search for s2 in s1 + if (var) + i = var - s1; // Offset to match + else + i = -1; + } else { + i = strlen(s1); // Length + } + tfree(s1); + tfree(s2); + cp_vset(wl->wl_word, CP_NUM, &i); +} + +void com_strslice(wordlist *wl) +{ + char *var, *s1, *tp, tmp; + int offset, length, actual; + + var = wl->wl_word; + wl = wl->wl_next; + s1 = cp_unquote(wl->wl_word); + wl = wl->wl_next; + offset = atoi(wl->wl_word); + length = atoi(wl->wl_next->wl_word); + actual = strlen(s1); + if (offset < 0) + offset = actual + offset; + if (length + offset > actual) + length = actual - offset; + if (length > 0 && offset >= 0) { + tp = s1 + offset + length; + tmp = *tp; + *tp = '\0'; + cp_vset(var, CP_STRING, s1 + offset); + *tp = tmp; + } else { + cp_vset(var, CP_STRING, ""); + } + tfree(s1); +} diff --git a/src/frontend/com_strcmp.h b/src/frontend/com_strcmp.h index cdaeaffda..e9a3dd68d 100644 --- a/src/frontend/com_strcmp.h +++ b/src/frontend/com_strcmp.h @@ -1,7 +1,8 @@ #ifndef ngspice_COM_STRCMP_H #define ngspice_COM_STRCMP_H - -void com_strcmp(wordlist *wl); +extern void com_strcmp(wordlist *wl); +extern void com_strstr(wordlist *wl); +extern void com_strslice(wordlist *wl); #endif diff --git a/src/frontend/commands.c b/src/frontend/commands.c index 8e2988c56..36f30f72f 100644 --- a/src/frontend/commands.c +++ b/src/frontend/commands.c @@ -82,6 +82,7 @@ #include "resource.h" #include "diff.h" #include "com_strcmp.h" +#include "com_fileio.h" #include "ngspice/randnumb.h" #include "../spicelib/analysis/com_optran.h" #include "com_wr_ic.h" @@ -613,6 +614,30 @@ struct comm spcp_coms[] = { { 0, 0, 0, 0 }, E_DEFHMASK, 3, 3, NULL, "varname s1 s2 : Set $varname to strcmp(s1, s2)." } , + { "strstr", com_strstr, FALSE, FALSE, + { 0, 0, 0, 0 }, E_DEFHMASK, 3, 3, + NULL, + "varname s1 s2 : Set $varname to strstr(s1, s2)." } , + { "strslice", com_strslice, FALSE, FALSE, + { 0, 0, 0, 0 }, E_DEFHMASK, 4, 4, + NULL, + "varname s1 offset length : " + "Set $varname to s1[offset ... offset+length]" } , + { "fopen", com_fopen, FALSE, FALSE, + { 0, 0, 0, 0 }, E_DEFHMASK, 2, 3, + NULL, + "handle file_name [mode] : " + "Open file_name with mode, return handle in $handle" } , + { "fread", com_fread, FALSE, FALSE, + { 0, 0, 0, 0 }, E_DEFHMASK, 2, 3, + NULL, + "handle result [length] : " + "Read a line from open file handle, " + "data in $result, status in $length" } , + { "fclose", com_fclose, FALSE, FALSE, + { 0, 0, 0, 0 }, E_DEFHMASK, 1, 1, + NULL, + "handle : Close open file" } , { "linearize", com_linearize, FALSE, FALSE, { 040000, 040000, 040000, 040000 }, E_DEFHMASK, 0, LOTS, NULL, @@ -1033,6 +1058,30 @@ struct comm nutcp_coms[] = { { 0, 0, 0, 0 }, E_DEFHMASK, 3, 3, NULL, "varname s1 s2 : Set $varname to strcmp(s1, s2)." } , + { "strstr", com_strstr, FALSE, FALSE, + { 0, 0, 0, 0 }, E_DEFHMASK, 3, 3, + NULL, + "varname s1 s2 : Set $varname to strstr(s1, s2)." } , + { "strslice", com_strslice, FALSE, FALSE, + { 0, 0, 0, 0 }, E_DEFHMASK, 4, 4, + NULL, + "varname s1 offset length : " + "Set $varname to s1[offset ... offset+length]" } , + { "fopen", com_fopen, FALSE, FALSE, + { 0, 0, 0, 0 }, E_DEFHMASK, 2, 3, + NULL, + "handle file_name [mode] : " + "Open file_name with mode, return handle in $handle" } , + { "fread", com_fread, FALSE, FALSE, + { 0, 0, 0, 0 }, E_DEFHMASK, 2, 3, + NULL, + "handle result [length] : " + "Read a line from open file handle, " + "data in $result, status in $length" } , + { "fclose", com_fclose, FALSE, FALSE, + { 0, 0, 0, 0 }, E_DEFHMASK, 1, 1, + NULL, + "handle : Close open file" } , { "linearize", com_linearize, TRUE, FALSE, { 040000, 040000, 040000, 040000 }, E_DEFHMASK, 0, LOTS, NULL, diff --git a/visualc/vngspice.vcxproj b/visualc/vngspice.vcxproj index 279cc36d8..476250aa7 100644 --- a/visualc/vngspice.vcxproj +++ b/visualc/vngspice.vcxproj @@ -885,6 +885,7 @@ + @@ -1498,6 +1499,7 @@ + @@ -2894,4 +2896,4 @@ - \ No newline at end of file +