/*****************************************************

	make.c

	1993.7.29	make by Ken

******************************************************/
#include    <stdio.h>
#include    <stdlib.h>
#include    <string.h>
#include    <ctype.h>
#ifndef	LSIC
#include    <sys/types.h>
#endif
#include    <sys/stat.h>
#include    "defs.h"

#define	SBDFALC	8
#define	MKDFALC	16

	char	*version = "%s version 0.12 1995.01.26 Nanno-NET(Ken)\n";

	int	debug_flag = 0;
	int	all_target_flag = FALSE;

	ENVPTR	*env_tab[HASH_SIZE];
	TAGPTR	*tag_tab[HASH_SIZE];

	TAGPTR	*tag_top = NULL;
	TAGPTR	*tag_now = NULL;

	TAGPTR	*tag_list = NULL;

	RULPTR	*rule_top = NULL;
	SUBPTR	*rule_now = NULL;

	CMDPTR	*make_top = NULL;
	CMDPTR	*make_btm = NULL;

static	SUBPTR	*sub_free = NULL;
static	MAKPTR	*mk_free = NULL;

extern	int	optind;
extern	char	*optarg;

static	char	tmp_buff[STRLEN + 2];
static	char	*def_rule[] = {
#ifdef	UNIX
# include    "default.c"
#else
		"CC=cc",
		"CFLAGS=-O",
		".SUFFIXES: . .lzh .zip .zoo .lzs .gz .z .Z",
		".SUFFIXES: .dvi .tex .ref",
		".SUFFIXES: .lst .map .lnk .sym .mak .def",
		".SUFFIXES: .exe .exp .exg .com .rex",
		".SUFFIXES: .lib .obj .dlg .asm",
		".SUFFIXES: .o .a .c .cc .p .f .r .y .l .s .h",
		".c.obj:",
		" $(CC) $(CFLAGS) -c $<",
#endif
		NULL
	};

char	*trim(char *str)
{
    char    *p;

    while ( isspace(*str) )
	str++;
    for ( p = str + strlen(str) ; p > str && isspace(*(p - 1)) ; )
	p--;
    *p = '\0';

    return str;
}
char	*basename(char *buf, char *str)
{
    char *p;
    char *s;

    if ( (p = strrchr(str, '/')) == NULL &&
	 (p = strrchr(str, '\\')) == NULL &&
	 (p = strrchr(str, ':')) == NULL )
	p = str;

#ifdef	MAKE_OLD
    if ( p != str )
	str = p + 1;
#endif

    if ( (p = strrchr(p, '.')) == NULL ) {
	for ( p = str ; *p != '\0' ; p++ )
	    ;
    }

    s = buf;
    while ( *str != '\0' && str < p )
	*(s++) = *(str++);
    *s = '\0';

    return buf;
}
int	hash_calc(char *p)
{
    int     hs = 0;

    while ( *p != '\0' )
	hs = hs * 31 + *(p++);
    return (hs & HASH_MASK);
}
char	*get_env(char *name)
{
    int     hs;
    ENVPTR  *ep;

    hs = hash_calc(name);
    for ( ep = env_tab[hs] ; ep != NULL ; ep = ep->ev_next ) {
	if ( strcmp(name, ep->ev_name) == 0 )
	    return ep->ev_para;
    }
    return "";
}
char	*set_env(char *name, char *para)
{
    int     hs;
    ENVPTR  *ep;

    hs = hash_calc(name);
    for ( ep = env_tab[hs] ; ep != NULL ; ep = ep->ev_next ) {
	if ( strcmp(name, ep->ev_name) == 0 ) {
	    FREE(ep->ev_para);
	    if ( (ep->ev_para = STRDUP(para)) == NULL ) {
		fprintf(stderr, "strdup malloc error '%s'\n", para);
		exit(2);
	    }
	    return ep->ev_para;
	}
    }

    if ( (ep = (ENVPTR *)MALLOC(sizeof(ENVPTR) + strlen(name))) == NULL ) {
	fprintf(stderr, "envptr malloc error\n");
	exit(2);
    }

    if ( (ep->ev_para = STRDUP(para)) == NULL ) {
	fprintf(stderr, "strdup malloc error '%s'\n", para);
	exit(2);
    }

    strcpy(ep->ev_name, name);
    ep->ev_next = env_tab[hs];
    env_tab[hs] = ep;

    return ep->ev_para;
}
char	*get_macro(char *str)
{
    int  n, len;
    char *p, *s;
    char tmp[80 + 2];
    char rep[STRLEN + 2];
    char dmy[STRLEN + 2];

    for ( n = 0 ; *str != '\0' && *str != ':' ; ) {
	if ( n < 80 )
	    tmp[n++] = *(str++);
    }
    tmp[n] = '\0';

    if ( strchr(tmp, '*') != NULL || strchr(tmp, '?') != NULL ) {
	n = 0;
	while ( (p = wild_file(tmp)) != NULL ) {
	    if ( n < STRLEN )
		dmy[n++] = ' ';
	    while ( n < STRLEN && *p != '\0' )
		dmy[n++] = *(p++);
	}
	dmy[n] = '\0';
	p = dmy + 1;
    } else
	p = get_env(tmp);

    if ( *str == ':' ) {
	str++;
	for ( n = 0 ; *str != '\0' && *str != '=' ; ) {
	    if ( n < 80 )
	        tmp[n++] = *(str++);
        }
        tmp[n] = '\0';
	if ( *str == '=' )
	    str++;
	len = strlen(tmp);
	n = 0;
	while ( n < STRLEN && *p != '\0' ) {
	    if ( strncmp(p, tmp, len) == 0 ) {
		p += len;
		for ( s = str ; n < STRLEN && *s != '\0' ; )
		    rep[n++] = *(s++);
	    } else
		rep[n++] = *(p++);
	}
	rep[n] = '\0';
	p = rep;
    }

    return STRDUP(p);
}

char	*macro(char *buf, char *str, int max)
{
    int     n = 0;
    int     ch;
    char    *t;
    char    *b;
    static int nest = 0;

    while ( *str != '\0' && n < max ) {
	switch(*str) {
	case '$':
	    str++;
	    switch(*(str++)) {
	    case '$':
		buf[n++] = '$';
		break;

	    case '*':
		t = get_env("*");		/* target base name */
		goto STRCPY;
	    case '<':				/* depend name */
		t = get_env("<");
		goto STRCPY;
	    case '@':				/* target name */
		t = get_env("@");
		goto STRCPY;
	    case '?':				/* all depend name */
		t = get_env("?");
	    STRCPY:
		while ( *t != '\0' && n < max )
		    buf[n++] = *(t++);
		break;

	    case '(':
		ch = ')';
		goto MACRO;
	    case '{':
		ch = '}';
	    MACRO:
		if ( (b = strchr(str, ch)) != NULL ) {
		    *b = '\0';
		    if ( ++nest >= NEST_MAX ) {
			fprintf(stderr, "deep nesting macro '%s'\n", str);
			exit(2);
		    }
		    t = get_macro(str);
		    *(b++) = ch;
		    str = b;
		    macro(buf + n, t, max - n);
		    while ( buf[n] != '\0' )
			n++;
		    FREE(t);
		    nest--;
		    break;
		}

	    default:
		str -= 2;
		buf[n++] = *(str++);
		break;
	    }
	    break;
	default:
	    buf[n++] = *(str++);
	    break;
	}
    }
    buf[n] = '\0';

    return buf;
}
int	env_load(char *name)
{
    int n;
    char *para;
    char *p;
    char buf[STRLEN + 2];

    if ( (para = strchr(name, '=')) == NULL )
	return ERR;

    if ( para > name && *(para - 1) == '+' ) {
	*(para - 1) = '\0';
	para++;
	name = trim(name);
	p = get_env(name);
	for ( n = 0 ; n < STRLEN && *p != '\0' ; )
	    buf[n++] = *(p++);
	while ( n < STRLEN && *para != '\0' )
	    buf[n++] = *(para++);
	buf[n] = '\0';
	para = trim(buf);
	set_env(name, para);

    } else {
	*(para++) = '\0';
	name = trim(name);
	para = trim(para);
	set_env(name, para);
    }

    if ( debug_flag > 1 )
	fprintf(stderr, "set %s = %s\n", name, para);

    return FALSE;
}
TAGPTR	*set_target(char *file)
{
    int    hs;
    TAGPTR *tp;

    hs = hash_calc(file);
    for ( tp = tag_tab[hs] ; tp != NULL ; tp = tp->tg_next ) {
	if ( strcmp(file, tp->tg_file) == 0 )
	    return tp;
    }

    if ( (tp = (TAGPTR *)MALLOC(sizeof(TAGPTR) + strlen(file))) == NULL ) {
	fprintf(stderr, "tagptr malloc error '%s'\n", file);
	exit(2);
    }

    strcpy(tp->tg_file, file);
    tp->tg_list = NULL;
    tp->tg_link = NULL;
    tp->tg_cmds = NULL;
    tp->tg_status = TAG_NOCHECK;
    tp->tg_time = NOTIME;
    tp->tg_next = tag_tab[hs];
    tag_tab[hs] = tp;

    return tp;
}
TAGPTR	*get_target(char *file)
{
    int    hs;
    TAGPTR *tp;

    hs = hash_calc(file);
    for ( tp = tag_tab[hs] ; tp != NULL ; tp = tp->tg_next ) {
	if ( strcmp(file ,tp->tg_file) == 0 )
	    return tp;
    }
    return NULL;
}

MAKPTR	*makptr_alloc(char *file)
{
    int n;
    MAKPTR  *mp;
    TAGPTR  *tp;

    if ( mk_free == NULL ) {
	if ( (mp = (MAKPTR *)MALLOC(sizeof(MAKPTR) * MKDFALC )) == NULL ) {
	    fprintf(stderr, "makptr malloc error '%s'\n", file);
	    exit(2);
	}
	for ( n = 0 ; n < MKDFALC ; n++ ) {
	    mp->mk_next = mk_free;
	    mk_free = mp;
	    mp++;
	}
    }

    mp = mk_free;
    mk_free = mp->mk_next;

    tp = set_target(file);
    mp->mk_file = tp->tg_file;
    mp->mk_next = NULL;
    return mp;
}
void	makptr_free(MAKPTR *mp)
{
    mp->mk_next = mk_free;
    mk_free = mp;
}
MAKPTR	*makptr_wild(char *file)
{
    MAKPTR  *mp;
    MAKPTR  top;
    char    *p;

    if ( strchr(file, '*') != NULL || strchr(file, '?') != NULL ) {
	top.mk_next = NULL;
	mp = &top;
	while ( (p = wild_file(file)) != NULL ) {
	    mp->mk_next = makptr_alloc(p);
	    mp = mp->mk_next;
	}
	return top.mk_next;

    } else
	return makptr_alloc(file);
}
MAKPTR	*depend_link(char **ptr)
{
    char    *p;
    char    *s;
    MAKPTR  *mp = NULL;

    p = *ptr;
LOOP:
    while ( isspace(*p) )
	p++;

    if ( *p != '\0' ) {
	for ( s = p ; *s != '\0' ; s++ ) {
	    if ( isspace(*s) )
		break;
	}
	if ( *s == '\0' )
	    mp = makptr_wild(p);
	else {
	    *s = '\0';
	    mp = makptr_wild(p);
	    *(s++) = ' ';
	}
	p = s;
	if ( mp == NULL )
	    goto LOOP;
    }

    *ptr = p;
    return mp;
}
void	target_alloc(char *file, char *para)
{
    MAKPTR  *mp, *bp, *ap;
    TAGPTR  *tp;
    char    tmp[STRLEN + 2];

    rule_now = NULL;
    tp = get_target(file);
    tag_now = set_target(file);
    if ( tp == NULL ) {
	tag_now->tg_list = tag_list;
	tag_list = tag_now;
    }
    set_env("@", tag_now->tg_file);
    set_env("*", basename(tmp, tag_now->tg_file));
    set_env("<", "");
    set_env("?", "");
    para = macro(tmp, para, STRLEN);

    if ( tag_top == NULL )
	tag_top = tag_now;

    while ( (mp = depend_link(&para)) != NULL ) {
	do {
	    ap = bp = tag_now->tg_link;
	    while ( bp != NULL ) {
		if ( strcmp(bp->mk_file, mp->mk_file) == 0 ) {
		    if ( ap == bp )
			tag_now->tg_link = bp->mk_next;
		    else
			ap->mk_next = bp->mk_next;
		    makptr_free(bp);
		    break;
		}
		ap = bp;
		bp = bp->mk_next;
	    }

	    if ( debug_flag > 1 )
		fprintf(stderr, "target %s : %s\n", file, mp->mk_file);
	    bp = mp->mk_next;
	    mp->mk_next = tag_now->tg_link;
	    tag_now->tg_link = mp;
	    mp = bp;
	} while ( mp != NULL );
    }

    if ( tag_now->tg_link == NULL ) {
	if ( debug_flag > 1 )
	    fprintf(stderr, "target %s :\n", file);
	tag_now->tg_status = TAG_DEFEXEC;
    }
}
void	target_link(char *file, char *para)
{
    int n;
    char *p, *s;
    char tmp[STRLEN + 2];

    while ( *file != '\0' ) {
	for ( p = file ; *p != '\0' ; p++ ) {
	    if ( isspace(*p) ) {
		*(p++) = '\0';
		break;
	    }
	}

	if ( strchr(file, '*') != NULL || strchr(file, '?') != NULL ) {
	    n = 0;
	    while ( (s = wild_file(file)) != NULL ) {
		if ( n < STRLEN )
		    tmp[n++] = ' ';
		while ( n < STRLEN && *s != '\0' )
		    tmp[n++] = *(s++);
	    }
	    tmp[n] = '\0';
	    target_link(tmp + 1, para);
	} else
	    target_alloc(file, para);

	file = p;
	while ( isspace(*file) )
	    file++;
    }
}

void	suff_init(char *str)
{
    char *p;
    RULPTR *rp;

    while ( *str != '\0' ) {
	while ( isspace(*str) )
	    str++;
	p = str;
	while ( !isspace(*str) && *str != '\0' )
	    str++;
	if ( isspace(*str) )
	    *(str++) = '\0';

	if ( *p == '\0' )
	    continue;
	else if ( strcmp(p, ".") == 0 )
	    p++;

    	for ( rp = rule_top ; rp != NULL ; rp = rp->rl_next ) {
	    if ( strcmp(p, rp->rl_tag) == 0 )
	        break;
        }

        if ( rp == NULL ) {
            if ( (rp = (RULPTR *)MALLOC(sizeof(RULPTR) +
					strlen(p))) == NULL ) {
	        fprintf(stderr, "rulptr malloc error '%s'\n", p);
	        exit(2);
            }
	    strcpy(rp->rl_tag, p);
	    rp->rl_flag = FALSE;
	    rp->rl_link = NULL;
	    rp->rl_next = rule_top;
	    rule_top = rp;

	    if ( debug_flag > 1 )
		fprintf(stderr, "suffix %%%s\n", rp->rl_tag);
        }
    }
}
SUBPTR	*subptr_alloc(char *name)
{
    int n;
    SUBPTR *sp;

    if ( sub_free == NULL ) {
        if ( (sp = (SUBPTR *)MALLOC(sizeof(SUBPTR) * SBDFALC)) == NULL )
	    return NULL;
	for ( n = 0 ; n < SBDFALC ; n++ ) {
	    sp->sb_next = sub_free;
	    sub_free = sp;
	    sp++;
	}
    }

    sp = sub_free;
    sub_free = sp->sb_next;
    return sp;
}
int	suff_check(char *str)		/* .c.o */
{
    int n;
    RULPTR *tp;		/* .o */
    RULPTR *dp;		/* .c */
    SUBPTR *sp;
    SUBPTR *ap;

    for ( dp = rule_top ; dp != NULL ; dp = dp->rl_next ) {
	n = strlen(dp->rl_tag);
	if ( n > 0 && strncmp(str, dp->rl_tag, n) == 0 ) {
	    for ( tp = rule_top ; tp != NULL ; tp = tp->rl_next ) {
		if ( strcmp(str + n, tp->rl_tag) == 0 )
		    goto RULESET;
	    }
	}
    }
    return FALSE;

RULESET:

    for ( ap = sp = tp->rl_link ; sp != NULL ; ) {
	if ( strcmp(dp->rl_tag, sp->sb_dep) == 0 ) {
	    if ( ap != sp ) {
		ap->sb_next = sp->sb_next;
		sp->sb_next = tp->rl_link;
		tp->rl_link = sp;
	    }
	    sp->sb_cmds = NULL;
	    break;
	}
	ap = sp;
	sp = sp->sb_next;
    }

    if ( sp == NULL ) {
        if ( (sp = subptr_alloc(dp->rl_tag)) == NULL ) {
	    fprintf(stderr, "subptr malloc error '%s'\n", dp->rl_tag);
	    exit(2);
        }
        sp->sb_dep = dp->rl_tag;
        sp->sb_cmds = NULL;
        sp->sb_next = tp->rl_link;
        tp->rl_link = sp;
    }

    tag_now = NULL;
    rule_now = sp;

    if ( debug_flag > 1 )
	fprintf(stderr, "rule %%%s : %%%s\n", tp->rl_tag, dp->rl_tag);

    return TRUE;
}
int	make_load(char *file)
{
    char    *para;

    set_env("*", "");
    set_env("@", "");
    set_env("<", "");
    set_env("?", "");
    file = macro(tmp_buff, file, STRLEN);

    if ( (para = strchr(file, ':')) == NULL )
	return ERR;
    else if ( strcmp(file, ".SUFFIXES") == 0 )
	para = file + 9;

    *(para++) = '\0';
    file = trim(file);
    para = trim(para);

    if ( *para == ':' )
	para = trim(++para);

    if ( suff_check(file) )
	return FALSE;
    else if ( strcmp(file, ".SUFFIXES") == 0 )
	suff_init(para);
    else
        target_link(file, para);

    return FALSE;
}
int	cmds_load(CMDPTR *cp)
{
    char *p;

    if ( tag_now != NULL ) {
	cp->cd_next = tag_now->tg_cmds;
	tag_now->tg_cmds = cp;

    } else if ( rule_now != NULL ) {
	cp->cd_next = rule_now->sb_cmds;
	rule_now->sb_cmds = cp;

    } else
	return ERR;

    p = cp->cd_exec;
    while ( isspace(*p) )
	p++;

    if ( p > cp->cd_exec )
	strcpy(cp->cd_exec, p);

    if ( debug_flag > 1 )
	fprintf(stderr, "cmds : %s\n", cp->cd_exec);

    return FALSE;
}
int	make_parse()
{
    int st;
    CMDPTR *cp, *tp;

    suff_init(get_env("SUFFIXES"));

    for ( cp = tp = make_top ; cp != NULL ; cp = tp ) {
	tp = cp->cd_next;
	if ( isspace(cp->cd_exec[0]) )
	    st = cmds_load(cp);
	else {
	    st = make_load(cp->cd_exec);
	    FREE(cp);
	}

	if ( st ) {
	    fprintf(stderr, "make parse error '%s'\n", cp->cd_exec);
	    exit(2);
	}
    }

    tag_now = NULL;
    rule_now = NULL;

    return FALSE;
}
int	make_save(char *str)
{
    CMDPTR *cp;

    if ( (cp = (CMDPTR *)MALLOC(sizeof(CMDPTR) + strlen(str))) == NULL ) {
	fprintf(stderr, "make load malloc error '%s'\n", str);
	exit(2);
    }

    strcpy(cp->cd_exec, str);
    cp->cd_next = NULL;

    if ( make_top == NULL )
	make_top = cp;
    else
	make_btm->cd_next = cp;
    make_btm = cp;

    return FALSE;
}
int	parse_load(char *para)
{
    char    *str;

    str = para;

    while ( isspace(*str) )
	str++;

    if ( *str == '\0' || *str == '\n' || *str == '#' )
	return FALSE;

    while ( isalnum(*str) || *str == '_' || *str == '.' )
	str++;

    while ( isspace(*str) )
	str++;

    if ( *str == '=' )
	return env_load(para);
    else
	return make_save(para);
}
int	file_load(char *file)
{
    int     len = 0;
    int     line = 0;
    FILE    *fp;
    char    *p;

    if ( (fp = fopen(file, "r")) == NULL )
	return ERR;

    if ( debug_flag > 0 )
	fprintf(stderr, "rule file load '%s'\n", file);

    while ( fgets(tmp_buff + len, STRLEN - len, fp) != NULL ) {
	line++;

	if ( (p = strchr(tmp_buff, '\n')) != NULL )
	    *p = '\0';
	if ( (p = strchr(tmp_buff, '#')) != NULL )
	    *p = '\0';

	p = tmp_buff + strlen(tmp_buff) - 1;
	if ( p >= tmp_buff && *p == '\\' ) {
	    len = p - tmp_buff;
	    continue;
	}

	len = 0;
	if ( parse_load(tmp_buff) ) {
	    fprintf(stderr, "parse error '%s' in %d line\n", file, line);
	    fprintf(stderr, "%s\n", tmp_buff);
	    exit(2);
	}
    }

    fclose(fp);
    return FALSE;
}

/**************************************************************/

int	suff_cmp(char *file, char *ptn)
{
    int n;
    char *p;

    if ( *ptn == '\0' ) {
	if ( (p = strrchr(file, '/')) == NULL &&
	     (p = strrchr(file, '\\')) == NULL )
	    p = file;

	if ( strchr(p, '.') == NULL )
	    return strlen(file);
	else
	    return (-1);

    } else if ( (n = strlen(file) - strlen(ptn)) > 0 &&
		strcmp(file + n, ptn) == 0 )
	return n;
    else
	return (-1);
}
int	rule_exec(TAGPTR *tag, char *file)
{
    int    n, i;
    RULPTR *rp;
    SUBPTR *sp;
    TAGPTR *tp;
    MAKPTR *mp;
    struct stat st;
    time_t ti = NOTIME;
    char   tmp[STRLEN + 2];
    static int deps = 0;

    deps++;

    if ( tag != NULL )
	file = tag->tg_file;

    for ( rp = rule_top ; rp != NULL ; rp = rp->rl_next ) {
	if ( rp->rl_flag == FALSE &&
		(n = suff_cmp(file, rp->rl_tag)) > 0 ) {

	    rp->rl_flag = TRUE;
	    strcpy(tmp, file);

	    if ( tag != NULL ) {
	        for ( sp = rp->rl_link ; sp != NULL ; sp = sp->sb_next ) {
	            strcpy(tmp + n, sp->sb_dep);
		    for ( mp = tag->tg_link ; mp != NULL ; mp = mp->mk_next ) {
		        if ( strcmp(tmp, mp->mk_file) == 0 )
			    goto RULEMACH;
		    }
		}
	    }

	    for ( sp = rp->rl_link ; sp != NULL ; sp = sp->sb_next ) {
	        strcpy(tmp + n, sp->sb_dep);

		if ( debug_flag > 1 ) {
		    for ( i = 0 ; i < deps ; i++ )
			fprintf(stderr, " ");
		   fprintf(stderr, "chk rule %s ? %s\n", file, tmp);
		}

		if ( !stat(tmp, &st) ) {
		    ti = st.st_mtime;
		    goto RULEMACH;
		} else if ( (tp = get_target(tmp)) != NULL &&
				rule_exec(tp, NULL) )
		    goto RULEMACH;
		else if ( rule_exec(NULL, tmp) )
		    goto RULEMACH;
	    }
	    rp->rl_flag = FALSE;
	    break;
	}
    }

    deps--;
    return FALSE;

RULEMACH:
    if ( debug_flag > 1 ) {
	for ( n = 0 ; n < deps ; n++ )
	    fprintf(stderr, " ");
        fprintf(stderr, "def rule %s -> %s\n", file, tmp);
    }

    target_alloc(file, tmp);
    tp = get_target(tmp);
    tp->tg_time = ti;
    if ( tag_now->tg_cmds == NULL )
	tag_now->tg_cmds = sp->sb_cmds;
    rp->rl_flag = FALSE;

    deps--;
    return TRUE;
}
void	cmds_exec(CMDPTR *cp)
{
    int     n;
    char    *p;
    int     noecho = FALSE;
    int     noerrret = FALSE;
    int     useshell = FALSE;

    if ( cp == NULL )
	return;

    cmds_exec(cp->cd_next);
    p = macro(tmp_buff, cp->cd_exec, STRLEN);

    if ( debug_flag > 1 )
	fprintf(stderr, "exec %s\n", p);

    for ( ; ; ) {
        if ( *p == '@' ) {
	    p++;
	    noecho = TRUE;
        } else if ( *p == '-' ) {
	    p++;
	    noerrret = TRUE;
        } else if ( *p == '+' ) {
	    p++;
	    useshell = TRUE;
        } else
	    break;
    }

    if ( !noecho )
	fprintf(stderr, "%s\n", p);

    if ( debug_flag <= 1 && (n = SYSTEM(useshell, p)) && !noerrret ) {
	fprintf(stderr, "*** error code %d %s\n", n, p);
	exit(1);
    }
}
int	make_exec(TAGPTR *tp)
{
    int rt = FALSE;
    int n;
    MAKPTR *mp;
    TAGPTR *dp;
    char *p;
    struct stat st;
    static int deps = 0;

    if ( tp == NULL || (tp->tg_status & TAG_UPDATE) )
	return FALSE;

    deps++;
    tp->tg_status |= TAG_UPDATE;

    if ( (tp->tg_status & TAG_DEFEXEC) )
	rt = TRUE;

    if ( tp->tg_cmds == NULL )
	rule_exec(tp, NULL);

    for ( mp = tp->tg_link ; mp != NULL ; mp = mp->mk_next ) {
	dp = get_target(mp->mk_file);
	make_exec(dp);

	if ( tp->tg_time == NOTIME ) {
	    if ( stat(tp->tg_file, &st) )
		tp->tg_time = NOFILE;
	    else
		tp->tg_time = st.st_mtime;
	}

	if ( dp->tg_time == NOTIME ) {
	    if ( stat(dp->tg_file, &st) ) {
		fprintf(stderr, "not found depend file '%s'\n", dp->tg_file);
		exit(1);
	    }
	    dp->tg_time = st.st_mtime;
	}

	if ( debug_flag > 0 ) {
	    for ( n = 0 ; n < deps ; n++ )
		fprintf(stderr, " ");
	    fprintf(stderr, "check %s(%ld) ? %s(%ld) %s\n",
		tp->tg_file, tp->tg_time,
		dp->tg_file, dp->tg_time,
		tp->tg_time < dp->tg_time ? "domake" : "");
	}

	if ( tp->tg_time < dp->tg_time )
	    rt = TRUE;
    }

    if ( rt ) {
	set_env("*", basename(tmp_buff, tp->tg_file));
	set_env("@", tp->tg_file);
	if ( (mp = tp->tg_link) != NULL ) {
	    set_env("<", mp->mk_file);
	    n = 0;
	    for ( ; ; ) {
		p = mp->mk_file;
		while ( *p != '\0' && n < STRLEN )
		    tmp_buff[n++] = *(p++);
		if ( (mp = mp->mk_next) == NULL )
		    break;
		tmp_buff[n++] = ' ';
	    }
	    tmp_buff[n] = '\0';
	    set_env("?", tmp_buff);
	} else {
	    set_env("<", "");
	    set_env("?", "");
	}

        cmds_exec(tp->tg_cmds);
	time(&(tp->tg_time));
    }

    deps--;
    return rt;
}
void	make_list(TAGPTR *tp)
{
    if ( tp != NULL ) {
	make_list(tp->tg_list);
	make_exec(tp);
    }
}

void	free_memory()
{
    int n;
    int a;
    long l = 0L;
    char *tmp[32];

    a = (16 * 1024);
    n = 0;
    for ( ; n < 32 && a > 4 ; ) {
	if ( (tmp[n] = (char *)MALLOC(a)) == NULL )
	    a /= 2;
	else {
	    l += a;
	    n++;
	}
    }
    for ( a = 0 ; a < n ; a++ )
	FREE(tmp[a]);

    fprintf(stderr, "free memory size %ld\n", l);
}

void	usage(char *prog)
{
#ifdef	MAKE_OLD
    fprintf(stderr, "Usage: %s [-dv] [-t <Target>] [Makefile...]\n", prog);
#else
    fprintf(stderr, "Usage: %s [-a] [-dv] [-f <Makefile>] [Target...]\n", prog);
#endif
}
int	main(int ac, char *av[], char *ev[])
{
    int     n;
    int     mx = 1;
    int     mkc = 0;
    char    *mkv[10];
    int     tgc = 0;
    char    *tgv[10];
    char    *p;
    TAGPTR  *tp;

    for ( ; ; ) {
	while ( (n = getopt(ac, av, "advf:t:D:V")) != EOF ) {
	    switch(n) {
	    case 'a':
		all_target_flag = TRUE;
		break;
	    case 'd':
		debug_flag += 2;
		break;
	    case 'v':
		debug_flag += 1;
		break;
	    case 'f':
		if ( mkc >= 10 ) {
		    fprintf(stderr, "makefile overflow '%s'\n", optarg);
		    exit(2);
		}
		mkv[mkc++] = optarg;
		break;
	    case 't':
		if ( tgc >= 10 ) {
		    fprintf(stderr, "target overflow '%s'\n", optarg);
		    exit(2);
		}
		tgv[tgc++] = optarg;
		break;
	    case 'D':
		if ( env_load(strcpy(tmp_buff, optarg)) )
		    set_env(optarg, "");
		break;
	    case 'V':
		fprintf(stderr, version, av[0]);
		return 0;
	    default:
		usage(av[0]);
		exit(2);
	    }
	}
	if ( optind >= ac )
	    break;
	av[mx++] = av[optind++];
    }

    for ( n = 0 ; def_rule[n] != NULL ; n++ )
	parse_load(strcpy(tmp_buff, def_rule[n]));

    set_env("MAKE", av[0]);
    tmp_buff[0] = '\0';
    if ( all_target_flag )
	strcpy(tmp_buff, "-a");
    if ( debug_flag >= 2 )
	strcat(tmp_buff, " -d");
    if ( debug_flag >= 1 )
	strcat(tmp_buff, " -v");
    set_env("MAKEFLAGS", tmp_buff);

    if ( (p = get_env("MAKEFILE")) != NULL && *p != '\0' )
	file_load(p);

    for ( n = 0 ; ev[n] != NULL ; n++ )
	env_load(strcpy(tmp_buff, ev[n]));

#ifdef	MAKE_OLD
    for ( n = 1 ; n < mx ; n++ ) {
	if ( strchr(av[n], '=') == NULL ) {
	    if ( mkc >= 10 ) {
		fprintf(stderr, "makefile overflow '%s'\n", av[n]);
		exit(2);
	    }
	    mkv[mkc++] = av[n];
	}
    }
#endif

    if ( mkc > 0 ) {
	for ( n = 0 ; n < mkc ; n++ ) {
	    if ( file_load(mkv[n]) ) {
		fprintf(stderr, "can't open '%s'\n", mkv[n]);
		exit(1);
	    }
	}
    } else {
	if ( file_load("Makefile") && file_load("makefile") ) {
	    fprintf(stderr, "can't open makefile\n");
	    exit(1);
	}
    }

    for ( n = 1 ; n < mx ; n++ ) {
	if ( env_load(strcpy(tmp_buff, av[n])) ) {
#ifndef	MAKE_OLD
	    if ( tgc >= 10 ) {
		fprintf(stderr, "target overflow '%s'\n", av[n]);
		exit(2);
	    }
	    tgv[tgc++] = av[n];
#endif
	}
    }

    make_parse();

    if ( tgc > 0 ) {
        for ( n = 0 ; n < tgc ; n++ ) {
	    if ( (tp = get_target(tgv[n])) == NULL ) {
	        fprintf(stderr, "No rule to make target `%s'.\n", tgv[n]);
		exit(2);
	    } else if ( !make_exec(tp) )
	        fprintf(stderr, "`%s' is up to date.\n", tp->tg_file);
	}
    } else if ( all_target_flag ) {
	make_list(tag_list);

    } else {
#ifdef	MAKE_OLD
	make_list(tag_list);
#else
	if ( !make_exec(tag_top) )
	    fprintf(stderr, "`%s' is up to date.\n", tag_top->tg_file);
#endif
    }

    if ( debug_flag > 1 )
	free_memory();

    return 0;
}
