aboutsummaryrefslogtreecommitdiffstats
path: root/io.c
diff options
context:
space:
mode:
authorArnold D. Robbins <arnold@skeeve.com>2010-07-15 23:12:49 +0300
committerArnold D. Robbins <arnold@skeeve.com>2010-07-15 23:12:49 +0300
commit3697ec5ca140f686643d204a54181a5ddbf9a799 (patch)
tree592873e8614475012ddd5f4e6d0482acadbfc9e2 /io.c
parentf3d9dd233ac07f764a554528c85be3768a1d1ddb (diff)
downloadegawk-3697ec5ca140f686643d204a54181a5ddbf9a799.tar.gz
egawk-3697ec5ca140f686643d204a54181a5ddbf9a799.tar.bz2
egawk-3697ec5ca140f686643d204a54181a5ddbf9a799.zip
Moved to gawk 2.11.
Diffstat (limited to 'io.c')
-rw-r--r--io.c765
1 files changed, 765 insertions, 0 deletions
diff --git a/io.c b/io.c
new file mode 100644
index 00000000..ac5f35fa
--- /dev/null
+++ b/io.c
@@ -0,0 +1,765 @@
+/*
+ * io.c - routines for dealing with input and output and records
+ */
+
+/*
+ * Copyright (C) 1986, 1988, 1989 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "awk.h"
+#ifndef O_RDONLY
+#include <fcntl.h>
+#endif
+#if defined(MSDOS)
+#include "popen.h"
+#endif
+#include <signal.h>
+
+extern FILE *popen();
+
+static void do_file();
+static IOBUF *nextfile();
+static int get_a_record();
+static int iop_close();
+static IOBUF *iop_alloc();
+static void close_one();
+static int close_redir();
+static IOBUF *gawk_popen();
+static int gawk_pclose();
+
+static struct redirect *red_head = NULL;
+static int getline_redirect = 0; /* "getline <file" being executed */
+
+extern char *line_buf;
+extern int output_is_tty;
+extern NODE *ARGC_node;
+extern NODE *ARGV_node;
+extern NODE **fields_arr;
+
+int field_num;
+
+static IOBUF *
+nextfile()
+{
+ static int i = 1;
+ static int files = 0;
+ static IOBUF *curfile = NULL;
+ char *arg;
+ char *cp;
+ int fd = -1;
+
+ if (curfile != NULL && curfile->cnt != EOF)
+ return curfile;
+ for (; i < (int) (ARGC_node->lnode->numbr); i++) {
+ arg = (*assoc_lookup(ARGV_node, tmp_number((AWKNUM) i)))->stptr;
+ if (*arg == '\0')
+ continue;
+ cp = strchr(arg, '=');
+ if (cp != NULL) {
+ *cp++ = '\0';
+ variable(arg)->var_value = make_string(cp, strlen(cp));
+ *--cp = '='; /* restore original text of ARGV */
+ } else {
+ files++;
+ if (STREQ(arg, "-"))
+ fd = 0;
+ else
+ fd = devopen(arg, "r");
+ if (fd == -1)
+ fatal("cannot open file `%s' for reading (%s)",
+ arg, strerror(errno));
+ /* NOTREACHED */
+ /* This is a kludge. */
+ deref = FILENAME_node->var_value;
+ do_deref();
+ FILENAME_node->var_value =
+ make_string(arg, strlen(arg));
+ FNR_node->var_value->numbr = 0.0;
+ i++;
+ break;
+ }
+ }
+ if (files == 0) {
+ files++;
+ /* no args. -- use stdin */
+ /* FILENAME is init'ed to "-" */
+ /* FNR is init'ed to 0 */
+ fd = 0;
+ }
+ if (fd == -1)
+ return NULL;
+ return curfile = iop_alloc(fd);
+}
+
+static IOBUF *
+iop_alloc(fd)
+int fd;
+{
+ IOBUF *iop;
+ struct stat stb;
+
+ /*
+ * System V doesn't have the file system block size in the
+ * stat structure. So we have to make some sort of reasonable
+ * guess. We use stdio's BUFSIZ, since that is what it was
+ * meant for in the first place.
+ */
+#ifdef BLKSIZE_MISSING
+#define DEFBLKSIZE BUFSIZ
+#else
+#define DEFBLKSIZE (stb.st_blksize ? stb.st_blksize : BUFSIZ)
+#endif
+
+ if (fd == -1)
+ return NULL;
+ emalloc(iop, IOBUF *, sizeof(IOBUF), "nextfile");
+ iop->flag = 0;
+ if (isatty(fd)) {
+ iop->flag |= IOP_IS_TTY;
+ iop->size = BUFSIZ;
+ } else if (fstat(fd, &stb) == -1)
+ fatal("can't stat fd %d (%s)", fd, strerror(errno));
+ else if (lseek(fd, 0L, 0) == -1)
+ iop->size = DEFBLKSIZE;
+ else
+ iop->size = (stb.st_size < DEFBLKSIZE ?
+ stb.st_size+1 : DEFBLKSIZE);
+ errno = 0;
+ iop->fd = fd;
+ emalloc(iop->buf, char *, iop->size, "nextfile");
+ iop->off = iop->buf;
+ iop->cnt = 0;
+ iop->secsiz = iop->size < BUFSIZ ? iop->size : BUFSIZ;
+ emalloc(iop->secbuf, char *, iop->secsiz, "nextfile");
+ return iop;
+}
+
+void
+do_input()
+{
+ IOBUF *iop;
+ extern int exiting;
+
+ while ((iop = nextfile()) != NULL) {
+ do_file(iop);
+ if (exiting)
+ break;
+ }
+}
+
+static int
+iop_close(iop)
+IOBUF *iop;
+{
+ int ret;
+
+ ret = close(iop->fd);
+ if (ret == -1)
+ warning("close of fd %d failed (%s)", iop->fd, strerror(errno));
+ free(iop->buf);
+ free(iop->secbuf);
+ free((char *)iop);
+ return ret == -1 ? 1 : 0;
+}
+
+/*
+ * This reads in a record from the input file
+ */
+static int
+inrec(iop)
+IOBUF *iop;
+{
+ int cnt;
+ int retval = 0;
+
+ cnt = get_a_record(&line_buf, iop);
+ if (cnt == EOF) {
+ cnt = 0;
+ retval = 1;
+ } else {
+ if (!getline_redirect) {
+ assign_number(&NR_node->var_value,
+ NR_node->var_value->numbr + 1.0);
+ assign_number(&FNR_node->var_value,
+ FNR_node->var_value->numbr + 1.0);
+ }
+ }
+ set_record(line_buf, cnt);
+
+ return retval;
+}
+
+static void
+do_file(iop)
+IOBUF *iop;
+{
+ /* This is where it spends all its time. The infamous MAIN LOOP */
+ if (inrec(iop) == 0)
+ while (interpret(expression_value) && inrec(iop) == 0)
+ ;
+ (void) iop_close(iop);
+}
+
+int
+get_rs()
+{
+ register NODE *tmp;
+
+ tmp = force_string(RS_node->var_value);
+ if (tmp->stlen == 0)
+ return 0;
+ return *(tmp->stptr);
+}
+
+/* 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;
+
+ switch (tree->type) {
+ case Node_redirect_append:
+ tflag = RED_APPEND;
+ case Node_redirect_output:
+ outflag = (RED_FILE|RED_WRITE);
+ tflag |= outflag;
+ break;
+ case Node_redirect_pipe:
+ tflag = (RED_PIPE|RED_WRITE);
+ break;
+ case Node_redirect_pipein:
+ tflag = (RED_PIPE|RED_READ);
+ break;
+ case Node_redirect_input:
+ tflag = (RED_FILE|RED_READ);
+ break;
+ default:
+ fatal ("invalid tree type %d in redirect()", tree->type);
+ break;
+ }
+ tmp = force_string(tree_eval(tree->subnode));
+ str = tmp->stptr;
+ for (rp = red_head; rp != NULL; rp = rp->next)
+ if (STREQ(rp->value, str)
+ && ((rp->flag & ~RED_NOBUF) == 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+1);
+ rp->value = str;
+ rp->flag = tflag;
+ rp->offset = 0;
+ rp->fp = NULL;
+ rp->iop = NULL;
+ /* 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) {
+ mode = NULL;
+ errno = 0;
+ switch (tree->type) {
+ case Node_redirect_output:
+ mode = "w";
+ 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_alloc(devopen(str, "r"));
+ break;
+ default:
+ cant_happen();
+ }
+ if (mode != NULL) {
+ fd = devopen(str, mode);
+ if (fd != -1) {
+ 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 == ENFILE || 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 = 1;
+ if (tree->type == Node_redirect_output
+ || tree->type == Node_redirect_append)
+ fatal("can't redirect %s `%s' (%s)",
+ direction, str, strerror(errno));
+ else
+ return NULL;
+ }
+ }
+ }
+ if (rp->offset != 0) /* this file was previously open */
+ if (fseek(rp->fp, rp->offset, 0) == -1)
+ fatal("can't seek to %ld on `%s' (%s)",
+ rp->offset, str, strerror(errno));
+ free_temp(tmp);
+ return rp;
+}
+
+static void
+close_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->offset = ftell(rp->fp);
+ 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 (STREQ(rp->value, tmp->stptr))
+ break;
+ }
+ free_temp(tmp);
+ if (rp == NULL) /* no match */
+ return tmp_number((AWKNUM) 0.0);
+ return tmp_number((AWKNUM)close_redir(rp));
+}
+
+static int
+close_redir(rp)
+register struct redirect *rp;
+{
+ int status = 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);
+
+ }
+ /* SVR4 awk checks and warns about status of close */
+ if (status)
+ warning("failure status (%d) on %s close of \"%s\" (%s).",
+ status,
+ (rp->flag & RED_PIPE) ? "pipe" :
+ "file", rp->value, strerror(errno));
+ 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;
+}
+
+int
+flush_io ()
+{
+ register struct redirect *rp;
+ int status = 0;
+
+ errno = 0;
+ if (fflush(stdout)) {
+ warning("error writing standard output (%s).", strerror(errno));
+ status++;
+ }
+ errno = 0;
+ 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;
+}
+
+int
+close_io ()
+{
+ register struct redirect *rp;
+ int status = 0;
+
+ for (rp = red_head; rp != NULL; rp = rp->next)
+ if (close_redir(rp))
+ status++;
+ return status;
+}
+
+/* devopen --- handle /dev/std{in,out,err}, /dev/fd/N, regular files */
+int
+devopen (name, mode)
+char *name, *mode;
+{
+ int openfd = -1;
+ FILE *fdopen ();
+ char *cp;
+ int flag = 0;
+
+ switch(mode[0]) {
+ case 'r':
+ flag = O_RDONLY;
+ break;
+
+ case 'w':
+ flag = O_WRONLY|O_CREAT|O_TRUNC;
+ break;
+
+ case 'a':
+ flag = O_WRONLY|O_APPEND|O_CREAT;
+ break;
+ default:
+ cant_happen();
+ }
+
+#if defined(STRICT) || defined(NO_DEV_FD)
+ return (open (name, flag, 0666));
+#else
+ if (strict)
+ return (open (name, flag, 0666));
+
+ if (!STREQN (name, "/dev/", 5))
+ return (open (name, flag, 0666));
+ else
+ cp = name + 5;
+
+ /* XXX - first three tests ignore mode */
+ if (STREQ(cp, "stdin"))
+ return (0);
+ else if (STREQ(cp, "stdout"))
+ return (1);
+ else if (STREQ(cp, "stderr"))
+ return (2);
+ else if (STREQN(cp, "fd/", 3)) {
+ cp += 3;
+ if (sscanf (cp, "%d", & openfd) == 1 && openfd >= 0)
+ /* got something */
+ return openfd;
+ else
+ return -1;
+ } else
+ return (open (name, flag, 0666));
+#endif
+}
+
+static IOBUF *
+gawk_popen(cmd, rp)
+char *cmd;
+struct redirect *rp;
+{
+ int p[2];
+ register int pid;
+
+ rp->pid = -1;
+ rp->iop = NULL;
+ if (pipe(p) < 0)
+ return NULL;
+ if((pid = fork()) == 0) {
+ close(p[0]);
+ dup2(p[1], 1);
+ close(p[1]);
+ execl("/bin/sh", "sh", "-c", cmd, 0);
+ _exit(127);
+ }
+ if(pid == -1)
+ return NULL;
+ rp->pid = pid;
+ close(p[1]);
+ return (rp->iop = iop_alloc(p[0]));
+}
+
+static int
+gawk_pclose(rp)
+struct redirect *rp;
+{
+ void (*hstat)(), (*istat)(), (*qstat)();
+ int pid;
+ int status;
+ struct redirect *redp;
+ extern int errno;
+
+ iop_close(rp->iop);
+ if (rp->pid == -1)
+ return rp->status;
+ hstat = signal(SIGHUP, SIG_IGN);
+ istat = signal(SIGINT, SIG_IGN);
+ qstat = signal(SIGQUIT, SIG_IGN);
+ for (;;) {
+ pid = wait(&status);
+ if (pid == -1 && errno == ECHILD)
+ break;
+ else if (pid == rp->pid) {
+ rp->pid = -1;
+ rp->status = status;
+ break;
+ } else {
+ for (redp = red_head; redp != NULL; redp = redp->next)
+ if (pid == redp->pid) {
+ redp->pid = -1;
+ redp->status = status;
+ break;
+ }
+ }
+ }
+ signal(SIGHUP, hstat);
+ signal(SIGINT, istat);
+ signal(SIGQUIT, qstat);
+ return(rp->status);
+}
+
+#define DO_END_OF_BUF len = bp - iop->off;\
+ used = last - start;\
+ while (len + used > iop->secsiz) {\
+ iop->secsiz *= 2;\
+ erealloc(iop->secbuf,char *,iop->secsiz,"get");\
+ }\
+ last = iop->secbuf + used;\
+ start = iop->secbuf;\
+ memcpy(last, iop->off, len);\
+ last += len;\
+ iop->cnt = read(iop->fd, iop->buf, iop->size);\
+ if (iop->cnt < 0)\
+ return iop->cnt;\
+ end_data = iop->buf + iop->cnt;\
+ iop->off = bp = iop->buf;
+
+#define DO_END_OF_DATA iop->cnt = read(iop->fd, end_data, end_buf - end_data);\
+ if (iop->cnt < 0)\
+ return iop->cnt;\
+ end_data += iop->cnt;\
+ if (iop->cnt == 0)\
+ break;\
+ iop->cnt = end_data - iop->buf;
+
+static int
+get_a_record(res, iop)
+char **res;
+IOBUF *iop;
+{
+ register char *end_data;
+ register char *end_buf;
+ char *start;
+ register char *bp;
+ register char *last;
+ int len, used;
+ register char rs = get_rs();
+
+ if (iop->cnt < 0)
+ return iop->cnt;
+ if ((iop->flag & IOP_IS_TTY) && output_is_tty)
+ fflush(stdout);
+ end_data = iop->buf + iop->cnt;
+ if (iop->off >= end_data) {
+ iop->cnt = read(iop->fd, iop->buf, iop->size);
+ if (iop->cnt <= 0)
+ return iop->cnt = EOF;
+ end_data = iop->buf + iop->cnt;
+ iop->off = iop->buf;
+ }
+ last = start = bp = iop->off;
+ end_buf = iop->buf + iop->size;
+ if (rs == 0) {
+ while (!(*bp == '\n' && bp != iop->buf && bp[-1] == '\n')) {
+ if (++bp == end_buf) {
+ DO_END_OF_BUF
+ }
+ if (bp == end_data) {
+ DO_END_OF_DATA
+ }
+ }
+ if (*bp == '\n' && bp != iop->off && bp[-1] == '\n') {
+ int tmp = 0;
+
+ /* allow for more than two newlines */
+ while (*bp == '\n') {
+ tmp++;
+ if (++bp == end_buf) {
+ DO_END_OF_BUF
+ }
+ if (bp == end_data) {
+ DO_END_OF_DATA
+ }
+ }
+ iop->off = bp;
+ bp -= 1 + tmp;
+ } else if (bp != iop->buf && bp[-1] != '\n') {
+ warning("record not terminated");
+ iop->off = bp + 2;
+ } else {
+ bp--;
+ iop->off = bp + 2;
+ }
+ } else {
+ while (*bp++ != rs) {
+ if (bp == end_buf) {
+ DO_END_OF_BUF
+ }
+ if (bp == end_data) {
+ DO_END_OF_DATA
+ }
+ }
+ if (*--bp != rs) {
+ warning("record not terminated");
+ bp++;
+ }
+ iop->off = bp + 1;
+ }
+ if (start == iop->secbuf) {
+ len = bp - iop->buf;
+ if (len > 0) {
+ used = last - start;
+ while (len + used > iop->secsiz) {
+ iop->secsiz *= 2;
+ erealloc(iop->secbuf,char *,iop->secsiz,"get2");
+ }
+ last = iop->secbuf + used;
+ start = iop->secbuf;
+ memcpy(last, iop->buf, len);
+ last += len;
+ }
+ } else
+ last = bp;
+ *last = '\0';
+ *res = start;
+ return last - start;
+}
+
+NODE *
+do_getline(tree)
+NODE *tree;
+{
+ struct redirect *rp;
+ IOBUF *iop;
+ int cnt;
+ NODE **lhs;
+ int redir_error = 0;
+
+ if (tree->rnode == NULL) { /* no redirection */
+ iop = nextfile();
+ if (iop == NULL) /* end of input */
+ return tmp_number((AWKNUM) 0.0);
+ } else {
+ rp = redirect(tree->rnode, &redir_error);
+ if (rp == NULL && redir_error) /* failed redirect */
+ return tmp_number((AWKNUM) -1.0);
+ iop = rp->iop;
+ getline_redirect++;
+ }
+ if (tree->lnode == NULL) { /* no optional var. -- read in $0 */
+ if (inrec(iop) != 0) {
+ getline_redirect = 0;
+ return tmp_number((AWKNUM) 0.0);
+ }
+ } else { /* read in a named variable */
+ char *s = NULL;
+
+ lhs = get_lhs(tree->lnode, 1);
+ cnt = get_a_record(&s, iop);
+ if (!getline_redirect) {
+ assign_number(&NR_node->var_value,
+ NR_node->var_value->numbr + 1.0);
+ assign_number(&FNR_node->var_value,
+ FNR_node->var_value->numbr + 1.0);
+ }
+ if (cnt == EOF) {
+ getline_redirect = 0;
+ free(s);
+ return tmp_number((AWKNUM) 0.0);
+ }
+ *lhs = make_string(s, strlen(s));
+ do_deref();
+ /* we may have to regenerate $0 here! */
+ if (field_num == 0)
+ set_record(fields_arr[0]->stptr, fields_arr[0]->stlen);
+ field_num = -1;
+ }
+ getline_redirect = 0;
+ return tmp_number((AWKNUM) 1.0);
+}