mirror of
https://github.com/lcn2/calc.git
synced 2025-08-16 01:03:29 +03:00
Changed calc_errno a global int variable so that is may be directly accessed by libcalc users. Further improve help files for help/errno, help/error, help/newerror, help/stoponerror and help/strerror by adding to documentation of the calc error code system as well as libcalc interface where applicable. Changed #define E_USERDEF to #define E__USERDEF. Removed use of E_USERDEF, E__BASE, E__COUNT, and E__HIGHEST from custom/c_sysinfo because the c_sysinfo is just a demo and this will simplify the custom/Makefile. The include file calcerr.h is now the errsym.h include file. The calcerr.tbl has been replaced by errtbl.c and errtbl.h. The calcerr_c.awk, calcerr_c.sed, calcerr_h.awk, and calcerr_h.sed files are now obsolete and have been removed. The calcerr.c and calcerr.h now obsolete and are no longer built. The calc computation error codes, symbols and messages are now in a error_table[] array of struct errtbl. An E_STRING is a string corresponds to an error code #define. For example, the E_STRING for the calc error E_STRCAT, is the string "E_STRING". An E_STRING must now match the regular expression: "^E_[A-Z0-9_]+$". The old array error_table[] of error message strings has been replaced by a new error_table[] array of struct errtbl. The struct errtbl array holds calc errnum error codes, the related E_STRING symbol as a string, and the original related error message. To add new computation error codes, add them near the bottom of the error_table[] array, just before the NULL entry. The ./errcode utility, when run, will verify the consistency of the error_table[] array. The Makefile uses ./errcode -e to generate the contents of help/errorcodes file. The help errorcodes now prints information from the new cstruct errtbl error_table[] array. The help/errorcodes.hdr and help/errorcodes.sed files are now obsolete and have been removed. The Makefile uses ./errcode -d to generate the contents of the errsym.h include file. Updated .gitignore and trailblank to support the above changes.
2854 lines
54 KiB
C
2854 lines
54 KiB
C
/*
|
|
* file - file I/O routines callable by users
|
|
*
|
|
* Copyright (C) 1999-2007,2018,2021-2023 David I. Bell and Landon Curt Noll
|
|
*
|
|
* Primary author: David I. Bell
|
|
*
|
|
* Calc is open software; you can redistribute it and/or modify it under
|
|
* the terms of the version 2.1 of the GNU Lesser General Public License
|
|
* as published by the Free Software Foundation.
|
|
*
|
|
* Calc is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
|
|
* Public License for more details.
|
|
*
|
|
* A copy of version 2.1 of the GNU Lesser General Public License is
|
|
* distributed with calc under the filename COPYING-LGPL. You should have
|
|
* received a copy with calc; if not, write to Free Software Foundation, Inc.
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* Under source code control: 1991/07/20 00:21:56
|
|
* File existed as early as: 1991
|
|
*
|
|
* chongo <was here> /\oo/\ http://www.isthe.com/chongo/
|
|
* Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <stdlib.h>
|
|
#include "have_unistd.h"
|
|
#if defined(HAVE_UNISTD_H)
|
|
# include <unistd.h>
|
|
#endif /* HAVE_UNISTD_H */
|
|
#include <ctype.h>
|
|
#include "calc.h"
|
|
#include "alloc.h"
|
|
#include "longbits.h"
|
|
#include "have_fgetsetpos.h"
|
|
#include "have_fpos_pos.h"
|
|
#include "fposval.h"
|
|
#include "file.h"
|
|
#include "errsym.h"
|
|
#include "strl.h"
|
|
|
|
#if defined(_WIN32) || defined(_WIN64)
|
|
# include <io.h>
|
|
#endif
|
|
|
|
|
|
#include "attribute.h"
|
|
#include "banned.h" /* include after system header <> includes */
|
|
|
|
|
|
#define READSIZE 1024 /* buffer size for reading */
|
|
|
|
#define MIN(a,b) (((a) <= (b)) ? (a) : (b))
|
|
|
|
/*
|
|
* external STDIO functions
|
|
*/
|
|
E_FUNC void math_setfp(FILE *fp);
|
|
E_FUNC FILE *f_open(char *name, char *mode);
|
|
|
|
|
|
/*
|
|
* Table of opened files.
|
|
* The first three entries always correspond to stdin, stdout, and stderr,
|
|
* and cannot be closed. Their file ids are always 0, 1, and 2.
|
|
*/
|
|
STATIC FILEIO files[MAXFILES] = {
|
|
{FILEID_STDIN, NULL, (dev_t)0, (ino_t)0,
|
|
"(stdin)", true, false, false, false, 'r', "r"},
|
|
{FILEID_STDOUT, NULL, (dev_t)0, (ino_t)0,
|
|
"(stdout)", false, true, false, false, 'w', "w"},
|
|
{FILEID_STDERR, NULL, (dev_t)0, (ino_t)0,
|
|
"(stderr)", false, true, false, false, 'w', "w"}
|
|
};
|
|
|
|
|
|
STATIC int ioindex[MAXFILES] = {0,1,2}; /* Indices for FILEIO table */
|
|
STATIC FILEID lastid = FILEID_STDERR; /* Last allocated file id */
|
|
STATIC int idnum = 3; /* Number of allocated file ids */
|
|
|
|
|
|
/* forward static declarations */
|
|
S_FUNC ZVALUE filepos2z(FILEPOS pos);
|
|
S_FUNC FILEPOS z2filepos(ZVALUE pos);
|
|
S_FUNC int set_open_pos(FILE *fp, ZVALUE zpos);
|
|
S_FUNC int get_open_pos(FILE *fp, ZVALUE *res);
|
|
S_FUNC ZVALUE off_t2z(off_t siz);
|
|
S_FUNC ZVALUE dev2z(dev_t dev);
|
|
S_FUNC ZVALUE inode2z(ino_t inode);
|
|
S_FUNC void getscanfield(FILE *fp, bool skip, unsigned int width,
|
|
int scannum, char *scanptr, char **strptr);
|
|
S_FUNC void getscanwhite(FILE *fp, bool skip, unsigned int width,
|
|
int scannum, char **strptr);
|
|
S_FUNC int fscanfile(FILE *fp, char *fmt, int count, VALUE **vals);
|
|
S_FUNC void freadnum(FILE *fp, VALUE *valptr);
|
|
S_FUNC void freadsum(FILE *fp, VALUE *valptr);
|
|
S_FUNC void freadprod(FILE *fp, VALUE *valptr);
|
|
S_FUNC void fskipnum(FILE *fp);
|
|
|
|
|
|
/*
|
|
* file_init - perform needed initialization work
|
|
*
|
|
* On some systems, one cannot initialize a pointer to a FILE *.
|
|
* This routine, called once at startup is a work-a-round for
|
|
* systems with such bogons.
|
|
*
|
|
* We will also probe for any open files beyond stderr and set them up.
|
|
*/
|
|
void
|
|
file_init(void)
|
|
{
|
|
STATIC int done = 0; /* 1 => routine already called */
|
|
struct stat sbuf; /* file status */
|
|
FILEIO *fiop;
|
|
FILE *fp;
|
|
int i;
|
|
|
|
if (!done) {
|
|
/*
|
|
* setup the default set
|
|
*/
|
|
files[0].fp = stdin;
|
|
files[1].fp = stdout;
|
|
files[2].fp = stderr;
|
|
for (i = 0; i < 3; ++i) {
|
|
if (fstat(i, &sbuf) >= 0) {
|
|
files[i].dev = sbuf.st_dev;
|
|
files[i].inode = sbuf.st_ino;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* note any other files that we can find
|
|
*/
|
|
fiop = &files[3];
|
|
for (i = 3; i < MAXFILES; fiop++, ++i) {
|
|
char *tname;
|
|
|
|
fiop->name = NULL;
|
|
files[idnum].reading = true;
|
|
files[idnum].writing = true;
|
|
files[idnum].action = 0;
|
|
memset(files[idnum].mode, 0, MODE_LEN+1);
|
|
/*
|
|
* stat the descriptor to see what we have
|
|
*/
|
|
if (fstat(i, &sbuf) >= 0) {
|
|
size_t snprintf_len; /* malloced snprintf length */
|
|
fp = (FILE *) fdopen(i,"r+"); /*guess mode*/
|
|
if (fp) {
|
|
strlcpy(files[idnum].mode, "r+",
|
|
sizeof(files[idnum].mode));
|
|
} else {
|
|
fp = (FILE *) fdopen(i, "r");
|
|
if (fp) {
|
|
strlcpy(files[idnum].mode, "r",
|
|
sizeof(files[idnum].mode));
|
|
files[idnum].writing = false;
|
|
} else {
|
|
fp = (FILE *) fdopen(i, "w");
|
|
if (fp) {
|
|
strlcpy(files[idnum].mode, "w",
|
|
sizeof(files[idnum].mode));
|
|
files[idnum].reading = false;
|
|
}
|
|
else
|
|
continue;
|
|
}
|
|
}
|
|
snprintf_len =
|
|
sizeof("descriptor[12345678901234567890]") + 1;
|
|
tname = (char *)malloc(snprintf_len+1);
|
|
if (tname == NULL) {
|
|
math_error("Out of memory for init_file");
|
|
not_reached();
|
|
}
|
|
snprintf(tname, snprintf_len, "descriptor[%d]", i);
|
|
tname[snprintf_len] = '\0'; /* paranoia */
|
|
files[idnum].name = tname;
|
|
files[idnum].id = idnum;
|
|
files[idnum].fp = fp;
|
|
files[idnum].dev = sbuf.st_dev;
|
|
files[idnum].inode = sbuf.st_ino;
|
|
ioindex[idnum] = idnum;
|
|
idnum++;
|
|
lastid++;
|
|
}
|
|
}
|
|
|
|
done = 1;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* init_fileio - initialize a FILEIO structure
|
|
*
|
|
* This function initializes a calc FILEIO structure. It will optionally
|
|
* malloc the filename string if given an non-NULL associated filename.
|
|
* It will canonicalize the file open mode string.
|
|
*
|
|
* given:
|
|
* fiop pointer to FILEIO structure to initialize
|
|
* name associated filename (NULL => caller will setup filename)
|
|
* mode open mode (one of {r,w,a}{,b}{,+})
|
|
* sbufp pointer to stat of open file
|
|
* id calc file ID
|
|
* fp open file stream
|
|
*/
|
|
S_FUNC void
|
|
init_fileio(FILEIO *fiop, char *name, char *mode,
|
|
struct stat *sbufp, FILEID id, FILE *fp)
|
|
{
|
|
char modestr[MODE_LEN+1]; /* mode [rwa]b?\+? */
|
|
size_t namelen; /* length of name */
|
|
|
|
/* clear modestr */
|
|
memset(modestr, 0, sizeof(modestr));
|
|
|
|
/* allocate filename if requested */
|
|
namelen = 0;
|
|
if (name != NULL) {
|
|
namelen = strlen(name);
|
|
fiop->name = (char *)malloc(namelen + 1);
|
|
if (fiop->name == NULL) {
|
|
math_error("No memory for filename");
|
|
not_reached();
|
|
}
|
|
}
|
|
|
|
/* initialize FILEIO structure */
|
|
if (name != NULL) {
|
|
strlcpy(fiop->name, name, namelen+1);
|
|
}
|
|
fiop->id = id;
|
|
fiop->fp = fp;
|
|
fiop->dev = sbufp->st_dev;
|
|
fiop->inode = sbufp->st_ino;
|
|
fiop->reading = false;
|
|
fiop->writing = false;
|
|
fiop->appending = false;
|
|
fiop->binary = false;
|
|
fiop->action = 0;
|
|
memset(fiop->mode, 0, sizeof(fiop->mode));
|
|
|
|
/*
|
|
* determine file open mode
|
|
*
|
|
* While a leading 'r' is for reading and a leading 'w' is
|
|
* for writing, the presence of a '+' in the string means
|
|
* both reading and writing. A leading 'a' means append
|
|
* which is writing.
|
|
*/
|
|
/* canonicalize read modes */
|
|
if (mode[0] == 'r') {
|
|
|
|
/* note read mode */
|
|
strlcpy(modestr, "r", sizeof(modestr));
|
|
fiop->reading = true;
|
|
|
|
/* note binary mode even though mode is not used / ignored */
|
|
if (strchr(mode, 'b') != NULL) {
|
|
strlcat(modestr, "b", sizeof(modestr));
|
|
}
|
|
|
|
/* note if reading and writing */
|
|
if (strchr(mode, '+') != NULL) {
|
|
fiop->writing = true;
|
|
strlcat(modestr, "+", sizeof(modestr));
|
|
}
|
|
|
|
/* canonicalize write modes */
|
|
} else if (mode[0] == 'w') {
|
|
|
|
/* note write mode */
|
|
strlcpy(modestr, "w", sizeof(modestr));
|
|
fiop->writing = true;
|
|
|
|
/* note binary mode even though mode is not used / ignored */
|
|
if (strchr(mode, 'b') != NULL) {
|
|
strlcat(modestr, "b", sizeof(modestr));
|
|
}
|
|
|
|
/* note if reading and writing */
|
|
if (strchr(mode, '+') != NULL) {
|
|
fiop->reading = true;
|
|
strlcat(modestr, "+", sizeof(modestr));
|
|
}
|
|
|
|
/* canonicalize append modes */
|
|
} else if (mode[0] == 'a') {
|
|
|
|
/* note append mode */
|
|
strlcpy(modestr, "a", sizeof(modestr));
|
|
fiop->writing = true;
|
|
fiop->appending = true;
|
|
|
|
/* note binary mode even though mode is not used / ignored */
|
|
if (strchr(mode, 'b') != NULL) {
|
|
strlcat(modestr, "b", sizeof(modestr));
|
|
}
|
|
|
|
/* note if reading and writing */
|
|
if (strchr(mode, '+') != NULL) {
|
|
fiop->reading = true;
|
|
strlcat(modestr, "+", sizeof(modestr));
|
|
}
|
|
|
|
/* canonicalize no I/O modes */
|
|
} else {
|
|
modestr[0] = '\0';
|
|
}
|
|
modestr[MODE_LEN] = '\0'; /* firewall */
|
|
|
|
/* record canonical open mode string */
|
|
strlcpy(fiop->mode, modestr, sizeof(fiop->mode));
|
|
}
|
|
|
|
|
|
/*
|
|
* openid - open the specified file name for reading or writing
|
|
*
|
|
* given:
|
|
* name file name
|
|
* mode open mode (one of {r,w,a}{,b}{,+})
|
|
*
|
|
* returns:
|
|
* >=3 FILEID which can be used to do I/O to the file
|
|
* <0 if the open failed
|
|
*
|
|
* NOTE: This function will not return 0, 1 or 2 since they are
|
|
* reserved for stdin, stdout, stderr. In fact, it must not
|
|
* return 0, 1, or 2 because it will confuse those who call
|
|
* the opensearchfile() function
|
|
*/
|
|
FILEID
|
|
openid(char *name, char *mode)
|
|
{
|
|
FILEIO *fiop; /* file structure */
|
|
FILEID id; /* new file id */
|
|
FILE *fp;
|
|
struct stat sbuf; /* file status */
|
|
int i;
|
|
|
|
/* find the next open slot in the files array */
|
|
if (idnum >= MAXFILES)
|
|
return -E_MANYOPEN;
|
|
fiop = &files[3];
|
|
for (i = 3; i < MAXFILES; fiop++,i++) {
|
|
if (fiop->name == NULL)
|
|
break;
|
|
}
|
|
if (i == MAXFILES)
|
|
math_error("This should not happen in openid()!!!");
|
|
|
|
/* open the file */
|
|
fp = f_open(name, mode);
|
|
if (fp == NULL) {
|
|
return FILEID_NONE;
|
|
}
|
|
if (fstat(fileno(fp), &sbuf) < 0) {
|
|
math_error("bad fstat");
|
|
not_reached();
|
|
}
|
|
|
|
/* get a new FILEID */
|
|
id = ++lastid;
|
|
ioindex[idnum++] = i;
|
|
|
|
/* initialize FILEIO structure */
|
|
init_fileio(fiop, name, mode, &sbuf, id, fp);
|
|
|
|
/* return calc open file ID */
|
|
return id;
|
|
}
|
|
|
|
|
|
/*
|
|
* openpathid - open the specified base filename, or
|
|
* relative filename along a search path
|
|
*
|
|
* given:
|
|
* name file name
|
|
* mode open mode (one of {r,w,a}{,b}{,+})
|
|
* pathlist list of colon separated paths (or NULL)
|
|
*
|
|
* returns:
|
|
* >=3 FILEID which can be used to do I/O to the file
|
|
* <0 if the open failed
|
|
*
|
|
* NOTE: This function will not return 0, 1 or 2 since they are
|
|
* reserved for stdin, stdout, stderr. In fact, it must not
|
|
* return 0, 1, or 2 because it will confuse those who call
|
|
* the opensearchfile() function
|
|
*/
|
|
FILEID
|
|
openpathid(char *name, char *mode, char *pathlist)
|
|
{
|
|
FILEIO *fiop; /* file structure */
|
|
FILEID id; /* new file id */
|
|
FILE *fp;
|
|
struct stat sbuf; /* file status */
|
|
char *openpath; /* malloc copy of path that was opened */
|
|
int i;
|
|
|
|
/* find the next open slot in the files array */
|
|
if (idnum >= MAXFILES)
|
|
return -E_MANYOPEN;
|
|
fiop = &files[3];
|
|
for (i = 3; i < MAXFILES; fiop++,i++) {
|
|
if (fiop->name == NULL)
|
|
break;
|
|
}
|
|
if (i == MAXFILES)
|
|
math_error("This should not happen in openpathid()!!!");
|
|
|
|
/* open a file - searching along a path */
|
|
openpath = NULL;
|
|
fp = f_pathopen(name, mode, pathlist, &openpath);
|
|
if (fp == NULL) {
|
|
if (openpath != NULL) {
|
|
/* should not happen, but just in case */
|
|
free(openpath);
|
|
}
|
|
return FILEID_NONE;
|
|
}
|
|
if (fstat(fileno(fp), &sbuf) < 0) {
|
|
if (openpath != NULL) {
|
|
free(openpath);
|
|
}
|
|
math_error("bad fstat");
|
|
not_reached();
|
|
}
|
|
if (openpath == NULL) {
|
|
fclose(fp);
|
|
math_error("bad openpath");
|
|
not_reached();
|
|
}
|
|
|
|
/* get a new FILEID */
|
|
id = ++lastid;
|
|
ioindex[idnum++] = i;
|
|
|
|
/* initialize FILEIO structure */
|
|
init_fileio(fiop, NULL, mode, &sbuf, id, fp);
|
|
fiop->name = openpath; /* already malloced by f_pathopen */
|
|
|
|
/* return calc open file ID */
|
|
return id;
|
|
}
|
|
|
|
|
|
/*
|
|
* reopenid - reopen a FILEID
|
|
*
|
|
* given:
|
|
* id FILEID to reopen
|
|
* mode new mode to open as
|
|
* mode new mode to open as (one of "r", "w", "a", "r+", "w+", "a+")
|
|
* name name of new file
|
|
*
|
|
* returns:
|
|
* FILEID which can be used to do I/O to the file
|
|
* <0 if the open failed
|
|
*/
|
|
FILEID
|
|
reopenid(FILEID id, char *mode, char *name)
|
|
{
|
|
FILEIO *fiop; /* file structure */
|
|
FILE *fp;
|
|
struct stat sbuf;
|
|
int i;
|
|
|
|
/* firewall */
|
|
if ((id == FILEID_STDIN) || (id == FILEID_STDOUT) ||
|
|
(id == FILEID_STDERR)) {
|
|
math_error("Cannot freopen stdin, stdout, or stderr");
|
|
not_reached();
|
|
}
|
|
|
|
/* reopen the file */
|
|
fiop = NULL;
|
|
for (i = 3; i < idnum; i++) {
|
|
fiop = &files[ioindex[i]];
|
|
if (fiop->id == id)
|
|
break;
|
|
}
|
|
if (i == idnum) {
|
|
if (name == NULL) {
|
|
fprintf(stderr, "File not open, need file name\n");
|
|
return FILEID_NONE;
|
|
}
|
|
if (idnum >= MAXFILES) {
|
|
fprintf(stderr, "Too many open files\n");
|
|
return FILEID_NONE;
|
|
}
|
|
for (fiop = &files[3], i = 3; i < MAXFILES; fiop++, i++) {
|
|
if (fiop->name == NULL)
|
|
break;
|
|
}
|
|
if (i >= MAXFILES) {
|
|
math_error("This should not happen in reopenid");
|
|
not_reached();
|
|
}
|
|
fp = f_open(name, mode);
|
|
if (fp == NULL) {
|
|
fprintf(stderr, "Cannot open file\n");
|
|
return FILEID_NONE;
|
|
}
|
|
ioindex[idnum++] = i;
|
|
fiop->id = id;
|
|
} else {
|
|
if (name == NULL)
|
|
fp = freopen(fiop->name, mode, fiop->fp);
|
|
else
|
|
fp = freopen(name, mode, fiop->fp);
|
|
if (fp == NULL) {
|
|
free(fiop->name);
|
|
fiop->name = NULL;
|
|
idnum--;
|
|
for (; i < idnum; i++)
|
|
ioindex[i] = ioindex[i + 1];
|
|
return FILEID_NONE;
|
|
}
|
|
}
|
|
if (fstat(fileno(fp), &sbuf) < 0) {
|
|
math_error("bad fstat");
|
|
not_reached();
|
|
}
|
|
|
|
/* initialize FILEIO structure */
|
|
if (name == NULL) {
|
|
if (fiop->name == NULL) {
|
|
math_error("old and new reopen filenames are NULL");
|
|
}
|
|
} else if (fiop->name != NULL) {
|
|
free(fiop->name);
|
|
fiop->name = NULL;
|
|
}
|
|
init_fileio(fiop, name, mode, &sbuf, id, fp);
|
|
|
|
/* return calc open file ID */
|
|
return id;
|
|
}
|
|
|
|
|
|
/*
|
|
* Find the file I/O structure for the specified file id, and verifies that
|
|
* it is opened in the required manner (0 for reading or 1 for writing).
|
|
* If writable is -1, then no open checks are made at all and NULL is then
|
|
* returned if the id represents a closed file.
|
|
*/
|
|
FILEIO *
|
|
findid(FILEID id, int writable)
|
|
{
|
|
FILEIO *fiop; /* file structure */
|
|
int i;
|
|
|
|
fiop = NULL;
|
|
|
|
if ((id < 0) || (id > lastid))
|
|
return NULL;
|
|
|
|
for (i = 0; i < idnum; i++) {
|
|
fiop = &files[ioindex[i]];
|
|
if (fiop->id == id)
|
|
break;
|
|
}
|
|
|
|
if (i == idnum)
|
|
return NULL;
|
|
|
|
if (writable >= 0) {
|
|
if ((writable && !fiop->writing) ||
|
|
(!writable && !fiop->reading)) {
|
|
return NULL;
|
|
}
|
|
}
|
|
return fiop;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return whether or not a file id is valid. This is used for if tests.
|
|
*/
|
|
bool
|
|
validid(FILEID id)
|
|
{
|
|
return (findid(id, -1) != NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the file with id = index if this is the id of a file that has been
|
|
* opened (it may have since been closed). Otherwise returns FILEID_NONE.
|
|
*/
|
|
FILEID
|
|
indexid(long index)
|
|
{
|
|
FILEID id;
|
|
|
|
id = (FILEID) index;
|
|
|
|
if ((index < 0) || (id > lastid))
|
|
return FILEID_NONE;
|
|
return id;
|
|
}
|
|
|
|
|
|
/*
|
|
* Close the specified file id. Returns true if there was an error.
|
|
* Closing of stdin, stdout, or stderr is illegal, but closing of already
|
|
* closed files is allowed.
|
|
*/
|
|
int
|
|
closeid(FILEID id)
|
|
{
|
|
FILEIO *fiop; /* file structure */
|
|
int i;
|
|
int err;
|
|
|
|
fiop = NULL;
|
|
|
|
/* firewall */
|
|
if ((id == FILEID_STDIN) || (id == FILEID_STDOUT) ||
|
|
(id == FILEID_STDERR)) {
|
|
math_error("Cannot close stdin, stdout, or stderr");
|
|
not_reached();
|
|
}
|
|
|
|
/* get file structure */
|
|
for (i = 3; i < idnum; i++) {
|
|
fiop = &files[ioindex[i]];
|
|
if (fiop->id == id)
|
|
break;
|
|
}
|
|
if (i == idnum)
|
|
return 1; /* File not open */
|
|
idnum--;
|
|
for (; i < idnum; i++)
|
|
ioindex[i] = ioindex[i + 1];
|
|
|
|
free(fiop->name);
|
|
fiop->name = NULL;
|
|
|
|
/* close file and note error state */
|
|
err = ferror(fiop->fp);
|
|
err |= fclose(fiop->fp);
|
|
fiop->fp = NULL;
|
|
|
|
/* return success or failure */
|
|
return (err ? EOF : 0);
|
|
}
|
|
|
|
|
|
int
|
|
closeall(void)
|
|
{
|
|
FILEIO *fiop;
|
|
int i;
|
|
int err;
|
|
|
|
err = 0;
|
|
for (i = 3; i < idnum; i++) {
|
|
fiop = &files[ioindex[i]];
|
|
if (fiop->fp) {
|
|
free(fiop->name);
|
|
fiop->name = NULL;
|
|
err |= fclose(fiop->fp);
|
|
}
|
|
}
|
|
idnum = 3;
|
|
return err;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return whether or not an error occurred to a file.
|
|
*/
|
|
bool
|
|
errorid(FILEID id)
|
|
{
|
|
FILEIO *fiop; /* file structure */
|
|
|
|
fiop = findid(id, -1);
|
|
if (fiop == NULL)
|
|
return EOF;
|
|
return (ferror(fiop->fp) != 0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Return whether or not end of file occurred to a file.
|
|
*/
|
|
bool
|
|
eofid(FILEID id)
|
|
{
|
|
FILEIO *fiop; /* file structure */
|
|
|
|
fiop = findid(id, -1);
|
|
if (fiop == NULL)
|
|
return EOF;
|
|
return (feof(fiop->fp) != 0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Flush output to an opened file.
|
|
*/
|
|
int
|
|
flushid(FILEID id)
|
|
{
|
|
FILEIO *fiop; /* file structure */
|
|
|
|
fiop = findid(id, -1);
|
|
if (fiop == NULL)
|
|
return 0;
|
|
if (!fiop->writing || fiop->action == 'r')
|
|
return 0;
|
|
return fflush(fiop->fp);
|
|
}
|
|
|
|
|
|
#if !defined(_WIN32) && !defined(_WIN64)
|
|
int
|
|
flushall(void)
|
|
{
|
|
FILEIO *fiop;
|
|
int i;
|
|
int err;
|
|
|
|
err = 0;
|
|
for (i = 3; i < idnum; i++) {
|
|
fiop = &files[ioindex[i]];
|
|
if (fiop->writing && fiop->action != 'r')
|
|
err |= fflush(fiop->fp);
|
|
}
|
|
return err;
|
|
}
|
|
#endif /* Windows free systems */
|
|
|
|
|
|
/*
|
|
* Read the next line, string or word from an opened file.
|
|
* Returns a pointer to an allocated string holding a null-terminated
|
|
* or newline terminated string. Where reading stops is controlled by
|
|
* flags:
|
|
*
|
|
* bit 0: at newline
|
|
* bit 1: at null character
|
|
* bit 2: at white space (also skips leading white space)
|
|
*
|
|
* If neither '\n' nor '\0' is encountered reading continues until EOF.
|
|
* If bit 3 is set the stop character is removed.
|
|
*
|
|
* given:
|
|
* id file to read from
|
|
* flags read flags (see above)
|
|
* retstr returned pointer to string
|
|
*/
|
|
int
|
|
readid(FILEID id, int flags, STRING **retstr)
|
|
{
|
|
FILEIO *fiop; /* file structure */
|
|
FILE *fp;
|
|
char *str; /* current string */
|
|
unsigned long n; /* current number characters read into buf */
|
|
unsigned long totlen; /* total length of string copied from buf */
|
|
char buf[READSIZE]; /* temporary buffer */
|
|
char *b;
|
|
int c;
|
|
bool nlstop, nullstop, wsstop, rmstop, done;
|
|
FILEPOS fpos;
|
|
STRING *newstr;
|
|
|
|
totlen = 0;
|
|
str = NULL;
|
|
|
|
fiop = findid(id, false);
|
|
if (fiop == NULL)
|
|
return 1;
|
|
nlstop = (flags & 1);
|
|
nullstop = (flags & 2);
|
|
wsstop = (flags & 4);
|
|
rmstop = (flags & 8);
|
|
|
|
fp = fiop->fp;
|
|
|
|
if (fiop->action == 'w') {
|
|
f_tell(fp, &fpos);
|
|
fflush(fp);
|
|
if (f_seek_set(fp, &fpos) < 0)
|
|
return 3;
|
|
}
|
|
fiop->action = 'r';
|
|
|
|
if (wsstop) {
|
|
while (isspace(c = fgetc(fp)));
|
|
ungetc(c, fp);
|
|
}
|
|
|
|
for (;;) {
|
|
b = buf;
|
|
n = 0;
|
|
do {
|
|
c = fgetc(fp);
|
|
if (c == EOF)
|
|
break;
|
|
n++;
|
|
if (nlstop && c == '\n')
|
|
break;
|
|
if (nullstop && c == '\0')
|
|
break;
|
|
if (wsstop && isspace(c))
|
|
break;
|
|
*b++ = c;
|
|
} while (n < READSIZE);
|
|
done = ((nlstop && c == '\n') || (nullstop && c == '\0') ||
|
|
(wsstop && isspace(c)) || c == EOF);
|
|
if (done && rmstop && c != EOF)
|
|
n--;
|
|
if (totlen)
|
|
str = (char *)realloc(str, totlen + n + 1);
|
|
else
|
|
str = (char *)malloc(n + 1);
|
|
if (str == NULL) {
|
|
math_error("Out of memory for readid");
|
|
not_reached();
|
|
}
|
|
if (n > 0)
|
|
memcpy(&str[totlen], buf, n);
|
|
totlen += n;
|
|
if (done)
|
|
break;
|
|
}
|
|
if (totlen == 0 && c == EOF) {
|
|
free(str);
|
|
return EOF;
|
|
}
|
|
if ((nlstop && c == '\n') && !rmstop)
|
|
str[totlen - 1] = '\n';
|
|
if ((nullstop && c == '\0') && !rmstop)
|
|
str[totlen - 1] = '\0';
|
|
str[totlen] = '\0';
|
|
newstr = stralloc();
|
|
newstr->s_len = totlen;
|
|
newstr->s_str = str;
|
|
*retstr = newstr;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the next character from an opened file.
|
|
* Returns EOF if there was an error or end of file.
|
|
*/
|
|
int
|
|
getcharid(FILEID id)
|
|
{
|
|
FILEIO *fiop;
|
|
FILEPOS fpos;
|
|
|
|
fiop = findid(id, false);
|
|
if (fiop == NULL)
|
|
return -2;
|
|
if (fiop->action == 'w') {
|
|
f_tell(fiop->fp, &fpos);
|
|
fflush(fiop->fp);
|
|
if (f_seek_set(fiop->fp, &fpos) < 0)
|
|
return -3;
|
|
}
|
|
fiop->action = 'r';
|
|
|
|
return fgetc(fiop->fp);
|
|
}
|
|
|
|
|
|
/*
|
|
* Print out the name of an opened file.
|
|
* If the file has been closed, a null name is printed.
|
|
* If flags contain PRINT_UNAMBIG then extra information is printed
|
|
* identifying the output as a file and some data about it.
|
|
*/
|
|
int
|
|
printid(FILEID id, int flags)
|
|
{
|
|
FILEIO *fiop; /* file structure */
|
|
FILE *fp;
|
|
ZVALUE pos; /* file position */
|
|
|
|
/*
|
|
* filewall - file is closed
|
|
*/
|
|
fiop = findid(id, -1);
|
|
if (fiop == NULL) {
|
|
if (flags & PRINT_UNAMBIG)
|
|
math_fmt("FILE %ld closed", id);
|
|
else
|
|
math_str("\"\"");
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* print quoted filename and mode
|
|
*/
|
|
if ((flags & PRINT_UNAMBIG) == 0) {
|
|
math_chr('"');
|
|
math_str(fiop->name);
|
|
math_chr('"');
|
|
return 0;
|
|
}
|
|
math_fmt("FILE %ld \"%s\" (%s", id, fiop->name, fiop->mode);
|
|
|
|
/*
|
|
* print file position
|
|
*/
|
|
|
|
fp = fiop->fp;
|
|
|
|
if (get_open_pos(fp, &pos) < 0) {
|
|
if (fileno(fp) > 2)
|
|
math_str("Error while determining file position!");
|
|
math_chr(')');
|
|
return 0;
|
|
}
|
|
|
|
math_str(", pos ");
|
|
zprintval(pos, 0, 0);
|
|
zfree(pos);
|
|
|
|
/*
|
|
* report special status
|
|
*/
|
|
if (ferror(fp))
|
|
math_str(", error");
|
|
if (feof(fp))
|
|
math_str(", eof");
|
|
math_chr(')');
|
|
|
|
printf(" fileno: %d ", fileno(fp));
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Print a formatted string similar to printf. Various formats of output
|
|
* are possible, depending on the format string AND the actual types of the
|
|
* values. Mismatches do not cause errors, instead something reasonable is
|
|
* printed instead. The output goes to the file with the specified id.
|
|
*
|
|
* given:
|
|
* id file id to print to
|
|
* count print count
|
|
* fmt standard format string
|
|
* vals table of values to print
|
|
*/
|
|
int
|
|
idprintf(FILEID id, char *fmt, int count, VALUE **vals)
|
|
{
|
|
FILEIO *fiop;
|
|
VALUE *vp;
|
|
char *str;
|
|
int ch;
|
|
size_t len;
|
|
int oldmode, newmode;
|
|
long olddigits, newdigits;
|
|
long width, precision;
|
|
bool didneg, didprecision;
|
|
FILEPOS fpos;
|
|
bool printstring;
|
|
bool printchar;
|
|
|
|
fiop = findid(id, true);
|
|
if (fiop == NULL)
|
|
return 1;
|
|
if (fiop->action == 'r') {
|
|
f_tell(fiop->fp, &fpos);
|
|
if (f_seek_set(fiop->fp, &fpos) < 0)
|
|
return 3;
|
|
}
|
|
|
|
fiop->action = 'w';
|
|
|
|
printstring = false;
|
|
printchar = false;
|
|
|
|
math_setfp(fiop->fp);
|
|
|
|
while ((ch = *fmt++) != '\0') {
|
|
if (ch != '%') {
|
|
math_chr(ch);
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Here to handle formats.
|
|
*/
|
|
didneg = false;
|
|
didprecision = false;
|
|
width = 0;
|
|
precision = 0;
|
|
|
|
ch = *fmt++;
|
|
if (ch == '-') {
|
|
didneg = true;
|
|
ch = *fmt++;
|
|
}
|
|
while ((ch >= '0') && (ch <= '9')) {
|
|
width = width * 10 + (ch - '0');
|
|
ch = *fmt++;
|
|
}
|
|
if (ch == '.') {
|
|
didprecision = true;
|
|
ch = *fmt++;
|
|
while ((ch >= '0') && (ch <= '9')) {
|
|
precision = precision * 10 + (ch - '0');
|
|
ch = *fmt++;
|
|
}
|
|
}
|
|
if (ch == 'l')
|
|
ch = *fmt++;
|
|
|
|
oldmode = conf->outmode;
|
|
newmode = oldmode;
|
|
olddigits = conf->outdigits;
|
|
newdigits = olddigits;
|
|
if (didprecision)
|
|
newdigits = precision;
|
|
|
|
switch (ch) {
|
|
case 's':
|
|
printstring = true;
|
|
/*FALLTHRU*/
|
|
case 'c':
|
|
printchar = true;
|
|
case 'd':
|
|
break;
|
|
case 'f':
|
|
newmode = MODE_REAL;
|
|
break;
|
|
case 'e':
|
|
newmode = MODE_EXP;
|
|
break;
|
|
case 'n':
|
|
newmode = MODE_ENG;
|
|
break;
|
|
case 'g':
|
|
newmode = MODE_REAL_AUTO;
|
|
break;
|
|
case 'r':
|
|
newmode = MODE_FRAC;
|
|
break;
|
|
case 'o':
|
|
newmode = MODE_OCTAL;
|
|
break;
|
|
case 'x':
|
|
newmode = MODE_HEX;
|
|
break;
|
|
case 'b':
|
|
newmode = MODE_BINARY;
|
|
break;
|
|
case 0:
|
|
math_setfp(stdout);
|
|
return 0;
|
|
default:
|
|
math_chr(ch);
|
|
continue;
|
|
}
|
|
|
|
if (--count < 0) {
|
|
while (width-- > 0)
|
|
math_chr(' ');
|
|
continue;
|
|
}
|
|
vp = *vals++;
|
|
|
|
math_setdigits(newdigits);
|
|
math_setmode(newmode);
|
|
|
|
/*
|
|
* If there is no width specification, or if the type of
|
|
* value requires multiple lines, then just output the
|
|
* value directly.
|
|
*/
|
|
if ((width == 0) ||
|
|
(vp->v_type == V_MAT) || (vp->v_type == V_LIST)) {
|
|
switch(vp->v_type) {
|
|
case V_OCTET:
|
|
if (printstring)
|
|
math_str((char *)vp->v_octet);
|
|
else if (printchar)
|
|
math_chr(*vp->v_octet);
|
|
else
|
|
printvalue(vp, PRINT_NORMAL);
|
|
break;
|
|
case V_BLOCK:
|
|
if (printstring)
|
|
math_str((char *)
|
|
vp->v_block->data);
|
|
else if (printchar)
|
|
math_chr(*vp->v_block->data);
|
|
else
|
|
printvalue(vp, PRINT_NORMAL);
|
|
break;
|
|
case V_NBLOCK:
|
|
if (printstring) {
|
|
if (vp->v_nblock->blk->data !=
|
|
NULL)
|
|
math_str((char *)
|
|
vp->v_nblock
|
|
->blk->data);
|
|
} else if (printchar) {
|
|
if (vp->v_nblock->blk->data !=
|
|
NULL)
|
|
math_chr(*vp->v_nblock->
|
|
blk->data);
|
|
} else {
|
|
printvalue(vp, PRINT_NORMAL);
|
|
}
|
|
break;
|
|
default:
|
|
printvalue(vp, PRINT_NORMAL);
|
|
}
|
|
|
|
math_setmode(oldmode);
|
|
math_setdigits(olddigits);
|
|
continue;
|
|
}
|
|
|
|
|
|
/*
|
|
* There is a field width. Collect the output in a string,
|
|
* print it padded appropriately with spaces, and free it.
|
|
* However, if the output contains a newline, then ignore
|
|
* the field width.
|
|
*/
|
|
math_divertio();
|
|
switch(vp->v_type) {
|
|
case V_OCTET:
|
|
if (printstring)
|
|
math_str((char *)vp->v_octet);
|
|
else if (printchar)
|
|
math_chr(*vp->v_octet);
|
|
else
|
|
printvalue(vp, PRINT_NORMAL);
|
|
break;
|
|
case V_BLOCK:
|
|
if (printstring)
|
|
math_str((char *)vp->v_block->data);
|
|
else if (printchar)
|
|
math_chr(*vp->v_block->data);
|
|
else
|
|
printvalue(vp, PRINT_NORMAL);
|
|
break;
|
|
case V_NBLOCK:
|
|
if (printstring) {
|
|
if (vp->v_nblock->blk->data != NULL)
|
|
math_str((char *)
|
|
vp->v_nblock->blk->data);
|
|
}
|
|
else if (printchar) {
|
|
if (vp->v_nblock->blk->data != NULL)
|
|
math_chr(*vp->v_nblock->blk->data);
|
|
}
|
|
else
|
|
printvalue(vp, PRINT_NORMAL);
|
|
break;
|
|
default:
|
|
printvalue(vp, PRINT_NORMAL);
|
|
}
|
|
str = math_getdivertedio();
|
|
if (strchr(str, '\n'))
|
|
width = 0;
|
|
len = strlen(str);
|
|
while (!didneg && ((size_t)width > len)) {
|
|
width--;
|
|
math_chr(' ');
|
|
}
|
|
math_str(str);
|
|
free(str);
|
|
while (didneg && ((size_t)width > len)) {
|
|
width--;
|
|
math_chr(' ');
|
|
}
|
|
math_setmode(oldmode);
|
|
math_setdigits(olddigits);
|
|
}
|
|
math_setfp(stdout);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Write a character to a file.
|
|
*
|
|
* given:
|
|
* id file id to print to
|
|
* ch character to write
|
|
*/
|
|
int
|
|
idfputc(FILEID id, int ch)
|
|
{
|
|
FILEIO *fiop;
|
|
FILEPOS fpos;
|
|
|
|
/* get the file info pointer */
|
|
fiop = findid(id, true);
|
|
if (fiop == NULL)
|
|
return 1;
|
|
if (fiop->action == 'r') {
|
|
f_tell(fiop->fp, &fpos);
|
|
if (f_seek_set(fiop->fp, &fpos) < 0)
|
|
return 2;
|
|
}
|
|
|
|
fiop->action = 'w';
|
|
|
|
/* set output to file */
|
|
math_setfp(fiop->fp);
|
|
|
|
/* write char */
|
|
math_chr(ch);
|
|
|
|
/* restore output to stdout */
|
|
math_setfp(stdout);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Unget a character read from a file.
|
|
*
|
|
* given:
|
|
* id file id to print to
|
|
* ch character to write
|
|
*/
|
|
int
|
|
idungetc(FILEID id, int ch)
|
|
{
|
|
FILEIO *fiop;
|
|
|
|
fiop = findid(id, false);
|
|
if (fiop == NULL)
|
|
return -2;
|
|
if (fiop->action != 'r')
|
|
return -2;
|
|
return ungetc(ch, fiop->fp);
|
|
}
|
|
|
|
|
|
/*
|
|
* Write a string to a file.
|
|
*
|
|
* given:
|
|
* id file id to print to
|
|
* str string to write
|
|
*/
|
|
int
|
|
idfputs(FILEID id, STRING *str)
|
|
{
|
|
FILEIO *fiop;
|
|
FILEPOS fpos;
|
|
FILE *fp;
|
|
char *c;
|
|
long len;
|
|
|
|
/* get the file info pointer */
|
|
fiop = findid(id, true);
|
|
if (fiop == NULL)
|
|
return 1;
|
|
|
|
if (fiop->action == 'r') {
|
|
f_tell(fiop->fp, &fpos);
|
|
if (f_seek_set(fiop->fp, &fpos) < 0)
|
|
return 2;
|
|
}
|
|
|
|
fiop->action = 'w';
|
|
|
|
fp = fiop->fp;
|
|
len = str->s_len;
|
|
c = str->s_str;
|
|
|
|
while (len-- > 0)
|
|
fputc(*c++, fp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Same as idfputs but writes a terminating null character
|
|
*
|
|
* given:
|
|
* id file id to print to
|
|
* str string to write
|
|
*/
|
|
int
|
|
idfputstr(FILEID id, char *str)
|
|
{
|
|
FILEIO *fiop;
|
|
FILEPOS fpos;
|
|
|
|
/* get the file info pointer */
|
|
fiop = findid(id, true);
|
|
if (fiop == NULL)
|
|
return 1;
|
|
|
|
if (fiop->action == 'r') {
|
|
f_tell(fiop->fp, &fpos);
|
|
if (f_seek_set(fiop->fp, &fpos) < 0)
|
|
return 2;
|
|
}
|
|
|
|
fiop->action = 'w';
|
|
|
|
/* set output to file */
|
|
math_setfp(fiop->fp);
|
|
|
|
/* write the string */
|
|
math_str(str);
|
|
|
|
math_chr('\0');
|
|
|
|
/* restore output to stdout */
|
|
math_setfp(stdout);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
rewindid(FILEID id)
|
|
{
|
|
FILEIO *fiop;
|
|
fiop = findid(id, -1);
|
|
if (fiop == NULL)
|
|
return 1;
|
|
rewind(fiop->fp);
|
|
fiop->action = 0;
|
|
return 0;
|
|
}
|
|
|
|
|
|
void
|
|
rewindall(void)
|
|
{
|
|
FILEIO *fiop;
|
|
int i;
|
|
|
|
for (i = 3; i < idnum; i++) {
|
|
fiop = &files[ioindex[i]];
|
|
if (fiop != NULL) {
|
|
(void) rewind(fiop->fp);
|
|
fiop->action = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* filepos2z - convert a positive file position into a ZVALUE
|
|
*
|
|
* given:
|
|
* pos file position
|
|
*
|
|
* returns:
|
|
* file position as a ZVALUE
|
|
*
|
|
* NOTE: Does not support negative file positions.
|
|
*/
|
|
/*ARGSUSED*/
|
|
S_FUNC ZVALUE
|
|
filepos2z(FILEPOS pos)
|
|
{
|
|
ZVALUE ret; /* ZVALUE file position to return */
|
|
|
|
/*
|
|
* store FILEPOS in a ZVALUE as a positive value
|
|
*/
|
|
ret.len = FILEPOS_BITS/BASEB;
|
|
ret.v = alloc(ret.len);
|
|
zclearval(ret);
|
|
SWAP_HALF_IN_FILEPOS(ret.v, &pos);
|
|
ret.sign = 0;
|
|
ztrim(&ret);
|
|
|
|
/*
|
|
* return our result
|
|
*/
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* z2filepos - convert a positive ZVALUE file position to a FILEPOS
|
|
*
|
|
* given:
|
|
* zpos file position as a ZVALUE
|
|
*
|
|
* returns:
|
|
* file position as a FILEPOS
|
|
*
|
|
* NOTE: Does not support negative file positions.
|
|
*/
|
|
S_FUNC FILEPOS
|
|
z2filepos(ZVALUE zpos)
|
|
{
|
|
#if FILEPOS_BITS > FULL_BITS
|
|
FILEPOS tmp; /* temp file position as a FILEPOS */
|
|
#endif
|
|
FILEPOS ret; /* file position as a FILEPOS */
|
|
#if FILEPOS_BITS < FULL_BITS
|
|
long pos; /* zpos as a long */
|
|
#else
|
|
FULL pos; /* zpos as a FULL */
|
|
#endif
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
zpos.sign = 0; /* deal only with the absolute value */
|
|
|
|
/*
|
|
* quick return if the position can fit into a long
|
|
*/
|
|
#if FILEPOS_BITS == FULL_BITS
|
|
/* ztofull puts the value into native byte order */
|
|
pos = ztofull(zpos);
|
|
memset(&ret, 0, sizeof(ret)); /* FILEPOS could be non-scalar */
|
|
memcpy((void *)&ret, (void *)&pos, MIN(sizeof(ret), sizeof(pos)));
|
|
return ret;
|
|
#elif FILEPOS_BITS < FULL_BITS
|
|
/* ztofull puts the value into native byte order */
|
|
pos = ztolong(zpos);
|
|
memset(&ret, 0, sizeof(ret)); /* FILEPOS could be non-scalar */
|
|
memcpy((void *)&ret, (void *)&pos, MIN(sizeof(ret), sizeof(pos)));
|
|
return ret;
|
|
#else /* FILEPOS_BITS > FULL_BITS */
|
|
if (!zgtmaxfull(zpos)) {
|
|
/* ztofull puts the value into native byte order */
|
|
pos = ztofull(zpos);
|
|
memset(&ret, 0, sizeof(ret)); /* FILEPOS could be non-scalar */
|
|
memcpy((void *)&ret, (void *)&pos,
|
|
MIN(sizeof(ret), sizeof(pos)));
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* copy (and swap if needed) lower part of the ZVALUE as needed
|
|
*/
|
|
if (zpos.len >= FILEPOS_BITS/BASEB) {
|
|
/* copy the lower FILEPOS_BITS of the ZVALUE */
|
|
memset(&tmp, 0, sizeof(tmp)); /* FILEPOS could be non-scalar */
|
|
memcpy(&tmp, zpos.v, MIN(sizeof(tmp), FILEPOS_LEN));
|
|
} else {
|
|
/* copy what bits we can into the temp value */
|
|
memset(&tmp, 0, sizeof(tmp)); /* FILEPOS could be non-scalar */
|
|
memcpy(&tmp, zpos.v, MIN(sizeof(tmp),
|
|
MIN(zpos.len*BASEB/8, FILEPOS_LEN)));
|
|
}
|
|
/* swap into native byte order */
|
|
SWAP_HALF_IN_FILEPOS(&ret, &tmp);
|
|
|
|
/*
|
|
* return our result
|
|
*/
|
|
return ret;
|
|
#endif /* FILEPOS_BITS <= FULL_BITS */
|
|
}
|
|
|
|
|
|
/*
|
|
* get_open_pos - get a an open file position
|
|
*
|
|
* given:
|
|
* fp open file stream
|
|
* res where to place the file position (ZVALUE)
|
|
*
|
|
* returns:
|
|
* 0 res points to the file position
|
|
* -1 error
|
|
*/
|
|
S_FUNC int
|
|
get_open_pos(FILE *fp, ZVALUE *res)
|
|
{
|
|
FILEPOS pos; /* current file position */
|
|
|
|
/*
|
|
* get the file position
|
|
*/
|
|
if (f_tell(fp, &pos) < 0) {
|
|
/* cannot get file position, return -1 */
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* update file position and return success
|
|
*/
|
|
*res = filepos2z(pos);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* getloc - get the current position of the file
|
|
*
|
|
* given:
|
|
* id file id of the file
|
|
* loc pointer to result
|
|
*
|
|
* returns:
|
|
* 0 able to get file position
|
|
* -1 unable to get file position
|
|
*/
|
|
int
|
|
getloc(FILEID id, ZVALUE *res)
|
|
{
|
|
FILEIO *fiop; /* file structure */
|
|
FILE *fp;
|
|
|
|
/*
|
|
* convert id to stream
|
|
*/
|
|
fiop = findid(id, -1);
|
|
if (fiop == NULL) {
|
|
/* file not open */
|
|
return -1;
|
|
}
|
|
fp = fiop->fp;
|
|
if (fp == NULL) {
|
|
math_error("Bogus internal file pointer!");
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* return result
|
|
*/
|
|
return get_open_pos(fp, res);
|
|
}
|
|
|
|
|
|
int
|
|
ftellid(FILEID id, ZVALUE *res)
|
|
{
|
|
FILEIO *fiop;
|
|
FILEPOS fpos; /* current file position */
|
|
|
|
/* get FILEIO */
|
|
fiop = findid(id, -1);
|
|
if (fiop == NULL)
|
|
return -2;
|
|
|
|
/* get the file position */
|
|
if (f_tell(fiop->fp, &fpos) < 0)
|
|
return -3;
|
|
|
|
/* convert file position to ZVALUE */
|
|
*res = filepos2z(fpos);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
fseekid(FILEID id, ZVALUE offset, int whence)
|
|
{
|
|
FILEIO *fiop; /* FILEIO of file */
|
|
FILEPOS off; /* offset as a FILEPOS */
|
|
ZVALUE cur, tmp; /* current or end of file location */
|
|
int ret = 0; /* return code */
|
|
|
|
/* setup */
|
|
fiop = findid(id, -1);
|
|
if (fiop == NULL)
|
|
return -2;
|
|
|
|
/* seek depending on whence */
|
|
switch (whence) {
|
|
case 0:
|
|
/* construct seek position, off = offset */
|
|
if (zisneg(offset))
|
|
return -3;
|
|
off = z2filepos(offset);
|
|
|
|
/* seek there */
|
|
ret = f_seek_set(fiop->fp, &off);
|
|
break;
|
|
|
|
case 1:
|
|
/* construct seek position, off = cur+offset */
|
|
f_tell(fiop->fp, &off);
|
|
cur = filepos2z(off);
|
|
zadd(cur, offset, &tmp);
|
|
zfree(cur);
|
|
if (zisneg(tmp)) {
|
|
zfree(tmp);
|
|
return -3;
|
|
}
|
|
off = z2filepos(tmp);
|
|
zfree(tmp);
|
|
|
|
/* seek there */
|
|
ret = f_seek_set(fiop->fp, &off);
|
|
break;
|
|
|
|
case 2:
|
|
/* construct seek position, off = len+offset */
|
|
if (get_open_siz(fiop->fp, &cur) < 0)
|
|
return -4;
|
|
zadd(cur, offset, &tmp);
|
|
zfree(cur);
|
|
if (zisneg(tmp)) {
|
|
zfree(tmp);
|
|
return -3;
|
|
}
|
|
off = z2filepos(tmp);
|
|
zfree(tmp);
|
|
|
|
/* seek there */
|
|
ret = f_seek_set(fiop->fp, &off);
|
|
break;
|
|
|
|
default:
|
|
return -5;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* set_open_pos - set a an open file position
|
|
*
|
|
* given:
|
|
* fp open file stream
|
|
* zpos file position (ZVALUE) to set
|
|
*
|
|
* returns:
|
|
* 0 res points to the file position
|
|
* -1 error
|
|
*
|
|
* NOTE: Due to fsetpos limitation, position is set relative to only
|
|
* the beginning of the file.
|
|
*/
|
|
S_FUNC int
|
|
set_open_pos(FILE *fp, ZVALUE zpos)
|
|
{
|
|
FILEPOS pos; /* current file position */
|
|
|
|
/*
|
|
* convert ZVALUE to file position
|
|
*/
|
|
pos = z2filepos(zpos);
|
|
|
|
/*
|
|
* set the file position
|
|
*/
|
|
if (f_seek_set(fp, &pos) < 0) {
|
|
/* cannot set file position, return -1 */
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* return success
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* setloc - set the current position of the file
|
|
*
|
|
* given:
|
|
* id file id of the file
|
|
* zpos file position (ZVALUE) to set
|
|
*
|
|
* returns:
|
|
* 0 able to set file position
|
|
* -1 unable to set file position
|
|
*/
|
|
int
|
|
setloc(FILEID id, ZVALUE zpos)
|
|
{
|
|
FILEIO *fiop; /* file structure */
|
|
FILE *fp;
|
|
|
|
/*
|
|
* firewall
|
|
*/
|
|
if ((id == FILEID_STDIN) || (id == FILEID_STDOUT) ||
|
|
(id == FILEID_STDERR)) {
|
|
math_error("Cannot fseek stdin, stdout, or stderr");
|
|
not_reached();
|
|
}
|
|
|
|
/*
|
|
* convert id to stream
|
|
*/
|
|
fiop = findid(id, -1);
|
|
if (fiop == NULL) {
|
|
/* file not open */
|
|
return -1;
|
|
}
|
|
fp = fiop->fp;
|
|
if (fp == NULL) {
|
|
math_error("Bogus internal file pointer!");
|
|
not_reached();
|
|
}
|
|
|
|
fiop->action = 0;
|
|
|
|
/*
|
|
* return result
|
|
*/
|
|
return set_open_pos(fp, zpos);
|
|
}
|
|
|
|
|
|
/*
|
|
* off_t2z - convert an off_t into a ZVALUE
|
|
*
|
|
* given:
|
|
* siz file size
|
|
*
|
|
* returns:
|
|
* file size as a ZVALUE
|
|
*/
|
|
/*ARGSUSED*/
|
|
S_FUNC ZVALUE
|
|
off_t2z(off_t siz)
|
|
{
|
|
ZVALUE ret; /* ZVALUE file size to return */
|
|
|
|
/*
|
|
* store off_t in a ZVALUE as a positive value
|
|
*/
|
|
ret.len = OFF_T_BITS/BASEB;
|
|
ret.v = alloc(ret.len);
|
|
zclearval(ret);
|
|
SWAP_HALF_IN_OFF_T(ret.v, &siz);
|
|
ret.sign = 0;
|
|
ztrim(&ret);
|
|
|
|
/*
|
|
* return our result
|
|
*/
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* dev2z - convert a stat.st_dev into a ZVALUE
|
|
*
|
|
* given:
|
|
* dev device
|
|
*
|
|
* returns:
|
|
* file size as a ZVALUE
|
|
*/
|
|
S_FUNC ZVALUE
|
|
dev2z(dev_t dev)
|
|
{
|
|
ZVALUE ret; /* ZVALUE file size to return */
|
|
|
|
/*
|
|
* store off_t in a ZVALUE as a positive value
|
|
*/
|
|
ret.len = DEV_BITS/BASEB;
|
|
ret.v = alloc(ret.len);
|
|
zclearval(ret);
|
|
SWAP_HALF_IN_DEV(ret.v, &dev);
|
|
ret.sign = 0;
|
|
ztrim(&ret);
|
|
|
|
/*
|
|
* return our result
|
|
*/
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* inode2z - convert a stat.st_ino into a ZVALUE
|
|
*
|
|
* given:
|
|
* inode file size
|
|
*
|
|
* returns:
|
|
* file size as a ZVALUE
|
|
*/
|
|
/*ARGSUSED*/
|
|
S_FUNC ZVALUE
|
|
inode2z(ino_t inode)
|
|
{
|
|
ZVALUE ret; /* ZVALUE file size to return */
|
|
|
|
/*
|
|
* store off_t in a ZVALUE as a positive value
|
|
*/
|
|
ret.len = INODE_BITS/BASEB;
|
|
ret.v = alloc(ret.len);
|
|
zclearval(ret);
|
|
SWAP_HALF_IN_INODE(ret.v, &inode);
|
|
ret.sign = 0;
|
|
ztrim(&ret);
|
|
|
|
/*
|
|
* return our result
|
|
*/
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* get_open_siz - get a an open file size
|
|
*
|
|
* given:
|
|
* fp open file stream
|
|
* res where to place the file size (ZVALUE)
|
|
*
|
|
* returns:
|
|
* 0 res points to the file size
|
|
* -1 error
|
|
*/
|
|
int
|
|
get_open_siz(FILE *fp, ZVALUE *res)
|
|
{
|
|
struct stat buf; /* file status */
|
|
|
|
/*
|
|
* get the file size
|
|
*/
|
|
if (fstat(fileno(fp), &buf) < 0) {
|
|
/* stat error */
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* update file size and return success
|
|
*/
|
|
*res = off_t2z(buf.st_size);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* getsize - get the current size of the file
|
|
*
|
|
* given:
|
|
* id file id of the file
|
|
* res pointer to result
|
|
*
|
|
* returns:
|
|
* 0 able to get file size
|
|
* EOF system error
|
|
* other nonzero file not open or other problem
|
|
*/
|
|
int
|
|
getsize(FILEID id, ZVALUE *res)
|
|
{
|
|
FILEIO *fiop; /* file structure */
|
|
FILE *fp;
|
|
|
|
/*
|
|
* convert id to stream
|
|
*/
|
|
fiop = findid(id, -1);
|
|
if (fiop == NULL) {
|
|
/* file not open */
|
|
return 1;
|
|
}
|
|
fp = fiop->fp;
|
|
if (fp == NULL) {
|
|
return 2;
|
|
}
|
|
|
|
/*
|
|
* return result
|
|
*/
|
|
return get_open_siz(fp, res);
|
|
}
|
|
|
|
|
|
/*
|
|
* getdevice - get the device of the file
|
|
*
|
|
* given:
|
|
* id file id of the file
|
|
* dev pointer to the result
|
|
*
|
|
* returns:
|
|
* 0 able to get device
|
|
* -1 unable to get device
|
|
*/
|
|
int
|
|
get_device(FILEID id, ZVALUE *dev)
|
|
{
|
|
FILEIO *fiop; /* file structure */
|
|
|
|
/*
|
|
* convert id to stream
|
|
*/
|
|
fiop = findid(id, -1);
|
|
if (fiop == NULL) {
|
|
/* file not open */
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* return result
|
|
*/
|
|
*dev = dev2z(fiop->dev);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* getinode - get the inode of the file
|
|
*
|
|
* given:
|
|
* id file id of the file
|
|
* inode pointer to the result
|
|
*
|
|
* returns:
|
|
* 0 able to get inode
|
|
* -1 unable to get inode
|
|
*/
|
|
int
|
|
get_inode(FILEID id, ZVALUE *inode)
|
|
{
|
|
FILEIO *fiop; /* file structure */
|
|
|
|
/*
|
|
* convert id to stream
|
|
*/
|
|
fiop = findid(id, -1);
|
|
if (fiop == NULL) {
|
|
/* file not open */
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* return result
|
|
*/
|
|
*inode = inode2z(fiop->inode);
|
|
return 0;
|
|
}
|
|
|
|
|
|
S_FUNC off_t
|
|
filesize(FILEIO *fiop)
|
|
{
|
|
struct stat sbuf;
|
|
|
|
/* return length */
|
|
if (fstat(fileno(fiop->fp), &sbuf) < 0) {
|
|
math_error("bad fstat");
|
|
not_reached();
|
|
}
|
|
return sbuf.st_size;
|
|
}
|
|
|
|
|
|
ZVALUE
|
|
zfilesize(FILEID id)
|
|
{
|
|
FILEIO *fiop;
|
|
off_t len; /* file length */
|
|
ZVALUE ret; /* file size as a ZVALUE return value */
|
|
|
|
/* file FILEIO */
|
|
fiop = findid(id, -1);
|
|
if (fiop == NULL) {
|
|
/* return neg value for non-file error */
|
|
itoz(-1, &ret);
|
|
return ret;
|
|
}
|
|
|
|
/* get length */
|
|
len = filesize(fiop);
|
|
ret = off_t2z(len);
|
|
return ret;
|
|
}
|
|
|
|
|
|
void
|
|
showfiles(void)
|
|
{
|
|
bool listed[MAXFILES];
|
|
FILEIO *fiop;
|
|
FILE *fp;
|
|
struct stat sbuf;
|
|
ino_t inodes[MAXFILES];
|
|
off_t sizes[MAXFILES];
|
|
int i, j;
|
|
|
|
for (i = 0; i < idnum; i++) {
|
|
listed[i] = false;
|
|
fiop = &files[ioindex[i]];
|
|
fp = fiop->fp;
|
|
if (fstat(fileno(fp), &sbuf) < 0) {
|
|
printf("Bad fstat for file %d\n", (int) fiop->id);
|
|
sizes[i] = -1;
|
|
} else {
|
|
inodes[i] = sbuf.st_ino;
|
|
sizes[i] = sbuf.st_size;
|
|
}
|
|
}
|
|
for (i = 0; i < idnum; i++) {
|
|
if (listed[i])
|
|
continue;
|
|
fiop = &files[ioindex[i]];
|
|
printf("\t");
|
|
printid(fiop->id, PRINT_UNAMBIG);
|
|
if (sizes[i] == -1) {
|
|
math_chr('\n');
|
|
continue;
|
|
}
|
|
printf(" size = %ld\n", (long int)sizes[i]);
|
|
for (j = i + 1; j < idnum; j++) {
|
|
if (listed[j] || sizes[j] == -1)
|
|
continue;
|
|
if (inodes[j] == inodes[i]) {
|
|
listed[j] = true;
|
|
fiop = &files[ioindex[j]];
|
|
printf("\t = ");
|
|
printid(fiop->id, PRINT_UNAMBIG);
|
|
printf("\n");
|
|
}
|
|
}
|
|
}
|
|
printf("\tNumber open = %d\n", idnum);
|
|
printf("\tLastid = %d\n", (int) lastid);
|
|
}
|
|
|
|
|
|
/*
|
|
* getscanfield - scan a field separated by some characters
|
|
*
|
|
* given:
|
|
* fp FILEID to scan
|
|
* skip
|
|
* width max field width
|
|
* scannum Number of characters in scanset
|
|
* scanptr string of characters considered separators
|
|
* strptr pointer to where the new field pointer may be found
|
|
*/
|
|
S_FUNC void
|
|
getscanfield(FILE *fp, bool skip, unsigned int width, int scannum,
|
|
char *scanptr, char **strptr)
|
|
{
|
|
char *str; /* current string */
|
|
unsigned long len; /* current length of string */
|
|
unsigned long totlen; /* total length of string */
|
|
char buf[READSIZE]; /* temporary buffer */
|
|
int c;
|
|
char *b;
|
|
bool comp; /* Use complement of scanset */
|
|
unsigned int chnum;
|
|
|
|
totlen = 0;
|
|
str = NULL;
|
|
|
|
comp = (scannum < 0);
|
|
if (comp)
|
|
scannum = -scannum;
|
|
|
|
chnum = 0;
|
|
|
|
for (;;) {
|
|
len = 0;
|
|
b = buf;
|
|
for(;;) {
|
|
c = fgetc(fp);
|
|
if (c == EOF || c == '\0')
|
|
break;
|
|
chnum++;
|
|
if(scannum &&
|
|
((memchr(scanptr,c,scannum)==NULL) ^ comp))
|
|
break;
|
|
if (!skip) {
|
|
*b++ = c;
|
|
len++;
|
|
if (len >= READSIZE)
|
|
break;
|
|
}
|
|
if (chnum == width)
|
|
break;
|
|
}
|
|
if (!skip) {
|
|
if (totlen)
|
|
str = (char *) realloc(str, totlen + len + 1);
|
|
else
|
|
str = (char *) malloc(len + 1);
|
|
if (str == NULL) {
|
|
math_error("Out of memory for scanning");
|
|
not_reached();
|
|
}
|
|
if (len)
|
|
memcpy(&str[totlen], buf, len);
|
|
totlen += len;
|
|
}
|
|
if (len < READSIZE)
|
|
break;
|
|
}
|
|
|
|
if (!(width && chnum == width) && c != '\0')
|
|
ungetc(c, fp);
|
|
|
|
if (!skip) {
|
|
str[totlen] = '\0';
|
|
*strptr = str;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* getscanwhite - scan a field separated by whitespace
|
|
*
|
|
* given:
|
|
* fp FILEID to scan
|
|
* skip
|
|
* width max field width
|
|
* scannum Number of characters in scanset
|
|
* strptr pointer to where the new field pointer may be found
|
|
*/
|
|
S_FUNC void
|
|
getscanwhite(FILE *fp, bool skip, unsigned int width, int scannum,
|
|
char **strptr)
|
|
{
|
|
char *str; /* current string */
|
|
unsigned long len; /* current length of string */
|
|
unsigned long totlen; /* total length of string */
|
|
char buf[READSIZE]; /* temporary buffer */
|
|
int c;
|
|
char *b;
|
|
bool comp; /* Use complement of scanset */
|
|
unsigned int chnum;
|
|
|
|
totlen = 0;
|
|
str = NULL;
|
|
|
|
comp = (scannum < 0);
|
|
if (comp)
|
|
scannum = -scannum;
|
|
|
|
chnum = 0;
|
|
|
|
for (;;) {
|
|
len = 0;
|
|
b = buf;
|
|
for(;;) {
|
|
c = fgetc(fp);
|
|
if (c == EOF || c == '\0')
|
|
break;
|
|
chnum++;
|
|
if(scannum && (!isspace(c) ^ comp))
|
|
break;
|
|
if (!skip) {
|
|
*b++ = c;
|
|
len++;
|
|
if (len >= READSIZE)
|
|
break;
|
|
}
|
|
if (chnum == width)
|
|
break;
|
|
}
|
|
if (!skip) {
|
|
if (totlen)
|
|
str = (char *) realloc(str, totlen + len + 1);
|
|
else
|
|
str = (char *) malloc(len + 1);
|
|
if (str == NULL) {
|
|
math_error("Out of memory for scanning");
|
|
not_reached();
|
|
}
|
|
if (len)
|
|
memcpy(&str[totlen], buf, len);
|
|
totlen += len;
|
|
}
|
|
if (len < READSIZE)
|
|
break;
|
|
}
|
|
|
|
if (!(width && chnum == width) && c != '\0')
|
|
ungetc(c, fp);
|
|
|
|
if (!skip) {
|
|
str[totlen] = '\0';
|
|
*strptr = str;
|
|
}
|
|
}
|
|
|
|
|
|
S_FUNC int
|
|
fscanfile(FILE *fp, char *fmt, int count, VALUE **vals)
|
|
{
|
|
int assnum; /* Number of assignments made */
|
|
int c; /* Character read from file */
|
|
char f; /* Character read from format string */
|
|
int scannum; /* Number of characters in scanlist */
|
|
char *scanptr; /* Start of scanlist */
|
|
char *str;
|
|
bool comp; /* True scanset is complementary */
|
|
bool skip; /* True if string to be skipped rather than read */
|
|
int width;
|
|
VALUE *var; /* lvalue to be assigned to */
|
|
unsigned short subtype; /* for var->v_subtype */
|
|
FILEPOS cur; /* current location */
|
|
|
|
if (feof(fp))
|
|
return EOF;
|
|
|
|
assnum = 0;
|
|
|
|
for (;;) {
|
|
for (;;) {
|
|
f = *fmt++;
|
|
if (isspace((int)f)) {
|
|
getscanwhite(fp,1,0,6,NULL);
|
|
do {
|
|
f = *fmt++;
|
|
} while (isspace((int)f));
|
|
}
|
|
c = fgetc(fp);
|
|
if (c == EOF)
|
|
return assnum;
|
|
if (f == '%') {
|
|
f = *fmt++;
|
|
if (f != '%' && f != '\0')
|
|
break;
|
|
}
|
|
if (f != c || f == '\0') {
|
|
ungetc(c, fp);
|
|
return assnum;
|
|
}
|
|
}
|
|
ungetc(c, fp);
|
|
skip = (f == '*');
|
|
if (!skip && count == 0) {
|
|
return assnum;
|
|
}
|
|
if (skip)
|
|
f = *fmt++;
|
|
width = 0;
|
|
while (f >= '0' && f <= '9') {
|
|
width = 10 * width + f - '0';
|
|
f = *fmt++;
|
|
}
|
|
switch (f) {
|
|
case 'c':
|
|
if (width == 0)
|
|
width = 1;
|
|
getscanfield(fp,skip,width,0,NULL,&str);
|
|
break;
|
|
case 's':
|
|
getscanwhite(fp,1,0,6,NULL);
|
|
if (feof(fp))
|
|
return assnum;
|
|
getscanwhite(fp,skip,width,-6,&str);
|
|
break;
|
|
case '[':
|
|
f = *fmt;
|
|
comp = (f == '^');
|
|
if (comp)
|
|
f = *++fmt;
|
|
scanptr = fmt;
|
|
if (f == '\0')
|
|
return assnum;
|
|
fmt = strchr((f == ']' ? fmt + 1 : fmt), ']');
|
|
if (fmt == NULL)
|
|
return assnum;
|
|
scannum = fmt - scanptr;
|
|
if (comp)
|
|
scannum = -scannum;
|
|
fmt++;
|
|
getscanfield(fp,skip,
|
|
width,scannum,scanptr,&str);
|
|
break;
|
|
case 'f':
|
|
case 'e':
|
|
case 'r':
|
|
case 'i':
|
|
getscanwhite(fp,1,0,6, NULL);
|
|
if (feof(fp))
|
|
return assnum;
|
|
if (skip) {
|
|
fskipnum(fp);
|
|
continue;
|
|
}
|
|
assnum++;
|
|
var = *vals++;
|
|
if (var->v_type != V_ADDR)
|
|
math_error("This should not happen!!");
|
|
var = var->v_addr;
|
|
subtype = var->v_subtype;
|
|
freevalue(var);
|
|
count--;
|
|
freadsum(fp, var);
|
|
var->v_subtype = subtype;
|
|
continue;
|
|
case 'n':
|
|
assnum++;
|
|
var = *vals++;
|
|
count--;
|
|
if (var->v_type != V_ADDR)
|
|
math_error("This should not happen!!");
|
|
var = var->v_addr;
|
|
subtype = var->v_subtype;
|
|
freevalue(var);
|
|
var->v_type = V_NUM;
|
|
var->v_num = qalloc();
|
|
f_tell(fp, &cur);
|
|
var->v_num->num = filepos2z(cur);
|
|
var->v_subtype = subtype;
|
|
continue;
|
|
default:
|
|
fprintf(stderr, "Unsupported scan specifier");
|
|
return assnum;
|
|
}
|
|
if (!skip) {
|
|
assnum++;
|
|
var = *vals++;
|
|
count--;
|
|
if (var->v_type != V_ADDR)
|
|
math_error("Assigning to non-variable");
|
|
var = var->v_addr;
|
|
subtype = var->v_subtype;
|
|
freevalue(var);
|
|
var->v_type = V_STR;
|
|
var->v_str = makestring(str);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
fscanfid(FILEID id, char *fmt, int count, VALUE **vals)
|
|
{
|
|
FILEIO *fiop;
|
|
FILE *fp;
|
|
FILEPOS fpos;
|
|
|
|
fiop = findid(id, false);
|
|
if (fiop == NULL)
|
|
return -2;
|
|
|
|
fp = fiop->fp;
|
|
|
|
if (fiop->action == 'w') {
|
|
f_tell(fp, &fpos);
|
|
fflush(fp);
|
|
if (f_seek_set(fp, &fpos) < 0)
|
|
return -4;
|
|
}
|
|
fiop->action = 'r';
|
|
|
|
return fscanfile(fp, fmt, count, vals);
|
|
}
|
|
|
|
|
|
int
|
|
scanfstr(char *str, char *fmt, int count, VALUE **vals)
|
|
{
|
|
FILE *fp;
|
|
int i;
|
|
|
|
fp = tmpfile();
|
|
if (fp == NULL)
|
|
return EOF;
|
|
fputs(str, fp);
|
|
rewind(fp);
|
|
i = fscanfile(fp, fmt, count, vals);
|
|
fclose(fp);
|
|
return i;
|
|
}
|
|
|
|
|
|
/*
|
|
* Read a number in floating-point format from a file. The first dot,
|
|
* if any, is considered as the decimal point; later dots are ignored.
|
|
* For example, -23.45..67. is interpreted as -23.4567
|
|
* An optional 'e' or 'E' indicates multiplication by a power or 10,
|
|
* e.g. -23.45e-6 has the effect of -23.45 * 10^-6. The reading
|
|
* ceases when a character other than a digit, a leading sign,
|
|
* a sign immediately following 'e' or 'E', or a dot is encountered.
|
|
* Absence of digits is interpreted as zero.
|
|
*/
|
|
S_FUNC void
|
|
freadnum(FILE *fp, VALUE *valptr)
|
|
{
|
|
ZVALUE num, zden, newnum, newden, div, tmp;
|
|
NUMBER *q;
|
|
COMPLEX *c;
|
|
VALUE val;
|
|
char ch;
|
|
LEN i;
|
|
HALF *a;
|
|
FULL f;
|
|
long decimals, exp;
|
|
bool sign, negexp, havedp, imag, exptoobig;
|
|
|
|
decimals = 0;
|
|
exp = 0;
|
|
sign = false;
|
|
negexp = false;
|
|
havedp = false;
|
|
imag = false;
|
|
exptoobig = false;
|
|
|
|
ch = fgetc(fp);
|
|
if (ch == '+' || ch == '-') {
|
|
if (ch == '-')
|
|
sign = true;
|
|
ch = fgetc(fp);
|
|
}
|
|
num.v = alloc(1);
|
|
*num.v = 0;
|
|
num.len = 1;
|
|
num.sign = sign;
|
|
for (;;) {
|
|
if (ch >= '0' && ch <= '9') {
|
|
f = (FULL) (ch - '0');
|
|
a = num.v;
|
|
i = num.len;
|
|
while (i-- > 0) {
|
|
f = 10 * (FULL) *a + f;
|
|
*a++ = (HALF) f;
|
|
f >>= BASEB;
|
|
}
|
|
if (f) {
|
|
a = alloc(num.len + 1);
|
|
memcpy(a, num.v, num.len * sizeof(HALF));
|
|
a[num.len] = (HALF) f;
|
|
num.len++;
|
|
freeh(num.v);
|
|
num.v = a;
|
|
}
|
|
if (havedp)
|
|
decimals++;
|
|
}
|
|
else if (ch == '.')
|
|
havedp = true;
|
|
else
|
|
break;
|
|
ch = fgetc(fp);
|
|
}
|
|
if (ch == 'e' || ch == 'E') {
|
|
ch = fgetc(fp);
|
|
if (ch == '+' || ch == '-') {
|
|
if (ch == '-')
|
|
negexp = true;
|
|
ch = fgetc(fp);
|
|
}
|
|
while (ch >= '0' && ch <= '9') {
|
|
if (!exptoobig) {
|
|
exp = (exp * 10) + ch - '0';
|
|
if (exp > 1000000)
|
|
exptoobig = true;
|
|
}
|
|
ch = fgetc(fp);
|
|
}
|
|
}
|
|
if (ch == 'i' || ch == 'I') {
|
|
imag = true;
|
|
} else {
|
|
ungetc(ch, fp);
|
|
}
|
|
|
|
if (ziszero(num)) {
|
|
zfree(num);
|
|
val.v_type = V_NUM;
|
|
val.v_subtype = V_NOSUBTYPE;
|
|
val.v_num = qlink(&_qzero_);
|
|
*valptr = val;
|
|
return;
|
|
}
|
|
if (exptoobig) {
|
|
zfree(num);
|
|
*valptr = error_value(E_BIGEXP);
|
|
return;
|
|
}
|
|
ztenpow(decimals, &zden);
|
|
if (exp) {
|
|
ztenpow(exp, &tmp);
|
|
if (negexp) {
|
|
zmul(zden, tmp, &newden);
|
|
zfree(zden);
|
|
zden = newden;
|
|
} else {
|
|
zmul(num, tmp, &newnum);
|
|
zfree(num);
|
|
num = newnum;
|
|
}
|
|
zfree(tmp);
|
|
}
|
|
if (!zisunit(num) && !zisunit(zden)) {
|
|
zgcd(num, zden, &div);
|
|
if (!zisunit(div)) {
|
|
zequo(num, div, &newnum);
|
|
zfree(num);
|
|
zequo(zden, div, &newden);
|
|
zfree(zden);
|
|
num = newnum;
|
|
zden = newden;
|
|
}
|
|
zfree(div);
|
|
}
|
|
q = qalloc();
|
|
q->num = num;
|
|
q->den = zden;
|
|
if (imag) {
|
|
c = comalloc();
|
|
qfree(c->imag);
|
|
c->imag = q;
|
|
val.v_type = V_COM;
|
|
val.v_com = c;
|
|
} else {
|
|
val.v_type = V_NUM;
|
|
val.v_num = q;
|
|
}
|
|
val.v_subtype = V_NOSUBTYPE;
|
|
*valptr = val;
|
|
}
|
|
|
|
|
|
S_FUNC void
|
|
freadsum(FILE *fp, VALUE *valptr)
|
|
{
|
|
VALUE v1, v2, v3;
|
|
char ch;
|
|
|
|
|
|
freadprod(fp, &v1);
|
|
|
|
ch = fgetc(fp);
|
|
while (ch == '+' || ch == '-') {
|
|
freadprod(fp, &v2);
|
|
if (ch == '+')
|
|
addvalue(&v1, &v2, &v3);
|
|
else
|
|
subvalue(&v1, &v2, &v3);
|
|
freevalue(&v1);
|
|
freevalue(&v2);
|
|
v1 = v3;
|
|
ch = fgetc(fp);
|
|
}
|
|
ungetc(ch, fp);
|
|
*valptr = v1;
|
|
}
|
|
|
|
|
|
S_FUNC void
|
|
freadprod(FILE *fp, VALUE *valptr)
|
|
{
|
|
VALUE v1, v2, v3;
|
|
char ch;
|
|
|
|
freadnum(fp, &v1);
|
|
ch = fgetc(fp);
|
|
while (ch == '*' || ch == '/') {
|
|
freadnum(fp, &v2);
|
|
if (ch == '*')
|
|
mulvalue(&v1, &v2, &v3);
|
|
else
|
|
divvalue(&v1, &v2, &v3);
|
|
freevalue(&v1);
|
|
freevalue(&v2);
|
|
v1 = v3;
|
|
ch = fgetc(fp);
|
|
}
|
|
ungetc(ch, fp);
|
|
*valptr = v1;
|
|
}
|
|
|
|
|
|
S_FUNC void
|
|
fskipnum(FILE *fp)
|
|
{
|
|
char ch;
|
|
|
|
do {
|
|
ch = fgetc(fp);
|
|
if (ch == '+' || ch == '-')
|
|
ch = fgetc(fp);
|
|
while ((ch >= '0' && ch <= '9') || ch == '.')
|
|
ch = fgetc(fp);
|
|
if (ch == 'e' || ch == 'E') {
|
|
ch = fgetc(fp);
|
|
if (ch == '+' || ch == '-')
|
|
ch = fgetc(fp);
|
|
while (ch >= '0' && ch <= '9')
|
|
ch = fgetc(fp);
|
|
}
|
|
if (ch == 'i' || ch == 'I')
|
|
ch = fgetc(fp);
|
|
} while (ch == '/' || ch == '*' || ch == '+' || ch == '-');
|
|
|
|
ungetc(ch, fp);
|
|
}
|
|
|
|
|
|
int
|
|
isattyid(FILEID id)
|
|
{
|
|
FILEIO *fiop;
|
|
|
|
fiop = findid(id, -1);
|
|
if (fiop == NULL)
|
|
return -2;
|
|
return isatty(fileno(fiop->fp));
|
|
}
|
|
|
|
|
|
/*
|
|
* fsearch - search for a string in a file
|
|
*
|
|
* given:
|
|
* id FILEID to search
|
|
* str string to look for
|
|
* pos file position to start at (NULL => current position)
|
|
*
|
|
* returns:
|
|
* EOF if system error
|
|
* other negative integer if file not open, etc.
|
|
* positive integer if string not found
|
|
* zero if string found, position stored at res
|
|
*
|
|
* XXX - This search is a translation of the original search that did not
|
|
* work with large files. The search algorithm used is slow and
|
|
* should be speed up much more.
|
|
*/
|
|
int
|
|
fsearch(FILEID id, char *str, ZVALUE start, ZVALUE end, ZVALUE *res)
|
|
{
|
|
FILEIO *fiop; /* FILEIO of file id */
|
|
FILEPOS cur; /* current file position */
|
|
ZVALUE tmp, tmp2; /* temporary ZVALUEs */
|
|
char c; /* str comparison character */
|
|
int r; /* character read from file */
|
|
char *s; /* str comparison pointer */
|
|
long k = 0;
|
|
|
|
/* get FILEIO */
|
|
fiop = findid(id, false);
|
|
if (fiop == NULL)
|
|
return -2;
|
|
|
|
/*
|
|
* file setup
|
|
*/
|
|
if (fiop->action == 'w')
|
|
fflush(fiop->fp);
|
|
|
|
zsub(end, start, &tmp2);
|
|
|
|
if (zisneg(tmp2)) {
|
|
zfree(tmp2);
|
|
return 1;
|
|
}
|
|
|
|
tmp.sign = 0;
|
|
tmp.len = tmp2.len;
|
|
tmp.v = alloc(tmp.len);
|
|
zcopyval(tmp2, tmp);
|
|
zfree(tmp2);
|
|
|
|
cur = z2filepos(start);
|
|
|
|
if (f_seek_set(fiop->fp, &cur) < 0) {
|
|
zfree(tmp);
|
|
return EOF;
|
|
}
|
|
|
|
/*
|
|
* search setup
|
|
*/
|
|
/* note the first str search character */
|
|
c = *str++;
|
|
|
|
if (c == '\0') {
|
|
zfree(tmp);
|
|
return 2;
|
|
}
|
|
clearerr(fiop->fp);
|
|
while ((r = fgetc(fiop->fp)) != EOF) {
|
|
if ((char)r == c) {
|
|
(void) f_tell(fiop->fp, &cur);
|
|
s = str;
|
|
while (*s) {
|
|
r = fgetc(fiop->fp);
|
|
if ((char)r != *s)
|
|
break;
|
|
s++;
|
|
}
|
|
if (r == EOF)
|
|
break;
|
|
if (*s == '\0') {
|
|
zfree(tmp);
|
|
tmp = filepos2z(cur);
|
|
zsub(tmp, _one_, res);
|
|
zfree(tmp);
|
|
return 0;
|
|
}
|
|
(void) f_seek_set(fiop->fp, &cur);
|
|
}
|
|
if (*tmp.v) {
|
|
(*tmp.v)--;
|
|
} else {
|
|
if (tmp.len == 1)
|
|
break;
|
|
k = 0;
|
|
do {
|
|
tmp.v[k++] = BASE1;
|
|
}
|
|
while (k < tmp.len && tmp.v[k] == 0);
|
|
if (k == tmp.len) {
|
|
math_error("This should not happen");
|
|
not_reached();
|
|
}
|
|
tmp.v[k]--;
|
|
if (tmp.v[tmp.len - 1] == 0)
|
|
tmp.len--;
|
|
}
|
|
}
|
|
zfree(tmp);
|
|
if (ferror(fiop->fp))
|
|
return EOF;
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* frsearch - reverse search for a string in a file
|
|
*
|
|
* given:
|
|
* id FILEID to search
|
|
* str string to look for
|
|
* search starts at pos = first and continues for decreasing
|
|
* pos >= last
|
|
*
|
|
* returns:
|
|
* EOF if system error
|
|
* other negative integer if file not open, etc.
|
|
* positive integer if string not found
|
|
* zero if string found, position stored at res
|
|
*
|
|
* XXX - This search is a translation of the original search that did not
|
|
* work with large files. The search algorithm used is so slow
|
|
* as to be painful to the user and needs to be sped up much more.
|
|
*/
|
|
int
|
|
frsearch(FILEID id, char *str, ZVALUE first, ZVALUE last, ZVALUE *res)
|
|
{
|
|
FILEIO *fiop; /* FILEIO of file id */
|
|
FILEPOS cur; /* current file position */
|
|
ZVALUE pos; /* current file position as ZVALUE */
|
|
ZVALUE tmp; /* temporary ZVALUEs */
|
|
char c; /* str comparison character */
|
|
int r; /* character read from file */
|
|
char *s; /* str comparison pointer */
|
|
|
|
/* get FILEIO */
|
|
fiop = findid(id, false);
|
|
if (fiop == NULL)
|
|
return -2;
|
|
|
|
/*
|
|
* file setup
|
|
*/
|
|
if (fiop->action == 'w')
|
|
fflush(fiop->fp);
|
|
|
|
zcopy(first, &pos);
|
|
|
|
/*
|
|
* search setup
|
|
*/
|
|
/* note the first str search character */
|
|
c = *str++;
|
|
|
|
if (c == '\0') {
|
|
cur = z2filepos(pos);
|
|
if (f_seek_set(fiop->fp, &cur) < 0) {
|
|
zfree(pos);
|
|
return EOF;
|
|
}
|
|
*res = pos;
|
|
return 0;
|
|
}
|
|
|
|
clearerr(fiop->fp);
|
|
|
|
while(zrel(pos, last) >= 0) {
|
|
cur = z2filepos(pos);
|
|
if (f_seek_set(fiop->fp, &cur) < 0) {
|
|
zfree(pos);
|
|
return EOF;
|
|
}
|
|
r = fgetc(fiop->fp);
|
|
if (r == EOF) {
|
|
zfree(pos);
|
|
return EOF;
|
|
}
|
|
if ((char) r == c) {
|
|
s = str;
|
|
while (*s) {
|
|
r = fgetc(fiop->fp);
|
|
if ((char)r != *s)
|
|
break;
|
|
s++;
|
|
}
|
|
if (r == EOF) {
|
|
zfree(pos);
|
|
return EOF;
|
|
}
|
|
if (*s == '\0') {
|
|
*res = pos;
|
|
ungetc(r, fiop->fp);
|
|
return 0;
|
|
}
|
|
}
|
|
zsub(pos, _one_, &tmp);
|
|
zfree(pos);
|
|
pos = tmp;
|
|
}
|
|
cur = z2filepos(last);
|
|
f_seek_set(fiop->fp, &cur);
|
|
zfree(pos);
|
|
if (ferror(fiop->fp))
|
|
return EOF;
|
|
return 1;
|
|
}
|
|
|
|
|
|
char *
|
|
findfname(FILEID id)
|
|
{
|
|
FILEIO *fiop;
|
|
|
|
fiop = findid(id, -1);
|
|
|
|
if (fiop == NULL)
|
|
return NULL;
|
|
|
|
return fiop->name;
|
|
}
|