Main Page | Class List | Directories | File List | Class Members | File Members

io.c

Go to the documentation of this file.
00001 /*
00002  * io.c --- routines for dealing with input and output and records
00003  */
00004 
00005 /* 
00006  * Copyright (C) 1986, 1988, 1989, 1991-2003 the Free Software Foundation, Inc.
00007  * 
00008  * This file is part of GAWK, the GNU implementation of the
00009  * AWK Programming Language.
00010  * 
00011  * GAWK is free software; you can redistribute it and/or modify
00012  * it under the terms of the GNU General Public License as published by
00013  * the Free Software Foundation; either version 2 of the License, or
00014  * (at your option) any later version.
00015  * 
00016  * GAWK is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  * 
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software
00023  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
00024  */
00025 
00026 #include "awk.h"
00027 
00028 #ifdef HAVE_SYS_PARAM_H
00029 #undef RE_DUP_MAX       /* avoid spurious conflict w/regex.h */
00030 #include <sys/param.h>
00031 #endif /* HAVE_SYS_PARAM_H */
00032 
00033 #ifndef O_RDONLY
00034 #include <fcntl.h>
00035 #endif
00036 #ifndef O_ACCMODE
00037 #define O_ACCMODE       (O_RDONLY|O_WRONLY|O_RDWR)
00038 #endif
00039 
00040 #ifdef HAVE_TERMIOS_H
00041 #include <termios.h>
00042 #endif
00043 #ifdef HAVE_STROPTS_H
00044 #include <stropts.h>
00045 #endif
00046 
00047 #ifdef HAVE_SOCKETS
00048 #ifdef HAVE_SYS_SOCKET_H
00049 #include <sys/socket.h>
00050 #else
00051 #include <socket.h>
00052 #endif /* HAVE_SYS_SOCKET_H */
00053 #ifdef HAVE_NETINET_IN_H
00054 #include <netinet/in.h>
00055 #else
00056 #include <in.h>
00057 #endif /* HAVE_NETINET_IN_H */
00058 #ifdef HAVE_NETDB_H
00059 #include <netdb.h>
00060 #endif /* HAVE_NETDB_H */
00061 #endif /* HAVE_SOCKETS */
00062 
00063 #ifdef __EMX__
00064 #include <process.h>
00065 #endif
00066 
00067 #ifndef ENFILE
00068 #define ENFILE EMFILE
00069 #endif
00070 
00071 extern int MRL;
00072 
00073 #ifdef HAVE_SOCKETS
00074 enum inet_prot { INET_NONE, INET_TCP, INET_UDP, INET_RAW };
00075 
00076 #ifndef SHUT_RD
00077 #define SHUT_RD         0
00078 #endif
00079 
00080 #ifndef SHUT_WR
00081 #define SHUT_WR         1
00082 #endif
00083 
00084 #ifndef SHUT_RDWR
00085 #define SHUT_RDWR       2
00086 #endif
00087 
00088 #endif /* HAVE_SOCKETS */
00089 
00090 #ifdef atarist
00091 #include <stddef.h>
00092 #endif
00093 
00094 #if defined(GAWK_AIX)
00095 #undef TANDEM   /* AIX defines this in one of its header files */
00096 #undef MSDOS    /* For good measure, */
00097 #undef WIN32    /* yes, I'm paranoid */
00098 #endif
00099 
00100 #if defined(MSDOS) || defined(WIN32) || defined(TANDEM)
00101 #define PIPES_SIMULATED
00102 #endif
00103 
00104 typedef enum { CLOSE_ALL, CLOSE_TO, CLOSE_FROM } two_way_close_type;
00105 
00106 /* Several macros make the code a bit clearer:                              */
00107 /*                                                                          */
00108 /*                                                                          */
00109 /* <defines and enums>=                                                     */
00110 #define at_eof(iop)     ((iop->flag & IOP_AT_EOF) != 0)
00111 #define has_no_data(iop)        (iop->dataend == NULL)
00112 #define no_data_left(iop)       (iop->off >= iop->dataend)
00113 /* The key point to the design is to split out the code that searches through */
00114 /* a buffer looking for the record and the terminator into separate routines, */
00115 /* with a higher-level routine doing the reading of data and buffer management. */
00116 /* This makes the code easier to manage; the buffering code is the same independent */
00117 /* of how we find a record.  Communication is via the return value:         */
00118 /*                                                                          */
00119 /*                                                                          */
00120 /* <defines and enums>=                                                     */
00121 typedef enum recvalues {
00122         REC_OK,         /* record and terminator found, recmatch struct filled in */
00123         NOTERM,         /* no terminator found, give me more input data */
00124         TERMATEND,      /* found terminator at end of buffer */
00125         TERMNEAREND,    /* found terminator close to end of buffer, for RE might be bigger */
00126 } RECVALUE;
00127 /* Between calls to a scanning routine, the state is stored in              */
00128 /* an [[enum scanstate]] variable.  Not all states apply to all             */
00129 /* variants, but the higher code doesn't really care.                       */
00130 /*                                                                          */
00131 /*                                                                          */
00132 /* <defines and enums>=                                                     */
00133 typedef enum scanstate {
00134         NOSTATE,        /* scanning not started yet (all) */
00135         INLEADER,       /* skipping leading data (RS = "") */
00136         INDATA,         /* in body of record (all) */
00137         INTERM,         /* scanning terminator (RS = "", RS = regexp) */
00138 } SCANSTATE;
00139 /* When a record is seen ([[REC_OK]] or [[TERMATEND]]), the following       */
00140 /* structure is filled in.                                                  */
00141 /*                                                                          */
00142 /*                                                                          */
00143 /* <recmatch>=                                                              */
00144 struct recmatch {
00145         char *start;    /* record start */
00146         size_t len;     /* length of record */
00147         char *rt_start; /* start of terminator */
00148         size_t rt_len;  /* length of terminator */
00149 };
00150 
00151 static IOBUF *nextfile P((int skipping));
00152 static int inrec P((IOBUF *iop));
00153 static int iop_close P((IOBUF *iop));
00154 struct redirect *redirect P((NODE *tree, int *errflg));
00155 static void close_one P((void));
00156 static int close_redir P((struct redirect *rp, int exitwarn, two_way_close_type how));
00157 #ifndef PIPES_SIMULATED
00158 static int wait_any P((int interesting));
00159 #endif
00160 static IOBUF *gawk_popen P((const char *cmd, struct redirect *rp));
00161 static IOBUF *iop_open P((const char *file, const char *how, IOBUF *buf));
00162 static IOBUF *iop_alloc P((int fd, const char *name, IOBUF *buf));
00163 static int gawk_pclose P((struct redirect *rp));
00164 static int do_pathopen P((const char *file));
00165 static int str2mode P((const char *mode));
00166 static void spec_setup P((IOBUF *iop, int len, int allocate));
00167 static int specfdopen P((IOBUF *iop, const char *name, const char *mode));
00168 static int pidopen P((IOBUF *iop, const char *name, const char *mode));
00169 static int useropen P((IOBUF *iop, const char *name, const char *mode));
00170 static int two_way_open P((const char *str, struct redirect *rp));
00171 static int pty_vs_pipe P((const char *command));
00172 
00173 static RECVALUE rs1scan P((IOBUF *iop, struct recmatch *recm, SCANSTATE *state));
00174 static RECVALUE rsnullscan P((IOBUF *iop, struct recmatch *recm, SCANSTATE *state));
00175 static RECVALUE rsrescan P((IOBUF *iop, struct recmatch *recm, SCANSTATE *state));
00176 
00177 static RECVALUE (*matchrec) P((IOBUF *iop, struct recmatch *recm, SCANSTATE *state)) = rs1scan;
00178 
00179 static int get_a_record P((char **out, IOBUF *iop, int *errcode));
00180 
00181 #if defined(HAVE_POPEN_H)
00182 #include "popen.h"
00183 #endif
00184 
00185 static struct redirect *red_head = NULL;
00186 static NODE *RS;
00187 static Regexp *RS_re_yes_case;
00188 static Regexp *RS_re_no_case;
00189 static Regexp *RS_regexp;
00190 
00191 int RS_is_null;
00192 
00193 extern int output_is_tty;
00194 extern NODE *ARGC_node;
00195 extern NODE *ARGV_node;
00196 extern NODE *ARGIND_node;
00197 extern NODE *ERRNO_node;
00198 extern NODE **fields_arr;
00199 
00200 static jmp_buf filebuf;         /* for do_nextfile() */
00201 
00202 #if defined(MSDOS) || defined(OS2) || defined(WIN32) \
00203  || defined(__EMX__) || defined(__CYGWIN__)
00204 static const char *
00205 binmode(const char *mode)
00206 {
00207         switch (mode[0]) {
00208         case 'r':
00209                 if ((BINMODE & 1) != 0)
00210                         mode = "rb";
00211                 break;
00212         case 'w':
00213         case 'a':
00214                 if ((BINMODE & 2) != 0)
00215                         mode = (mode[0] == 'w' ? "wb" : "ab");
00216                 break;
00217         }
00218         return mode;
00219 }
00220 #else
00221 #define binmode(mode)   (mode)
00222 #endif
00223 
00224 #ifdef VMS
00225 /* File pointers have an extra level of indirection, and there are cases where
00226    `stdin' can be null.  That can crash gawk if fileno() is used as-is.  */
00227 static int vmsrtl_fileno P((FILE *));
00228 static int vmsrtl_fileno(fp) FILE *fp; { return fileno(fp); }
00229 #undef fileno
00230 #define fileno(FP) (((FP) && *(FP)) ? vmsrtl_fileno(FP) : -1)
00231 #endif  /* VMS */
00232 
00233 /* do_nextfile --- implement gawk "nextfile" extension */
00234 
00235 void
00236 do_nextfile()
00237 {
00238         (void) nextfile(TRUE);
00239         longjmp(filebuf, 1);
00240 }
00241 
00242 /* nextfile --- move to the next input data file */
00243 
00244 static IOBUF *
00245 nextfile(int skipping)
00246 {
00247         static long i = 1;
00248         static int files = 0;
00249         NODE *arg;
00250         static IOBUF *curfile = NULL;
00251         static IOBUF mybuf;
00252         const char *fname;
00253 
00254         if (skipping) {
00255                 if (curfile != NULL)
00256                         iop_close(curfile);
00257                 curfile = NULL;
00258                 return NULL;
00259         }
00260         if (curfile != NULL) {
00261                 if (at_eof(curfile)) {
00262                         (void) iop_close(curfile);
00263                         curfile = NULL;
00264                 } else
00265                         return curfile;
00266         }
00267         for (; i < (long) (ARGC_node->lnode->numbr); i++) {
00268                 arg = *assoc_lookup(ARGV_node, tmp_number((AWKNUM) i), FALSE);
00269                 if (arg->stlen == 0)
00270                         continue;
00271                 arg->stptr[arg->stlen] = '\0';
00272                 if (! do_traditional) {
00273                         unref(ARGIND_node->var_value);
00274                         ARGIND_node->var_value = make_number((AWKNUM) i);
00275                 }
00276                 if (! arg_assign(arg->stptr, FALSE)) {
00277                         files++;
00278                         fname = arg->stptr;
00279                         curfile = iop_open(fname, binmode("r"), &mybuf);
00280                         if (curfile == NULL)
00281                                 goto give_up;
00282                         curfile->flag |= IOP_NOFREE_OBJ;
00283                         /* This is a kludge.  */
00284                         unref(FILENAME_node->var_value);
00285                         FILENAME_node->var_value = dupnode(arg);
00286                         FNR = 0;
00287                         i++;
00288                         break;
00289                 }
00290         }
00291         if (files == 0) {
00292                 files++;
00293                 /* no args. -- use stdin */
00294                 /* FNR is init'ed to 0 */
00295                 FILENAME_node->var_value = make_string("-", 1);
00296                 fname = "-";
00297                 curfile = iop_open(fname, binmode("r"), &mybuf);
00298                 if (curfile == NULL)
00299                         goto give_up;
00300                 curfile->flag |= IOP_NOFREE_OBJ;
00301         }
00302         return curfile;
00303 
00304  give_up:
00305         fatal(_("cannot open file `%s' for reading (%s)"),
00306                 fname, strerror(errno));
00307         /* NOTREACHED */
00308         return 0;
00309 }
00310 
00311 /* set_FNR --- update internal FNR from awk variable */
00312 
00313 void
00314 set_FNR()
00315 {
00316         FNR = (long) FNR_node->var_value->numbr;
00317 }
00318 
00319 /* set_NR --- update internal NR from awk variable */
00320 
00321 void
00322 set_NR()
00323 {
00324         NR = (long) NR_node->var_value->numbr;
00325 }
00326 
00327 /* inrec --- This reads in a record from the input file */
00328 
00329 static int
00330 inrec(IOBUF *iop)
00331 {
00332         char *begin;
00333         register int cnt;
00334         int retval = 0;
00335 
00336         if (at_eof(iop) && no_data_left(iop))
00337                 cnt = EOF;
00338         else if ((iop->flag & IOP_CLOSED) != 0)
00339                 cnt = EOF;
00340         else
00341                 cnt = get_a_record(&begin, iop, NULL);
00342 
00343         if (cnt == EOF) {
00344                 cnt = 0;
00345                 retval = 1;
00346         } else {
00347                 NR += 1;
00348                 FNR += 1;
00349                 set_record(begin, cnt);
00350         }
00351 
00352         return retval;
00353 }
00354 
00355 /* iop_close --- close an open IOP */
00356 
00357 static int
00358 iop_close(IOBUF *iop)
00359 {
00360         int ret;
00361 
00362         if (iop == NULL)
00363                 return 0;
00364         errno = 0;
00365 
00366         iop->flag &= ~IOP_AT_EOF;
00367         iop->flag |= IOP_CLOSED;        /* there may be dangling pointers */
00368         iop->dataend = NULL;
00369 #ifdef _CRAY
00370         /* Work around bug in UNICOS popen */
00371         if (iop->fd < 3)
00372                 ret = 0;
00373         else
00374 #endif
00375         /* save these for re-use; don't free the storage */
00376         if ((iop->flag & IOP_IS_INTERNAL) != 0) {
00377                 iop->off = iop->buf;
00378                 iop->end = iop->buf + strlen(iop->buf);
00379                 iop->count = 0;
00380                 return 0;
00381         }
00382 
00383         /* Don't close standard files or else crufty code elsewhere will lose */
00384         if (iop->fd == fileno(stdin)
00385             || iop->fd == fileno(stdout)
00386             || iop->fd == fileno(stderr))
00387                 ret = 0;
00388         else
00389                 ret = close(iop->fd);
00390 
00391         if (ret == -1)
00392                 warning(_("close of fd %d (`%s') failed (%s)"), iop->fd,
00393                                 iop->name, strerror(errno));
00394         if ((iop->flag & IOP_NO_FREE) == 0) {
00395                 /*
00396                  * Be careful -- $0 may still reference the buffer even though
00397                  * an explicit close is being done; in the future, maybe we
00398                  * can do this a bit better.
00399                  */
00400                 if (iop->buf) {
00401                         if ((fields_arr[0]->stptr >= iop->buf)
00402                             && (fields_arr[0]->stptr < (iop->buf + iop->size))) {
00403                                 NODE *t;
00404         
00405                                 t = make_string(fields_arr[0]->stptr,
00406                                                 fields_arr[0]->stlen);
00407                                 unref(fields_arr[0]);
00408                                 fields_arr[0] = t;
00409                                 /*
00410                                  * 1/27/2003: This used to be here:
00411                                  *
00412                                  * reset_record();
00413                                  *
00414                                  * Don't do that; reset_record() throws away all fields,
00415                                  * saves FS etc.  We just need to make sure memory isn't
00416                                  * corrupted and that references to $0 and fields work.
00417                                  */
00418                         }
00419                         free(iop->buf);
00420                         iop->buf = NULL;
00421                 }
00422                 if ((iop->flag & IOP_NOFREE_OBJ) == 0)
00423                         free((char *) iop);
00424         }
00425         return ret == -1 ? 1 : 0;
00426 }
00427 
00428 /* do_input --- the main input processing loop */
00429 
00430 void
00431 do_input()
00432 {
00433         IOBUF *iop;
00434         extern int exiting;
00435         int rval1, rval2, rval3;
00436 
00437         (void) setjmp(filebuf); /* for `nextfile' */
00438 
00439         while ((iop = nextfile(FALSE)) != NULL) {
00440                 /*
00441                  * This was:
00442                 if (inrec(iop) == 0)
00443                         while (interpret(expression_value) && inrec(iop) == 0)
00444                                 continue;
00445                  * Now expand it out for ease of debugging.
00446                  */
00447                 rval1 = inrec(iop);
00448                 if (rval1 == 0) {
00449                         for (;;) {
00450                                 rval2 = rval3 = -1;     /* for debugging */
00451                                 rval2 = interpret(expression_value);
00452                                 if (rval2 != 0)
00453                                         rval3 = inrec(iop);
00454                                 if (rval2 == 0 || rval3 != 0)
00455                                         break;
00456                         }
00457                 }
00458                 if (exiting)
00459                         break;
00460         }
00461 }
00462 
00463 /* redflags2str --- turn redirection flags into a string, for debugging */
00464 
00465 const char *
00466 redflags2str(int flags)
00467 {
00468         static const struct flagtab redtab[] = {
00469                 { RED_FILE,     "RED_FILE" },
00470                 { RED_PIPE,     "RED_PIPE" },
00471                 { RED_READ,     "RED_READ" },
00472                 { RED_WRITE,    "RED_WRITE" },
00473                 { RED_APPEND,   "RED_APPEND" },
00474                 { RED_NOBUF,    "RED_NOBUF" },
00475                 { RED_EOF,      "RED_EOF" },
00476                 { RED_TWOWAY,   "RED_TWOWAY" },
00477                 { RED_PTY,      "RED_PTY" },
00478                 { RED_SOCKET,   "RED_SOCKET" },
00479                 { RED_TCP,      "RED_TCP" },
00480                 { 0, NULL }
00481         };
00482 
00483         return genflags2str(flags, redtab);
00484 }
00485 
00486 /* redirect --- Redirection for printf and print commands */
00487 
00488 struct redirect *
00489 redirect(NODE *tree, int *errflg)
00490 {
00491         register NODE *tmp;
00492         register struct redirect *rp;
00493         register char *str;
00494         int tflag = 0;
00495         int outflag = 0;
00496         const char *direction = "to";
00497         const char *mode;
00498         int fd;
00499         const char *what = NULL;
00500 
00501         switch (tree->type) {
00502         case Node_redirect_append:
00503                 tflag = RED_APPEND;
00504                 /* FALL THROUGH */
00505         case Node_redirect_output:
00506                 outflag = (RED_FILE|RED_WRITE);
00507                 tflag |= outflag;
00508                 if (tree->type == Node_redirect_output)
00509                         what = ">";
00510                 else
00511                         what = ">>";
00512                 break;
00513         case Node_redirect_pipe:
00514                 tflag = (RED_PIPE|RED_WRITE);
00515                 what = "|";
00516                 break;
00517         case Node_redirect_pipein:
00518                 tflag = (RED_PIPE|RED_READ);
00519                 what = "|";
00520                 break;
00521         case Node_redirect_input:
00522                 tflag = (RED_FILE|RED_READ);
00523                 what = "<";
00524                 break;
00525         case Node_redirect_twoway:
00526                 tflag = (RED_READ|RED_WRITE|RED_TWOWAY);
00527                 what = "|&";
00528                 break;
00529         default:
00530                 fatal(_("invalid tree type %s in redirect()"),
00531                                         nodetype2str(tree->type));
00532                 break;
00533         }
00534         tmp = tree_eval(tree->subnode);
00535         if (do_lint && (tmp->flags & STRCUR) == 0)
00536                 lintwarn(_("expression in `%s' redirection only has numeric value"),
00537                         what);
00538         tmp = force_string(tmp);
00539         str = tmp->stptr;
00540 
00541         if (str == NULL || *str == '\0')
00542                 fatal(_("expression for `%s' redirection has null string value"),
00543                         what);
00544 
00545         if (do_lint
00546             && (STREQN(str, "0", tmp->stlen) || STREQN(str, "1", tmp->stlen)))
00547                 lintwarn(_("filename `%s' for `%s' redirection may be result of logical expression"), str, what);
00548 
00549 #ifdef HAVE_SOCKETS
00550         if (STREQN(str, "/inet/", 6)) {
00551                 tflag |= RED_SOCKET;
00552                 if (STREQN(str + 6, "tcp/", 4))
00553                         tflag |= RED_TCP;       /* use shutdown when closing */
00554         }
00555 #endif /* HAVE_SOCKETS */
00556 
00557         for (rp = red_head; rp != NULL; rp = rp->next) {
00558                 if (strlen(rp->value) == tmp->stlen
00559                     && STREQN(rp->value, str, tmp->stlen)
00560                     && ((rp->flag & ~(RED_NOBUF|RED_EOF|RED_PTY)) == tflag
00561                         || (outflag != 0
00562                             && (rp->flag & (RED_FILE|RED_WRITE)) == outflag))) {
00563 
00564                         int rpflag = (rp->flag & ~(RED_NOBUF|RED_EOF|RED_PTY));
00565                         int newflag = (tflag & ~(RED_NOBUF|RED_EOF|RED_PTY));
00566 
00567                         if (do_lint && rpflag != newflag)
00568                                 lintwarn(
00569                 _("unnecessary mixing of `>' and `>>' for file `%.*s'"),
00570                                         tmp->stlen, rp->value);
00571 
00572                         break;
00573                 }
00574         }
00575 
00576         if (rp == NULL) {
00577                 emalloc(rp, struct redirect *, sizeof(struct redirect),
00578                         "redirect");
00579                 emalloc(str, char *, tmp->stlen+1, "redirect");
00580                 memcpy(str, tmp->stptr, tmp->stlen);
00581                 str[tmp->stlen] = '\0';
00582                 rp->value = str;
00583                 rp->flag = tflag;
00584                 rp->fp = NULL;
00585                 rp->iop = NULL;
00586                 rp->pid = 0;    /* unlikely that we're worried about init */
00587                 rp->status = 0;
00588                 /* maintain list in most-recently-used first order */
00589                 if (red_head != NULL)
00590                         red_head->prev = rp;
00591                 rp->prev = NULL;
00592                 rp->next = red_head;
00593                 red_head = rp;
00594         } else
00595                 str = rp->value;        /* get \0 terminated string */
00596 
00597         while (rp->fp == NULL && rp->iop == NULL) {
00598                 if (rp->flag & RED_EOF)
00599                         /*
00600                          * encountered EOF on file or pipe -- must be cleared
00601                          * by explicit close() before reading more
00602                          */
00603                         return rp;
00604                 mode = NULL;
00605                 errno = 0;
00606                 switch (tree->type) {
00607                 case Node_redirect_output:
00608                         mode = binmode("w");
00609                         if ((rp->flag & RED_USED) != 0)
00610                                 mode = (rp->mode[1] == 'b') ? "ab" : "a";
00611                         break;
00612                 case Node_redirect_append:
00613                         mode = binmode("a");
00614                         break;
00615                 case Node_redirect_pipe:
00616                         /* synchronize output before new pipe */
00617                         (void) flush_io();
00618 
00619                         os_restore_mode(fileno(stdin));
00620                         if ((rp->fp = popen(str, binmode("w"))) == NULL)
00621                                 fatal(_("can't open pipe `%s' for output (%s)"),
00622                                         str, strerror(errno));
00623                         /* set close-on-exec */
00624                         os_close_on_exec(fileno(rp->fp), str, "pipe", "to");
00625                         rp->flag |= RED_NOBUF;
00626                         break;
00627                 case Node_redirect_pipein:
00628                         direction = "from";
00629                         if (gawk_popen(str, rp) == NULL)
00630                                 fatal(_("can't open pipe `%s' for input (%s)"),
00631                                         str, strerror(errno));
00632                         break;
00633                 case Node_redirect_input:
00634                         direction = "from";
00635                         rp->iop = iop_open(str, binmode("r"), NULL);
00636                         break;
00637                 case Node_redirect_twoway:
00638                         direction = "to/from";
00639                         if (! two_way_open(str, rp)) {
00640 #ifdef HAVE_SOCKETS
00641                                 /* multiple messages make life easier for translators */
00642                                 if (STREQN(str, "/inet/", 6))
00643                                         fatal(_("can't open two way socket `%s' for input/output (%s)"),
00644                                                 str, strerror(errno));
00645                                 else
00646 #endif
00647                                         fatal(_("can't open two way pipe `%s' for input/output (%s)"),
00648                                                 str, strerror(errno));
00649                         }
00650                         break;
00651                 default:
00652                         cant_happen();
00653                 }
00654                 if (mode != NULL) {
00655                         errno = 0;
00656                         fd = devopen(str, mode);
00657                         if (fd > INVALID_HANDLE) {
00658                                 if (fd == fileno(stdin))
00659                                         rp->fp = stdin;
00660                                 else if (fd == fileno(stdout))
00661                                         rp->fp = stdout;
00662                                 else if (fd == fileno(stderr))
00663                                         rp->fp = stderr;
00664                                 else {
00665 #if defined(F_GETFL) && defined(O_APPEND)
00666                                         int fd_flags;
00667 
00668                                         fd_flags = fcntl(fd, F_GETFL);
00669                                         if (fd_flags != -1 && (fd_flags & O_APPEND) == O_APPEND)
00670                                                 rp->fp = fdopen(fd, binmode("a"));
00671                                         else
00672 #endif
00673                                                 rp->fp = fdopen(fd, (const char *) mode);
00674                                         rp->mode = (const char *) mode;
00675                                         /* don't leak file descriptors */
00676                                         if (rp->fp == NULL)
00677                                                 close(fd);
00678                                 }
00679                                 if (rp->fp != NULL && isatty(fd))
00680                                         rp->flag |= RED_NOBUF;
00681                                 /* Move rp to the head of the list. */
00682                                 if (red_head != rp) {
00683                                         if ((rp->prev->next = rp->next) != NULL)
00684                                                 rp->next->prev = rp->prev;
00685                                         red_head->prev = rp;
00686                                         rp->prev = NULL;
00687                                         rp->next = red_head;
00688                                         red_head = rp;
00689                                 }
00690                         }
00691                 }
00692                 if (rp->fp == NULL && rp->iop == NULL) {
00693                         /* too many files open -- close one and try again */
00694                         if (errno == EMFILE || errno == ENFILE)
00695                                 close_one();
00696 #if defined __MINGW32__ || defined __sun
00697                         else if (errno == 0)    /* HACK! */
00698                                 close_one();
00699 #endif
00700 #ifdef VMS
00701                         /* Alpha/VMS V7.1's C RTL is returning this instead
00702                            of EMFILE (haven't tried other post-V6.2 systems) */
00703 #define SS$_EXQUOTA 0x001C
00704                         else if (errno == EIO && vaxc$errno == SS$_EXQUOTA)
00705                                 close_one();
00706 #endif
00707                         else {
00708                                 /*
00709                                  * Some other reason for failure.
00710                                  *
00711                                  * On redirection of input from a file,
00712                                  * just return an error, so e.g. getline
00713                                  * can return -1.  For output to file,
00714                                  * complain. The shell will complain on
00715                                  * a bad command to a pipe.
00716                                  */
00717                                 if (errflg != NULL)
00718                                         *errflg = errno;
00719                                 if (tree->type == Node_redirect_output
00720                                     || tree->type == Node_redirect_append) {
00721                                         /* multiple messages make life easier for translators */
00722                                         if (*direction == 'f')
00723                                                 fatal(_("can't redirect from `%s' (%s)"),
00724                                                         str, strerror(errno));
00725                                         else
00726                                                 fatal(_("can't redirect to `%s' (%s)"),
00727                                                         str, strerror(errno));
00728                                 } else {
00729                                         free_temp(tmp);
00730                                         return NULL;
00731                                 }
00732                         }
00733                 }
00734         }
00735         free_temp(tmp);
00736         return rp;
00737 }
00738 
00739 /* getredirect --- find the struct redirect for this file or pipe */
00740 
00741 struct redirect *
00742 getredirect(const char *str, int len)
00743 {
00744         struct redirect *rp;
00745 
00746         for (rp = red_head; rp != NULL; rp = rp->next)
00747                 if (strlen(rp->value) == len && STREQN(rp->value, str, len))
00748                         return rp;
00749 
00750         return NULL;
00751 }
00752 
00753 /* close_one --- temporarily close an open file to re-use the fd */
00754 
00755 static void
00756 close_one()
00757 {
00758         register struct redirect *rp;
00759         register struct redirect *rplast = NULL;
00760 
00761         static short warned = FALSE;
00762 
00763         if (do_lint && ! warned) {
00764                 warned = TRUE;
00765                 lintwarn(_("reached system limit for open files: starting to multiplex file descriptors"));
00766         }
00767 
00768         /* go to end of list first, to pick up least recently used entry */
00769         for (rp = red_head; rp != NULL; rp = rp->next)
00770                 rplast = rp;
00771         /* now work back up through the list */
00772         for (rp = rplast; rp != NULL; rp = rp->prev)
00773                 if (rp->fp != NULL && (rp->flag & RED_FILE) != 0) {
00774                         rp->flag |= RED_USED;
00775                         errno = 0;
00776                         if (/* do_lint && */ fclose(rp->fp) != 0)
00777                                 warning(_("close of `%s' failed (%s)."),
00778                                         rp->value, strerror(errno));
00779                         rp->fp = NULL;
00780                         break;
00781                 }
00782         if (rp == NULL)
00783                 /* surely this is the only reason ??? */
00784                 fatal(_("too many pipes or input files open")); 
00785 }
00786 
00787 /* do_close --- completely close an open file or pipe */
00788 
00789 NODE *
00790 do_close(NODE *tree)
00791 {
00792         NODE *tmp, *tmp2;
00793         register struct redirect *rp;
00794         two_way_close_type how = CLOSE_ALL;     /* default */
00795 
00796         tmp = force_string(tree_eval(tree->lnode));     /* 1st arg: redir to close */
00797 
00798         if (tree->rnode != NULL) {
00799                 /* 2nd arg if present: "to" or "from" for two-way pipe */
00800                 /* DO NOT use _() on the strings here! */
00801                 tmp2 = force_string(tree->rnode->lnode);
00802                 if (strcasecmp(tmp2->stptr, "to") == 0)
00803                         how = CLOSE_TO;
00804                 else if (strcasecmp(tmp2->stptr, "from") == 0)
00805                         how = CLOSE_FROM;
00806                 else
00807                         fatal(_("close: second argument must be `to' or `from'"));
00808                 free_temp(tmp2);
00809         }
00810 
00811         for (rp = red_head; rp != NULL; rp = rp->next) {
00812                 if (strlen(rp->value) == tmp->stlen
00813                     && STREQN(rp->value, tmp->stptr, tmp->stlen))
00814                         break;
00815         }
00816 
00817         if (rp == NULL) {       /* no match, return -1 */
00818                 char *cp;
00819 
00820                 if (do_lint)
00821                         lintwarn(_("close: `%.*s' is not an open file, pipe or co-process"),
00822                                 tmp->stlen, tmp->stptr);
00823 
00824                 /* update ERRNO manually, using errno = ENOENT is a stretch. */
00825                 cp = _("close of redirection that was never opened");
00826                 unref(ERRNO_node->var_value);
00827                 ERRNO_node->var_value = make_string(cp, strlen(cp));
00828 
00829                 free_temp(tmp);
00830                 return tmp_number((AWKNUM) -1.0);
00831         }
00832         free_temp(tmp);
00833         fflush(stdout); /* synchronize regular output */
00834         tmp = tmp_number((AWKNUM) close_redir(rp, FALSE, how));
00835         rp = NULL;
00836         /*
00837          * POSIX says close() returns 0 on success, non-zero otherwise.
00838          * For POSIX, at this point we just return 0.  Otherwise we
00839          * return the exit status of the process or of pclose(), depending.
00840          * This whole business is a mess.
00841          */
00842         if (do_posix) {
00843                 free_temp(tmp);
00844                 return tmp_number((AWKNUM) 0);
00845         }
00846         return tmp;
00847 }
00848 
00849 /* close_redir --- close an open file or pipe */
00850 
00851 static int
00852 close_redir(register struct redirect *rp, int exitwarn, two_way_close_type how)
00853 {
00854         int status = 0;
00855 
00856         if (rp == NULL)
00857                 return 0;
00858         if (rp->fp == stdout || rp->fp == stderr)
00859                 return 0;
00860 
00861         if (do_lint && (rp->flag & RED_TWOWAY) == 0 && how != CLOSE_ALL)
00862                 lintwarn(_("close: redirection `%s' not opened with `|&', second argument ignored"),
00863                                 rp->value);
00864 
00865         errno = 0;
00866         if ((rp->flag & RED_TWOWAY) != 0) {     /* two-way pipe */
00867                 /* write end: */
00868                 if ((how == CLOSE_ALL || how == CLOSE_TO) && rp->fp != NULL) {
00869 #ifdef HAVE_SOCKETS
00870                         if ((rp->flag & RED_TCP) != 0)
00871                                 (void) shutdown(fileno(rp->fp), SHUT_WR);
00872 #endif /* HAVE_SOCKETS */
00873 
00874                         if ((rp->flag & RED_PTY) != 0) {
00875                                 fwrite("\004\n", sizeof("\004\n") - 1, 1, rp->fp);
00876                                 fflush(rp->fp);
00877                         }
00878                         status = fclose(rp->fp);
00879                         rp->fp = NULL;
00880                 }
00881 
00882                 /* read end: */
00883                 if (how == CLOSE_ALL || how == CLOSE_FROM) {
00884                         if ((rp->flag & RED_SOCKET) != 0 && rp->iop != NULL) {
00885 #ifdef HAVE_SOCKETS
00886                                 if ((rp->flag & RED_TCP) != 0)
00887                                         (void) shutdown(rp->iop->fd, SHUT_RD);
00888 #endif /* HAVE_SOCKETS */
00889                                 (void) iop_close(rp->iop);
00890                         } else
00891                                 status = gawk_pclose(rp);
00892 
00893                         rp->iop = NULL;
00894                 }
00895         } else if ((rp->flag & (RED_PIPE|RED_WRITE)) == (RED_PIPE|RED_WRITE)) { /* write to pipe */
00896                 status = pclose(rp->fp);
00897                 if ((BINMODE & 1) != 0)
00898                         os_setbinmode(fileno(stdin), O_BINARY);
00899 
00900                 rp->fp = NULL;
00901         } else if (rp->fp != NULL) {    /* write to file */
00902                 status = fclose(rp->fp);
00903                 rp->fp = NULL;
00904         } else if (rp->iop != NULL) {   /* read from pipe/file */
00905                 if ((rp->flag & RED_PIPE) != 0)         /* read from pipe */
00906                         status = gawk_pclose(rp);
00907                         /* gawk_pclose sets rp->iop to null */
00908                 else {                                  /* read from file */
00909                         status = iop_close(rp->iop);
00910                         rp->iop = NULL;
00911                 }
00912         }
00913 
00914         /* SVR4 awk checks and warns about status of close */
00915         if (status != 0) {
00916                 char *s = strerror(errno);
00917 
00918                 /*
00919                  * Too many people have complained about this.
00920                  * As of 2.15.6, it is now under lint control.
00921                  */
00922                 if (do_lint) {
00923                         if ((rp->flag & RED_PIPE) != 0)
00924                                 lintwarn(_("failure status (%d) on pipe close of `%s' (%s)"),
00925                                          status, rp->value, s);
00926                         else
00927                                 lintwarn(_("failure status (%d) on file close of `%s' (%s)"),
00928                                          status, rp->value, s);
00929                 }
00930 
00931                 if (! do_traditional) {
00932                         /* set ERRNO too so that program can get at it */
00933                         update_ERRNO();
00934                 }
00935         }
00936 
00937         if (exitwarn) {
00938                 /*
00939                  * Don't use lintwarn() here.  If lint warnings are fatal,
00940                  * doing so prevents us from closing other open redirections.
00941                  *
00942                  * Using multiple full messages instead of string parameters
00943                  * for the types makes message translation easier.
00944                  */
00945                 if ((rp->flag & RED_SOCKET) != 0)
00946                         warning(_("no explicit close of socket `%s' provided"),
00947                                 rp->value);
00948                 else if ((rp->flag & RED_TWOWAY) != 0)
00949                         warning(_("no explicit close of co-process `%s' provided"),
00950                                 rp->value);
00951                 else if ((rp->flag & RED_PIPE) != 0)
00952                         warning(_("no explicit close of pipe `%s' provided"),
00953                                 rp->value);
00954                 else
00955                         warning(_("no explicit close of file `%s' provided"),
00956                                 rp->value);
00957         }
00958 
00959         /* remove it from the list if closing both or both ends have been closed */
00960         if (how == CLOSE_ALL || (rp->iop == NULL && rp->fp == NULL)) {
00961                 if (rp->next != NULL)
00962                         rp->next->prev = rp->prev;
00963                 if (rp->prev != NULL)
00964                         rp->prev->next = rp->next;
00965                 else
00966                         red_head = rp->next;
00967                 free(rp->value);
00968                 free((char *) rp);
00969         }
00970 
00971         return status;
00972 }
00973 
00974 /* flush_io --- flush all open output files */
00975 
00976 int
00977 flush_io()
00978 {
00979         register struct redirect *rp;
00980         int status = 0;
00981 
00982         errno = 0;
00983         if (fflush(stdout)) {
00984                 warning(_("error writing standard output (%s)"), strerror(errno));
00985                 status++;
00986         }
00987         if (fflush(stderr)) {
00988                 warning(_("error writing standard error (%s)"), strerror(errno));
00989                 status++;
00990         }
00991         for (rp = red_head; rp != NULL; rp = rp->next)
00992                 /* flush both files and pipes, what the heck */
00993                 if ((rp->flag & RED_WRITE) && rp->fp != NULL) {
00994                         if (fflush(rp->fp)) {
00995                                 if (rp->flag  & RED_PIPE)
00996                                         warning(_("pipe flush of `%s' failed (%s)."),
00997                                                 rp->value, strerror(errno));
00998                                 else if (rp->flag & RED_TWOWAY)
00999                                         warning(_("co-process flush of pipe to `%s' failed (%s)."),
01000                                                 rp->value, strerror(errno));
01001                                 else
01002                                         warning(_("file flush of `%s' failed (%s)."),
01003                                                 rp->value, strerror(errno));
01004                                 status++;
01005                         }
01006                 }
01007         if (status != 0)
01008                 status = -1;    /* canonicalize it */
01009         return status;
01010 }
01011 
01012 /* close_io --- close all open files, called when exiting */
01013 
01014 int
01015 close_io()
01016 {
01017         register struct redirect *rp;
01018         register struct