📄 input.c
字号:
/* * Copyright (c) 1994 David I. Bell * Permission is granted to use, distribute, or modify this source, * provided that this copyright notice remains intact. * * Nested input source file reader. * For terminal input, this also provides a simple command stack. */#include <ctype.h>#include <pwd.h>#include "calc.h"#include "config.h"#include "hist.h"#define TTYSIZE 100 /* reallocation size for terminal buffers */#define DEPTH 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_str; /* current string for input (if not NULL) */ char *i_origstr; /* original string so it can be freed */ 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[DEPTH]; /* input sources */static int openfile MATH_PROTO((char *name));static int ttychar MATH_PROTO((void));static void closeinput MATH_PROTO((void));static int isinoderead MATH_PROTO((struct stat *sbuf));static int findfreeread MATH_PROTO((void));static int addreadset MATH_PROTO((char *name, char *path, struct stat *sbuf));/* * Open an input file by possibly searching through a path list * and also possibly applying the specified extension. For example: * opensearchfile("barf", ".:/tmp", ".c") 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. */intopensearchfile(name, pathlist, extension, rd_once) char *name; /* file name to be read */ char *pathlist; /* list of colon separated paths (or NULL) */ char *extension; /* extra extension to try (or NULL) */ int rd_once; /* TRUE => do not reread a file */{ int i; char *cp; char path[PATHSIZE+1]; /* name being searched for */ struct stat statbuf; /* stat of the path */ /* * Don't try the extension if the filename already contains it. */ if (extension) { i = strlen(name) - strlen(extension); if ((i >= 0) && (strcmp(&name[i], extension) == 0)) extension = NULL; } /* * If the name is absolute, or if there is no path list, then * make one which just searches for the name straight. Then * search through the path list for the file, without and with * the specified extension. */ if (name[0] == PATHCHAR || name[0] == HOMECHAR || (name[0] == DOTCHAR && name[1] == PATHCHAR) || pathlist == NULL) { pathlist = ""; } pathlist--; do { pathlist++; cp = path; while (*pathlist && (*pathlist != LISTCHAR)) *cp++ = *pathlist++; if (cp != path) *cp++ = PATHCHAR; strcpy(cp, name); i = openfile(path); if ((i < 0) && extension) { strcat(path, extension); i = openfile(path); } } while ((i < 0) && *pathlist); /* examine what our search produced */ if (i < 0 || cip->i_fp == NULL) { /* cannot find a file to open */ return -1; } if (fstat(fileno(cip->i_fp), &statbuf) < 0) { /* unable to fstat the open file */ return -1; } /* 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(); return 1; } /* add this name to the readset */ if (addreadset(name, path, &statbuf) < 0) { /* cannot add to readset */ closeinput(); return -1; } /* file was added to/updated in readset */ return 0;}/* * 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. */static char *homeexpand(name) char *name; /* a filename with a leading ~ */{ 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[PATHSIZE+1]; /* extratced username */ /* 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 = (char *)malloc(strlen(ent->pw_dir)+1); strcpy(fullpath, ent->pw_dir); return fullpath; } if (after-name > PATHSIZE+1) { /* username is too big */ 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); 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); sprintf(fullpath, "%s%s", home2, after); return fullpath;}/* * f_open - ~-expand a filename and fopen() it */FILE *f_open(name, mode) char *name; /* the filename to open */ char *mode; /* the fopen mode to use */{ FILE *fp; /* open file descriptor */ char *fullname; /* file name with HOMECHAR expansion */ /* * 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. */static intopenfile(name) char *name; /* file name to be read */{ FILE *fp; /* open file descriptor */ if (depth >= DEPTH) return -1; fp = f_open(name, "r"); if (fp == NULL) return -1; cip++; cip->i_state = IS_READ; cip->i_char = '\0'; cip->i_str = NULL; cip->i_origstr = NULL; cip->i_ttystr = NULL; cip->i_fp = fp; cip->i_line = 1; cip->i_name = (char *)malloc(strlen(name) + 1); strcpy(cip->i_name, name); depth++; return 0;}/* * Open a string for scanning. String is ended by a null character. * String is copied into local memory so it can be trashed afterwards. * Returns -1 if cannot open string. */intopenstring(str) char *str; /* string to be opened */{ char *cp; /* copied string */ if ((depth >= DEPTH) || (str == NULL)) return -1; cp = (char *)malloc(strlen(str) + 1); if (cp == NULL) return -1; strcpy(cp, str); cip++; cip->i_state = IS_READ; cip->i_char = '\0'; cip->i_str = cp; cip->i_origstr = cp; cip->i_fp = NULL; cip->i_name = NULL; cip->i_ttystr = NULL; cip->i_line = 1; depth++; return 0;}/* * Set to read input from the terminal. * Returns -1 if there is no more depth for input. */intopenterminal(){ if (depth >= DEPTH) return -1; cip++; cip->i_state = IS_READ; cip->i_char = '\0'; cip->i_str = NULL; cip->i_origstr = NULL; cip->i_ttystr = NULL; cip->i_fp = NULL; cip->i_name = NULL; cip->i_line = 1; depth++; return 0;}/* * Close the current input source. */static voidcloseinput(){ if (depth <= 0) return; if (cip->i_origstr) free(cip->i_origstr); if (cip->i_fp) fclose(cip->i_fp); if (cip->i_name) free(cip->i_name); cip--; depth--;}/* * Reset the input sources back to the initial state. */voidresetinput(){ while (depth > 0) closeinput(); cip = inputs; noprompt = FALSE;}/* * Set the prompt for terminal input. */voidsetprompt(str) char *str;{ prompt = str; noprompt = FALSE;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -