📄 input.c
字号:
/* * input - nested input source file reader * * Copyright (C) 1999-2006 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. * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * * @(#) $Revision: 29.12 $ * @(#) $Id: input.c,v 29.12 2006/12/15 17:02:49 chongo Exp $ * @(#) $Source: /usr/local/src/cmd/calc/RCS/input.c,v $ * * 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)# include <pwd.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(__MSDOS__)#include <limits.h>#define _fullpath(f,n,s) (_fixpath(n,f),f)#define _MAX_PATH PATH_MAX#endif#include "calc.h"#include "conf.h"#include "hist.h"extern int stdin_tty; /* TRUE if stdin is a tty */extern FILE *f_open(char *name, char *mode);extern FILE *curstream(void);#define TTYSIZE 100 /* reallocation size for terminal buffers */#define MAXDEPTH 10 /* 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 */static int openfile(char *name);static int ttychar(void);static int isinoderead(struct stat *sbuf);static int findfreeread(void);static int addreadset(char *name, char *path, struct stat *sbuf);static 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 */intopensearchfile(char *name, char *pathlist, char *extension, int rd_once){ int i; char *cp; char *path; /* name being searched for */ 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"); /*NOTREACHED*/ } /* * 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 = malloc(pathlen+1 + 1 + namelen+1 + extlen+1 + 1 + 1); if (path == NULL) { math_error("Cannot allocate filename path buffer"); /*NOTREACHED*/ } /* * 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; strncpy(cp, name, namelen+1); i = openfile(path); if ((i < 0) && (extension != NULL && extension[0] != '\0')) { strcat(path, extension); 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 filename, or a 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"); /*NOTREACHED*/ } if (mode == NULL) { math_error("NULL mode given to f_pathopen"); /*NOTREACHED*/ } /* * 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"); /*NOTREACHED*/ } /* * Search through the path list for the file */ pathlist--; do { pathlist++; cp = path; while (*pathlist && (*pathlist != LISTCHAR)) *cp++ = *pathlist++; if (cp != path) *cp++ = PATHCHAR; strncpy(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); fclose(ret); math_error("cannot malloc return openpath buffer"); /*NOTREACHED*/ } } 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 ~ */static char *homeexpand(char *name){#if defined(_WIN32) return NULL;#else /* Windoz 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 */ /* 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; } strncpy(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; } strncpy(username, name+1, after-name-1); username[after-name-1] = '\0'; /* 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 */ fullpath = (char *)malloc(strlen(home2)+strlen(after)+1); if (fullpath == NULL) { return NULL; } sprintf(fullpath, "%s%s", home2, after); return fullpath;#endif /* Windoz 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 */static intopenfile(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; } strncpy(cip->i_name, name, namelen+1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -