Files
calc/input.c
Landon Curt Noll db77e29a23 convert ASCII TABs to ASCII SPACEs
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.
2024-07-11 22:03:52 -07:00

1151 lines
35 KiB
C

/*
* input - nested input source file reader
*
* Copyright (C) 1999-2007,2014,2018,2021-2023 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: 1990/02/15 01:48:16
* File existed as early as: before 1990
*
* Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/
*/
/*
* Nested input source file reader.
* For terminal input, this also provides a simple command stack.
*/
#include <stdio.h>
#include <ctype.h>
#if !defined(_WIN32) && !defined(_WIN64)
# include <pwd.h>
#else
# include <stdlib.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include "have_unistd.h"
#if defined(HAVE_UNISTD_H)
#include <unistd.h>
#endif
#if defined(_WIN32) || defined(_WIN64)
#include <limits.h>
#define _fullpath(f,n,s) (_fixpath(n,f),f)
#endif
#include "calc.h"
#include "conf.h"
#include "hist.h"
#include "strl.h"
#include "errtbl.h"
#include "banned.h" /* include after system header <> includes */
EXTERN int stdin_tty; /* TRUE if stdin is a tty */
E_FUNC FILE *f_open(char *name, char *mode);
E_FUNC FILE *curstream(void);
#define TTYSIZE 8191 /* reallocation size for terminal buffers */
#define MAXDEPTH 255 /* maximum depth of input */
#define IS_READ 1 /* reading normally */
#define IS_REREAD 2 /* reread current character */
#define chartoint(ch) ((ch) & 0xff) /* make sure char is not negative */
#define READSET_ALLOC 8 /* readset to allocate chunk size */
typedef struct {
int i_state; /* state (read, reread) */
int i_char; /* currently read char */
long i_line; /* line number */
char *i_cp; /* pointer to string character to be read */
char *i_str; /* start of string copy to be read, or NULL */
long i_num; /* number of string characters remaining */
char *i_ttystr; /* current character of tty line (or NULL) */
FILE *i_fp; /* current file for input (if not NULL) */
char *i_name; /* file name if known */
} INPUT;
/* files that calc has read or included */
typedef struct {
int active; /* != 0 => active entry, 0 => unused entry */
char *name; /* name used to read file */
char *path; /* real path used to open file */
struct stat inode; /* inode information for file */
} READSET;
STATIC READSET *readset = NULL; /* array of files read */
STATIC int maxreadset = 0; /* length of readset */
STATIC int linesize; /* current max size of input line */
STATIC char *linebuf; /* current input line buffer */
STATIC char *prompt; /* current prompt for terminal */
STATIC BOOL noprompt; /* TRUE if should not print prompt */
STATIC int depth; /* current input depth */
STATIC INPUT *cip; /* current input source */
STATIC INPUT inputs[MAXDEPTH]; /* input sources */
S_FUNC int openfile(char *name);
S_FUNC int ttychar(void);
S_FUNC int isinoderead(struct stat *sbuf);
S_FUNC int findfreeread(void);
S_FUNC int addreadset(char *name, char *path, struct stat *sbuf);
S_FUNC char *homeexpand(char *name);
/*
* Open an input file by possibly searching through a path list
* and also possibly applying the specified extension. For example:
*
* opensearchfile("barf", ".:/tmp", ".c", rd_once)
*
* searches in order for the files:
*
* "./barf", "./barf.c", "/tmp/barf", and "/tmp/barf.c".
*
* Returns -1 if we could not open a file or error.
* Returns 1 if file was opened and added to/updated in the readset
* Returns 0 if file was already in the readset and reopen was 0.
*
* given:
* name file name to be read
* pathlist list of colon separated paths (or NULL)
* extension extra extension to try (or NULL)
* rd_once TRUE => do not reread a file
*/
int
opensearchfile(char *name, char *pathlist, char *extension, int rd_once)
{
int i;
char *cp;
char *path; /* name being searched for */
size_t path_alloc; /* length of malloced path */
struct stat statbuf; /* stat of the path */
size_t namelen; /* length of name */
size_t extlen; /* length of the extension if non-NULL or 0 */
size_t pathlen; /* length of the pathlist if non-NULL or 0 */
/* firewall */
if (name == NULL) {
math_error("NULL name given to opensearchfile");
not_reached();
}
/*
* We ignore the pathlist of the file is absolute, dot-relative
* or tilde-relative or if there is no search path.
*/
if (name[0] == PATHCHAR ||
name[0] == HOMECHAR ||
(name[0] == DOTCHAR && name[1] == '\0') ||
(name[0] == DOTCHAR && name[1] == DOTCHAR && name[2] == '\0') ||
(name[0] == DOTCHAR && name[1] == PATHCHAR) ||
(name[0] == DOTCHAR && name[1] == DOTCHAR && name[2] == PATHCHAR) ||
pathlist == NULL) {
pathlist = "";
}
/*
* allocate storage for the longest name being searched for
*
* We will allocate more than we could ever want/need.
* The longest we could ever need would be:
*
* pathlist (as a single long string)
* /
* name
* .
* extension
* \0
* guard byte
*/
namelen = strlen(name);
if (extension != NULL) {
extlen = strlen(extension);
} else {
extlen = 0;
}
pathlen = strlen(pathlist);
path_alloc = pathlen+1 + 1 + namelen+1 + extlen+1 + 1 + 1;
path = malloc(path_alloc+1);
if (path == NULL) {
math_error("Cannot allocate filename path buffer");
not_reached();
}
path[0] = '\0'; /* paranoia */
path[path_alloc] = '\0'; /* paranoia */
/*
* Don't try the extension if the filename already contains it.
*/
if (extension != NULL && namelen >= extlen &&
strcmp(&name[namelen-extlen], extension) == 0) {
extension = NULL;
}
/*
* Search through the path list for the file
*/
pathlist--;
do {
pathlist++;
cp = path;
while (*pathlist && (*pathlist != LISTCHAR))
*cp++ = *pathlist++;
if (cp != path)
*cp++ = PATHCHAR;
strlcpy(cp, name, namelen+1);
i = openfile(path);
if ((i < 0) &&
(extension != NULL && extension[0] != '\0')) {
strlcat(path, extension, path_alloc+1);
i = openfile(path);
}
} while ((i < 0) && *pathlist);
/* examine what our search produced */
if (i < 0) {
free(path);
return i;
}
if (cip->i_fp == NULL) {
/* cannot find a file to open */
free(path);
return -3;
}
if (fstat(fileno(cip->i_fp), &statbuf) < 0) {
/* unable to fstat the open file */
free(path);
return -4;
}
/* note if we will reopen a file and if that is allowed */
if (rd_once == TRUE && isinoderead(&statbuf) >= 0) {
/* file is in readset and reopen is false */
closeinput();
free(path);
return 1;
}
/* add this name to the readset if allowed */
if (addreadset(name, path, &statbuf) < 0) {
/* cannot add to readset */
closeinput();
free(path);
return -1;
}
/* file was added to/updated in readset */
free(path);
return 0;
}
/*
* f_pathopen - open an absolute or relative filename along a search path
*
* Open a file by possibly searching through a path list. For example:
*
* f_pathopen("curds", ".:/tmp:~/pub", "r", NULL)
*
* searches in order for a file that it can open for reading:
*
* "./curds", "/tmp/curds", "~/pub/curds"
*
* NOTE: ~ is expanded accordingly (see homeexpand() below).
*
* However is the file is /absolue/path/name or a ./dot/relative/name, or
* a ~/home/dir/name, or a ~user/home/name, then the pathlist is ignored
* we just attempt to open name.
*
* and opens the first one that exists and allows the mode.
*
* name file name to be read
* mode fopen() mode argument
* (one of "r", "w", "a", "r+", "w+", "a+")
* pathlist list of colon separated paths (or NULL)
* openpath if non-NULL, and file was opened, set to malloced
* path used to open
*
* returns:
* open file stream, NULL ==> file was not found or error
* If file was open and openpath was non-NULL, changed to point
* to path used to open
*/
FILE *
f_pathopen(char *name, char *mode, char *pathlist, char **openpath)
{
char *cp;
char *path; /* name being searched for */
size_t namelen; /* length of name */
size_t pathlen; /* length of the pathlist if non-NULL or 0 */
FILE *ret; /* return open stream or NULL */
/* firewall */
if (name == NULL) {
math_error("NULL name given to f_pathopen");
not_reached();
}
if (mode == NULL) {
math_error("NULL mode given to f_pathopen");
not_reached();
}
/*
* We ignore the pathlist of the file is absolute, dot-relative
* or tilde-relative or if there is no search path.
*/
if (name[0] == PATHCHAR ||
name[0] == HOMECHAR ||
(name[0] == DOTCHAR && name[1] == '\0') ||
(name[0] == DOTCHAR && name[1] == DOTCHAR && name[2] == '\0') ||
(name[0] == DOTCHAR && name[1] == PATHCHAR) ||
(name[0] == DOTCHAR && name[1] == DOTCHAR && name[2] == PATHCHAR) ||
pathlist == NULL) {
pathlist = "";
}
/*
* allocate storage for the longest name being searched for
*
* We will allocate more than we could ever want/need.
* The longest we could ever need would be:
*
* pathlist (as a single long string)
* /
* name
* \0
* guard byte
*/
namelen = strlen(name);
pathlen = strlen(pathlist);
path = malloc(pathlen+1 + 1 + namelen+1 + 1 + 1);
if (path == NULL) {
math_error("Cannot allocate f_pathopen buffer");
not_reached();
}
/*
* Search through the path list for the file
*/
pathlist--;
do {
pathlist++;
cp = path;
while (*pathlist && (*pathlist != LISTCHAR))
*cp++ = *pathlist++;
if (cp != path)
*cp++ = PATHCHAR;
strlcpy(cp, name, namelen+1);
ret = f_open(path, mode);
} while ((ret == NULL) && *pathlist);
/* if caller wants to know the path, malloc it and return it */
if (openpath != NULL && ret != NULL) {
if (path[0] == HOMECHAR) {
*openpath = homeexpand(path);
} else {
*openpath = strdup(path);
}
if (*openpath == NULL) {
free(path);
if ((conf->calc_debug & CALCDBG_TTY) && ret == stdin) {
printf("f_pathopen: closing stdin "
"on malloc return error\n");
}
fclose(ret);
math_error("cannot malloc return openpath buffer");
not_reached();
}
}
free(path);
/* return open file or NULL */
return ret;
}
/*
* Given a filename with a leading ~, expand it into a home directory for
* that user. This function will malloc the space for the expanded path.
*
* If the path is just ~, or begins with ~/, expand it to the home
* directory of the current user. If the environment variable $HOME
* is known, it will be used, otherwise the password file will be
* consulted.
*
* If the path is just ~username, or ~username/, expand it to the home
* directory of that user by looking it up in the password file.
*
* If the password file must be consulted and the username is not found
* a NULL pointer is returned.
*
* given:
* name a filename with a leading ~
*/
S_FUNC char *
homeexpand(char *name)
{
#if defined(_WIN32) || defined(_WIN64)
return NULL;
#else /* Windows free systems */
struct passwd *ent; /* password entry */
char *home2; /* fullpath of the home directory */
char *fullpath; /* the malloced expanded path */
char *after; /* after the ~user or ~ */
char *username; /* extracted username */
size_t fullpath_len; /* length of fullpath */
size_t snprintf_len; /* malloced snprintf buffer length */
/* firewall */
if (name[0] != HOMECHAR)
return NULL;
/*
* obtain the home directory component
*/
switch (name[1]) {
case PATHCHAR: /* ~/... */
case '\0': /* ~ */
home2 = home;
after = name+1;
break;
default: /* ~username or ~username/... */
/* extract the username after the ~ */
after = (char *)strchr(name+2, PATHCHAR);
if (after == NULL) {
/* path is just ~username */
ent = (struct passwd *)getpwnam(name+1);
if (ent == NULL) {
/* unknown user */
return NULL;
}
/* just malloc the home directory and return it */
fullpath_len = strlen(ent->pw_dir);
fullpath = (char *)malloc(fullpath_len+1);
if (fullpath == NULL) {
return NULL;
}
strlcpy(fullpath, ent->pw_dir, fullpath_len+1);
return fullpath;
}
username = (char *) malloc(after-name + 1 + 1);
if (username == NULL) {
/* failed to malloc username */
return NULL;
}
strlcpy(username, name+1, after-name+1+1);
/* get that user's home directory */
ent = (struct passwd *)getpwnam(username);
free(username);
if (ent == NULL) {
/* unknown user */
return NULL;
}
home2 = ent->pw_dir;
break;
}
/*
* build the fullpath given the home directory
*/
snprintf_len = strlen(home2)+strlen(after) + 1;
fullpath = (char *)malloc(snprintf_len+1);
if (fullpath == NULL) {
return NULL;
}
snprintf(fullpath, snprintf_len, "%s%s", home2, after);
fullpath[snprintf_len] = '\0'; /* paranoia */
return fullpath;
#endif /* Windows free systems */
}
/*
* f_open - ~-expand a filename and fopen() it
*
* given:
* name the filename to open
* mode fopen() mode argument
* (one of "r", "w", "a", "r+", "w+", "a+")
*/
FILE *
f_open(char *name, char *mode)
{
FILE *fp; /* open file descriptor */
char *fullname; /* file name with HOMECHAR expansion */
/*
* be sore we are allowed to open a file in this mode
*/
if (!allow_read && !allow_write) {
/* no reads and no writes means no opens! */
if (run_state > RUN_BEGIN) {
fprintf(stderr,
"open of %s mode %s - %s\n", name, mode,
"open for read or write disallowed by -m\n");
}
return NULL;
} else if (!allow_read && strchr(mode, 'r') != NULL) {
/* reading new files disallowed */
if (run_state > RUN_BEGIN) {
fprintf(stderr,
"open of %s mode %s - %s\n", name, mode,
"open for read disallowed by -m\n");
}
return NULL;
} else if (!allow_write &&
(strchr(mode, 'w') != NULL ||
strchr(mode, 'a') != NULL ||
strchr(mode, '+') != NULL)) {
/* writing new files disallowed */
if (run_state > RUN_BEGIN) {
fprintf(stderr,
"open of %s mode %s - %s\n", name, mode,
"open for write disallowed by -m\n");
}
return NULL;
}
/*
* expand ~ if needed
*/
if (name[0] == HOMECHAR) {
fullname = homeexpand(name);
if (fullname == NULL)
return NULL;
fp = fopen(fullname, mode);
free(fullname);
} else {
fp = fopen(name, mode);
}
return fp;
}
/*
* Setup for reading from a input file.
* Returns -1 if file could not be opened.
*
* given:
* name file name to be read
*/
S_FUNC int
openfile(char *name)
{
FILE *fp; /* open file descriptor */
size_t namelen;
if (depth >= MAXDEPTH)
return -2;
fp = f_open(name, "r");
if (fp == NULL)
return -1;
cip = inputs + depth++;
cip->i_state = IS_READ;
cip->i_char = '\0';
cip->i_str = NULL;
cip->i_ttystr = NULL;
cip->i_fp = fp;
cip->i_line = 1;
namelen = strlen(name);
cip->i_name = (char *)malloc(namelen+1);
if (cip->i_name == NULL) {
return -1;
}
strlcpy(cip->i_name, name, namelen+1);
return 0;
}
/*
* Return the current input file stream, or NULL if none.
*/
FILE *
curstream(void)
{
if (depth <= 0 || depth > MAXDEPTH)
return NULL;
return cip->i_fp;
}
/*
* Open a string for scanning, num characters to be read.
* String is copied into local memory so it can be trashed afterwards.
* Returns -1 if cannot open string.
*
* given:
* str string to be opened
* num length of string to open
*/
int
openstring(char *str, size_t num)
{
char *cp; /* copied string */
if ((depth >= MAXDEPTH) || (str == NULL))
return -2;
cp = (char *) malloc(num + 1);
if (cp == NULL)
return -1;
strlcpy(cp, str, num+1);
cip = inputs + depth++;
cip->i_state = IS_READ;
cip->i_char = '\0';
cip->i_cp = cp;
cip->i_str = cp;
cip->i_num = num;
cip->i_fp = NULL;
cip->i_name = NULL;
cip->i_ttystr = NULL;
cip->i_line = 1;
return 0;
}
/*
* Set to read input from the terminal.
* Returns -1 if there is no more depth for input.
*/
int
openterminal(void)
{
if (depth >= MAXDEPTH)
return -2;
cip = inputs + depth++;
cip->i_state = IS_READ;
cip->i_char = '\0';
cip->i_str = NULL;
cip->i_ttystr = NULL;
cip->i_fp = NULL;
cip->i_name = NULL;
cip->i_line = 1;
return 0;
}
/*
* Close the current input source.
*/
void
closeinput(void)
{
if (depth <= 0)
return;
if (cip->i_str)
free(cip->i_str);
if (cip->i_fp) {
if ((conf->calc_debug & CALCDBG_TTY) && cip->i_fp == stdin) {
printf("closeinput: closing stdin "
"at depth: %d\n", depth);
}
fclose(cip->i_fp);
}
if (cip->i_name)
free(cip->i_name);
depth--;
cip = depth ? &inputs[depth - 1] : NULL;
}
/*
* Reset the input sources back to the initial state.
*/
void
resetinput(void)
{
while (depth > 0)
closeinput();
noprompt = FALSE;
}
/*
* Set the prompt for terminal input.
*/
void
setprompt(char *str)
{
prompt = str;
noprompt = FALSE;
}
/*
* Read the next character from the current input source.
* End of file closes current input source, and returns EOF character.
*/
int
nextchar(void)
{
int ch; /* current input character */
if (depth == 0) /* input finished */
return EOF;
if (cip->i_state == IS_REREAD) { /* rereading current char */
ch = cip->i_char;
cip->i_state = IS_READ;
if (ch == '\n')
cip->i_line++;
return ch;
}
if (cip->i_str) { /* from string */
if (cip->i_num) {
ch = chartoint(*cip->i_cp++);
cip->i_num--;
} else {
ch = EOF;
}
} else if (cip->i_fp) { /* from file */
ch = fgetc(cip->i_fp);
} else if (!stdin_tty) { /* from file */
ch = fgetc(stdin);
} else { /* from terminal */
ch = ttychar();
}
if (depth > 0)
cip->i_char = ch; /* save for rereads */
if (ch == '\n')
cip->i_line++;
return ch;
}
/*
* Read in the next line of input from the current input source.
* The line is terminated with a null character, and does not contain
* the final newline character. The returned string is only valid
* until the next such call, and so must be copied if necessary.
* Returns NULL on end of file.
*/
char *
nextline(void)
{
char *cp;
int ch;
int len;
cp = linebuf;
if (linesize == 0) {
cp = (char *)malloc(TTYSIZE + 1);
if (cp == NULL) {
math_error("Cannot allocate line buffer");
not_reached();
}
linebuf = cp;
linesize = TTYSIZE;
}
len = 0;
for (;;) {
noprompt = TRUE;
ch = nextchar();
noprompt = FALSE;
if (ch == EOF)
return NULL;
if (ch == '\0')
continue;
if (ch == '\n')
break;
if (len >= linesize) {
cp = (char *)realloc(cp, linesize + TTYSIZE + 1);
if (cp == NULL) {
math_error("Cannot realloc line buffer");
not_reached();
}
linebuf = cp;
linesize += TTYSIZE;
}
cp[len++] = (char)ch;
}
cp[len] = '\0';
return linebuf;
}
/*
* Read the next character from the terminal.
* The routines in the history module are called so that the user
* can use a command history and emacs-like editing of the line.
*/
S_FUNC int
ttychar(void)
{
int ch; /* current char */
int len; /* length of current command */
STATIC char charbuf[256*1024];
/*
* If we have more to read from the saved command line, then do that.
* When we see a newline character, then clear the pointer so we will
* read a new line on the next call.
*/
if (cip->i_ttystr) {
ch = chartoint(*cip->i_ttystr++);
if (ch == '\n')
cip->i_ttystr = NULL;
return ch;
}
/*
* We need another complete line.
*/
abortlevel = 0;
inputwait = TRUE;
len = hist_getline(noprompt ? "" : prompt, charbuf, sizeof(charbuf));
if (len == 0) {
inputwait = FALSE;
return EOF;
}
inputwait = FALSE;
/*
* Handle shell escape if present
*/
if (charbuf[0] == '!') { /* do a shell command */
char *cmd;
int ret;
cmd = charbuf + 1;
if (*cmd == '\0' || *cmd == '\n')
cmd = shell;
if (allow_exec) {
if (conf->calc_debug & CALCDBG_SYSTEM) {
printf("%s\n", cmd);
}
ret = system(cmd);
if (ret < 0) {
fprintf(stderr, "error in cmd: %s\n", cmd);
}
} else {
fprintf(stderr, "execution disallowed by -m flag\n");
}
return '\n';
}
hist_saveline(charbuf, len);
/*
* Return the first character of the line, and set up to
* return the rest of it with later calls.
*/
ch = chartoint(charbuf[0]);
if (ch != '\n')
cip->i_ttystr = charbuf + 1;
return ch;
}
/*
* Return whether or not the input source is the terminal.
*/
BOOL
inputisterminal(void)
{
return ((depth <= 0) || ((cip->i_str == NULL) && (cip->i_fp == NULL)));
}
/*
* Return depth of current input source
*/
int
inputlevel(void)
{
return depth - 1;
}
/*
* Return the name of the current input file.
* Returns NULL for terminal or strings.
*/
char *
inputname(void)
{
if (depth <= 0)
return NULL;
return cip->i_name;
}
/*
* Return the current line number.
*/
long
linenumber(void)
{
if (depth > 0)
return cip->i_line;
return 1;
}
/*
* Restore the next character to be read again on the next nextchar call.
*/
void
reread(void)
{
if ((depth <= 0) || (cip->i_state == IS_REREAD))
return;
cip->i_state = IS_REREAD;
if (cip->i_char == '\n')
cip->i_line--;
}
/*
* Process all startup files found in the $CALCRC path.
*/
void
runrcfiles(void)
{
char path[MAX_CALCRC+1+1]; /* name being searched for */
char *cp;
char *p;
/* execute each file in the list */
while (calcrc != NULL && *calcrc) {
cp = calcrc;
calcrc = (char *) strchr(calcrc + 1, LISTCHAR);
/* load file name into the path */
if (calcrc == NULL) {
strlcpy(path, cp, MAX_CALCRC+1);
} else {
strlcpy(path, cp, calcrc - cp + 1);
}
/* find the start of the path */
p = (path[0] == ':') ? path + 1 : path;
if (p[0] == '\0') {
continue;
}
/* process the current file in the list */
if (openfile(p) < 0) {
/* Unable to open rcfile */
if (c_flag && !d_flag)
fprintf(stderr,
"Unable to open rcfile \"%s\"\n", p);
continue;
}
getcommands(FALSE);
closeinput();
}
}
/*
* isinoderead - determine if we have read a given dev/inode
*
* This function returns the index of the readset element that matches
* a given device/inode, -1 otherwise.
*
*
* _WIN32 and _WIN64 NOTE:
*
* This function likely will not work under _WIN32 or _WIN64.
*
* The sbuf->st_ino is always zero because the FAT and NTFS filesystems
* do not support inodes. Those filesystems don't support links, which
* is why you need this function under UNIX. For _WIN32 or _WIN64, use
* _fullpath() to determine if you have already opened a file.
*
* given:
* sbuf stat of the inode in question
*/
S_FUNC int
isinoderead(struct stat *sbuf)
{
int i;
/* deal with the empty case */
if (readset == NULL || maxreadset <= 0) {
/* readset is empty */
return -1;
}
/* scan the entire readset */
for (i=0; i < maxreadset; ++i) {
#if defined(_WIN32) || defined(_WIN64)
char tmp[MAX_PATH+1];
tmp[MAX_PATH] = '\0';
if (_fullpath(tmp, cip->i_name, MAX_PATH) &&
readset[i].active &&
strcasecmp(readset[i].path, tmp) == 0) {
/* found a match */
return i;
}
#else /* Windows free systems */
if (readset[i].active &&
sbuf->st_dev == readset[i].inode.st_dev &&
sbuf->st_ino == readset[i].inode.st_ino) {
/* found a match */
return i;
}
#endif /* Windows free systems */
}
/* no match found */
return -1;
}
/*
* findfreeread - find the next free readset element
*
* This function will return the index of the next free readset element.
* If needed, this function will allocate new readset elements.
*
* This function returns the index of the next free element, or -1.
*/
S_FUNC int
findfreeread(void)
{
int i;
/* deal with an empty readset case */
if (readset == NULL || maxreadset <= 0) {
/* malloc a new readset */
readset = (READSET *)malloc((READSET_ALLOC+1)*sizeof(READSET));
if (readset == NULL) {
return -1;
}
maxreadset = READSET_ALLOC;
for (i=0; i < READSET_ALLOC; ++i) {
readset[i].active = 0;
}
/* return first entry */
return 0;
}
/* try to find a free readset entry */
for (i=0; i < maxreadset; ++i) {
if (readset[i].active == 0) {
/* found a free readset entry */
return i;
}
}
/* all readset entries are in use, allocate more */
readset = (READSET *)realloc(readset,
(maxreadset+READSET_ALLOC) * sizeof(READSET));
if (readset == NULL) {
return -1;
}
for (i=0; i < READSET_ALLOC; ++i) {
readset[i+maxreadset].active = 0;
}
maxreadset += READSET_ALLOC;
/* return the first newly allocated free entry */
return maxreadset-READSET_ALLOC;
}
/*
* addreadset - add a entry to the readset array if it is not already there
*
* This function attempts to add a file into the readset. If the readset
* has an entry with a matching dev/inode, then that entry is updated with
* the new name and path. If no such readset entry is found, a new entry
* is added.
*
* This function returns the index of the readset entry, or -1 if error.
*
* given:
* name name given to read or include
* path full pathname of file
* sbuf stat of the path
*/
S_FUNC int
addreadset(char *name, char *path, struct stat *sbuf)
{
int ret; /* index to return */
size_t name_len; /* length of read set name */
size_t path_len; /* length of path to read set name */
/* find the inode */
ret = isinoderead(sbuf);
if (ret < 0) {
/* not in readset, find a free node */
ret = findfreeread();
if (ret < 0) {
/* cannot find/form a free readset entry */
return -1;
}
} else {
/* found an readset entry, free old readset data */
if (readset[ret].name != NULL) {
free(readset[ret].name);
}
if (readset[ret].path != NULL) {
free(readset[ret].path);
}
}
/* load our information into the readset entry */
name_len = strlen(name);
readset[ret].name = (char *)malloc(name_len+1);
if (readset[ret].name == NULL) {
return -1;
}
strlcpy(readset[ret].name, name, name_len+1);
#if defined(_WIN32) || defined(_WIN64)
/*
* For _WIN32 or _WIN64, _fullpath expands the path to a fully qualified
* path name, which under _WIN32 or _WIN64 FAT and NTFS is unique, just
* like UNIX inodes. _fullpath also allocated the memory for
* this new longer path name.
*/
{
readset[ret].path = _fullpath(NULL, path, MAX_PATH);
if (readset[ret].path == NULL) {
return -1;
}
}
#else /* Windows free systems */
path_len = strlen(path);
readset[ret].path = (char *)malloc(path_len+1);
if (readset[ret].path == NULL) {
return -1;
}
strlcpy(readset[ret].path, path, path_len+1);
#endif /* Windows free systems */
readset[ret].inode = *sbuf;
readset[ret].active = 1;
/* return index of the newly added entry */
return ret;
}