📄 io.c
字号:
/* * io.c --- routines for dealing with input and output and records *//* * Copyright (C) 1986, 1988, 1989, 1991, 1992 the Free Software Foundation, Inc. * * This file is part of GAWK, the GNU implementation of the * AWK Progamming Language. * * GAWK is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GAWK 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GAWK; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <sys/param.h>#include "awk.h"#ifndef O_RDONLY#include <fcntl.h>#endif#if !defined(S_ISDIR) && defined(S_IFDIR)#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)#endif#ifndef atarist#define INVALID_HANDLE (-1)#else#define INVALID_HANDLE (__SMALLEST_VALID_HANDLE - 1)#endif#if defined(MSDOS) || defined(atarist)#define PIPES_SIMULATED#endifstatic IOBUF *nextfile P((int skipping));static int inrec P((IOBUF *iop));static int iop_close P((IOBUF *iop));struct redirect *redirect P((NODE *tree, int *errflg));static void close_one P((void));static int close_redir P((struct redirect *rp));#ifndef PIPES_SIMULATEDstatic int wait_any P((int interesting));#endifstatic IOBUF *gawk_popen P((char *cmd, struct redirect *rp));static IOBUF *iop_open P((char *file, char *how));static int gawk_pclose P((struct redirect *rp));static int do_pathopen P((char *file));extern FILE *fdopen();extern FILE *popen();static struct redirect *red_head = NULL;extern int output_is_tty;extern NODE *ARGC_node;extern NODE *ARGV_node;extern NODE *ARGIND_node;extern NODE *ERRNO_node;extern NODE **fields_arr;static jmp_buf filebuf; /* for do_nextfile() *//* do_nextfile --- implement gawk "next file" extension */voiddo_nextfile(){ (void) nextfile(1); longjmp(filebuf, 1);}static IOBUF *nextfile(skipping)int skipping;{ static int i = 1; static int files = 0; NODE *arg; int fd = INVALID_HANDLE; static IOBUF *curfile = NULL; if (skipping) { if (curfile != NULL) iop_close(curfile); curfile = NULL; return NULL; } if (curfile != NULL) { if (curfile->cnt == EOF) { (void) iop_close(curfile); curfile = NULL; } else return curfile; } for (; i < (int) (ARGC_node->lnode->numbr); i++) { arg = *assoc_lookup(ARGV_node, tmp_number((AWKNUM) i)); if (arg->stptr[0] == '\0') continue; arg->stptr[arg->stlen] = '\0'; if (! do_unix) { ARGIND_node->var_value->numbr = i; ARGIND_node->var_value->flags = NUM|NUMBER; } if (!arg_assign(arg->stptr)) { files++; curfile = iop_open(arg->stptr, "r"); if (curfile == NULL) fatal("cannot open file `%s' for reading (%s)", arg->stptr, strerror(errno)); /* NOTREACHED */ /* This is a kludge. */ unref(FILENAME_node->var_value); FILENAME_node->var_value = dupnode(arg); FNR = 0; i++; break; } } if (files == 0) { files++; /* no args. -- use stdin */ /* FILENAME is init'ed to "-" */ /* FNR is init'ed to 0 */ curfile = iop_alloc(fileno(stdin)); } return curfile;}voidset_FNR(){ FNR = (int) FNR_node->var_value->numbr;}voidset_NR(){ NR = (int) NR_node->var_value->numbr;}/* * This reads in a record from the input file */static intinrec(iop)IOBUF *iop;{ char *begin; register int cnt; int retval = 0; cnt = get_a_record(&begin, iop, *RS, NULL); if (cnt == EOF) { cnt = 0; retval = 1; } else { NR += 1; FNR += 1; } set_record(begin, cnt, 1); return retval;}static intiop_close(iop)IOBUF *iop;{ int ret; if (iop == NULL) return 0; errno = 0;#ifdef _CRAY /* Work around bug in UNICOS popen */ if (iop->fd < 3) ret = 0; else#endif /* save these for re-use; don't free the storage */ if ((iop->flag & IOP_IS_INTERNAL) != 0) { iop->off = iop->buf; iop->end = iop->buf + strlen(iop->buf); iop->cnt = 0; iop->secsiz = 0; return 0; } /* Don't close standard files or else crufty code elsewhere will lose */ if (iop->fd == fileno(stdin) || iop->fd == fileno(stdout) || iop->fd == fileno(stderr)) ret = 0; else ret = close(iop->fd); if (ret == -1) warning("close of fd %d failed (%s)", iop->fd, strerror(errno)); if ((iop->flag & IOP_NO_FREE) == 0) { /* * be careful -- $0 may still reference the buffer even though * an explicit close is being done; in the future, maybe we * can do this a bit better */ if (iop->buf) { if ((fields_arr[0]->stptr >= iop->buf) && (fields_arr[0]->stptr < iop->end)) { NODE *t; t = make_string(fields_arr[0]->stptr, fields_arr[0]->stlen); unref(fields_arr[0]); fields_arr [0] = t; reset_record (); } free(iop->buf); } free((char *)iop); } return ret == -1 ? 1 : 0;}voiddo_input(){ IOBUF *iop; extern int exiting; if (setjmp(filebuf) != 0) { } while ((iop = nextfile(0)) != NULL) { if (inrec(iop) == 0) while (interpret(expression_value) && inrec(iop) == 0) ; if (exiting) break; }}/* Redirection for printf and print commands */struct redirect *redirect(tree, errflg)NODE *tree;int *errflg;{ register NODE *tmp; register struct redirect *rp; register char *str; int tflag = 0; int outflag = 0; char *direction = "to"; char *mode; int fd; char *what = NULL; switch (tree->type) { case Node_redirect_append: tflag = RED_APPEND; /* FALL THROUGH */ case Node_redirect_output: outflag = (RED_FILE|RED_WRITE); tflag |= outflag; if (tree->type == Node_redirect_output) what = ">"; else what = ">>"; break; case Node_redirect_pipe: tflag = (RED_PIPE|RED_WRITE); what = "|"; break; case Node_redirect_pipein: tflag = (RED_PIPE|RED_READ); what = "|"; break; case Node_redirect_input: tflag = (RED_FILE|RED_READ); what = "<"; break; default: fatal ("invalid tree type %d in redirect()", tree->type); break; } tmp = tree_eval(tree->subnode); if (do_lint && ! (tmp->flags & STR)) warning("expression in `%s' redirection only has numeric value", what); tmp = force_string(tmp); str = tmp->stptr; if (str == NULL || *str == '\0') fatal("expression for `%s' redirection has null string value", what); if (do_lint && (STREQN(str, "0", tmp->stlen) || STREQN(str, "1", tmp->stlen))) warning("filename `%s' for `%s' redirection may be result of logical expression", str, what); for (rp = red_head; rp != NULL; rp = rp->next) if (strlen(rp->value) == tmp->stlen && STREQN(rp->value, str, tmp->stlen) && ((rp->flag & ~(RED_NOBUF|RED_EOF)) == tflag || (outflag && (rp->flag & (RED_FILE|RED_WRITE)) == outflag))) break; if (rp == NULL) { emalloc(rp, struct redirect *, sizeof(struct redirect), "redirect"); emalloc(str, char *, tmp->stlen+1, "redirect"); memcpy(str, tmp->stptr, tmp->stlen); str[tmp->stlen] = '\0'; rp->value = str; rp->flag = tflag; rp->fp = NULL; rp->iop = NULL; rp->pid = 0; /* unlikely that we're worried about init */ rp->status = 0; /* maintain list in most-recently-used first order */ if (red_head) red_head->prev = rp; rp->prev = NULL; rp->next = red_head; red_head = rp; } while (rp->fp == NULL && rp->iop == NULL) { if (rp->flag & RED_EOF) /* encountered EOF on file or pipe -- must be cleared * by explicit close() before reading more */ return rp; mode = NULL; errno = 0; switch (tree->type) { case Node_redirect_output: mode = "w"; if (rp->flag & RED_USED) mode = "a"; break; case Node_redirect_append: mode = "a"; break; case Node_redirect_pipe: if ((rp->fp = popen(str, "w")) == NULL) fatal("can't open pipe (\"%s\") for output (%s)", str, strerror(errno)); rp->flag |= RED_NOBUF; break; case Node_redirect_pipein: direction = "from"; if (gawk_popen(str, rp) == NULL) fatal("can't open pipe (\"%s\") for input (%s)", str, strerror(errno)); break; case Node_redirect_input: direction = "from"; rp->iop = iop_open(str, "r"); break; default: cant_happen(); } if (mode != NULL) { fd = devopen(str, mode); if (fd > INVALID_HANDLE) { if (fd == fileno(stdin)) rp->fp = stdin; else if (fd == fileno(stdout)) rp->fp = stdout; else if (fd == fileno(stderr)) rp->fp = stderr; else rp->fp = fdopen(fd, mode); if (isatty(fd)) rp->flag |= RED_NOBUF; } } if (rp->fp == NULL && rp->iop == NULL) { /* too many files open -- close one and try again */ if (errno == EMFILE) close_one(); else { /* * Some other reason for failure. * * On redirection of input from a file, * just return an error, so e.g. getline * can return -1. For output to file, * complain. The shell will complain on * a bad command to a pipe. */ *errflg = errno; if (tree->type == Node_redirect_output || tree->type == Node_redirect_append) fatal("can't redirect %s `%s' (%s)", direction, str, strerror(errno)); else { free_temp(tmp); return NULL; } } } } free_temp(tmp); return rp;}static voidclose_one(){ register struct redirect *rp; register struct redirect *rplast = NULL; /* go to end of list first, to pick up least recently used entry */ for (rp = red_head; rp != NULL; rp = rp->next) rplast = rp; /* now work back up through the list */ for (rp = rplast; rp != NULL; rp = rp->prev) if (rp->fp && (rp->flag & RED_FILE)) { rp->flag |= RED_USED; errno = 0; if (fclose(rp->fp)) warning("close of \"%s\" failed (%s).", rp->value, strerror(errno)); rp->fp = NULL; break; } if (rp == NULL) /* surely this is the only reason ??? */ fatal("too many pipes or input files open"); }NODE *do_close(tree)NODE *tree;{ NODE *tmp; register struct redirect *rp; tmp = force_string(tree_eval(tree->subnode)); for (rp = red_head; rp != NULL; rp = rp->next) { if (strlen(rp->value) == tmp->stlen && STREQN(rp->value, tmp->stptr, tmp->stlen)) break; } free_temp(tmp); if (rp == NULL) /* no match */ return tmp_number((AWKNUM) 0.0); fflush(stdout); /* synchronize regular output */ tmp = tmp_number((AWKNUM)close_redir(rp)); rp = NULL; return tmp;}static intclose_redir(rp)register struct redirect *rp;{ int status = 0; if (rp == NULL) return 0; if (rp->fp == stdout || rp->fp == stderr) return 0; errno = 0; if ((rp->flag & (RED_PIPE|RED_WRITE)) == (RED_PIPE|RED_WRITE)) status = pclose(rp->fp); else if (rp->fp) status = fclose(rp->fp); else if (rp->iop) { if (rp->flag & RED_PIPE) status = gawk_pclose(rp); else { status = iop_close(rp->iop); rp->iop = NULL; } } /* SVR4 awk checks and warns about status of close */ if (status) { char *s = strerror(errno); warning("failure status (%d) on %s close of \"%s\" (%s).", status, (rp->flag & RED_PIPE) ? "pipe" : "file", rp->value, s); if (! do_unix) { /* set ERRNO too so that program can get at it */ unref(ERRNO_node->var_value); ERRNO_node->var_value = make_string(s, strlen(s)); } } if (rp->next) rp->next->prev = rp->prev; if (rp->prev) rp->prev->next = rp->next; else red_head = rp->next; free(rp->value); free((char *)rp); return status;}intflush_io (){ register struct redirect *rp; int status = 0; errno = 0; if (fflush(stdout)) { warning("error writing standard output (%s).", strerror(errno)); status++; } if (fflush(stderr)) { warning("error writing standard error (%s).", strerror(errno)); status++; } for (rp = red_head; rp != NULL; rp = rp->next) /* flush both files and pipes, what the heck */ if ((rp->flag & RED_WRITE) && rp->fp != NULL) { if (fflush(rp->fp)) { warning("%s flush of \"%s\" failed (%s).", (rp->flag & RED_PIPE) ? "pipe" : "file", rp->value, strerror(errno)); status++; } } return status;}intclose_io (){ register struct redirect *rp; register struct redirect *next; int status = 0; errno = 0; if (fclose(stdout)) { warning("error writing standard output (%s).", strerror(errno)); status++; } if (fclose(stderr)) { warning("error writing standard error (%s).", strerror(errno)); status++; } for (rp = red_head; rp != NULL; rp = next) { next = rp->next; if (close_redir(rp)) status++; rp = NULL; } return status;}/* str2mode --- convert a string mode to an integer mode */static intstr2mode(mode)char *mode;{ int ret; switch(mode[0]) { case 'r': ret = O_RDONLY; break; case 'w': ret = O_WRONLY|O_CREAT|O_TRUNC; break; case 'a': ret = O_WRONLY|O_APPEND|O_CREAT; break; default: cant_happen(); } return ret;}/* devopen --- handle /dev/std{in,out,err}, /dev/fd/N, regular files *//* * This separate version is still needed for output, since file and pipe * output is done with stdio. iop_open() handles input with IOBUFs of * more "special" files. Those files are not handled here since it makes * no sense to use them for output. */intdevopen(name, mode)char *name, *mode;{ int openfd = INVALID_HANDLE; char *cp, *ptr;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -