/********************************************
vio_orig.c - virtual IO: original mawk file/pipe IO implementation
libmawk changes (C) 2009-2013, Tibor 'Igor2' Palinkas;
based on mawk code coming with the below copyright:
copyright 1991-94. Michael D. Brennan
This is a source file for mawk, an implementation of
the AWK programming language.
Mawk is distributed without warranty under the terms of
the GNU General Public License, version 2, 1991.
********************************************/
#include "conf.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include "mawk.h"
#include "files.h"
#include "memory.h"
#include "fin.h"
#include "field.h"
#include "vio.h"
#include "array_environ.h"
/* The original mawk file IO layer uses FILE * for output (because fprintf()
and fflush() works on them) and int fd for input (probably for short reads
and manual buffering). Pipes (background processes) also need a pid for
wait(). In some cases stdin was read by fgets().
The vio reimplementation follows the same conventions, defining mawk_vio_t
to contain a FILE *, an int fd and a pid. For output FILE * is required,
input is done exclusively using int fd.
*/
#ifdef V7
#include <sgtty.h> /* defines FIOCLEX */
#endif
#ifndef NO_FCNTL_H
#include <fcntl.h>
#define CLOSE_ON_EXEC(fd) fcntl(fd, F_SETFD, 1)
#else
#define CLOSE_ON_EXEC(fd) ioctl(fd, FIOCLEX, (PTR) 0)
#endif
typedef struct mawk_vio_orig_s {
mawk_vio_t vio_common_head;
pid_t pid; /* we need to wait() when we close a pipe */
FILE *f;
int fd;
int at_eof;
int no_close;
} mawk_vio_orig_t;
const mawk_vio_imp_t mawk_vio_orig_imp;
static mawk_vio_orig_t *vio_alloc_(mawk_state_t *MAWK)
{
mawk_vio_orig_t *vf;
vf = mawk_zmalloc(MAWK, sizeof(mawk_vio_orig_t));
vf->vio_common_head.imp = &mawk_vio_orig_imp;
vf->vio_common_head.refco = 0;
vf->pid = -1;
vf->fd = -1;
vf->f = NULL;
vf->at_eof = 0;
vf->no_close = 0;
return vf;
}
int mawk_vio_orig_close(mawk_state_t *MAWK, mawk_vio_t *vf_)
{
mawk_vio_orig_t *vf = (mawk_vio_orig_t *)vf_;
if (vf->f != NULL) {
if (!vf->no_close) {
fclose(vf->f);
vf->f = NULL;
}
}
if (vf->fd >= 0) {
if (!vf->no_close) {
close(vf->fd);
vf->fd = -1;
}
}
#ifndef MAWK_NO_FORK
if (vf->pid > 0) {
if (!vf->no_close) {
mawk_wait_for(MAWK, vf->pid);
vf->pid = -1;
}
}
#endif
mawk_zfree(MAWK, vf, sizeof(mawk_vio_orig_t));
return 0;
}
int mawk_vio_orig_flush(mawk_state_t *MAWK, mawk_vio_t *vf_)
{
mawk_vio_orig_t *vf = (mawk_vio_orig_t *)vf_;
if (vf->f != NULL) {
if (fflush(vf->f) < 0) {
mawk_errmsg(MAWK, errno, "unexpected write error");
mawk_exitval(MAWK, 2, -1);
}
}
return 0;
}
static void bufsetup(FILE *f)
{
if (isatty(fileno(f)))
setbuf(f, (char *) 0);
}
/* fopen() but no buffering to ttys */
static FILE *tfopen(const char *name, const char *mode)
{
FILE *retval = fopen(name, mode);
if (retval)
bufsetup(retval);
return retval;
}
static mawk_vio_t *mawk_vio_orig_fdopen(mawk_state_t *MAWK, int fd, mawk_vio_open_mode_t mode)
{
mawk_vio_orig_t *vf;
FILE *f;
switch(mode) {
case MAWK_VIO_O_TRUNC:
f = fdopen(fd, "w");
if (f == NULL)
return NULL;
bufsetup(f);
break;
case MAWK_VIO_O_APPEND:
f = fdopen(fd, "a");
if (f == NULL)
return NULL;
bufsetup(f);
break;
case MAWK_VIO_I:
f = NULL;
break;
}
vf = vio_alloc_(MAWK);
vf->f = f;
vf->fd = fd;
return (mawk_vio_t *)vf;
}
TODO("TODO print runtime warnings")
mawk_vio_t *mawk_vio_orig_open(mawk_state_t *MAWK, const char *name, mawk_vio_open_mode_t mode)
{
mawk_vio_orig_t *vf;
FILE *f;
int fd = -1;
if (((name[0] == '-') && (name[1] == 0)) || (strcmp(name, "/dev/stdin") == 0))
return mawk_vio_orig_fdopen(MAWK, 0, mode);
/* /dev/fd/xxx should be fdopen */
if (strncmp(name, "/dev/fd/", 8) == 0) {
int fd;
char *end;
fd = strtol(name+8, &end, 10);
if (*end == '\0')
return mawk_vio_orig_fdopen(MAWK, fd, mode);
}
switch(mode) {
case MAWK_VIO_O_TRUNC:
f = tfopen(name, "w");
if (f == NULL)
return NULL;
break;
case MAWK_VIO_O_APPEND:
f = tfopen(name, "a");
if (f == NULL)
return NULL;
break;
case MAWK_VIO_I:
f = NULL;
fd = open(name, O_RDONLY);
if (fd < 0)
return NULL;
}
vf = vio_alloc_(MAWK);
vf->f = f;
vf->fd = fd;
return (mawk_vio_t *)vf;
}
#ifdef MAWK_NO_FORK
mawk_vio_t *mawk_vio_orig_open_pipe(mawk_state_t *MAWK, const char *name, int type)
{
if (!MAWK->do_exit)
mawk_rt_error(MAWK, "process execution not supported");
return NULL;
}
void mawk_vio_orig_exec_shell(mawk_state_t *MAWK, const char *cmd)
{
if (!MAWK->do_exit)
mawk_rt_error(MAWK, "process execution not supported");
}
#else
void mawk_vio_orig_exec_shell(mawk_state_t *MAWK, const char *cmd)
{
char **envp = mawk_environ_extract(MAWK);
if (envp == NULL) {
mawk_errmsg(MAWK, errno, "failed to exec %s -c %s: can't set up environ[]", MAWK->shell, cmd);
fflush(stderr);
_exit(128);
}
execle(MAWK->shell, MAWK->shell, "-c", cmd, (char *) 0, envp);
mawk_errmsg(MAWK, errno, "failed to exec %s -c %s", MAWK->shell, cmd);
fflush(stderr);
_exit(128);
}
#ifdef MAWK_NO_PIPE
mawk_vio_t *mawk_vio_orig_open_pipe(mawk_state_t *MAWK, const char *name, int type)
{
if (!MAWK->do_exit)
mawk_rt_error(MAWK, "pipes (and background processes) not supported");
return NULL;
}
#else
mawk_vio_t *mawk_vio_orig_open_pipe(mawk_state_t *MAWK, const char *name, int type)
{
int the_pipe[2], local_fd, remote_fd;
mawk_vio_orig_t *vf;
pid_t pid;
FILE *f;
if (pipe(the_pipe) < 0)
return NULL;
local_fd = the_pipe[type == PIPE_OUT];
remote_fd = the_pipe[type == PIPE_IN];
/* output files shall have a FILE * */
if (type == PIPE_OUT) {
f = fdopen(local_fd, "w");
if (f == NULL) {
close(local_fd);
close(remote_fd);
return NULL;
}
}
/* to keep output ordered correctly */
fflush(stdout);
fflush(stderr);
pid = fork();
switch (pid) {
case -1:
close(local_fd);
close(remote_fd);
return NULL;
case 0:
close(local_fd);
close(type == PIPE_IN);
dup(remote_fd);
close(remote_fd);
mawk_vio_exec_shell(MAWK, name);
default:
close(remote_fd);
/* we could deadlock if future child inherit the local fd ,
set close on exec flag */
TODO(": better do this by hand - close on exec is not really portable")
CLOSE_ON_EXEC(local_fd);
break;
}
vf = vio_alloc_(MAWK);
vf->pid = pid;
/* for an output pipe store FILE * for consistent writes */
if (type == PIPE_OUT)
vf->f = f;
else
vf->fd = local_fd;
return (mawk_vio_t *)vf;
}
#endif
#endif
int mawk_vio_orig_putc(mawk_state_t *MAWK, mawk_vio_t *vf_, char c)
{
mawk_vio_orig_t *vf = (mawk_vio_orig_t *)vf_;
if (vf->f == NULL)
return -1;
if (fputc(c, vf->f) != c)
return -1;
return 0;
}
int mawk_vio_orig_write(mawk_state_t *MAWK, mawk_vio_t *vf_, const char *data, int len)
{
mawk_vio_orig_t *vf = (mawk_vio_orig_t *)vf_;
if (vf->f == NULL)
return -1;
if (fwrite(data, 1, len, vf->f) == 1)
return 0;
return -1;
}
int mawk_vio_orig_write_str(mawk_state_t *MAWK, mawk_vio_t *vf_, const char *str)
{
mawk_vio_orig_t *vf = (mawk_vio_orig_t *)vf_;
if (vf->f == NULL)
return -1;
return fprintf(vf->f, "%s", str);
}
int mawk_vio_orig_printf(mawk_state_t *MAWK, mawk_vio_t *vf_, const char *fmt, ...)
{
mawk_vio_orig_t *vf = (mawk_vio_orig_t *)vf_;
int len;
va_list ap;
if (vf->f == NULL)
return -1;
va_start(ap, fmt);
len = vfprintf(vf->f, fmt, ap);
va_end(ap);
return len;
}
int mawk_vio_orig_error(mawk_state_t *MAWK, mawk_vio_t *vf_)
{
mawk_vio_orig_t *vf = (mawk_vio_orig_t *)vf_;
if (vf->f == NULL)
return -1;
return ferror(vf->f);
}
int mawk_vio_orig_setbuf(mawk_state_t *MAWK, mawk_vio_t *vf_, int buf_enable)
{
mawk_vio_orig_t *vf = (mawk_vio_orig_t *)vf_;
if (vf->f == NULL)
return -1;
if (buf_enable)
setbuf(vf->f, "");
else
setbuf(vf->f, NULL);
return 0;
}
int mawk_vio_orig_read(mawk_state_t *MAWK, mawk_vio_t *vf_, char *dst, long int size)
{
mawk_vio_orig_t *vf = (mawk_vio_orig_t *)vf_;
int len;
/* input file must not have a FILE * */
if (vf->f != NULL)
return -1;
/* already at eof, don't attempt to read any more */
if (vf->at_eof)
return 0;
/* invalid fd, how did we get here? */
if (vf->fd < 0)
return -1;
len = read(vf->fd, dst, size);
if (len <= 0)
vf->at_eof = 1;
return len;
}
void mawk_vio_orig_mark_no_close(mawk_state_t *MAWK, mawk_vio_t *vf_)
{
mawk_vio_orig_t *vf = (mawk_vio_orig_t *)vf_;
vf->no_close = 1;
}
const mawk_vio_imp_t mawk_vio_orig_imp = {
mawk_vio_orig_putc,
mawk_vio_orig_write_str,
mawk_vio_orig_write,
mawk_vio_orig_printf,
mawk_vio_orig_read,
mawk_vio_orig_close,
mawk_vio_orig_flush,
mawk_vio_orig_error,
mawk_vio_orig_mark_no_close
};
const mawk_vio_init_t mawk_vio_orig_init = {
mawk_vio_orig_open,
mawk_vio_orig_open_pipe,
mawk_vio_orig_exec_shell,
1
};
void mawk_vio_orig_setup_stdio(mawk_state_t * MAWK, int enable_stdin, int enable_stdout, int enable_stderr)
{
mawk_vio_orig_t *vf;
if (enable_stdin) {
vf = (mawk_vio_orig_t *)mawk_vio_orig_open(MAWK, "/dev/stdin", MAWK_VIO_I);
mawk_file_register(MAWK, "/dev/stdin", F_IN, (mawk_vio_t *)vf);
mawk_vio_orig_mark_no_close(MAWK, (mawk_vio_t *)vf);
}
if (enable_stdout) {
vf = vio_alloc_(MAWK);
vf->f = stdout;
mawk_file_register_nofin(MAWK, "/dev/stdout", F_TRUNC, (mawk_vio_t *)vf);
mawk_vio_orig_mark_no_close(MAWK, (mawk_vio_t *)vf);
/* don't buffer stdout in interactive mode */
if (MAWK->interactive_flag)
mawk_vio_orig_setbuf(MAWK, (mawk_vio_t *)vf, 0);
}
if (enable_stderr) {
vf = vio_alloc_(MAWK);
vf->f = stderr;
mawk_file_register_nofin(MAWK, "/dev/stderr", F_TRUNC, (mawk_vio_t *)vf);
mawk_vio_orig_mark_no_close(MAWK, (mawk_vio_t *)vf);
/* never buffer stderr */
mawk_vio_orig_setbuf(MAWK, (mawk_vio_t *)vf, 0);
}
}