mirror of
https://github.com/lcn2/calc.git
synced 2025-08-16 01:03:29 +03:00
Converted all ASCII tabs to ASCII spaces using a 8 character tab stop, for all files, except for all Makefiles (plus rpm.mk). The `git diff -w` reports no changes.
2853 lines
80 KiB
C
2853 lines
80 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 "strl.h"
|
|
|
|
#if defined(_WIN32) || defined(_WIN64)
|
|
# include <io.h>
|
|
#endif
|
|
|
|
|
|
#include "errtbl.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, (HALF *)&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;
|
|
}
|