|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T i
Length: 17467 (0x443b) Types: TextFile Names: »io.c«
└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit └─⟦6dcdebfcf⟧ »EurOpenD3/gnu/gawk/gawk-2.11.1.tar.Z« └─⟦a05ed705a⟧ Bits:30007078 DKUUG GNU 2/12/89 └─⟦6dcdebfcf⟧ »./gawk-2.11.1.tar.Z« └─⟦3c42ca21a⟧ └─⟦this⟧ »gawk-2.11/io.c«
/* * 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 #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); fflush(stdout); /* synchronize regular output */ 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 } #ifndef MSDOS 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; { SIGTYPE (*hstat)(), (*istat)(), (*qstat)(); int pid; int status; struct redirect *redp; 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); } #else static struct { char *command; char *name; } pipes[_NFILE]; static IOBUF * gawk_popen(cmd, rp) char *cmd; struct redirect *rp; { extern char *strdup(const char *); int current; char *name; static char cmdbuf[256]; /* get a name to use. */ if ((name = tempnam(".", "pip")) == NULL) return NULL; sprintf(cmdbuf,"%s > %s", cmd, name); system(cmdbuf); if ((current = open(name,O_RDONLY)) == -1) return NULL; pipes[current].name = name; pipes[current].command = strdup(cmd); return (rp->iop = iop_alloc(current)); } static int gawk_pclose(rp) struct redirect *rp; { int cur = rp->iop->fd; int rval; rval = iop_close(rp->iop); /* check for an open file */ if (pipes[cur].name == NULL) return -1; unlink(pipes[cur].name); free(pipes[cur].name); pipes[cur].name = NULL; free(pipes[cur].command); return rval; } #endif #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); }