Evaluate the rule in rules corresponding to the given target.
 If target is NULL, evaluate the first rule instead.
 If pflag is 0, evaluate each dependency in sequence.
 If pflag is 1, then evaluate each dependency in parallel (by creating one
 new process per dependency). In this case, the parent process will wait until
 all child processes have terminated before checking dependency modified times
 to decide whether to execute the actions.

void update_dependency(Rule *dep_rule, Rule *rules, int pflag);

int execute_actions(Action *actions) {
    pid_t pid;
    int status;

    while (actions != NULL) {
        printf("%s\n", args_to_string(actions->args, (char[MAXLINE]){0}, MAXLINE));

        if ((pid = fork()) == 0) {
            execvp(actions->args[0], actions->args);
            perror("execvp");
            exit(1);
        }

        if (wait(&status) < 0) {
            perror("wait");
            exit(1);
        }

        if (WEXITSTATUS(status) != 0) {
            return -1;
        }

        actions = actions->next_act;
    }

    return 0;
}

void run_make(char *target, Rule *rules, int pflag) {
    Rule *rule_to_execute = rules;

    if (target != NULL) {
        while (rule_to_execute != NULL && strcmp(rule_to_execute->target, target) != 0) {
            rule_to_execute = rule_to_execute->next_rule;
        }

        if (rule_to_execute == NULL) {
            fprintf(stderr, "No rule found for target: %s\n", target);
            exit(1);
        }
    }

    update_dependency(rule_to_execute, rules, pflag);
}

void update_dependency(Rule *dep_rule, Rule *rules, int pflag) {
    Dependency *dep = dep_rule->dependencies;
    int need_to_execute = 0;
    struct stat target_stat, dep_stat;

    if (stat(dep_rule->target, &target_stat) < 0) {
        need_to_execute = 1;
    }

    while (dep != NULL) {
        Rule *dependency_rule = dep->rule;
        update_dependency(dependency_rule, rules, pflag);

        if (stat(dependency_rule->target, &dep_stat) < 0) {
            perror("stat");
            exit(1);
        }

        if (!need_to_execute && (dep_stat.st_mtimespec.tv_sec > target_stat.st_mtimespec.tv_sec ||
                                 (dep_stat.st_mtimespec.tv_sec == target_stat.st_mtimespec.tv_sec &&
                                  dep_stat.st_mtimespec.tv_nsec > target_stat.st_mtimespec.tv_nsec))) {
            need_to_execute = 1;
        }

        dep = dep->next_dep;
    }

    if (need_to_execute) {
        if (execute_actions(dep_rule->actions) != 0) {
            fprintf(stderr, "Failed to execute actions for target: %s\n", dep_rule->target);
            exit(1);
        }
    }
}
