root/usr/src/cmd/make/bin/parallel.cc
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
 * Use is subject to license terms.
 */


/*
 *      parallel.cc
 *
 *      Deal with the parallel processing
 */

/*
 * Included files
 */
#include <errno.h>              /* errno */
#include <fcntl.h>
#include <mk/defs.h>
#include <mksh/dosys.h>         /* redirect_io() */
#include <mksh/macro.h>         /* expand_value() */
#include <mksh/misc.h>          /* getmem() */
#include <sys/signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include <unistd.h>
#include <netdb.h>
#include <libintl.h>



/*
 * Defined macros
 */
#define MAXRULES                100

/*
 * This const should be in avo_dms/include/AvoDmakeCommand.h
 */
const int local_host_mask = 0x20;


/*
 * typedefs & structs
 */


/*
 * Static variables
 */
static  Boolean         just_did_subtree = false;
static  char            local_host[MAXNAMELEN] = "";
static  char            user_name[MAXNAMELEN] = "";
static  int             pmake_max_jobs = 0;
static  pid_t           process_running = -1;
static  Running         *running_tail = &running_list;
static  Name            subtree_conflict;
static  Name            subtree_conflict2;


/*
 * File table of contents
 */
static  void            delete_running_struct(Running rp);
static  Boolean         dependency_conflict(Name target);
static  Doname          distribute_process(char **commands, Property line);
static  void            doname_subtree(Name target, Boolean do_get, Boolean implicit);
static  void            dump_out_file(char *filename, Boolean err);
static  void            finish_doname(Running rp);
static  void            maybe_reread_make_state(void);
static  void            process_next(void);
static  void            reset_conditionals(int cnt, Name *targets, Property *locals);
static  pid_t           run_rule_commands(char *host, char **commands);
static  Property        *set_conditionals(int cnt, Name *targets);
static  void            store_conditionals(Running rp);


/*
 *      execute_parallel(line, waitflg)
 *
 *      DMake 2.x:
 *      parallel mode: spawns a parallel process to execute the command group.
 *
 *      Return value:
 *                              The result of the execution
 *
 *      Parameters:
 *              line            The command group to execute
 */
Doname
execute_parallel(Property line, Boolean waitflg, Boolean local)
{
        int                     argcnt;
        int                     cmd_options = 0;
        char                    *commands[MAXRULES + 5];
        char                    *cp;
        Name                    dmake_name;
        Name                    dmake_value;
        int                     ignore;
        Name                    make_machines_name;
        char                    **p;
        Property                prop;
        Doname                  result = build_ok;
        Cmd_line                rule;
        Boolean                 silent_flag;
        Name                    target = line->body.line.target;
        Boolean                 wrote_state_file = false;

        if ((pmake_max_jobs == 0) &&
            (dmake_mode_type == parallel_mode)) {
                if (local_host[0] == '\0') {
                        (void) gethostname(local_host, MAXNAMELEN);
                }
                MBSTOWCS(wcs_buffer, "DMAKE_MAX_JOBS");
                dmake_name = GETNAME(wcs_buffer, FIND_LENGTH);
                if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) &&
                    ((dmake_value = prop->body.macro.value) != NULL)) {
                        pmake_max_jobs = atoi(dmake_value->string_mb);
                        if (pmake_max_jobs <= 0) {
                                warning(gettext("DMAKE_MAX_JOBS cannot be less than or equal to zero."));
                                warning(gettext("setting DMAKE_MAX_JOBS to %d."), PMAKE_DEF_MAX_JOBS);
                                pmake_max_jobs = PMAKE_DEF_MAX_JOBS;
                        }
                } else {
                        /*
                         * For backwards compatibility w/ PMake 1.x, when
                         * DMake 2.x is being run in parallel mode, DMake
                         * should parse the PMake startup file
                         * $(HOME)/.make.machines to get the pmake_max_jobs.
                         */
                        MBSTOWCS(wcs_buffer, "PMAKE_MACHINESFILE");
                        dmake_name = GETNAME(wcs_buffer, FIND_LENGTH);
                        if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) &&
                            ((dmake_value = prop->body.macro.value) != NULL)) {
                                make_machines_name = dmake_value;
                        } else {
                                make_machines_name = NULL;
                        }
                        if ((pmake_max_jobs = read_make_machines(make_machines_name)) <= 0) {
                                pmake_max_jobs = PMAKE_DEF_MAX_JOBS;
                        }
                }
        }

        if ((dmake_mode_type == serial_mode) ||
            ((dmake_mode_type == parallel_mode) && (waitflg))) {
                return (execute_serial(line));
        }

        {
                p = commands;
        }

        argcnt = 0;
        for (rule = line->body.line.command_used;
             rule != NULL;
             rule = rule->next) {
                if (posix && (touch || quest) && !rule->always_exec) {
                        continue;
                }
                if (vpath_defined) {
                        rule->command_line =
                          vpath_translation(rule->command_line);
                }

                silent_flag = false;
                ignore = 0;

                if (rule->command_line->hash.length > 0) {
                        if (++argcnt == MAXRULES) {
                                return build_serial;
                        }
                        {
                                if (rule->silent && !silent) {
                                        silent_flag = true;
                                }
                                if (rule->ignore_error) {
                                        ignore++;
                                }
                                /* XXX - need to add support for + prefix */
                                if (silent_flag || ignore) {
                                        *p = getmem((silent_flag ? 1 : 0) +
                                                    ignore +
                                                    (strlen(rule->
                                                           command_line->
                                                           string_mb)) +
                                                    1);
                                        cp = *p++;
                                        if (silent_flag) {
                                                *cp++ = (int) at_char;
                                        }
                                        if (ignore) {
                                                *cp++ = (int) hyphen_char;
                                        }
                                        (void) strcpy(cp, rule->command_line->string_mb);
                                } else {
                                        *p++ = rule->command_line->string_mb;
                                }
                        }
                }
        }
        if ((argcnt == 0) ||
            (report_dependencies_level > 0)) {
                return build_ok;
        }
        {
                *p = NULL;

                Doname res = distribute_process(commands, line);
                if (res == build_running) {
                        parallel_process_cnt++;
                }

                /*
                 * Return only those memory that were specially allocated
                 * for part of commands.
                 */
                for (int i = 0; commands[i] != NULL; i++) {
                        if ((commands[i][0] == (int) at_char) ||
                            (commands[i][0] == (int) hyphen_char)) {
                                retmem_mb(commands[i]);
                        }
                }
                return res;
        }
}



#include <unistd.h>     /* sysconf(_SC_NPROCESSORS_ONLN) */
#include <sys/ipc.h>            /* ftok() */
#include <sys/shm.h>            /* shmget(), shmat(), shmdt(), shmctl() */
#include <semaphore.h>          /* sem_init(), sem_trywait(), sem_post(), sem_destroy() */
#include <sys/loadavg.h>        /* getloadavg() */

/*
 *      adjust_pmake_max_jobs (int pmake_max_jobs)
 *
 *      Parameters:
 *              pmake_max_jobs  - max jobs limit set by user
 *
 *      External functions used:
 *              sysconf()
 *              getloadavg()
 */
static int
adjust_pmake_max_jobs (int pmake_max_jobs)
{
        static int      ncpu = 0;
        double          loadavg[3];
        int             adjustment;
        int             adjusted_max_jobs;

        if (ncpu <= 0) {
                if ((ncpu = sysconf(_SC_NPROCESSORS_ONLN)) <= 0) {
                        ncpu = 1;
                }
        }
        if (getloadavg(loadavg, 3) != 3) return(pmake_max_jobs);
        adjustment = ((int)loadavg[LOADAVG_1MIN]);
        if (adjustment < 2) return(pmake_max_jobs);
        if (ncpu > 1) {
                adjustment = adjustment / ncpu;
        }
        adjusted_max_jobs = pmake_max_jobs - adjustment;
        if (adjusted_max_jobs < 1) adjusted_max_jobs = 1;
        return(adjusted_max_jobs);
}

/*
 *  M2 adjust mode data and functions
 *
 *  m2_init()           - initializes M2 shared semaphore
 *  m2_acquire_job()    - decrements M2 semaphore counter
 *  m2_release_job()    - increments M2 semaphore counter
 *  m2_fini()           - destroys M2 semaphore and shared memory*
 *
 *  Environment variables:
 *      __DMAKE_M2_FILE__
 *
 *  External functions:
 *      ftok(), shmget(), shmat(), shmdt(), shmctl()
 *      sem_init(), sem_trywait(), sem_post(), sem_destroy()
 *      creat(), close(), unlink()
 *      getenv(), putenv()
 *
 *  Static variables:
 *      m2_file         - tmp file name to create ipc key for shared memory
 *      m2_shm_id       - shared memory id
 *      m2_shm_sem      - shared memory semaphore
 */

static char     m2_file[MAXPATHLEN];
static int      m2_shm_id = -1;
static sem_t*   m2_shm_sem = 0;

static int
m2_init() {
        char    *var;
        key_t   key;

        if ((var = getenv("__DMAKE_M2_FILE__")) == 0) {
                /* compose tmp file name */
                sprintf(m2_file, "%s/dmake.m2.%d.XXXXXX", tmpdir, getpid());

                /* create tmp file */
                int fd = mkstemp(m2_file);
                if (fd < 0) {
                        return -1;
                } else {
                        close(fd);
                }
        } else {
                /* using existing semaphore */
                strcpy(m2_file, var);
        }

        /* combine IPC key */
        if ((key = ftok(m2_file, 38)) == (key_t) -1) {
                return -1;
        }

        /* create shared memory */
        if ((m2_shm_id = shmget(key, sizeof(*m2_shm_sem), 0666 | (var ? 0 : IPC_CREAT|IPC_EXCL))) == -1) {
                return -1;
        }

        /* attach shared memory */
        if ((m2_shm_sem = (sem_t*) shmat(m2_shm_id, 0, 0666)) == (sem_t*)-1) {
                return -1;
        }

        /* root process */
        if (var == 0) {
                /* initialize semaphore */
                if (sem_init(m2_shm_sem, 1, pmake_max_jobs)) {
                        return -1;
                }

                /* alloc memory for env variable */
                if ((var = (char*) malloc(MAXPATHLEN)) == 0) {
                        return -1;
                }

                /* put key to env */
                sprintf(var, "__DMAKE_M2_FILE__=%s", m2_file);
                if (putenv(var)) {
                        return -1;
                }
        }
        return 0;
}

static void
m2_fini() {
        if (m2_shm_id >= 0) {
                struct shmid_ds stat;

                /* determine the number of attached processes */
                if (shmctl(m2_shm_id, IPC_STAT, &stat) == 0) {
                        if (stat.shm_nattch <= 1) {
                                /* destroy semaphore */
                                if (m2_shm_sem != 0) {
                                        (void) sem_destroy(m2_shm_sem);
                                }

                                /* destroy shared memory */
                                (void) shmctl(m2_shm_id, IPC_RMID, &stat);

                                /* remove tmp file created for the key */
                                (void) unlink(m2_file);
                        } else {
                                /* detach shared memory */
                                if (m2_shm_sem != 0) {
                                        (void) shmdt((char*) m2_shm_sem);
                                }
                        }
                }

                m2_shm_id = -1;
                m2_shm_sem = 0;
        }
}

static int
m2_acquire_job() {
        if ((m2_shm_id >= 0) && (m2_shm_sem != 0)) {
                if (sem_trywait(m2_shm_sem) == 0) {
                        return 1;
                }
                if (errno == EAGAIN) {
                        return 0;
                }
        }
        return -1;
}

static int
m2_release_job() {
        if ((m2_shm_id >= 0) && (m2_shm_sem != 0)) {
                if (sem_post(m2_shm_sem) == 0) {
                        return 0;
                }
        }
        return -1;
}

/*
 *  job adjust mode
 *
 *  Possible values:
 *    ADJUST_M1         - adjustment by system load (default)
 *    ADJUST_M2         - fixed limit of jobs for the group of nested dmakes
 *    ADJUST_NONE       - no adjustment - fixed limit of jobs for the current dmake
 */
static enum {
        ADJUST_UNKNOWN,
        ADJUST_M1,
        ADJUST_M2,
        ADJUST_NONE
} job_adjust_mode = ADJUST_UNKNOWN;

/*
 *  void job_adjust_fini()
 *
 *  Description:
 *      Cleans up job adjust data.
 *
 *  Static variables:
 *      job_adjust_mode Current job adjust mode
 */
void
job_adjust_fini() {
        if (job_adjust_mode == ADJUST_M2) {
                m2_fini();
        }
}

/*
 *  void job_adjust_error()
 *
 *  Description:
 *      Prints warning message, cleans up job adjust data, and disables job adjustment
 *
 *  Environment:
 *      DMAKE_ADJUST_MAX_JOBS
 *
 *  External functions:
 *      putenv()
 *
 *  Static variables:
 *      job_adjust_mode Current job adjust mode
 */
static void
job_adjust_error() {
        if (job_adjust_mode != ADJUST_NONE) {
                /* cleanup internals */
                job_adjust_fini();

                /* warning message for the user */
                warning(gettext("Encountered max jobs auto adjustment error - disabling auto adjustment."));

                /* switch off job adjustment for the children */
                putenv(strdup("DMAKE_ADJUST_MAX_JOBS=NO"));

                /* and for this dmake */
                job_adjust_mode = ADJUST_NONE;
        }
}

/*
 *  void job_adjust_init()
 *
 *  Description:
 *      Parses DMAKE_ADJUST_MAX_JOBS env variable
 *      and performs appropriate initializations.
 *
 *  Environment:
 *      DMAKE_ADJUST_MAX_JOBS
 *        DMAKE_ADJUST_MAX_JOBS == "NO" - no adjustment
 *        DMAKE_ADJUST_MAX_JOBS == "M2" - M2 adjust mode
 *        other                         - M1 adjust mode
 *
 *  External functions:
 *      getenv()
 *
 *  Static variables:
 *      job_adjust_mode Current job adjust mode
 */
static void
job_adjust_init() {
        if (job_adjust_mode == ADJUST_UNKNOWN) {
                /* default mode */
                job_adjust_mode = ADJUST_M1;

                /* determine adjust mode */
                if (char *var = getenv("DMAKE_ADJUST_MAX_JOBS")) {
                        if (strcasecmp(var, "NO") == 0) {
                                job_adjust_mode = ADJUST_NONE;
                        } else if (strcasecmp(var, "M2") == 0) {
                                job_adjust_mode = ADJUST_M2;
                        }
                }

                /* M2 specific initialization */
                if (job_adjust_mode == ADJUST_M2) {
                        if (m2_init()) {
                                job_adjust_error();
                        }
                }
        }
}


/*
 *      distribute_process(char **commands, Property line)
 *
 *      Parameters:
 *              commands        argv vector of commands to execute
 *
 *      Return value:
 *                              The result of the execution
 *
 *      Static variables used:
 *              process_running Set to the pid of the process set running
 *              job_adjust_mode Current job adjust mode
 */
static Doname
distribute_process(char **commands, Property line)
{
        static unsigned file_number = 0;
        wchar_t         string[MAXPATHLEN];
        char            mbstring[MAXPATHLEN];
        int             filed;
        int             res;
        int             tmp_index;
        char            *tmp_index_str_ptr;

        /* initialize adjust mode, if not initialized */
        if (job_adjust_mode == ADJUST_UNKNOWN) {
                job_adjust_init();
        }

        /* actions depend on adjust mode */
        switch (job_adjust_mode) {
        case ADJUST_M1:
                while (parallel_process_cnt >= adjust_pmake_max_jobs (pmake_max_jobs)) {
                        await_parallel(false);
                        finish_children(true);
                }
                break;
        case ADJUST_M2:
                if ((res = m2_acquire_job()) == 0) {
                        if (parallel_process_cnt > 0) {
                                await_parallel(false);
                                finish_children(true);

                                if ((res = m2_acquire_job()) == 0) {
                                        return build_serial;
                                }
                        } else {
                                return build_serial;
                        }
                }
                if (res < 0) {
                        /* job adjustment error */
                        job_adjust_error();

                        /* no adjustment */
                        while (parallel_process_cnt >= pmake_max_jobs) {
                                await_parallel(false);
                                finish_children(true);
                        }
                }
                break;
        default:
                while (parallel_process_cnt >= pmake_max_jobs) {
                        await_parallel(false);
                        finish_children(true);
                }
        }

        setvar_envvar();
        /*
         * Tell the user what DMake is doing.
         */
        if (!silent && output_mode != txt2_mode) {
                /*
                 * Print local_host --> x job(s).
                 */
                (void) fprintf(stdout,
                               gettext("%s --> %d %s\n"),
                               local_host,
                               parallel_process_cnt + 1,
                               (parallel_process_cnt == 0) ? gettext("job") : gettext("jobs"));

                /* Print command line(s). */
                tmp_index = 0;
                while (commands[tmp_index] != NULL) {
                    /* No @ char. */
                    /* XXX - need to add [2] when + prefix is added */
                    if ((commands[tmp_index][0] != (int) at_char) &&
                        (commands[tmp_index][1] != (int) at_char)) {
                        tmp_index_str_ptr = commands[tmp_index];
                        if (*tmp_index_str_ptr == (int) hyphen_char) {
                                tmp_index_str_ptr++;
                        }
                        (void) fprintf(stdout, "%s\n", tmp_index_str_ptr);
                    }
                    tmp_index++;
                }
                (void) fflush(stdout);
        }

        (void) sprintf(mbstring,
                        "%s/dmake.stdout.%d.%d.XXXXXX",
                        tmpdir,
                        getpid(),
                        file_number++);

        mktemp(mbstring);

        stdout_file = strdup(mbstring);
        stderr_file = NULL;

        if (!out_err_same) {
                (void) sprintf(mbstring,
                                "%s/dmake.stderr.%d.%d.XXXXXX",
                                tmpdir,
                                getpid(),
                                file_number++);

                mktemp(mbstring);

                stderr_file = strdup(mbstring);
        }

        process_running = run_rule_commands(local_host, commands);

        return build_running;
}

/*
 *      doname_parallel(target, do_get, implicit)
 *
 *      Processes the given target and finishes up any parallel
 *      processes left running.
 *
 *      Return value:
 *                              Result of target build
 *
 *      Parameters:
 *              target          Target to build
 *              do_get          True if sccs get to be done
 *              implicit        True if this is an implicit target
 */
Doname
doname_parallel(Name target, Boolean do_get, Boolean implicit)
{
        Doname          result;

        result = doname_check(target, do_get, implicit, false);
        if (result == build_ok || result == build_failed) {
                return result;
        }
        finish_running();
        return (Doname) target->state;
}

/*
 *      doname_subtree(target, do_get, implicit)
 *
 *      Completely computes an object and its dependents for a
 *      serial subtree build.
 *
 *      Parameters:
 *              target          Target to build
 *              do_get          True if sccs get to be done
 *              implicit        True if this is an implicit target
 *
 *      Static variables used:
 *              running_tail    Tail of the list of running processes
 *
 *      Global variables used:
 *              running_list    The list of running processes
 */
static void
doname_subtree(Name target, Boolean do_get, Boolean implicit)
{
        Running         save_running_list;
        Running         *save_running_tail;

        save_running_list = running_list;
        save_running_tail = running_tail;
        running_list = NULL;
        running_tail = &running_list;
        target->state = build_subtree;
        target->checking_subtree = true;
        while(doname_check(target, do_get, implicit, false) == build_running) {
                target->checking_subtree = false;
                finish_running();
                target->state = build_subtree;
        }
        target->checking_subtree = false;
        running_list = save_running_list;
        running_tail = save_running_tail;
}

/*
 *      finish_running()
 *
 *      Keeps processing until the running_list is emptied out.
 *
 *      Parameters:
 *
 *      Global variables used:
 *              running_list    The list of running processes
 */
void
finish_running(void)
{
        while (running_list != NULL) {
                {
                        await_parallel(false);
                        finish_children(true);
                }
                if (running_list != NULL) {
                        process_next();
                }
        }
}

/*
 *      process_next()
 *
 *      Searches the running list for any targets which can start processing.
 *      This can be a pending target, a serial target, or a subtree target.
 *
 *      Parameters:
 *
 *      Static variables used:
 *              running_tail            The end of the list of running procs
 *              subtree_conflict        A target which conflicts with a subtree
 *              subtree_conflict2       The other target which conflicts
 *
 *      Global variables used:
 *              commands_done           True if commands executed
 *              debug_level             Controls debug output
 *              parallel_process_cnt    Number of parallel process running
 *              recursion_level         Indentation for debug output
 *              running_list            List of running processes
 */
static void
process_next(void)
{
        Running         rp;
        Running         *rp_prev;
        Property        line;
        Chain           target_group;
        Dependency      dep;
        Boolean         quiescent = true;
        Running         *subtree_target;
        Boolean         saved_commands_done;
        Property        *conditionals;

        subtree_target = NULL;
        subtree_conflict = NULL;
        subtree_conflict2 = NULL;
        /*
         * If nothing currently running, build a serial target, if any.
         */
start_loop_1:
        for (rp_prev = &running_list, rp = running_list;
             rp != NULL && parallel_process_cnt == 0;
             rp = rp->next) {
                if (rp->state == build_serial) {
                        *rp_prev = rp->next;
                        if (rp->next == NULL) {
                                running_tail = rp_prev;
                        }
                        recursion_level = rp->recursion_level;
                        rp->target->state = build_pending;
                        (void) doname_check(rp->target,
                                            rp->do_get,
                                            rp->implicit,
                                            false);
                        quiescent = false;
                        delete_running_struct(rp);
                        goto start_loop_1;
                } else {
                        rp_prev = &rp->next;
                }
        }
        /*
         * Find a target to build.  The target must be pending, have all
         * its dependencies built, and not be in a target group with a target
         * currently building.
         */
start_loop_2:
        for (rp_prev = &running_list, rp = running_list;
             rp != NULL;
             rp = rp->next) {
                if (!(rp->state == build_pending ||
                      rp->state == build_subtree)) {
                        quiescent = false;
                        rp_prev = &rp->next;
                } else if (rp->state == build_pending) {
                        line = get_prop(rp->target->prop, line_prop);
                        for (dep = line->body.line.dependencies;
                             dep != NULL;
                             dep = dep->next) {
                                if (dep->name->state == build_running ||
                                    dep->name->state == build_pending ||
                                    dep->name->state == build_serial) {
                                        break;
                                }
                        }
                        if (dep == NULL) {
                                for (target_group = line->body.line.target_group;
                                     target_group != NULL;
                                     target_group = target_group->next) {
                                        if (is_running(target_group->name)) {
                                                break;
                                        }
                                }
                                if (target_group == NULL) {
                                        *rp_prev = rp->next;
                                        if (rp->next == NULL) {
                                                running_tail = rp_prev;
                                        }
                                        recursion_level = rp->recursion_level;
                                        rp->target->state = rp->redo ?
                                          build_dont_know : build_pending;
                                        saved_commands_done = commands_done;
                                        conditionals =
                                                set_conditionals
                                                    (rp->conditional_cnt,
                                                     rp->conditional_targets);
                                        rp->target->dont_activate_cond_values = true;
                                        if ((doname_check(rp->target,
                                                          rp->do_get,
                                                          rp->implicit,
                                                          rp->target->has_target_prop ? true : false) !=
                                             build_running) &&
                                            !commands_done) {
                                                commands_done =
                                                  saved_commands_done;
                                        }
                                        rp->target->dont_activate_cond_values = false;
                                        reset_conditionals
                                                (rp->conditional_cnt,
                                                 rp->conditional_targets,
                                                 conditionals);
                                        quiescent = false;
                                        delete_running_struct(rp);
                                        goto start_loop_2;
                                } else {
                                        rp_prev = &rp->next;
                                }
                        } else {
                                rp_prev = &rp->next;
                        }
                } else {
                        rp_prev = &rp->next;
                }
        }
        /*
         * If nothing has been found to build and there exists a subtree
         * target with no dependency conflicts, build it.
         */
        if (quiescent) {
start_loop_3:
                for (rp_prev = &running_list, rp = running_list;
                     rp != NULL;
                     rp = rp->next) {
                        if (rp->state == build_subtree) {
                                if (!dependency_conflict(rp->target)) {
                                        *rp_prev = rp->next;
                                        if (rp->next == NULL) {
                                                running_tail = rp_prev;
                                        }
                                        recursion_level = rp->recursion_level;
                                        doname_subtree(rp->target,
                                                       rp->do_get,
                                                       rp->implicit);
                                        quiescent = false;
                                        delete_running_struct(rp);
                                        goto start_loop_3;
                                } else {
                                        subtree_target = rp_prev;
                                        rp_prev = &rp->next;
                                }
                        } else {
                                rp_prev = &rp->next;
                        }
                }
        }
        /*
         * If still nothing found to build, we either have a deadlock
         * or a subtree with a dependency conflict with something waiting
         * to build.
         */
        if (quiescent) {
                if (subtree_target == NULL) {
                        fatal(gettext("Internal error: deadlock detected in process_next"));
                } else {
                        rp = *subtree_target;
                        if (debug_level > 0) {
                                warning(gettext("Conditional macro conflict encountered for %s between %s and %s"),
                                        subtree_conflict2->string_mb,
                                        rp->target->string_mb,
                                        subtree_conflict->string_mb);
                        }
                        *subtree_target = (*subtree_target)->next;
                        if (rp->next == NULL) {
                                running_tail = subtree_target;
                        }
                        recursion_level = rp->recursion_level;
                        doname_subtree(rp->target, rp->do_get, rp->implicit);
                        delete_running_struct(rp);
                }
        }
}

/*
 *      set_conditionals(cnt, targets)
 *
 *      Sets the conditional macros for the targets given in the array of
 *      targets.  The old macro values are returned in an array of
 *      Properties for later resetting.
 *
 *      Return value:
 *                                      Array of conditional macro settings
 *
 *      Parameters:
 *              cnt                     Number of targets
 *              targets                 Array of targets
 */
static Property *
set_conditionals(int cnt, Name *targets)
{
        Property        *locals, *lp;
        Name            *tp;

        locals = (Property *) getmem(cnt * sizeof(Property));
        for (lp = locals, tp = targets;
             cnt > 0;
             cnt--, lp++, tp++) {
                *lp = (Property) getmem((*tp)->conditional_cnt *
                                        sizeof(struct _Property));
                set_locals(*tp, *lp);
        }
        return locals;
}

/*
 *      reset_conditionals(cnt, targets, locals)
 *
 *      Resets the conditional macros as saved in the given array of
 *      Properties.  The resets are done in reverse order.  Afterwards the
 *      data structures are freed.
 *
 *      Parameters:
 *              cnt                     Number of targets
 *              targets                 Array of targets
 *              locals                  Array of dependency macro settings
 */
static void
reset_conditionals(int cnt, Name *targets, Property *locals)
{
        Name            *tp;
        Property        *lp;

        for (tp = targets + (cnt - 1), lp = locals + (cnt - 1);
             cnt > 0;
             cnt--, tp--, lp--) {
                reset_locals(*tp,
                             *lp,
                             get_prop((*tp)->prop, conditional_prop),
                             0);
                retmem_mb((caddr_t) *lp);
        }
        retmem_mb((caddr_t) locals);
}

/*
 *      dependency_conflict(target)
 *
 *      Returns true if there is an intersection between
 *      the subtree of the target and any dependents of the pending targets.
 *
 *      Return value:
 *                                      True if conflict found
 *
 *      Parameters:
 *              target                  Subtree target to check
 *
 *      Static variables used:
 *              subtree_conflict        Target conflict found
 *              subtree_conflict2       Second conflict found
 *
 *      Global variables used:
 *              running_list            List of running processes
 *              wait_name               .WAIT, not a real dependency
 */
static Boolean
dependency_conflict(Name target)
{
        Property        line;
        Property        pending_line;
        Dependency      dp;
        Dependency      pending_dp;
        Running         rp;

        /* Return if we are already checking this target */
        if (target->checking_subtree) {
                return false;
        }
        target->checking_subtree = true;
        line = get_prop(target->prop, line_prop);
        if (line == NULL) {
                target->checking_subtree = false;
                return false;
        }
        /* Check each dependency of the target for conflicts */
        for (dp = line->body.line.dependencies; dp != NULL; dp = dp->next) {
                /* Ignore .WAIT dependency */
                if (dp->name == wait_name) {
                        continue;
                }
                /*
                 * For each pending target, look for a dependency which
                 * is the same as a dependency of the subtree target.  Since
                 * we can't build the subtree until all pending targets have
                 * finished which depend on the same dependency, this is
                 * a conflict.
                 */
                for (rp = running_list; rp != NULL; rp = rp->next) {
                        if (rp->state == build_pending) {
                                pending_line = get_prop(rp->target->prop,
                                                        line_prop);
                                if (pending_line == NULL) {
                                        continue;
                                }
                                for(pending_dp = pending_line->
                                                        body.line.dependencies;
                                    pending_dp != NULL;
                                    pending_dp = pending_dp->next) {
                                        if (dp->name == pending_dp->name) {
                                                target->checking_subtree
                                                                = false;
                                                subtree_conflict = rp->target;
                                                subtree_conflict2 = dp->name;
                                                return true;
                                        }
                                }
                        }
                }
                if (dependency_conflict(dp->name)) {
                        target->checking_subtree = false;
                        return true;
                }
        }
        target->checking_subtree = false;
        return false;
}

/*
 *      await_parallel(waitflg)
 *
 *      Waits for parallel children to exit and finishes their processing.
 *      If waitflg is false, the function returns after update_delay.
 *
 *      Parameters:
 *              waitflg         dwight
 */
void
await_parallel(Boolean waitflg)
{
        Boolean         nohang;
        pid_t           pid;
        int             status;
        Running         rp;
        int             waiterr;

        nohang = false;
        for ( ; ; ) {
                if (!nohang) {
                        (void) alarm((int) update_delay);
                }
                pid = waitpid((pid_t)-1,
                              &status,
                              nohang ? WNOHANG : 0);
                waiterr = errno;
                if (!nohang) {
                        (void) alarm(0);
                }
                if (pid <= 0) {
                        if (waiterr == EINTR) {
                                if (waitflg) {
                                        continue;
                                } else {
                                        return;
                                }
                        } else {
                                return;
                        }
                }
                for (rp = running_list;
                     (rp != NULL) && (rp->pid != pid);
                     rp = rp->next) {
                        ;
                }
                if (rp == NULL) {
                        fatal(gettext("Internal error: returned child pid not in running_list"));
                } else {
                        rp->state = (WIFEXITED(status) && WEXITSTATUS(status) == 0) ? build_ok : build_failed;
                }
                nohang = true;
                parallel_process_cnt--;

                if (job_adjust_mode == ADJUST_M2) {
                        if (m2_release_job()) {
                                job_adjust_error();
                        }
                }
        }
}

/*
 *      finish_children(docheck)
 *
 *      Finishes the processing for all targets which were running
 *      and have now completed.
 *
 *      Parameters:
 *              docheck         Completely check the finished target
 *
 *      Static variables used:
 *              running_tail    The tail of the running list
 *
 *      Global variables used:
 *              continue_after_error  -k flag
 *              fatal_in_progress  True if we are finishing up after fatal err
 *              running_list    List of running processes
 */
void
finish_children(Boolean docheck)
{
        int             cmds_length;
        Property        line;
        Property        line2;
        struct stat     out_buf;
        Running         rp;
        Running         *rp_prev;
        Cmd_line        rule;
        Boolean         silent_flag;

        for (rp_prev = &running_list, rp = running_list;
             rp != NULL;
             rp = rp->next) {
bypass_for_loop_inc_4:
                /*
                 * If the state is ok or failed, then this target has
                 * finished building.
                 * In parallel_mode, output the accumulated stdout/stderr.
                 * Read the auto dependency stuff, handle a failed build,
                 * update the target, then finish the doname process for
                 * that target.
                 */
                if (rp->state == build_ok || rp->state == build_failed) {
                        *rp_prev = rp->next;
                        if (rp->next == NULL) {
                                running_tail = rp_prev;
                        }
                        if ((line2 = rp->command) == NULL) {
                                line2 = get_prop(rp->target->prop, line_prop);
                        }


                        /*
                         * Check if there were any job output
                         * from the parallel build.
                         */
                        if (rp->stdout_file != NULL) {
                                if (stat(rp->stdout_file, &out_buf) < 0) {
                                        fatal(gettext("stat of %s failed: %s"),
                                            rp->stdout_file,
                                            errmsg(errno));
                                }

                                if ((line2 != NULL) &&
                                    (out_buf.st_size > 0)) {
                                        cmds_length = 0;
                                        for (rule = line2->body.line.command_used,
                                                 silent_flag = silent;
                                             rule != NULL;
                                             rule = rule->next) {
                                                cmds_length += rule->command_line->hash.length + 1;
                                                silent_flag = BOOLEAN(silent_flag || rule->silent);
                                        }
                                        if (out_buf.st_size != cmds_length || silent_flag ||
                                            output_mode == txt2_mode) {
                                                dump_out_file(rp->stdout_file, false);
                                        }
                                }
                                (void) unlink(rp->stdout_file);
                                retmem_mb(rp->stdout_file);
                                rp->stdout_file = NULL;
                        }

                        if (!out_err_same && (rp->stderr_file != NULL)) {
                                if (stat(rp->stderr_file, &out_buf) < 0) {
                                        fatal(gettext("stat of %s failed: %s"),
                                            rp->stderr_file,
                                            errmsg(errno));
                                }
                                if ((line2 != NULL) &&
                                    (out_buf.st_size > 0)) {
                                        dump_out_file(rp->stderr_file, true);
                                }
                                (void) unlink(rp->stderr_file);
                                retmem_mb(rp->stderr_file);
                                rp->stderr_file = NULL;
                        }

                        check_state(rp->temp_file);
                        if (rp->temp_file != NULL) {
                                free_name(rp->temp_file);
                        }
                        rp->temp_file = NULL;
                        if (rp->state == build_failed) {
                                line = get_prop(rp->target->prop, line_prop);
                                if (line != NULL) {
                                        line->body.line.command_used = NULL;
                                }
                                if (continue_after_error ||
                                    fatal_in_progress ||
                                    !docheck) {
                                        warning(gettext("Command failed for target `%s'"),
                                                rp->command ? line2->body.line.target->string_mb : rp->target->string_mb);
                                        build_failed_seen = true;
                                } else {
                                        /*
                                         * XXX??? - DMake needs to exit(),
                                         * but shouldn't call fatal().
                                         */
#ifdef PRINT_EXIT_STATUS
                                        warning("I'm in finish_children. rp->state == build_failed.");
#endif

                                        fatal(gettext("Command failed for target `%s'"),
                                                rp->command ? line2->body.line.target->string_mb : rp->target->string_mb);
                                }
                        }
                        if (!docheck) {
                                delete_running_struct(rp);
                                rp = *rp_prev;
                                if (rp == NULL) {
                                        break;
                                } else {
                                        goto bypass_for_loop_inc_4;
                                }
                        }
                        update_target(get_prop(rp->target->prop, line_prop),
                                      rp->state);
                        finish_doname(rp);
                        delete_running_struct(rp);
                        rp = *rp_prev;
                        if (rp == NULL) {
                                break;
                        } else {
                                goto bypass_for_loop_inc_4;
                        }
                } else {
                        rp_prev = &rp->next;
                }
        }
}

/*
 *      dump_out_file(filename, err)
 *
 *      Write the contents of the file to stdout, then unlink the file.
 *
 *      Parameters:
 *              filename        Name of temp file containing output
 *
 *      Global variables used:
 */
static void
dump_out_file(char *filename, Boolean err)
{
        int             chars_read;
        char            copybuf[BUFSIZ];
        int             fd;
        int             out_fd = (err ? 2 : 1);

        if ((fd = open(filename, O_RDONLY)) < 0) {
                fatal(gettext("open failed for output file %s: %s"),
                      filename,
                      errmsg(errno));
        }
        if (!silent && output_mode != txt2_mode) {
                (void) fprintf(err ? stderr : stdout,
                               err ?
                                gettext("%s --> Job errors\n") :
                                gettext("%s --> Job output\n"),
                               local_host);
                (void) fflush(err ? stderr : stdout);
        }
        for (chars_read = read(fd, copybuf, BUFSIZ);
             chars_read > 0;
             chars_read = read(fd, copybuf, BUFSIZ)) {
                /*
                 * Read buffers from the source file until end or error.
                 */
                if (write(out_fd, copybuf, chars_read) < 0) {
                        fatal(gettext("write failed for output file %s: %s"),
                              filename,
                              errmsg(errno));
                }
        }
        (void) close(fd);
        (void) unlink(filename);
}

/*
 *      finish_doname(rp)
 *
 *      Completes the processing for a target which was left running.
 *
 *      Parameters:
 *              rp              Running list entry for target
 *
 *      Global variables used:
 *              debug_level     Debug flag
 *              recursion_level Indentation for debug output
 */
static void
finish_doname(Running rp)
{
        int             auto_count = rp->auto_count;
        Name            *automatics = rp->automatics;
        Doname          result = rp->state;
        Name            target = rp->target;
        Name            true_target = rp->true_target;
        Property        *conditionals;

        recursion_level = rp->recursion_level;
        if (result == build_ok) {
                if (true_target == NULL) {
                        (void) printf("Target = %s\n", target->string_mb);
                        (void) printf(" State = %d\n", result);
                        fatal("Internal error: NULL true_target in finish_doname");
                }
                /* If all went OK, set a nice timestamp */
                if (true_target->stat.time == file_doesnt_exist) {
                        true_target->stat.time = file_max_time;
                }
        }
        target->state = result;
        if (target->is_member) {
                Property member;

                /* Propagate the timestamp from the member file to the member */
                if ((target->stat.time != file_max_time) &&
                    ((member = get_prop(target->prop, member_prop)) != NULL) &&
                    (exists(member->body.member.member) > file_doesnt_exist)) {
                        target->stat.time =
/*
                          exists(member->body.member.member);
 */
                          member->body.member.member->stat.time;
                }
        }
        /*
         * Check if we found any new auto dependencies when we
         * built the target.
         */
        if ((result == build_ok) && check_auto_dependencies(target,
                                                            auto_count,
                                                            automatics)) {
                if (debug_level > 0) {
                        (void) printf(gettext("%*sTarget `%s' acquired new dependencies from build, checking all dependencies\n"),
                                      recursion_level,
                                      "",
                                      true_target->string_mb);
                }
                target->rechecking_target = true;
                target->state = build_running;

                /* [tolik, Tue Mar 25 1997]
                 * Fix for bug 4038824:
                 *       command line options set by conditional macros get dropped
                 * rp->conditional_cnt and rp->conditional_targets must be copied
                 * to new 'rp' during add_pending(). Set_conditionals() stores
                 * rp->conditional_targets to the global variable 'conditional_targets'
                 * Add_pending() will use this variable to set up 'rp'.
                 */
                conditionals = set_conditionals(rp->conditional_cnt, rp->conditional_targets);
                add_pending(target,
                            recursion_level,
                            rp->do_get,
                            rp->implicit,
                            true);
                reset_conditionals(rp->conditional_cnt, rp->conditional_targets, conditionals);
        }
}

/*
 *      new_running_struct()
 *
 *      Constructor for Running struct. Creates a structure and initializes
 *      its fields.
 *
 */
static Running new_running_struct()
{
        Running         rp;

        rp = ALLOC(Running);
        rp->target = NULL;
        rp->true_target = NULL;
        rp->command = NULL;
        rp->sprodep_value = NULL;
        rp->sprodep_env = NULL;
        rp->auto_count = 0;
        rp->automatics = NULL;
        rp->pid = -1;
        rp->job_msg_id = -1;
        rp->stdout_file = NULL;
        rp->stderr_file = NULL;
        rp->temp_file = NULL;
        rp->next = NULL;
        return rp;
}

/*
 *      add_running(target, true_target, command, recursion_level, auto_count,
 *                                      automatics, do_get, implicit)
 *
 *      Adds a record on the running list for this target, which
 *      was just spawned and is running.
 *
 *      Parameters:
 *              target          Target being built
 *              true_target     True target for target
 *              command         Running command.
 *              recursion_level Debug indentation level
 *              auto_count      Count of automatic dependencies
 *              automatics      List of automatic dependencies
 *              do_get          Sccs get flag
 *              implicit        Implicit flag
 *
 *      Static variables used:
 *              running_tail    Tail of running list
 *              process_running PID of process
 *
 *      Global variables used:
 *              current_line    Current line for target
 *              current_target  Current target being built
 *              stderr_file     Temporary file for stdout
 *              stdout_file     Temporary file for stdout
 *              temp_file_name  Temporary file for auto dependencies
 */
void
add_running(Name target, Name true_target, Property command, int recursion_level, int auto_count, Name *automatics, Boolean do_get, Boolean implicit)
{
        Running         rp;
        Name            *p;

        rp = new_running_struct();
        rp->state = build_running;
        rp->target = target;
        rp->true_target = true_target;
        rp->command = command;
        rp->recursion_level = recursion_level;
        rp->do_get = do_get;
        rp->implicit = implicit;
        rp->auto_count = auto_count;
        if (auto_count > 0) {
                rp->automatics = (Name *) getmem(auto_count * sizeof (Name));
                for (p = rp->automatics; auto_count > 0; auto_count--) {
                        *p++ = *automatics++;
                }
        } else {
                rp->automatics = NULL;
        }
        {
                rp->pid = process_running;
                process_running = -1;
                childPid = -1;
        }
        rp->job_msg_id = job_msg_id;
        rp->stdout_file = stdout_file;
        rp->stderr_file = stderr_file;
        rp->temp_file = temp_file_name;
        rp->redo = false;
        rp->next = NULL;
        store_conditionals(rp);
        stdout_file = NULL;
        stderr_file = NULL;
        temp_file_name = NULL;
        current_target = NULL;
        current_line = NULL;
        *running_tail = rp;
        running_tail = &rp->next;
}

/*
 *      add_pending(target, recursion_level, do_get, implicit, redo)
 *
 *      Adds a record on the running list for a pending target
 *      (waiting for its dependents to finish running).
 *
 *      Parameters:
 *              target          Target being built
 *              recursion_level Debug indentation level
 *              do_get          Sccs get flag
 *              implicit        Implicit flag
 *              redo            True if this target is being redone
 *
 *      Static variables used:
 *              running_tail    Tail of running list
 */
void
add_pending(Name target, int recursion_level, Boolean do_get, Boolean implicit, Boolean redo)
{
        Running         rp;
        rp = new_running_struct();
        rp->state = build_pending;
        rp->target = target;
        rp->recursion_level = recursion_level;
        rp->do_get = do_get;
        rp->implicit = implicit;
        rp->redo = redo;
        store_conditionals(rp);
        *running_tail = rp;
        running_tail = &rp->next;
}

/*
 *      add_serial(target, recursion_level, do_get, implicit)
 *
 *      Adds a record on the running list for a target which must be
 *      executed in serial after others have finished.
 *
 *      Parameters:
 *              target          Target being built
 *              recursion_level Debug indentation level
 *              do_get          Sccs get flag
 *              implicit        Implicit flag
 *
 *      Static variables used:
 *              running_tail    Tail of running list
 */
void
add_serial(Name target, int recursion_level, Boolean do_get, Boolean implicit)
{
        Running         rp;

        rp = new_running_struct();
        rp->target = target;
        rp->recursion_level = recursion_level;
        rp->do_get = do_get;
        rp->implicit = implicit;
        rp->state = build_serial;
        rp->redo = false;
        store_conditionals(rp);
        *running_tail = rp;
        running_tail = &rp->next;
}

/*
 *      add_subtree(target, recursion_level, do_get, implicit)
 *
 *      Adds a record on the running list for a target which must be
 *      executed in isolation after others have finished.
 *
 *      Parameters:
 *              target          Target being built
 *              recursion_level Debug indentation level
 *              do_get          Sccs get flag
 *              implicit        Implicit flag
 *
 *      Static variables used:
 *              running_tail    Tail of running list
 */
void
add_subtree(Name target, int recursion_level, Boolean do_get, Boolean implicit)
{
        Running         rp;

        rp = new_running_struct();
        rp->target = target;
        rp->recursion_level = recursion_level;
        rp->do_get = do_get;
        rp->implicit = implicit;
        rp->state = build_subtree;
        rp->redo = false;
        store_conditionals(rp);
        *running_tail = rp;
        running_tail = &rp->next;
}

/*
 *      store_conditionals(rp)
 *
 *      Creates an array of the currently active targets with conditional
 *      macros (found in the chain conditional_targets) and puts that
 *      array in the Running struct.
 *
 *      Parameters:
 *              rp              Running struct for storing chain
 *
 *      Global variables used:
 *              conditional_targets  Chain of current dynamic conditionals
 */
static void
store_conditionals(Running rp)
{
        int             cnt;
        Chain           cond_name;

        if (conditional_targets == NULL) {
                rp->conditional_cnt = 0;
                rp->conditional_targets = NULL;
                return;
        }
        cnt = 0;
        for (cond_name = conditional_targets;
             cond_name != NULL;
             cond_name = cond_name->next) {
                cnt++;
        }
        rp->conditional_cnt = cnt;
        rp->conditional_targets = (Name *) getmem(cnt * sizeof(Name));
        for (cond_name = conditional_targets;
             cond_name != NULL;
             cond_name = cond_name->next) {
                rp->conditional_targets[--cnt] = cond_name->name;
        }
}

/*
 *      parallel_ok(target, line_prop_must_exists)
 *
 *      Returns true if the target can be run in parallel
 *
 *      Return value:
 *                              True if can run in parallel
 *
 *      Parameters:
 *              target          Target being tested
 *
 *      Global variables used:
 *              all_parallel    True if all targets default to parallel
 *              only_parallel   True if no targets default to parallel
 */
Boolean
parallel_ok(Name target, Boolean line_prop_must_exists)
{
        Boolean         assign;
        Boolean         make_refd;
        Property        line;
        Cmd_line        rule;

        assign = make_refd = false;
        if (((line = get_prop(target->prop, line_prop)) == NULL) &&
            line_prop_must_exists) {
                return false;
        }
        if (line != NULL) {
                for (rule = line->body.line.command_used;
                     rule != NULL;
                     rule = rule->next) {
                        if (rule->assign) {
                                assign = true;
                        } else if (rule->make_refd) {
                                make_refd = true;
                        }
                }
        }
        if (assign) {
                return false;
        } else if (target->parallel) {
                return true;
        } else if (target->no_parallel) {
                return false;
        } else if (all_parallel) {
                return true;
        } else if (only_parallel) {
                return false;
        } else if (make_refd) {
                return false;
        } else {
                return true;
        }
}

/*
 *      is_running(target)
 *
 *      Returns true if the target is running.
 *
 *      Return value:
 *                              True if target is running
 *
 *      Parameters:
 *              target          Target to check
 *
 *      Global variables used:
 *              running_list    List of running processes
 */
Boolean
is_running(Name target)
{
        Running         rp;

        if (target->state != build_running) {
                return false;
        }
        for (rp = running_list;
             rp != NULL && target != rp->target;
             rp = rp->next);
        if (rp == NULL) {
                return false;
        } else {
                return (rp->state == build_running) ? true : false;
        }
}

/*
 * This function replaces the makesh binary.
 */


static pid_t
run_rule_commands(char *host, char **commands)
{
        Boolean         always_exec;
        Name            command;
        Boolean         ignore;
        int             length;
        Doname          result;
        Boolean         silent_flag;
        wchar_t         *tmp_wcs_buffer;

        childPid = fork();
        switch (childPid) {
        case -1:        /* Error */
                fatal(gettext("Could not fork child process for dmake job: %s"),
                      errmsg(errno));
                break;
        case 0:         /* Child */
                /* To control the processed targets list is not the child's business */
                running_list = NULL;
                if(out_err_same) {
                        redirect_io(stdout_file, (char*)NULL);
                } else {
                        redirect_io(stdout_file, stderr_file);
                }
                for (commands = commands;
                     (*commands != (char *)NULL);
                     commands++) {
                        silent_flag = silent;
                        ignore = false;
                        always_exec = false;
                        while ((**commands == (int) at_char) ||
                               (**commands == (int) hyphen_char) ||
                               (**commands == (int) plus_char)) {
                                if (**commands == (int) at_char) {
                                        silent_flag = true;
                                }
                                if (**commands == (int) hyphen_char) {
                                        ignore = true;
                                }
                                if (**commands == (int) plus_char) {
                                        always_exec = true;
                                }
                                (*commands)++;
                        }
                        if ((length = strlen(*commands)) >= MAXPATHLEN) {
                                tmp_wcs_buffer = ALLOC_WC(length + 1);
                                (void) mbstowcs(tmp_wcs_buffer, *commands, length + 1);
                                command = GETNAME(tmp_wcs_buffer, FIND_LENGTH);
                                retmem(tmp_wcs_buffer);
                        } else {
                                MBSTOWCS(wcs_buffer, *commands);
                                command = GETNAME(wcs_buffer, FIND_LENGTH);
                        }
                        if ((command->hash.length > 0) &&
                            !silent_flag) {
                                (void) printf("%s\n", command->string_mb);
                        }
                        result = dosys(command,
                                       ignore,
                                       false,
                                       false, /* bugs #4085164 & #4990057 */
                                       /* BOOLEAN(silent_flag && ignore), */
                                       always_exec,
                                       (Name) NULL);
                        if (result == build_failed) {
                                if (silent_flag) {
                                        (void) printf(gettext("The following command caused the error:\n%s\n"), command->string_mb);
                                }
                                if (!ignore) {
                                        _exit(1);
                                }
                        }
                }
                _exit(0);
                break;
        default:
                break;
        }
        return childPid;
}

static void
maybe_reread_make_state(void)
{
        /* Copying dosys()... */
        if (report_dependencies_level == 0) {
                make_state->stat.time = file_no_time;
                (void) exists(make_state);
                if (make_state_before == make_state->stat.time) {
                        return;
                }
                makefile_type = reading_statefile;
                if (read_trace_level > 1) {
                        trace_reader = true;
                }
                temp_file_number++;
                (void) read_simple_file(make_state,
                                        false,
                                        false,
                                        false,
                                        false,
                                        false,
                                        true);
                trace_reader = false;
        }
}


static void
delete_running_struct(Running rp)
{
        if ((rp->conditional_cnt > 0) &&
            (rp->conditional_targets != NULL)) {
                retmem_mb((char *) rp->conditional_targets);
        }
/**/
        if ((rp->auto_count > 0) &&
            (rp->automatics != NULL)) {
                retmem_mb((char *) rp->automatics);
        }
/**/
        if(rp->sprodep_value) {
                free_name(rp->sprodep_value);
        }
        if(rp->sprodep_env) {
                retmem_mb(rp->sprodep_env);
        }
        retmem_mb((char *) rp);

}