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

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

#line 461 "io.bi"
static mu_debug_handle_t debug_handle;
#line 1026 "../../src/builtin/snarf.m4"

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

/* End of snarf.m4 */
#line 1 "io.bi"
/* This file is part of Mailfromd.             -*- c -*-
   Copyright (C) 2006-2025 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"), str->name);
			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 136 "io.bi"
;
static size_t io_buffer_size_loc
#line 137 "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 143
		(
#line 143
	env_throw_bi(env, rc, NULL, _("%s: error flushing stream"),str->name)
#line 143
)
#line 146
;
	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 156
		(
#line 156
	env_throw_bi(env, mfe_range, NULL, _("buffer size out of allowed range"))
#line 156
)
#line 158
;
		if (size != str->buf_size) {
			char *p = realloc(str->buf, size);
				if (!(p != NULL))
#line 161
		(
#line 161
	env_throw_bi(env, mfe_failure, NULL, _("can't allocate stream buffer"))
#line 161
)
#line 163
;
			str->buf = p;
			str->buf_size = size;
		}
		str->buf_type = type;
		break;

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

	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 199
	env_throw_bi(env, mfe_eof, NULL, "short write")
#line 199
);
				if (!(n > 0))
#line 200
		(
#line 200
	env_throw_bi(env, mfe_io, NULL, _("write error on %s: %s"),iostr->name,mu_strerror(errno))
#line 200
)
#line 203
;
			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 222
		(
#line 222
	env_throw_bi(env, mfe_failure, NULL, _("cannot reallocate stream buffer"))
#line 222
)
#line 224
;
				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 236
		(
#line 236
	env_throw_bi(env, ec, NULL, _("%s: error flushing stream"),iostr->name)
#line 236
)
#line 239
;
			}

			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 252
		(
#line 252
	env_throw_bi(env, ec, NULL, _("%s: error flushing stream"),iostr->name)
#line 252
)
#line 255
;
				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 277
		(
#line 277
	env_throw_bi(env, ec, NULL, _("%s: error flushing stream"),iostr->name)
#line 277
)
#line 279
;
		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 290
		(
#line 290
	env_throw_bi(env, mfe_failure, NULL, _("cannot reallocate stream buffer"))
#line 290
)
#line 292
;
					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 303
		(
#line 303
	env_throw_bi(env, mfe_io, NULL, _("read error on %s: %s"),iostr->name,mu_strerror(errno))
#line 303
)
#line 306
;

			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 327
		(
#line 327
	env_throw_bi(env, mfe_io, NULL, _("read error on %s: %s"),iostr->name,mu_strerror(errno))
#line 327
)
#line 330
;
		} 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 353
		(
#line 353
	env_throw_bi(env, mfe_io, NULL, _("read error on %s: %s"),iostr->name,mu_strerror(errno))
#line 353
)
#line 356
;
		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 379
	env_throw_bi(env, mfe_eof, NULL, _("EOF on %s"),iostr->name)
#line 379
);
			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 461 "io.bi"

#line 461
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 465

		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 515

#line 515
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 630
		(
#line 630
	env_throw_bi(env, mfe_failure, NULL, _("pipe left: %s"),mu_strerror(errno))
#line 630
)
#line 632
;
		env_function_cleanup_add(env, CLEANUP_THROW, leftp, pipe_cleanup);
	}

	if (REDIRECT_STDOUT_P(flags) && !HASFD(ioe, 1)) {
			if (!(pipe(rightp) == 0))
#line 637
		(
#line 637
	env_throw_bi(env, mfe_failure, NULL, _("pipe right: %s"),mu_strerror(errno))
#line 637
)
#line 639
;
		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 674

#line 674
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 700
	env_throw_bi(env, mfe_failure, NULL, _("fork: %s"),mu_strerror(errno))
#line 700
);
		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 736
	env_throw_bi(env, mfe_failure, NULL, _("can't open %s: %s"),file,mu_strerror(errno))
#line 736
);
#line 738
}


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;
		struct sockaddr_in6 s_in6;
	} 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 763
		(
#line 763
	env_throw_bi(env, mfe_failure, NULL, _("invalid connection type: %s; "
			    "port is meaningless for UNIX sockets"),cstr)
#line 763
)
#line 767
;

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

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

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

	} 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 798
		(
#line 798
	env_throw_bi(env, mfe_failure, NULL, _("invalid connection type: %s; "
			    "missing port number"),cstr)
#line 798
)
#line 802
;

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

				if (!(sp != NULL))
#line 815
		(
#line 815
	env_throw_bi(env, mfe_failure, NULL, _("invalid connection type: "
				    "%s; unknown port name"),cstr)
#line 815
)
#line 819
;
			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 827
		(
#line 827
	env_throw_bi(env, mfe_failure, NULL, _("unknown host name %s"),path)
#line 827
)
#line 830
;
			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 839
	env_throw_bi(env, mfe_range, NULL, _("invalid connection type: "
					   "%s; unsupported address family"),cstr)
#line 839
);
#line 843
			}
		}
	} else if (strcmp(proto, "inet6") == 0) {
		struct addrinfo hints;
		struct addrinfo *res;

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

		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 868
	env_throw_bi(env, mfe_failure, NULL, _("%s:%s: cannot parse address: %s"),path,port,strerror(errno))
#line 868
);
#line 871

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

		case EAI_MEMORY:
			mu_alloc_die();

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

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

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

	/* FIXME: Bind to the source ? */

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

	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 954
	env_throw_bi(env, mfe_failure, NULL, _("can't parse connection string %s"),addr)
#line 954
);
#line 956
	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 989

#line 989
static int IO_id;
#line 989 "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 1017
		(
#line 1017
	env_throw_bi(env, mfe_range, NULL, _("invalid file descriptor"))
#line 1017
)
#line 1019
;
	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 1030
		(
#line 1030
	env_throw_bi(env, mfe_range, NULL, _("invalid file descriptor"))
#line 1030
)
#line 1032
;
	return descr;
}

void
#line 1036
bi_setbuf(eval_environ_t env)
#line 1036

#line 1036

#line 1036 "io.bi"
{
#line 1036
	
#line 1036

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

#line 1036

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

{
	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 1043
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1043
	return;
#line 1043
}

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

#line 1045

#line 1045 "io.bi"
{
#line 1045
	
#line 1045

#line 1045
        long  fn;
#line 1045
        
#line 1045

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

#line 1045

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

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

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

#line 1052

#line 1052 "io.bi"
{
#line 1052
	
#line 1052

#line 1052
        long  fn;
#line 1052
        
#line 1052

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

#line 1052

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

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

#line 1059

#line 1059 "io.bi"
{
#line 1059
	
#line 1059

#line 1059
        long  fn;
#line 1059
        
#line 1059

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

#line 1059

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

{
	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 1068
do {
#line 1068
  push(env, (STKVAL)(mft_number)(n));
#line 1068
  goto endlab;
#line 1068
} while (0);
}
endlab:
#line 1070
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1070
	return;
#line 1070
}

static void
stream_cleanup(void *data)
{
	struct io_stream *ios = data;
	close_stream(ios);
}

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

#line 1079

#line 1079 "io.bi"
{
#line 1079
	
#line 1079

#line 1079
        char *  name;
#line 1079
        
#line 1079

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

#line 1079

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

{
	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 1088
	env_throw_bi(env, mfe_failure, "open", _("no more files available"))
#line 1088
);

	
#line 1090

#line 1090
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 1127

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

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

#line 1132

#line 1132 "io.bi"
{
#line 1132
	
#line 1132

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

#line 1132

#line 1132
	if (builtin_module_trace(BUILTIN_IDX_io))
#line 1132
		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 1141
	env_throw_bi(env, mfe_failure, "spawn", _("no more files available"))
#line 1141
);

	
#line 1143

#line 1143
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 1178

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


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

#line 1184

#line 1184 "io.bi"
{
#line 1184
	
#line 1184

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

#line 1184

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

{
	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 1196
	env_throw_bi(env, mfe_failure, "tempfile", _("no more files available"))
#line 1196
);

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

	template = mf_c_val(heap_tempspace(env, (dirlen ? dirlen + 1 : 0) +
#line 1201
				      sizeof(PATTERN)), ptr);
#line 1203
	if (dirlen) {
		memcpy(template, dir, dirlen);
		template[dirlen++] = '/';
	}
	strcpy(template + dirlen, PATTERN);
	u = umask(077);
	fd = mkstemp(template);
	umask(u);
		if (!(fd >= 0))
#line 1211
		(
#line 1211
	env_throw_bi(env, mfe_failure, "tempfile", "mkstemp failed: %s",mu_strerror(errno))
#line 1211
)
#line 1214
;
	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 1222
do {
#line 1222
  push(env, (STKVAL)(mft_number)(i));
#line 1222
  goto endlab;
#line 1222
} while (0);
#undef PATTERN
}
endlab:
#line 1225
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1225
	return;
#line 1225
}

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

#line 1227

#line 1227 "io.bi"
{
#line 1227
	
#line 1227

#line 1227
        long  fn;
#line 1227
        
#line 1227

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

#line 1227

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

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

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

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

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

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

#line 1246

#line 1246 "io.bi"
{
#line 1246
	
#line 1246

#line 1246
        long  fn;
#line 1246
        long  how;
#line 1246
        
#line 1246

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

#line 1246

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

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

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

	rc = flush_stream(ios);
		if (!(rc == mfe_success))
#line 1263
		(
#line 1263
	env_throw_bi(env, rc, "shutdown", _("%s: error flushing stream"),ios->name)
#line 1263
)
#line 1266
;
	if (ios->shutdown) {
		int rc = ios->shutdown(ios, mode);
			if (!(rc == 0))
#line 1269
		(
#line 1269
	env_throw_bi(env, mfe_io, "shutdown", "shutdown failed: %s",mu_strerror(rc))
#line 1269
)
#line 1272
;
	} 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 1292
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1292
	return;
#line 1292
}

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

#line 1294

#line 1294 "io.bi"
{
#line 1294
	
#line 1294

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

#line 1294

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

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

	
#line 1298

#line 1298
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 1304
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1304
	return;
#line 1304
}


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

#line 1307

#line 1307 "io.bi"
{
#line 1307
	
#line 1307

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

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

#line 1307

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

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

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

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

#line 1314

#line 1314 "io.bi"
{
#line 1314
	
#line 1314

#line 1314
        long  fn;
#line 1314
        long  size;
#line 1314
        
#line 1314

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

#line 1314

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

{
	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 1325
		(
#line 1325
	env_throw_bi(env, mfe_eof, "read", _("EOF on %s"),ios->name)
#line 1325
)
#line 1325
;

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

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

#line 1333

#line 1333 "io.bi"
{
#line 1333
	
#line 1333

#line 1333
        long  fn;
#line 1333
        
#line 1333

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

#line 1333

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

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

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

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

#define MINBUFSIZE 128
#define MAXBUFSIZE 65535

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

#line 1353

#line 1353 "io.bi"
{
#line 1353
	
#line 1353

#line 1353
        long  dst;
#line 1353
        long  src;
#line 1353
        
#line 1353

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

#line 1353

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

{
	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 1367
		(
#line 1367
	env_throw_bi(env, rc, "copy", _("%s: error flushing stream"),ios->name)
#line 1367
)
#line 1370
;
	ifd = ios->fd;

	ios = io_get_open_stream(env, dst);
	rc = flush_stream(ios);
		if (!(rc == mfe_success))
#line 1375
		(
#line 1375
	env_throw_bi(env, rc, "copy", _("%s: error flushing stream"),ios->name)
#line 1375
)
#line 1378
;
	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 1405
	env_throw_bi(env, mfe_io, "copy", "write error: %s",mu_strerror(errno))
#line 1405
);
#line 1408
			} else if (wrbytes == 0) {
				if (buffer != bs)
					free(buffer);
				(
#line 1411
	env_throw_bi(env, mfe_io, "copy", "short write")
#line 1411
);
#line 1413
			}
			p += wrbytes;
			rdbytes -= wrbytes;
			total += wrbytes;
		}
	}
	if (buffer != bs)
		free(buffer);
	
#line 1421
do {
#line 1421
  push(env, (STKVAL)(mft_number)(total));
#line 1421
  goto endlab;
#line 1421
} while (0);
}
endlab:
#line 1423
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1423
	return;
#line 1423
}

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

#line 1425

#line 1425 "io.bi"
{
#line 1425
	
#line 1425

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

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

#line 1425

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

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

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

#line 1434

#line 1434 "io.bi"
{
#line 1434
	
#line 1434

#line 1434
        long  fn;
#line 1434
        
#line 1434

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

#line 1434

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

{
	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 1439
do {
#line 1439
  push(env, (STKVAL) (heap_obstack_finish(env)));
#line 1439
  goto endlab;
#line 1439
} while (0);
}
endlab:
#line 1441
        env_function_cleanup_flush(env, CLEANUP_RETURN);
#line 1441
	return;
#line 1441
}

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

#line 1443

#line 1443 "io.bi"
{
#line 1443
	
#line 1443

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

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

#line 1443

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

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

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

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

#line 1451

#line 1451 "io.bi"
{
#line 1451
	
#line 1451

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

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

#line 1451

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

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

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

#line 1026

#line 1026

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

#line 1026
	#line 136 "io.bi"
	builtin_variable_install("io_buffering", dtype_number, SYM_VOLATILE, &io_buffering_loc);
#line 137 "io.bi"
	builtin_variable_install("io_buffer_size", dtype_number, SYM_VOLATILE, &io_buffer_size_loc);
#line 989 "io.bi"
IO_id = builtin_priv_register(alloc_streams, destroy_streams,
#line 989
NULL);
#line 1036 "io.bi"
va_builtin_install_ex("setbuf", bi_setbuf, 0, dtype_unspecified, 3, 2, 0|0, dtype_number, dtype_number, dtype_number);
#line 1045 "io.bi"
va_builtin_install_ex("getbuftype", bi_getbuftype, 0, dtype_number, 1, 0, 0|0, dtype_number);
#line 1052 "io.bi"
va_builtin_install_ex("getbufsize", bi_getbufsize, 0, dtype_number, 1, 0, 0|0, dtype_number);
#line 1059 "io.bi"
va_builtin_install_ex("get_output", bi_get_output, 0, dtype_number, 1, 0, 0|0, dtype_number);
#line 1079 "io.bi"
va_builtin_install_ex("open", bi_open, 0, dtype_number, 1, 0, 0|0, dtype_string);
#line 1132 "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 1184 "io.bi"
va_builtin_install_ex("tempfile", bi_tempfile, 0, dtype_number, 1, 1, 0|0, dtype_string);
#line 1227 "io.bi"
va_builtin_install_ex("close", bi_close, 0, dtype_unspecified, 1, 0, 0|0, dtype_number);
#line 1246 "io.bi"
va_builtin_install_ex("shutdown", bi_shutdown, 0, dtype_unspecified, 2, 0, 0|0, dtype_number, dtype_number);
#line 1294 "io.bi"
va_builtin_install_ex("write", bi_write, 0, dtype_unspecified, 3, 1, 0|0, dtype_number, dtype_string, dtype_number);
#line 1307 "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 1314 "io.bi"
va_builtin_install_ex("read", bi_read, 0, dtype_string, 2, 0, 0|0, dtype_number, dtype_number);
#line 1333 "io.bi"
va_builtin_install_ex("rewind", bi_rewind, 0, dtype_unspecified, 1, 0, 0|0, dtype_number);
#line 1353 "io.bi"
va_builtin_install_ex("copy", bi_copy, 0, dtype_number, 2, 0, 0|0, dtype_number, dtype_number);
#line 1425 "io.bi"
va_builtin_install_ex("getdelim", bi_getdelim, 0, dtype_string, 2, 0, 0|0, dtype_number, dtype_string);
#line 1434 "io.bi"
va_builtin_install_ex("getline", bi_getline, 0, dtype_string, 1, 0, 0|0, dtype_number);
#line 1443 "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 1451 "io.bi"
va_builtin_install_ex("fd_delimiter", bi_fd_delimiter, 0, dtype_string, 2, 0, 0|0, dtype_number, dtype_string);

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

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

