#line 996 "../../src/builtin/snarf.m4"
/* -*- buffer-read-only: t -*- vi: set ro:
   THIS FILE IS GENERATED AUTOMATICALLY.  PLEASE DO NOT EDIT.
*/
#line 996
#ifdef HAVE_CONFIG_H
#line 996
# include <config.h>
#line 996
#endif
#line 996
#include <sys/types.h>
#line 996

#line 996
#include "mailfromd.h"
#line 996
#include "prog.h"
#line 996
#include "builtin.h"
#line 996

#line 462 "io.bi"
static mu_debug_handle_t debug_handle;
#line 996 "../../src/builtin/snarf.m4"

#line 1036 "../../src/builtin/snarf.m4"

/* End of snarf.m4 */
#line 1 "io.bi"
/* This file is part of Mailfromd.             -*- c -*-
   Copyright (C) 2006-2022 Sergey Poznyakoff

   This program 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 3, or (at your option)
   any later version.

   This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. */



#include <mflib/status.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include "global.h"
#include "msg.h"

static size_t nstreams = MAX_IOSTREAMS;

static struct mu_cfg_param io_cfg_param[] = {
	{ "max-streams", mu_c_size, &nstreams, 0, NULL,
	  N_("Maximum number of stream descriptors.") },
	{ NULL }
};

enum buffering {
	buf_none,
	buf_full,
	buf_line
};

struct io_stream {
	char *name;              /* Stream name */
	int fd;                  /* File descriptor */
	pid_t pid;               /* PID of the associated process, if any */
	struct io_stream *strout;/* Output stream */
	enum buffering buf_type; /* Buffering type */
	char *buf;               /* I/O buffer */
	size_t buf_size;         /* Buffer capacity */
	size_t buf_len;          /* Number of bytes available in buffer */
	size_t buf_pos;          /* Current position in buffer */
	int buf_dirty;           /* 1 if the buffer was modified */
	int (*shutdown)(struct io_stream *, int what);
	void (*cleanup)(void*);
	void *cleanup_data;
	char *delim;
};

static struct io_stream *
io_strout(struct io_stream *str)
{
	return (str->strout != NULL) ? str->strout : str;
}

static int
full_write(struct io_stream *str, char const *buf, size_t len)
{
	int fd = io_strout(str)->fd;
	while (len > 0) {
		ssize_t n = write(fd, buf, len);
		if (n == 0) {
			mu_error(_("%s: short write trying to write %zu bytes"),
				 str->name, len);
			return mfe_eof;
		}
		if (n == -1) {
			mu_error(_("%s: write error: %s"),
				 str->name, mu_strerror(errno));
			return mfe_io;
		}
		len -= n;
	}
	return mfe_success;
}

static int
flush_stream(struct io_stream *str)
{
	int rc = mfe_success;
	if (str->buf_dirty) {
		rc = full_write(str, str->buf, str->buf_len);
		str->buf_dirty = 0;
	}
	str->buf_len = 0;
	str->buf_pos = 0;
	return rc;
}

static int
close_stream(struct io_stream *str)
{
	int rc;

	if ((rc = flush_stream(str)) != mfe_success)
		return rc;
	if (str->strout && (rc = close_stream(str->strout)) != mfe_success)
		return rc;
	if (str->fd != -1)
		close(str->fd);
	if (str->pid) {
		int status;
		waitpid(str->pid, &status, 0);
	}
	str->fd = -1;
	str->pid = 0;
	str->strout = NULL;
	if (str->cleanup)
		str->cleanup(str->cleanup_data);
	str->cleanup = NULL;
	str->cleanup_data = NULL;
	if (str->name) {
		free(str->name);
		str->name = NULL;
	}
	if (str->delim) {
		free(str->delim);
		str->delim = NULL;
	}
	free(str->buf);
	str->buf_type = buf_none;
	str->buf = NULL;
	str->buf_size = 0;
	str->buf_len = 0;
	str->buf_pos = 0;
	str->buf_dirty = 0;
	return mfe_success;
}

static size_t io_buffering_loc
#line 137 "io.bi"
;
static size_t io_buffer_size_loc
#line 138 "io.bi"
;

static void
io_set_buffer(eval_environ_t env, struct io_stream *str, long type, long size)
{
	int rc = flush_stream(str);
		if (!(rc == mfe_success))
#line 144
		(
#line 144
	env_throw_bi(env, rc, NULL, _("%s: error flushing stream"),str->name)
#line 144
)
#line 147
;
	switch (type) {
	case buf_none:
		free(str->buf);
		str->buf = NULL;
		str->buf_size = 0;
		break;

	case buf_line:
	case buf_full:
			if (!(size > 0))
#line 157
		(
#line 157
	env_throw_bi(env, mfe_range, NULL, _("buffer size out of allowed range"))
#line 157
)
#line 159
;
		if (size != str->buf_size) {
			char *p = realloc(str->buf, size);
				if (!(p != NULL))
#line 162
		(
#line 162
	env_throw_bi(env, mfe_failure, NULL, _("can't allocate stream buffer"))
#line 162
)
#line 164
;
			str->buf = p;
			str->buf_size = size;
		}
		str->buf_type = type;
		break;

	default:
		(
#line 172
	env_throw_bi(env, mfe_range, NULL, _("bad buffering type: %ld"),type)
#line 172
);
	}

	str->buf_len = 0;
	str->buf_pos = 0;
	str->buf_dirty = 0;
}

static inline size_t
io_buffer_avail(struct io_stream *str)
{
	return str->buf_size - str->buf_len;
}

static inline size_t
io_buffer_data(struct io_stream *str)
{
	return str->buf_len - str->buf_pos;
}

static void
io_write(eval_environ_t env, struct io_stream *iostr, char const *str, size_t size)
{
	if (iostr->buf_type == buf_none || io_strout(iostr)->fd != -1) {
		int fd = io_strout(iostr)->fd;
		while (size > 0) {
			ssize_t n = write(fd, str, size);
			if (n == 0)
				(
#line 200
	env_throw_bi(env, mfe_eof, NULL, "short write")
#line 200
);
				if (!(n > 0))
#line 201
		(
#line 201
	env_throw_bi(env, mfe_io, NULL, _("write error on %s: %s"),iostr->name,mu_strerror(errno))
#line 201
)
#line 204
;
			size -= n;
		}
	} else if (iostr->buf_type == buf_line) {
		while (size > 0) {
			size_t len, avail;
			char *p;

			if ((p = memchr(str, '\n', size)) != NULL) {
				len = p - str + 1;
			} else {
				len = size;
			}

			avail = io_buffer_avail(iostr);
			if (avail == 0) {
				char *p = mu_2nrealloc(iostr->buf,
						       &iostr->buf_size,
						       1);
					if (!(p != NULL))
#line 223
		(
#line 223
	env_throw_bi(env, mfe_failure, NULL, _("cannot reallocate stream buffer"))
#line 223
)
#line 225
;
				iostr->buf = p;
				avail = io_buffer_avail(iostr);
			}

			if (len > avail)
				len = avail;
			memcpy(iostr->buf + iostr->buf_len, str, len);
			iostr->buf_len += len;
			iostr->buf_dirty = 1;
			if (iostr->buf[iostr->buf_len-1] == '\n') {
				int ec = flush_stream(iostr);
					if (!(ec == mfe_success))
#line 237
		(
#line 237
	env_throw_bi(env, ec, NULL, _("%s: error flushing stream"),iostr->name)
#line 237
)
#line 240
;
			}

			str += len;
			size -= len;
		}
	} else /* if (iostr->buf_type == buf_full) */ {
		while (size > 0) {
			size_t avail = io_buffer_avail(iostr);
			size_t len = size;

			if (avail == 0) {
				int ec = flush_stream(iostr);
					if (!(ec == mfe_success))
#line 253
		(
#line 253
	env_throw_bi(env, ec, NULL, _("%s: error flushing stream"),iostr->name)
#line 253
)
#line 256
;
				avail = io_buffer_avail(iostr);
			}
			if (len > avail)
				len = avail;
			memcpy(iostr->buf + iostr->buf_len, str, len);
			iostr->buf_len += len;
			iostr->buf_dirty = 1;

			str += len;
			size -= len;
		}
	}
}

static int
io_fillbuf(eval_environ_t env, struct io_stream *iostr)
{
	if (iostr->buf_type == buf_none) {
		abort();
	} else {
		int ec = flush_stream(iostr);
			if (!(ec == mfe_success))
#line 278
		(
#line 278
	env_throw_bi(env, ec, NULL, _("%s: error flushing stream"),iostr->name)
#line 278
)
#line 280
;
		for (;;) {
			ssize_t n;
			size_t avail = io_buffer_avail(iostr);
			if (avail == 0) {
				if (iostr->buf_type == buf_full)
					break;
				else {
					char *p = mu_2nrealloc(iostr->buf,
							       &iostr->buf_size,
							       1);
						if (!(p != NULL))
#line 291
		(
#line 291
	env_throw_bi(env, mfe_failure, NULL, _("cannot reallocate stream buffer"))
#line 291
)
#line 293
;
					iostr->buf = p;
					continue;
				}
			}
			n = read(iostr->fd, iostr->buf + iostr->buf_len, avail);
			if (n == 0) {
				if (iostr->buf_len == 0)
					return 0;
				break;
			}
				if (!(n > 0))
#line 304
		(
#line 304
	env_throw_bi(env, mfe_io, NULL, _("read error on %s: %s"),iostr->name,mu_strerror(errno))
#line 304
)
#line 307
;

			iostr->buf_len += n;
			if (iostr->buf_type == buf_line &&
			    memchr(iostr->buf + iostr->buf_len, '\n', n))
				break;
		}
	}
	return 1;
}

static size_t
io_read(eval_environ_t env, struct io_stream *iostr, char *str, size_t size)
{
	size_t total = 0;
	while (total < size) {
		ssize_t n;
		if (iostr->buf_type == buf_none) {
			n = read(iostr->fd, str, size - total);
			if (n == 0)
				break;
				if (!(n > 0))
#line 328
		(
#line 328
	env_throw_bi(env, mfe_io, NULL, _("read error on %s: %s"),iostr->name,mu_strerror(errno))
#line 328
)
#line 331
;
		} else {
			if ((n = io_buffer_data(iostr)) == 0) {
				if (io_fillbuf(env, iostr) == 0)
					break;
				n = io_buffer_data(iostr);
			}
			if (n > size - total)
				n = size - total;
			memcpy(str, iostr->buf + iostr->buf_pos, n);
			iostr->buf_pos += n;
		}

		total += n;
	}
	return total;
}

static int
io_getc(eval_environ_t env, struct io_stream *iostr, char *retc)
{
	if (iostr->buf_type == buf_none) {
		ssize_t n = read(iostr->fd, retc, 1);
			if (!(n >= 0))
#line 354
		(
#line 354
	env_throw_bi(env, mfe_io, NULL, _("read error on %s: %s"),iostr->name,mu_strerror(errno))
#line 354
)
#line 357
;
		if (n == 0)
			return 0;
	} else {
		if (io_buffer_data(iostr) == 0) {
			if (io_fillbuf(env, iostr) == 0)
				return 0;
		}
		*retc = iostr->buf[iostr->buf_pos++];
	}
	return 1;
}

static void
io_read_delim(eval_environ_t env, struct io_stream *iostr, const char *delim)
{
	size_t delim_len = strlen(delim);
	size_t i = 0;
	for (;;) {
		char c;

		if (io_getc(env, iostr, &c) == 0) {
			if (i == 0)
				(
#line 380
	env_throw_bi(env, mfe_eof, NULL, _("EOF on %s"),iostr->name)
#line 380
);
			break;
		}

		do { char __c = c; heap_obstack_grow(env, &__c, 1); } while(0);
		i++;
		if (i >= delim_len &&
		    memcmp((char*)heap_obstack_base(env) + i - delim_len, delim, delim_len) == 0) {
			heap_obstack_reclaim(env, delim_len);
			break;
		}
	}

	do { char __c = 0; heap_obstack_grow(env, &__c, 1); } while(0);
}

static struct io_stream *io_find_avail(eval_environ_t env, int *sn);
static struct io_stream *io_get_open_stream(eval_environ_t env, long fn);


#define REDIRECT_STDIN_P(f) ((f) & (O_WRONLY|O_RDWR))
#define REDIRECT_STDOUT_P(f) (!((f) & O_WRONLY))

#define STDERR_SHUT        0
#define STDERR_NULL        1
#define STDERR_LOG         2
#define STDERR_FILE        3
#define STDERR_FILE_APPEND 4

#define LOG_TAG_PFX "mailfromd:"
#define LOG_TAG_PFX_LEN (sizeof(LOG_TAG_PFX)-1)

static void
stderr_to_log(char *arg, const char *cmd)
{
	int p[2];
	pid_t pid;

	if (pipe(p)) {
		mu_error(_("pipe failed: %s"), mu_strerror(errno));
		close(2);
		return;
	}

	pid = fork();

	if (pid == (pid_t) -1) {
		mu_error(_("fork failed: %s"), mu_strerror(errno));
		close(p[0]);
		close(p[1]);
		close(2);
		return;
	}

	/* Child */
	if (pid == 0) {
		FILE *fp;
		fd_set fdset;
		size_t len;
		char buf[1024];
		char *tag;
		int fac = mu_log_facility, pri = LOG_ERR;

		if (arg) {
			char *p = strchr(arg, '.');

			if (p)
				*p++ = 0;
			if (mu_string_to_syslog_facility(arg, &fac)) {
				mu_error(_("unknown syslog facility (%s), "
					   "redirecting stderr to %s"),
					 arg,
					 mu_syslog_facility_to_string(fac));
			}

			if (p && mu_string_to_syslog_priority(p, &pri)) {
				mu_error(_("unknown syslog priority (%s), "
					   "redirecting stderr to %s"),
					 arg,
					 mu_syslog_priority_to_string(pri));
			}
		}
		
#line 462 "io.bi"

#line 462
mu_debug(debug_handle, MU_DEBUG_TRACE2,("redirecting stderr to syslog %s.%s",
			   mu_syslog_facility_to_string(fac),
			   mu_syslog_priority_to_string(pri)));
#line 466

		len = strcspn(cmd, " \t");
		tag = malloc(LOG_TAG_PFX_LEN + len + 1);
		if (!tag)
			tag = (char*) cmd;
		else {
			strcpy(tag, LOG_TAG_PFX);
			memcpy(tag + LOG_TAG_PFX_LEN, cmd, len);
			tag[LOG_TAG_PFX_LEN + len] = 0;
		}
		mf_proctitle_format("%s redirector", cmd);

		FD_ZERO(&fdset);
		FD_SET(p[0], &fdset);
		logger_fdset(&fdset);
		close_fds_except(&fdset);

		fp = fdopen(p[0], "r");
		logger_open();
		while (fgets(buf, sizeof(buf), fp))
			syslog(pri, "%s", buf);
		exit(0);
	}

	/* Parent */
	close(p[0]);
	dup2(p[1], 2);
	close(p[1]);
}

static void
stderr_handler(int mode, char *arg, const char *cmd)
{
	int fd;
	int append = O_TRUNC;

	switch (mode) {
	case STDERR_SHUT:
		close(2);
		break;

	case STDERR_NULL:
		arg = "/dev/null";
	case STDERR_FILE_APPEND:
		append = O_APPEND;
	case STDERR_FILE:
		if (!arg || !*arg) {
			close(2);
			break;
		}
		
#line 516

#line 516
mu_debug(debug_handle, MU_DEBUG_TRACE2,("redirecting stderr to %s", arg));
		fd = open(arg, O_CREAT|O_WRONLY|append, 0644);
		if (fd < 0) {
			mu_error(_("cannot open file %s for appending: %s"),
				 arg, mu_strerror(errno));
			close(2);
			return;
		}
		if (fd != 2) {
			dup2(fd, 2);
			close(fd);
		}
		break;

	case STDERR_LOG:
		stderr_to_log(arg, cmd);
	}
}

static void
parse_stderr_redirect(const char **pcmd, int *perr, char **parg)
{
	int err;
	size_t len;
	char *arg;
	const char *cmdline = *pcmd;

	while (*cmdline && mu_isspace(*cmdline))
		cmdline++;
	if (strncmp(cmdline, "2>file:", 7) == 0) {
		cmdline += 7;
		err = STDERR_FILE;
	} else if (strncmp(cmdline, "2>>file:", 8) == 0) {
		cmdline += 8;
		err = STDERR_FILE_APPEND;
	} else if (strncmp(cmdline, "2>null:", 7) == 0) {
		cmdline += 7;
		err = STDERR_NULL;
	} else if (strncmp(cmdline, "2>syslog:", 9) == 0) {
		cmdline += 9;
		err = STDERR_LOG;
	} else
		return;

	len = strcspn(cmdline, " \t");
	if (len > 0 && cmdline[len-1] == 0)
		return;
	if (len == 0)
		arg = NULL;
	else {
		arg = malloc(len + 1);
		if (!arg)
			return;
		memcpy(arg, cmdline, len);
		arg[len] = 0;
	}

	*pcmd = cmdline + len;
	*perr = err;
	*parg = arg;
}

static int
attach_strout(eval_environ_t env, struct io_stream *str, int fd)
{
	struct io_stream *ios;
	static char const pfx[] = "stdin of ";
	size_t len;

	if ((ios = io_find_avail(env, NULL)) == NULL) {
		return ENFILE;
	}

	len = strlen(pfx) + strlen(str->name);
	if ((ios->name = malloc(len + 1)) == NULL) {
		return errno;
	}
	strcat(strcpy(ios->name, pfx), str->name);

	ios->fd = fd;
	io_set_buffer(env, ios, mf_c_val(*env_data_ref(env, io_buffering_loc),long) ,
		      mf_c_val(*env_data_ref(env, io_buffer_size_loc),long) );
	str->strout = ios;
	return 0;
}

static void
pipe_cleanup(void *data)
{
	int *p = data;
	close(p[0]);
	close(p[1]);
}

#define HASFD(i,n) ((i) != NULL && (i)[n] != -1)

static void
open_program_stream_ioe(eval_environ_t env,
			struct io_stream *str, const char *cmdline,
			int flags,
			int ioe[2])
{
	int rightp[2], leftp[2];
	pid_t pid;
	int err = STDERR_SHUT;
	char *arg = NULL;
	struct mu_wordsplit ws;
	char *shell;
	
	parse_stderr_redirect(&cmdline, &err, &arg);
	while (*cmdline && (*cmdline == ' ' || *cmdline == '\t'))
		cmdline++;

	env_function_cleanup_add(env, CLEANUP_ALWAYS, arg, NULL);
	if (REDIRECT_STDIN_P(flags) && !HASFD(ioe, 0)) {
			if (!(pipe(leftp) == 0))
#line 631
		(
#line 631
	env_throw_bi(env, mfe_failure, NULL, _("pipe left: %s"),mu_strerror(errno))
#line 631
)
#line 633
;
		env_function_cleanup_add(env, CLEANUP_THROW, leftp, pipe_cleanup);
	}

	if (REDIRECT_STDOUT_P(flags) && !HASFD(ioe, 1)) {
			if (!(pipe(rightp) == 0))
#line 638
		(
#line 638
	env_throw_bi(env, mfe_failure, NULL, _("pipe right: %s"),mu_strerror(errno))
#line 638
)
#line 640
;
		env_function_cleanup_add(env, CLEANUP_THROW, rightp, pipe_cleanup);
	}

	switch (pid = fork()) {
		/* The child branch.  */
	case 0:
		/* attach the pipes */

		/* Right-end */
		if (HASFD(ioe, 1)) {
			dup2(ioe[1], 1);
		} else if (REDIRECT_STDOUT_P(flags)) {
			if (rightp[1] != 1)
				dup2(rightp[1], 1);
		}

		/* Left-end */
		if (HASFD(ioe, 0)) {
			dup2(ioe[0], 0);
		} else if (REDIRECT_STDIN_P(flags)) {
			if (leftp[0] != 0)
				dup2(leftp[0], 0);
		}

		if (HASFD(ioe, 2) && ioe[2] != 2)
			dup2(ioe[2], 2);
		else
			stderr_handler(err, arg, cmdline);

		/* Close unneeded descriptors */
		close_fds_above(2);

		shell = getenv("SHELL");
		if (!shell) {
			
#line 675

#line 675
mu_debug(debug_handle, MU_DEBUG_TRACE3,("running %s", cmdline));
			if (mu_wordsplit(cmdline, &ws,
					 MU_WRDSF_DEFFLAGS & ~MU_WRDSF_CESCAPES)) {
				mu_error(_("cannot parse command line %s: %s"),
					 cmdline, mu_wordsplit_strerror(&ws));
				exit(127);
			}
			execvp(ws.ws_wordv[0], ws.ws_wordv);
		} else {
			char *xargv[] = {
				shell,
				"-c",
				(char*)cmdline,
				NULL
			};
			execv(shell, xargv);
		}
		mu_error(_("cannot run %s: %s"),
			 cmdline, mu_strerror(errno));
		exit(127);
		/********************/

		/* Parent branches: */
	case -1:
		/* Fork has failed */
		/* Restore things */
		(
#line 701
	env_throw_bi(env, mfe_failure, NULL, _("fork: %s"),mu_strerror(errno))
#line 701
);
		break;

	default:
		str->pid = pid;
		if (REDIRECT_STDOUT_P(flags) && !HASFD(ioe, 1)) {
			str->fd = rightp[0];
			close(rightp[1]);
		} else
			str->fd = -1;

		if (REDIRECT_STDIN_P(flags) && !HASFD(ioe, 0)) {
			if (str->fd == -1) {
				str->fd = leftp[1];
				str->strout = NULL;
			} else
				attach_strout(env, str, leftp[1]);
		} else
			str->strout = NULL;
	}
}

static void
open_program_stream(eval_environ_t env,
		    struct io_stream *str, const char *cmdline,
		    int flags)
{
	open_program_stream_ioe(env, str, cmdline, flags, NULL);
}

static void
open_file_stream(eval_environ_t env,
		 struct io_stream *str, const char *file, int flags)
{
	/* FIXME: file mode is hardcoded */
	if ((str->fd = open(file, flags, 0644)) == -1)
		(
#line 737
	env_throw_bi(env, mfe_failure, NULL, _("can't open %s: %s"),file,mu_strerror(errno))
#line 737
);
#line 739
}


static void
open_parsed_inet_stream(eval_environ_t env,
			struct io_stream *str,
			const char *cstr,
			char *proto, char *port, char *path,
			int flags)
{
	union {
		struct sockaddr sa;
		struct sockaddr_in s_in;
		struct sockaddr_un s_un;
#ifdef GACOPYZ_IPV6
		struct sockaddr_in6 s_in6;
#endif
	} addr;

	socklen_t socklen;
	int fd;
	int rc;

	if (!proto
	    || strcmp(proto, "unix") == 0 || strcmp(proto, "local") == 0) {
		struct stat st;

			if (!(port == NULL))
#line 766
		(
#line 766
	env_throw_bi(env, mfe_failure, NULL, _("invalid connection type: %s; "
			    "port is meaningless for UNIX sockets"),cstr)
#line 766
)
#line 770
;

			if (!(strlen(path) <= sizeof addr.s_un.sun_path))
#line 772
		(
#line 772
	env_throw_bi(env, mfe_range, NULL, _("%s: UNIX socket name too long"),path)
#line 772
)
#line 775
;

		addr.sa.sa_family = PF_UNIX;
		socklen = sizeof(addr.s_un);
		strcpy(addr.s_un.sun_path, path);

		if (stat(path, &st)) {
			(
#line 782
	env_throw_bi(env, mfe_failure, NULL, _("%s: cannot stat socket: %s"),path,strerror(errno))
#line 782
);
#line 785
		} else {
			/* FIXME: Check permissions? */
				if (!(S_ISSOCK(st.st_mode)))
#line 787
		(
#line 787
	env_throw_bi(env, mfe_failure, NULL, _("%s: not a socket"),path)
#line 787
)
#line 790
;
		}

	} else if (strcmp(proto, "inet") == 0) {
		short pnum;
		long num;
		char *p;

		addr.sa.sa_family = PF_INET;
		socklen = sizeof(addr.s_in);

			if (!(port != NULL))
#line 801
		(
#line 801
	env_throw_bi(env, mfe_failure, NULL, _("invalid connection type: %s; "
			    "missing port number"),cstr)
#line 801
)
#line 805
;

		num = pnum = strtol(port, &p, 0);
		if (*p == 0) {
				if (!(num == pnum))
#line 809
		(
#line 809
	env_throw_bi(env, mfe_range, NULL, _("invalid connection type: "
				    "%s; bad port number"),cstr)
#line 809
)
#line 813
;
			pnum = htons(pnum);
		} else {
			struct servent *sp = getservbyname(port, "tcp");

				if (!(sp != NULL))
#line 818
		(
#line 818
	env_throw_bi(env, mfe_failure, NULL, _("invalid connection type: "
				    "%s; unknown port name"),cstr)
#line 818
)
#line 822
;
			pnum = sp->s_port;
		}

		if (!path)
			addr.s_in.sin_addr.s_addr = INADDR_ANY;
		else {
			struct hostent *hp = gethostbyname(path);
				if (!(hp != NULL))
#line 830
		(
#line 830
	env_throw_bi(env, mfe_failure, NULL, _("unknown host name %s"),path)
#line 830
)
#line 833
;
			addr.sa.sa_family = hp->h_addrtype;
			switch (hp->h_addrtype) {
			case AF_INET:
				memmove(&addr.s_in.sin_addr, hp->h_addr, 4);
				addr.s_in.sin_port = pnum;
				break;

			default:
				(
#line 842
	env_throw_bi(env, mfe_range, NULL, _("invalid connection type: "
					   "%s; unsupported address family"),cstr)
#line 842
);
#line 846
			}
		}
#ifdef GACOPYZ_IPV6
	} else if (strcmp(proto, "inet6") == 0) {
		struct addrinfo hints;
		struct addrinfo *res;

			if (!(port != NULL))
#line 853
		(
#line 853
	env_throw_bi(env, mfe_failure, NULL, _("invalid connection type: %s; "
			    "missing port number"),cstr)
#line 853
)
#line 857
;

		memset(&hints, 0, sizeof(hints));
		hints.ai_family = AF_INET6;
		hints.ai_socktype = SOCK_STREAM;
		if (!path)
			hints.ai_flags |= AI_PASSIVE;

		rc = getaddrinfo(path, port, &hints, &res);

		switch (rc) {
		case 0:
			break;

		case EAI_SYSTEM:
			(
#line 872
	env_throw_bi(env, mfe_failure, NULL, _("%s:%s: cannot parse address: %s"),path,port,strerror(errno))
#line 872
);
#line 875

		case EAI_BADFLAGS:
		case EAI_SOCKTYPE:
			(
#line 878
	env_throw_bi(env, mfe_failure, NULL, _("%s:%d: internal error converting %s:%s"),__FILE__,__LINE__,path,port)
#line 878
);
#line 881

		case EAI_MEMORY:
			mu_alloc_die();

		default:
			(
#line 886
	env_throw_bi(env, mfe_failure, NULL, "%s:%s: %s",path,port,gai_strerror(rc))
#line 886
);
#line 889
		}

		socklen = res->ai_addrlen;
		if (socklen > sizeof(addr)) {
			freeaddrinfo(res);
			(
#line 894
	env_throw_bi(env, mfe_failure, NULL, _("%s:%s: address length too big (%lu)"),path,port,(unsigned long) socklen)
#line 894
);
#line 898
		}
		memcpy(&addr, res->ai_addr, res->ai_addrlen);
		freeaddrinfo(res);
#endif
	} else {
		(
#line 903
	env_throw_bi(env, mfe_range, NULL, _("unsupported protocol: %s"),proto)
#line 903
);
#line 906
	}

	fd = socket(addr.sa.sa_family, SOCK_STREAM, 0);
		if (!(fd != -1))
#line 909
		(
#line 909
	env_throw_bi(env, mfe_failure, NULL, _("unable to create new socket: %s"),strerror(errno))
#line 909
)
#line 912
;

	/* FIXME: Bind to the source ? */

	rc = connect(fd, &addr.sa, socklen);
	if (rc) {
		close(fd);
		(
#line 919
	env_throw_bi(env, mfe_failure, NULL, _("cannot connect to %s: %s"),cstr,strerror(errno))
#line 919
);
#line 922
	}

	str->fd = fd;
}

static int
shutdown_inet_stream(struct io_stream *str, int how)
{
	switch (how) {
	case 0:
		how = SHUT_RD;
		break;

	case 1:
		how = SHUT_WR;
		break;

	case 2:
		how = SHUT_RDWR;
		break;

	default:
		return EINVAL;
	}
	if (shutdown(str->fd, how))
		return errno;
	return 0;
}

static void
open_inet_stream(eval_environ_t env,
		 struct io_stream *str, const char *addr, int flags)
{
	char *proto, *port, *path;

	if (gacopyz_parse_connection(addr, &proto, &port, &path)
	    != MI_SUCCESS)
		(
#line 959
	env_throw_bi(env, mfe_failure, NULL, _("can't parse connection string %s"),addr)
#line 959
);
#line 961
	else {
		env_function_cleanup_add(env, CLEANUP_ALWAYS, proto, NULL);
		env_function_cleanup_add(env, CLEANUP_ALWAYS, port, NULL);
		env_function_cleanup_add(env, CLEANUP_ALWAYS, path, NULL);
		open_parsed_inet_stream(env,
					str, addr,
					proto, port, path, flags);
		str->shutdown = shutdown_inet_stream;
	}
}


static void *
alloc_streams()
{
	struct io_stream *p, *stab = mu_calloc(nstreams, sizeof *stab);
	for (p = stab; p < stab + nstreams; p++)
		p->fd = -1;
	return stab;
}

static void
destroy_streams(void *data)
{
	struct io_stream *stab = data;
	struct io_stream *p;
	for (p = stab; p < stab + nstreams; p++) {
		close_stream(p);
		free(p->buf);
	}
	free(stab);
}


#line 994

#line 994
static int IO_id;
#line 994 "io.bi"


static inline int
io_stream_is_avail(struct io_stream *str)
{
	return str->fd == -1 && str->pid == 0;
}

static struct io_stream *
io_find_avail(eval_environ_t env, int *sn)
{
	int i;
	struct io_stream *iotab = env_get_builtin_priv(env,IO_id);

	for (i = 0; i < nstreams; i++) {
		if (io_stream_is_avail(&iotab[i])) {
			if (sn)
				*sn = i;
			return &iotab[i];
		}
	}
	return NULL;
}

static struct io_stream *
io_get_open_stream(eval_environ_t env, long fn)
{
	struct io_stream *iotab = env_get_builtin_priv(env,IO_id);
		if (!(fn >= 0 && fn < nstreams && !io_stream_is_avail(&iotab[fn])))
#line 1022
		(
#line 1022
	env_throw_bi(env, mfe_range, NULL, _("invalid file descriptor"))
#line 1022
)
#line 1024
;
	return &iotab[fn];
}

int
_bi_io_fd(eval_environ_t env, int fn, int what)
{
	struct io_stream *ios = io_get_open_stream(env, fn);
	int descr;

	descr = what == 0 ? ios->fd : io_strout(ios)->fd;
		if (!(descr >= 0))
#line 1035
		(
#line 1035
	env_throw_bi(env, mfe_range, NULL, _("invalid file descriptor"))
#line 1035
)
#line 1037
;
	return descr;
}

void
#line 1041
bi_setbuf(eval_environ_t env)
#line 1041

#line 1041

#line 1041 "io.bi"
{
#line 1041
	
#line 1041

#line 1041
        long  fn;
#line 1041
        long  type;
#line 1041
        long  size;
#line 1041
        
#line 1041
        long __bi_argcnt;
#line 1041
        get_numeric_arg(env, 0, &__bi_argcnt);
#line 1041
        get_numeric_arg(env, 1, &fn);
#line 1041
        if (__bi_argcnt > 1)
#line 1041
                get_numeric_arg(env, 2, &type);
#line 1041
        if (__bi_argcnt > 2)
#line 1041
                get_numeric_arg(env, 3, &size);
#line 1041
        
#line 1041
        adjust_stack(env, __bi_argcnt + 1);
#line 1041

#line 1041

#line 1041
	if (builtin_module_trace(BUILTIN_IDX_io))
#line 1041
		prog_trace(env, "setbuf %lu %lu %lu",fn, ((__bi_argcnt > 1) ? type : 0), ((__bi_argcnt > 2) ? size : 0));;
#line 1041

{
	struct io_stream *ios = io_get_open_stream(env, fn);
	io_set_buffer(env, ios,
		      ((__bi_argcnt > 1) ? type : mf_c_val(*env_data_ref(env, io_buffering_loc),long) ),
		      ((__bi_argcnt > 2) ? size : mf_c_val(*env_data_ref(env, io_buffer_size_loc),long) ));
}

#line 1048
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1048
	return;
#line 1048
}

void
#line 1050
bi_getbuftype(eval_environ_t env)
#line 1050

#line 1050

#line 1050 "io.bi"
{
#line 1050
	
#line 1050

#line 1050
        long  fn;
#line 1050
        
#line 1050

#line 1050
        get_numeric_arg(env, 0, &fn);
#line 1050
        
#line 1050
        adjust_stack(env, 1);
#line 1050

#line 1050

#line 1050
	if (builtin_module_trace(BUILTIN_IDX_io))
#line 1050
		prog_trace(env, "getbuftype %lu",fn);;
#line 1050

{
	struct io_stream *ios = io_get_open_stream(env, fn);
	
#line 1053
do {
#line 1053
  push(env, (STKVAL)(mft_number)(ios->buf_type));
#line 1053
  goto endlab;
#line 1053
} while (0);
}
endlab:
#line 1055
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1055
	return;
#line 1055
}

void
#line 1057
bi_getbufsize(eval_environ_t env)
#line 1057

#line 1057

#line 1057 "io.bi"
{
#line 1057
	
#line 1057

#line 1057
        long  fn;
#line 1057
        
#line 1057

#line 1057
        get_numeric_arg(env, 0, &fn);
#line 1057
        
#line 1057
        adjust_stack(env, 1);
#line 1057

#line 1057

#line 1057
	if (builtin_module_trace(BUILTIN_IDX_io))
#line 1057
		prog_trace(env, "getbufsize %lu",fn);;
#line 1057

{
	struct io_stream *ios = io_get_open_stream(env, fn);
	
#line 1060
do {
#line 1060
  push(env, (STKVAL)(mft_number)(ios->buf_size));
#line 1060
  goto endlab;
#line 1060
} while (0);
}
endlab:
#line 1062
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1062
	return;
#line 1062
}

void
#line 1064
bi_get_output(eval_environ_t env)
#line 1064

#line 1064

#line 1064 "io.bi"
{
#line 1064
	
#line 1064

#line 1064
        long  fn;
#line 1064
        
#line 1064

#line 1064
        get_numeric_arg(env, 0, &fn);
#line 1064
        
#line 1064
        adjust_stack(env, 1);
#line 1064

#line 1064

#line 1064
	if (builtin_module_trace(BUILTIN_IDX_io))
#line 1064
		prog_trace(env, "get_output %lu",fn);;
#line 1064

{
	struct io_stream *iotab = env_get_builtin_priv(env,IO_id);
	struct io_stream *ios = io_get_open_stream(env, fn);
	int n;
	if (ios->strout == NULL)
		n = -1;
	else
		n = ios->strout - iotab;
	
#line 1073
do {
#line 1073
  push(env, (STKVAL)(mft_number)(n));
#line 1073
  goto endlab;
#line 1073
} while (0);
}
endlab:
#line 1075
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1075
	return;
#line 1075
}

static void
stream_cleanup(void *data)
{
	struct io_stream *ios = data;
	close_stream(ios);
}

void
#line 1084
bi_open(eval_environ_t env)
#line 1084

#line 1084

#line 1084 "io.bi"
{
#line 1084
	
#line 1084

#line 1084
        char *  name;
#line 1084
        
#line 1084

#line 1084
        get_string_arg(env, 0, &name);
#line 1084
        
#line 1084
        adjust_stack(env, 1);
#line 1084

#line 1084

#line 1084
	if (builtin_module_trace(BUILTIN_IDX_io))
#line 1084
		prog_trace(env, "open %s",name);;
#line 1084

{
	int i;
	int flags = 0;
	void (*opf)(eval_environ_t env,
		   struct io_stream *, const char *, int) = open_file_stream;
	struct io_stream *ios;

	if ((ios = io_find_avail(env, &i)) == NULL)
		(
#line 1093
	env_throw_bi(env, mfe_failure, "open", _("no more files available"))
#line 1093
);

	
#line 1095

#line 1095
mu_debug(debug_handle, MU_DEBUG_TRACE1,("opening stream %s", name));
	ios->name = mu_strdup(name);
	ios->delim = NULL;
	if (*name == '>') {
		flags |= O_RDWR|O_CREAT;
		name++;
		if (*name == '>') {
			flags |= O_APPEND;
			name++;
		} else
			flags |= O_TRUNC;
	} else if (*name == '|') {
		opf = open_program_stream;
		flags = O_WRONLY;
		name++;
		if (*name == '&') {
			flags = O_RDWR;
			name++;
		} else if (*name == '<') {
			flags = O_RDONLY;
			name++;
		}
	} else if (*name == '@') {
		name++;
		opf = open_inet_stream;
		flags = O_RDWR;
	} else
		flags = O_RDONLY;

	for (;*name && mu_isspace(*name); name++)
		;

	env_function_cleanup_add(env, CLEANUP_THROW, ios, stream_cleanup);
	opf(env, ios, name, flags);
	io_set_buffer(env, ios, mf_c_val(*env_data_ref(env, io_buffering_loc),long) ,
		      mf_c_val(*env_data_ref(env, io_buffer_size_loc),long) );

	
#line 1132

#line 1132
mu_debug(debug_handle, MU_DEBUG_TRACE1,("open(%s) = %d", name, i));
	
#line 1133
do {
#line 1133
  push(env, (STKVAL)(mft_number)(i));
#line 1133
  goto endlab;
#line 1133
} while (0);
}
endlab:
#line 1135
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1135
	return;
#line 1135
}

void
#line 1137
bi_spawn(eval_environ_t env)
#line 1137

#line 1137

#line 1137 "io.bi"
{
#line 1137
	
#line 1137

#line 1137
        char *  name;
#line 1137
        long  fin;
#line 1137
        long  fout;
#line 1137
        long  ferr;
#line 1137
        
#line 1137
        long __bi_argcnt;
#line 1137
        get_numeric_arg(env, 0, &__bi_argcnt);
#line 1137
        get_string_arg(env, 1, &name);
#line 1137
        if (__bi_argcnt > 1)
#line 1137
                get_numeric_arg(env, 2, &fin);
#line 1137
        if (__bi_argcnt > 2)
#line 1137
                get_numeric_arg(env, 3, &fout);
#line 1137
        if (__bi_argcnt > 3)
#line 1137
                get_numeric_arg(env, 4, &ferr);
#line 1137
        
#line 1137
        adjust_stack(env, __bi_argcnt + 1);
#line 1137

#line 1137

#line 1137
	if (builtin_module_trace(BUILTIN_IDX_io))
#line 1137
		prog_trace(env, "spawn %s %lu %lu %lu",name, ((__bi_argcnt > 1) ? fin : 0), ((__bi_argcnt > 2) ? fout : 0), ((__bi_argcnt > 3) ? ferr : 0));;

{
	int i;
	struct io_stream *ios;
	int ioe[3];
	int flags;

	if ((ios = io_find_avail(env, &i)) == NULL)
		(
#line 1146
	env_throw_bi(env, mfe_failure, "spawn", _("no more files available"))
#line 1146
);

	
#line 1148

#line 1148
mu_debug(debug_handle, MU_DEBUG_TRACE1,("spawning %s", name));
	ios->name = mu_strdup(name);
	ios->delim = NULL;
	env_function_cleanup_add(env, CLEANUP_THROW, ios, stream_cleanup);

	flags = O_WRONLY;
	if (*name == '|')
		name++;
	if (*name == '&') {
		flags = O_RDWR;
		name++;
	} else if (*name == '<') {
		flags = O_RDONLY;
		name++;
	}

	for (;*name && mu_isspace(*name); name++)
		;

	if ((__bi_argcnt > 1))
		ioe[0] = _bi_io_fd(env, ((__bi_argcnt > 1) ? fin : 0), 0);
	else
		ioe[0] = -1;
	if ((__bi_argcnt > 2))
		ioe[1] = _bi_io_fd(env, ((__bi_argcnt > 2) ? fout : 0), 1);
	else
		ioe[1] = -1;
	if ((__bi_argcnt > 3))
		ioe[2] = _bi_io_fd(env, ((__bi_argcnt > 2) ? fout : 0), 1);
	else
		ioe[2] = -1;

	open_program_stream_ioe(env, ios, name, flags, ioe);
	io_set_buffer(env, ios, mf_c_val(*env_data_ref(env, io_buffering_loc),long) ,
		      mf_c_val(*env_data_ref(env, io_buffer_size_loc),long) );
	
#line 1183

#line 1183
mu_debug(debug_handle, MU_DEBUG_TRACE1,("spawn(%s) = %d", name, i));
	
#line 1184
do {
#line 1184
  push(env, (STKVAL)(mft_number)(i));
#line 1184
  goto endlab;
#line 1184
} while (0);
}
endlab:
#line 1186
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1186
	return;
#line 1186
}


void
#line 1189
bi_tempfile(eval_environ_t env)
#line 1189

#line 1189

#line 1189 "io.bi"
{
#line 1189
	
#line 1189

#line 1189
        char * MFL_DATASEG tempdir;
#line 1189
        
#line 1189
        long __bi_argcnt;
#line 1189
        get_numeric_arg(env, 0, &__bi_argcnt);
#line 1189
        if (__bi_argcnt > 0)
#line 1189
                get_string_arg(env, 1, &tempdir);
#line 1189
        
#line 1189
        adjust_stack(env, __bi_argcnt + 1);
#line 1189

#line 1189

#line 1189
	if (builtin_module_trace(BUILTIN_IDX_io))
#line 1189
		prog_trace(env, "tempfile %s",((__bi_argcnt > 0) ? tempdir : ""));;
#line 1189

{
	struct io_stream *ios;
	int i;
	char *dir = ((__bi_argcnt > 0) ? tempdir : "/tmp");
	size_t dirlen = strlen(dir);
	mode_t u;
	int fd;
	char *template;
#define PATTERN "mfdXXXXXX"

	if ((ios = io_find_avail(env, &i)) == NULL)
		(
#line 1201
	env_throw_bi(env, mfe_failure, "tempfile", _("no more files available"))
#line 1201
);

	while (dirlen > 0 && dir[dirlen-1] == '/')
		dirlen--;

	template = mf_c_val(heap_tempspace(env, (dirlen ? dirlen + 1 : 0) +
#line 1206
				      sizeof(PATTERN)), ptr);
#line 1208
	if (dirlen) {
		memcpy(template, dir, dirlen);
		template[dirlen++] = '/';
	}
	strcpy(template + dirlen, PATTERN);
	u = umask(077);
	fd = mkstemp(template);
	umask(u);
		if (!(fd >= 0))
#line 1216
		(
#line 1216
	env_throw_bi(env, mfe_failure, "tempfile", "mkstemp failed: %s",mu_strerror(errno))
#line 1216
)
#line 1219
;
	unlink(template);

	ios->fd = fd;
	ios->name = mu_strdup(template);
	io_set_buffer(env, ios, mf_c_val(*env_data_ref(env, io_buffering_loc),long) ,
		      mf_c_val(*env_data_ref(env, io_buffer_size_loc),long) );

	
#line 1227
do {
#line 1227
  push(env, (STKVAL)(mft_number)(i));
#line 1227
  goto endlab;
#line 1227
} while (0);
#undef PATTERN
}
endlab:
#line 1230
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1230
	return;
#line 1230
}

void
#line 1232
bi_close(eval_environ_t env)
#line 1232

#line 1232

#line 1232 "io.bi"
{
#line 1232
	
#line 1232

#line 1232
        long  fn;
#line 1232
        
#line 1232

#line 1232
        get_numeric_arg(env, 0, &fn);
#line 1232
        
#line 1232
        adjust_stack(env, 1);
#line 1232

#line 1232

#line 1232
	if (builtin_module_trace(BUILTIN_IDX_io))
#line 1232
		prog_trace(env, "close %lu",fn);;
#line 1232

{
	struct io_stream *ios = io_get_open_stream(env, fn);
	int rc;

	rc = close_stream(ios);
		if (!(rc == mfe_success))
#line 1238
		(
#line 1238
	env_throw_bi(env, rc, "close", _("%s: error flushing stream"),ios->name)
#line 1238
)
#line 1241
;
}

#line 1243
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1243
	return;
#line 1243
}

static struct builtin_const_trans shutdown_modes[] = {
	{ _MFL_SHUT_RD, SHUT_RD },
	{ _MFL_SHUT_WR, SHUT_WR },
	{ _MFL_SHUT_RDWR, SHUT_RDWR }
};

void
#line 1251
bi_shutdown(eval_environ_t env)
#line 1251

#line 1251

#line 1251 "io.bi"
{
#line 1251
	
#line 1251

#line 1251
        long  fn;
#line 1251
        long  how;
#line 1251
        
#line 1251

#line 1251
        get_numeric_arg(env, 0, &fn);
#line 1251
        get_numeric_arg(env, 1, &how);
#line 1251
        
#line 1251
        adjust_stack(env, 2);
#line 1251

#line 1251

#line 1251
	if (builtin_module_trace(BUILTIN_IDX_io))
#line 1251
		prog_trace(env, "shutdown %lu %lu",fn, how);;
#line 1251

{
	struct io_stream *ios = io_get_open_stream(env, fn);
	int mode;
	int rc;

		if (!(how >= 0 && how <= 2))
#line 1257
		(
#line 1257
	env_throw_bi(env, mfe_range, "shutdown", _("invalid file descriptor"))
#line 1257
)
#line 1259
;
		if (!(_builtin_const_to_c(shutdown_modes,
#line 1260
				      MU_ARRAY_SIZE(shutdown_modes),
#line 1260
				      how,
#line 1260
				      &mode) == 0))
#line 1260
		(
#line 1260
	env_throw_bi(env, mfe_failure, "shutdown", "bad shutdown mode")
#line 1260
)
#line 1265
;

	rc = flush_stream(ios);
		if (!(rc == mfe_success))
#line 1268
		(
#line 1268
	env_throw_bi(env, rc, "shutdown", _("%s: error flushing stream"),ios->name)
#line 1268
)
#line 1271
;
	if (ios->shutdown) {
		int rc = ios->shutdown(ios, mode);
			if (!(rc == 0))
#line 1274
		(
#line 1274
	env_throw_bi(env, mfe_io, "shutdown", "shutdown failed: %s",mu_strerror(rc))
#line 1274
)
#line 1277
;
	} else {
		switch (how) {
		case _MFL_SHUT_RDWR:
			close_stream(ios);
			break;

		case _MFL_SHUT_WR:
			if (ios->strout) {
				close_stream(ios->strout);
				ios->strout = NULL;
			} //FIXME: else?
			break;

		case _MFL_SHUT_RD:
			close(ios->fd);
			ios->fd = -1;
		}
	}
}

#line 1297
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1297
	return;
#line 1297
}

void
#line 1299
bi_write(eval_environ_t env)
#line 1299

#line 1299

#line 1299 "io.bi"
{
#line 1299
	
#line 1299

#line 1299
        long  fn;
#line 1299
        char *  str;
#line 1299
        long  size;
#line 1299
        
#line 1299
        long __bi_argcnt;
#line 1299
        get_numeric_arg(env, 0, &__bi_argcnt);
#line 1299
        get_numeric_arg(env, 1, &fn);
#line 1299
        get_string_arg(env, 2, &str);
#line 1299
        if (__bi_argcnt > 2)
#line 1299
                get_numeric_arg(env, 3, &size);
#line 1299
        
#line 1299
        adjust_stack(env, __bi_argcnt + 1);
#line 1299

#line 1299

#line 1299
	if (builtin_module_trace(BUILTIN_IDX_io))
#line 1299
		prog_trace(env, "write %lu %s %lu",fn, str, ((__bi_argcnt > 2) ? size : 0));;
#line 1299

{
	struct io_stream *ios = io_get_open_stream(env, fn);

	
#line 1303

#line 1303
mu_debug(debug_handle, MU_DEBUG_TRACE1,("writing %s to %lu", str, fn));
	if (!(__bi_argcnt > 2))
		size = strlen(str);

	io_write(env, ios, str, size);
}

#line 1309
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1309
	return;
#line 1309
}


void
#line 1312
bi_write_body(eval_environ_t env)
#line 1312

#line 1312

#line 1312 "io.bi"
{
#line 1312
	
#line 1312

#line 1312
        long  fn;
#line 1312
        void *  str;
#line 1312
        long  n;
#line 1312
        
#line 1312

#line 1312
        get_numeric_arg(env, 0, &fn);
#line 1312
        get_pointer_arg(env, 1, &str);
#line 1312
        get_numeric_arg(env, 2, &n);
#line 1312
        
#line 1312
        adjust_stack(env, 3);
#line 1312

#line 1312

#line 1312
	if (builtin_module_trace(BUILTIN_IDX_io))
#line 1312
		prog_trace(env, "write_body %lu %p %lu",fn, str, n);;
#line 1312

{
	struct io_stream *ios = io_get_open_stream(env, fn);
	io_write(env, ios, str, n);
}

#line 1317
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1317
	return;
#line 1317
}

void
#line 1319
bi_read(eval_environ_t env)
#line 1319

#line 1319

#line 1319 "io.bi"
{
#line 1319
	
#line 1319

#line 1319
        long  fn;
#line 1319
        long  size;
#line 1319
        
#line 1319

#line 1319
        get_numeric_arg(env, 0, &fn);
#line 1319
        get_numeric_arg(env, 1, &size);
#line 1319
        
#line 1319
        adjust_stack(env, 2);
#line 1319

#line 1319

#line 1319
	if (builtin_module_trace(BUILTIN_IDX_io))
#line 1319
		prog_trace(env, "read %lu %lu",fn, size);;
#line 1319

{
	struct io_stream *ios = io_get_open_stream(env, fn);
	char *s;

	heap_obstack_begin(env);
	s = heap_obstack_grow(env, NULL, size + 1);
	size = io_read(env, ios, s, size);

	/* FIXME: This is for backward compatibility. Now I'm not sure
	   this is right. */
		if (!(size > 0))
#line 1330
		(
#line 1330
	env_throw_bi(env, mfe_eof, "read", _("EOF on %s"),ios->name)
#line 1330
)
#line 1330
;

	s[size] = 0;
	heap_obstack_truncate(env, size + 1);
	
#line 1334
do {
#line 1334
  push(env, (STKVAL) (heap_obstack_finish(env)));
#line 1334
  goto endlab;
#line 1334
} while (0);
}
endlab:
#line 1336
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1336
	return;
#line 1336
}

void
#line 1338
bi_rewind(eval_environ_t env)
#line 1338

#line 1338

#line 1338 "io.bi"
{
#line 1338
	
#line 1338

#line 1338
        long  fn;
#line 1338
        
#line 1338

#line 1338
        get_numeric_arg(env, 0, &fn);
#line 1338
        
#line 1338
        adjust_stack(env, 1);
#line 1338

#line 1338

#line 1338
	if (builtin_module_trace(BUILTIN_IDX_io))
#line 1338
		prog_trace(env, "rewind %lu",fn);;
#line 1338

{
	struct io_stream *ios = io_get_open_stream(env, fn);
	int rc;

	rc = flush_stream(ios);
		if (!(rc == mfe_success))
#line 1344
		(
#line 1344
	env_throw_bi(env, rc, "rewind", _("%s: error flushing stream"),ios->name)
#line 1344
)
#line 1347
;
	if (lseek(ios->fd, 0, SEEK_SET) == -1)
		(
#line 1349
	env_throw_bi(env, mfe_io, "rewind", "seek failed: %s",mu_strerror(errno))
#line 1349
);
#line 1352
}

#line 1353
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1353
	return;
#line 1353
}

#define MINBUFSIZE 128
#define MAXBUFSIZE 65535

void
#line 1358
bi_copy(eval_environ_t env)
#line 1358

#line 1358

#line 1358 "io.bi"
{
#line 1358
	
#line 1358

#line 1358
        long  dst;
#line 1358
        long  src;
#line 1358
        
#line 1358

#line 1358
        get_numeric_arg(env, 0, &dst);
#line 1358
        get_numeric_arg(env, 1, &src);
#line 1358
        
#line 1358
        adjust_stack(env, 2);
#line 1358

#line 1358

#line 1358
	if (builtin_module_trace(BUILTIN_IDX_io))
#line 1358
		prog_trace(env, "copy %lu %lu",dst, src);;
#line 1358

{
	struct io_stream *ios;
	int ifd, ofd;
	char *buffer;
	size_t bufsize = MAXBUFSIZE;
	char bs[MINBUFSIZE];
	off_t cur, end;
	size_t total = 0;
	ssize_t rdbytes;
	int rc;

	ios = io_get_open_stream(env, src);
	rc = flush_stream(ios);
		if (!(rc == mfe_success))
#line 1372
		(
#line 1372
	env_throw_bi(env, rc, "copy", _("%s: error flushing stream"),ios->name)
#line 1372
)
#line 1375
;
	ifd = ios->fd;

	ios = io_get_open_stream(env, dst);
	rc = flush_stream(ios);
		if (!(rc == mfe_success))
#line 1380
		(
#line 1380
	env_throw_bi(env, rc, "copy", _("%s: error flushing stream"),ios->name)
#line 1380
)
#line 1383
;
	ofd = io_strout(ios)->fd;

	cur = lseek(ifd, 0, SEEK_CUR);
	if (cur != -1) {
		end = lseek(ifd, 0, SEEK_END);
		if (end != -1) {
			if (end < MAXBUFSIZE)
				bufsize = end;
			lseek(ifd, cur, SEEK_SET);
		}
	}

	for (; (buffer = malloc(bufsize)) == NULL; bufsize >>= 1)
		if (bufsize < MINBUFSIZE) {
			buffer = bs;
			bufsize = MINBUFSIZE;
			break;
		}

	while ((rdbytes = read(ifd, buffer, bufsize)) > 0) {
		char *p = buffer;
		while (rdbytes) {
			ssize_t wrbytes = write(ofd, p, rdbytes);
			if (wrbytes == -1) {
				if (buffer != bs)
					free(buffer);
				(
#line 1410
	env_throw_bi(env, mfe_io, "copy", "write error: %s",mu_strerror(errno))
#line 1410
);
#line 1413
			} else if (wrbytes == 0) {
				if (buffer != bs)
					free(buffer);
				(
#line 1416
	env_throw_bi(env, mfe_io, "copy", "short write")
#line 1416
);
#line 1418
			}
			p += wrbytes;
			rdbytes -= wrbytes;
			total += wrbytes;
		}
	}
	if (buffer != bs)
		free(buffer);
	
#line 1426
do {
#line 1426
  push(env, (STKVAL)(mft_number)(total));
#line 1426
  goto endlab;
#line 1426
} while (0);
}
endlab:
#line 1428
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1428
	return;
#line 1428
}

void
#line 1430
bi_getdelim(eval_environ_t env)
#line 1430

#line 1430

#line 1430 "io.bi"
{
#line 1430
	
#line 1430

#line 1430
        long  fn;
#line 1430
        char * MFL_DATASEG delim;
#line 1430
        
#line 1430

#line 1430
        get_numeric_arg(env, 0, &fn);
#line 1430
        get_string_arg(env, 1, &delim);
#line 1430
        
#line 1430
        adjust_stack(env, 2);
#line 1430

#line 1430

#line 1430
	if (builtin_module_trace(BUILTIN_IDX_io))
#line 1430
		prog_trace(env, "getdelim %lu %s",fn, delim);;
#line 1430

{
	struct io_stream *ios = io_get_open_stream(env, fn);
	heap_obstack_begin(env);
	io_read_delim(env, ios, delim);
	
#line 1435
do {
#line 1435
  push(env, (STKVAL) (heap_obstack_finish(env)));
#line 1435
  goto endlab;
#line 1435
} while (0);
}
endlab:
#line 1437
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1437
	return;
#line 1437
}

void
#line 1439
bi_getline(eval_environ_t env)
#line 1439

#line 1439

#line 1439 "io.bi"
{
#line 1439
	
#line 1439

#line 1439
        long  fn;
#line 1439
        
#line 1439

#line 1439
        get_numeric_arg(env, 0, &fn);
#line 1439
        
#line 1439
        adjust_stack(env, 1);
#line 1439

#line 1439

#line 1439
	if (builtin_module_trace(BUILTIN_IDX_io))
#line 1439
		prog_trace(env, "getline %lu",fn);;
#line 1439

{
	struct io_stream *ios = io_get_open_stream(env, fn);
	heap_obstack_begin(env);
	io_read_delim(env, ios, ios->delim ? ios->delim : "\n");
	
#line 1444
do {
#line 1444
  push(env, (STKVAL) (heap_obstack_finish(env)));
#line 1444
  goto endlab;
#line 1444
} while (0);
}
endlab:
#line 1446
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1446
	return;
#line 1446
}

void
#line 1448
bi_fd_set_delimiter(eval_environ_t env)
#line 1448

#line 1448

#line 1448 "io.bi"
{
#line 1448
	
#line 1448

#line 1448
        long  fn;
#line 1448
        char *  delim;
#line 1448
        
#line 1448

#line 1448
        get_numeric_arg(env, 0, &fn);
#line 1448
        get_string_arg(env, 1, &delim);
#line 1448
        
#line 1448
        adjust_stack(env, 2);
#line 1448

#line 1448

#line 1448
	if (builtin_module_trace(BUILTIN_IDX_io))
#line 1448
		prog_trace(env, "fd_set_delimiter %lu %s",fn, delim);;
#line 1448

{
	struct io_stream *ios = io_get_open_stream(env, fn);
	free(ios->delim);
	ios->delim = mu_strdup(delim);
}

#line 1454
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1454
	return;
#line 1454
}

void
#line 1456
bi_fd_delimiter(eval_environ_t env)
#line 1456

#line 1456

#line 1456 "io.bi"
{
#line 1456
	
#line 1456

#line 1456
        long  fn;
#line 1456
        char * MFL_DATASEG delim;
#line 1456
        
#line 1456

#line 1456
        get_numeric_arg(env, 0, &fn);
#line 1456
        get_string_arg(env, 1, &delim);
#line 1456
        
#line 1456
        adjust_stack(env, 2);
#line 1456

#line 1456

#line 1456
	if (builtin_module_trace(BUILTIN_IDX_io))
#line 1456
		prog_trace(env, "fd_delimiter %lu %s",fn, delim);;
#line 1456

{
	struct io_stream *ios = io_get_open_stream(env, fn);
	
#line 1459
do {
#line 1459
  pushs(env, ios->delim ? ios->delim : "\n");
#line 1459
  goto endlab;
#line 1459
} while (0);
}
endlab:
#line 1461
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1461
	return;
#line 1461
}

 
#line 996 "../../src/builtin/snarf.m4"

#line 996

#line 996

#line 996
void
#line 996
io_init_builtin(void)
#line 996
{
#line 996
		debug_handle = mu_debug_register_category("bi_io");
#line 996

#line 996
	#line 137 "io.bi"
	builtin_variable_install("io_buffering", dtype_number, SYM_VOLATILE, &io_buffering_loc);
#line 138 "io.bi"
	builtin_variable_install("io_buffer_size", dtype_number, SYM_VOLATILE, &io_buffer_size_loc);
#line 994 "io.bi"
IO_id = builtin_priv_register(alloc_streams, destroy_streams,
#line 994
NULL);
#line 1041 "io.bi"
va_builtin_install_ex("setbuf", bi_setbuf, 0, dtype_unspecified, 3, 2, 0|0, dtype_number, dtype_number, dtype_number);
#line 1050 "io.bi"
va_builtin_install_ex("getbuftype", bi_getbuftype, 0, dtype_number, 1, 0, 0|0, dtype_number);
#line 1057 "io.bi"
va_builtin_install_ex("getbufsize", bi_getbufsize, 0, dtype_number, 1, 0, 0|0, dtype_number);
#line 1064 "io.bi"
va_builtin_install_ex("get_output", bi_get_output, 0, dtype_number, 1, 0, 0|0, dtype_number);
#line 1084 "io.bi"
va_builtin_install_ex("open", bi_open, 0, dtype_number, 1, 0, 0|0, dtype_string);
#line 1137 "io.bi"
va_builtin_install_ex("spawn", bi_spawn, 0, dtype_number, 4, 3, 0|0, dtype_string, dtype_number, dtype_number, dtype_number);
#line 1189 "io.bi"
va_builtin_install_ex("tempfile", bi_tempfile, 0, dtype_number, 1, 1, 0|0, dtype_string);
#line 1232 "io.bi"
va_builtin_install_ex("close", bi_close, 0, dtype_unspecified, 1, 0, 0|0, dtype_number);
#line 1251 "io.bi"
va_builtin_install_ex("shutdown", bi_shutdown, 0, dtype_unspecified, 2, 0, 0|0, dtype_number, dtype_number);
#line 1299 "io.bi"
va_builtin_install_ex("write", bi_write, 0, dtype_unspecified, 3, 1, 0|0, dtype_number, dtype_string, dtype_number);
#line 1312 "io.bi"
va_builtin_install_ex("write_body", bi_write_body, STATMASK(smtp_state_body), dtype_unspecified, 3, 0, 0|0, dtype_number, dtype_pointer, dtype_number);
#line 1319 "io.bi"
va_builtin_install_ex("read", bi_read, 0, dtype_string, 2, 0, 0|0, dtype_number, dtype_number);
#line 1338 "io.bi"
va_builtin_install_ex("rewind", bi_rewind, 0, dtype_unspecified, 1, 0, 0|0, dtype_number);
#line 1358 "io.bi"
va_builtin_install_ex("copy", bi_copy, 0, dtype_number, 2, 0, 0|0, dtype_number, dtype_number);
#line 1430 "io.bi"
va_builtin_install_ex("getdelim", bi_getdelim, 0, dtype_string, 2, 0, 0|0, dtype_number, dtype_string);
#line 1439 "io.bi"
va_builtin_install_ex("getline", bi_getline, 0, dtype_string, 1, 0, 0|0, dtype_number);
#line 1448 "io.bi"
va_builtin_install_ex("fd_set_delimiter", bi_fd_set_delimiter, 0, dtype_unspecified, 2, 0, 0|0, dtype_number, dtype_string);
#line 1456 "io.bi"
va_builtin_install_ex("fd_delimiter", bi_fd_delimiter, 0, dtype_string, 2, 0, 0|0, dtype_number, dtype_string);

#line 996 "../../src/builtin/snarf.m4"
	
#line 996
	 long n;
#line 996

#line 996
	 mf_add_runtime_params(io_cfg_param);
#line 996
	 n = buf_none;
#line 996
	 ds_init_variable("io_buffering", &n);
#line 996
	 n = sysconf(_SC_PAGESIZE);
#line 996
	 ds_init_variable("io_buffer_size", &n);
#line 996
	 
#line 996
}
#line 996 "../../src/builtin/snarf.m4"

