/********************************************
files_children.c

libmawk changes (C) 2009-2014, 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 <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "mawk.h"
#include "files.h"
#include "memory.h"
#include "fin.h"


static void add_to_child_list(mawk_state_t *, int, int);
static struct child *remove_from_child_list(mawk_state_t *, int);

/* we need to wait for children at the end of output pipes to
   complete so we know any files they have created are complete */

static void add_to_child_list(mawk_state_t *MAWK, int pid, int exit_status)
{
	register struct child *p = MAWK_ZMALLOC(MAWK, struct child);

	p->pid = pid;
	p->exit_status = exit_status;
	p->link = MAWK->child_list;
	MAWK->child_list = p;
}

static struct child *remove_from_child_list(mawk_state_t *MAWK, int pid)
{
	struct child dummy;
	register struct child *p;
	struct child *q = &dummy;

	dummy.link = p = MAWK->child_list;
	while (p) {
		if (p->pid == pid) {
			q->link = p->link;
			break;
		}
		else {
			q = p;
			p = p->link;
		}
	}

	MAWK->child_list = dummy.link;
	return p;
	/* null return if not in the list */
}


#ifndef MAWK_NO_FORK
/* wait for a specific child to complete and return its
   exit status

   If pid is zero, wait for any single child and
   put it on the dead children list
*/
int mawk_wait_for(mawk_state_t *MAWK, int pid)
{
	int exit_status;
	struct child *p;
	int id;

	if (pid == 0) {
		id = wait(&exit_status);
		add_to_child_list(MAWK, id, exit_status);
	}
	/* see if an earlier wait() caught our child */
	else if ((p = remove_from_child_list(MAWK, pid))) {
		exit_status = p->exit_status;
		MAWK_ZFREE(MAWK, p);
	}
	else {
		/* need to really wait */
		while ((id = wait(&exit_status)) != pid) {
			if (id == -1)							/* can't happen */
				mawk_bozo(MAWK, "mawk_wait_for");
			else {
				/* we got the exit status of another child
				   put it on the child list and try again */
				add_to_child_list(MAWK, id, exit_status);
			}
		}
	}

	if (exit_status & 0xff)
		exit_status = 128 + (exit_status & 0xff);
	else
		exit_status = (exit_status & 0xff00) >> 8;

	return exit_status;
}
#endif

