/*
 *	Do the actual making for make
 */

#include <stdio.h>
#ifdef unix
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/errno.h>
#endif
#ifdef eon
#include <sys/stat.h>
#include <sys/err.h>
#endif
#ifdef os9
#include <time.h>
#include <os9.h>
#include <modes.h>
#include <direct.h>
#include <errno.h>
#endif
#ifdef amiga
#include <ctype.h>
#include <errno.h>
#include <libraries/dosextens.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <exec/ports.h>
#include <functions.h>
#endif
#include "h.h"



/*
 *	Exec a shell that returns exit status correctly (/bin/esh).
 *	The standard EON shell returns the process number of the last
 *	async command, used by the debugger (ugg).
 *	[exec on eon is like a fork+exec on unix]
 */
int
dosh(string, shell)
    char           *string;
    char           *shell;
{
    int             number;

#ifdef amiga
    char           *av[3], *s, *p;
    int             i;

    p = s = malloc((unsigned) strlen(string) + 1);
    strcpy(p, string);			/* make a copy of the string */
    i = 0;
    av[0] = gettok(&p);			/* get first argument */
    av[1] = p;				/* get rest of command line */
    av[2] = NULL;
    if (fexecv(av[0], av) == -1)
	fatal("couldn't execute command '%s', error return %02x\n",
	      av[0], errno);
    number = wait();
    free(s);
    return number;
#endif

#ifdef unix
    return system(string);
#endif
#ifdef eon
    return ((number = execl(shell, shell, "-c", string, 0)) == -1) ?
	-1 :			/* couldn't start the shell */
	wait(number);		/* return its exit status */
#endif
#ifdef os9
    int             status, pid;

    strcat(string, "\n");
    if ((number = os9fork(shell, strlen(string), string, 0, 0, 0)) == -1)
	return -1;		/* Couldn't start a shell */
    do {
	if ((pid = wait(&status)) == -1)
	    return -1;		/* child already died!?!? */
    } while (pid != number);

    return status;
#endif
}


/*
 *	Do commands to make a target
 */
void
docmds1(np, lp)
    struct name    *np;
    struct line    *lp;
{
    bool            ssilent;
    bool            signore;
    int             estat;
    register char  *q;
    register char  *p;
    char           *shell;
    register struct cmd *cp;
#ifdef amiga
    long SetSignal();
#endif


    if (*(shell = getmacro("SHELL")) == '\0')
#ifdef eon
	shell = ":bin/esh";
#endif
#ifdef unix
    shell = "/bin/sh";
#endif
#ifdef os9
    shell = "shell";
#endif
#ifdef amiga
    shell = NULL;
#endif

    for (cp = lp->l_cmd; cp; cp = cp->c_next) {
	strcpy(str1, cp->c_cmd);
	expand(str1);
	q = str1;
	ssilent = silent;
	signore = ignore;
	while ((*q == '@') || (*q == '-')) {
	    if (*q == '@')	/* Specific silent  */
		ssilent = TRUE;
	    else		/* Specific ignore  */
		signore = TRUE;
	    q++;		/* Not part of the command  */
	}

	if (!domake)
	    ssilent = 0;

	if (!ssilent)
	    fputs("    ", stdout);

	for (p = q; *p; p++) {
	    if (*p == '\n' && p[1] != '\0') {
		*p = ' ';
		if (!ssilent)
		    fputs("\\\n", stdout);
	    } else if (!ssilent)
		putchar(*p);
	}
	if (!ssilent)
	    putchar('\n');

	if (domake) {		/* Get the shell to execute it  */
	    if ((estat = dosh(q, shell)) != 0) {
		if (estat == -1)
		    fatal("Couldn't execute %s", shell);
		else {
		    printf("%s: Error code %d", myname, estat);
		    if (signore)
			fputs(" (Ignored)\n", stdout);
		    else {
			putchar('\n');
			if (!(np->n_flag & N_PREC))
			    if (unlink(np->n_name) == 0)
				printf("%s: '%s' removed.\n", myname, np->n_name);
			exit(estat);
		    }
		}
	    }
#ifdef amiga
	    if ((SetSignal(0L, SIGBREAKF_CTRL_D) & SIGBREAKF_CTRL_D) != 0) {
		fputs("make: abort due to ^D\n", stdout);
		exit(1);
	    }
#endif	    
	}
    }
}


docmds(np)
    struct name    *np;
{
    register struct line *lp;


    for (lp = np->n_line; lp; lp = lp->l_next)
	docmds1(np, lp);
}


#ifdef os9
/*
 *	Some stuffing around to get the modified time of a file
 *	in an os9 file system
 */
getmdate(fd, tbp)
    struct sgtbuf  *tbp;
{
    struct registers regs;
    static struct fildes fdbuf;


    regs.rg_a = fd;
    regs.rg_b = SS_FD;
    regs.rg_x = &fdbuf;
    regs.rg_y = sizeof(fdbuf);

    if (_os9(I_GETSTT, &regs) == -1) {
	errno = regs.rg_b & 0xff;
	return -1;
    }
    if (tbp) {
	_strass(tbp, fdbuf.fd_date, sizeof(fdbuf.fd_date));
	tbp->t_second = 0;	/* Files are only acurate to mins */
    }
    return 0;
}


/*
 *	Kludge routine to return an aproximation of how many
 *	seconds since 1980.  Dates will be in order, but will not
 *	be lineer
 */
time_t
cnvtime(tbp)
    struct sgtbuf  *tbp;
{
    long            acc;


    acc = tbp->t_year - 80;	/* Baseyear is 1980 */
    acc = acc * 12 + tbp->t_month;
    acc = acc * 31 + tbp->t_day;
    acc = acc * 24 + tbp->t_hour;
    acc = acc * 60 + tbp->t_minute;
    acc = acc * 60 + tbp->t_second;

    return acc;
}


/*
 *	Get the current time in the internal format
 */
time(tp)
    time_t         *tp;
{
    struct sgtbuf   tbuf;


    if (getime(&tbuf) < 0)
	return -1;

    if (tp)
	*tp = cnvtime(&tbuf);

    return 0;
}
#endif


/*
 *	Get the modification time of a file.  If the first
 *	doesn't exist, it's modtime is set to 0.
 */
void
modtime(np)
    struct name    *np;
{
#ifdef unix
    struct stat     info;
    int             fd;


    if (stat(np->n_name, &info) < 0) {
	if (errno != ENOENT)
	    fatal("Can't open %s; error %d", np->n_name, errno);

	np->n_time = 0L;
    } else
	np->n_time = info.st_mtime;
#endif
#ifdef eon
    struct stat     info;
    int             fd;


    if ((fd = open(np->n_name, 0)) < 0) {
	if (errno != ER_NOTF)
	    fatal("Can't open %s; error %02x", np->n_name, errno);

	np->n_time = 0L;
    } else if (getstat(fd, &info) < 0)
	fatal("Can't getstat %s; error %02x", np->n_name, errno);
    else
	np->n_time = info.st_mod;

    close(fd);
#endif
#ifdef os9
    struct sgtbuf   info;
    int             fd;


    if ((fd = open(np->n_name, 0)) < 0) {
	if (errno != E_PNNF)
	    fatal("Can't open %s; error %02x", np->n_name, errno);

	np->n_time = 0L;
    } else if (getmdate(fd, &info) < 0)
	fatal("Can't getstat %s; error %02x", np->n_name, errno);
    else
	np->n_time = cnvtime(&info);

    close(fd);
#endif
#ifdef amiga
    struct FileInfoBlock *fib;
    struct Lock *myLock;
    long ioErr;

    fib = (struct FileInfoBlock *) malloc((unsigned) sizeof(struct FileInfoBlock));
    if ((myLock = Lock(np->n_name, ACCESS_READ)) == NULL) {
	if ((ioErr = IoErr()) != ERROR_OBJECT_NOT_FOUND)
	    fatal("Can't Lock '%s'; error %3ld", np->n_name, ioErr);
	np->n_time = 0L;
    } else if (!Examine(myLock, fib)) {
	UnLock(myLock);
	fatal("Can't Examine '%s'; error %3ld", np->n_name, IoErr());
    } else {
 	np->n_time = fib->fib_Date.ds_Tick/TICKS_PER_SECOND +
		 60*fib->fib_Date.ds_Minute + 86400*fib->fib_Date.ds_Days;
    	UnLock(myLock);
    }
    free((char *) fib);
#endif

}


/*
 *	Update the mod time of a file to now.
 */
void
touch(np)
    struct name    *np;
{
    char            c;
    int             fd;


    if (!domake || !silent)
	printf("    touch(%s)\n", np->n_name);

    if (domake) {
#ifdef unix
	long            a[2];

	a[0] = a[1] = time(0);
	if (utime(np->n_name, &a[0]) < 0)
	    printf("%s: '%s' not touched - non-existant\n",
		   myname, np->n_name);
#endif
#ifdef eon
	if ((fd = open(np->n_name, 0)) < 0)
	    printf("%s: '%s' not touched - non-existant\n",
		   myname, np->n_name);
	else {
	    uread(fd, &c, 1, 0);
	    uwrite(fd, &c, 1);
	}
	close(fd);
#endif
#ifdef os9
	/*
	 * Strange that something almost as totally useless as this is easy
	 * to do in os9! 
	 */
	if ((fd = open(np->n_name, S_IWRITE)) < 0)
	    printf("%s: '%s' not touched - non-existant\n",
		   myname, np->n_name);
	close(fd);
#endif
#ifdef amiga
	struct MsgPort *task;
	ULONG dateStamp[3];
	struct Lock *lock, *plock;
	UBYTE *pointer;

	if(!(pointer= (UBYTE *)AllocMem(64L,MEMF_PUBLIC)))
		fatal("Can't get 64 bytes for pointer");
	if(!(task=(struct MsgPort *)DeviceProc(np->n_name))) {
		printf("%s: can't get MsgPort for '%s'\n", myname, np->n_name);
		FreeMem((void *) pointer, 64L);
		return;
	}
	if(!(lock = Lock(np->n_name,SHARED_LOCK))) {
		printf("%s: '%s' not touched - non-existant\n",
			myname, np->n_name);
		FreeMem((void *) pointer, 64L);
		return;
	}
	plock = ParentDir(lock);
	UnLock(lock);

	strcpy((pointer + 1),np->n_name);
	*pointer = strlen(np->n_name);
 
	dos_packet(task, ACTION_SET_DATE, NULL, plock, (ULONG) &pointer[0] >> 2,
		(ULONG) DateStamp(dateStamp), 0L, 0L, 0L);

	UnLock(plock);
	FreeMem((void *) pointer, 64L);
#endif
    }
}

/*
 * Recursive routine to make a target. 
 */
int
make(np, level)
    struct name    *np;
    int             level;
{
    register struct depend *dp;
    register struct line *lp;
    register struct depend *qdp;
    time_t          dtime = 1, time();
    bool            didsomething = 0;


    if (np->n_flag & N_DONE)
	return 0;

    if (!np->n_time)
	modtime(np);		/* Gets modtime of this file  */

    if (rules) {
	for (lp = np->n_line; lp; lp = lp->l_next)
	    if (lp->l_cmd)
		break;
	if (!lp)
	    dyndep(np);
    }
    if (!(np->n_flag & N_TARG) && np->n_time == 0L)
	fatal("Don't know how to make %s", np->n_name);

    for (qdp = (struct depend *) 0, lp = np->n_line; lp; lp = lp->l_next) {
	for (dp = lp->l_dep; dp; dp = dp->d_next) {
	    make(dp->d_name, level + 1);
	    if (np->n_time < dp->d_name->n_time)
		qdp = newdep(dp->d_name, qdp);
	    dtime = max(dtime, dp->d_name->n_time);
	}
	if (!quest && (np->n_flag & N_DOUBLE) && (np->n_time < dtime)) {
	    make1(np, lp, qdp);	/* free()'s qdp */
	    dtime = 1;
	    qdp = (struct depend *) 0;
	    didsomething++;
	}
    }

    np->n_flag |= N_DONE;

    if (quest) {
	long            t;

	t = np->n_time;
	time(&np->n_time);
	return t < dtime;
    } else if (np->n_time < dtime && !(np->n_flag & N_DOUBLE)) {
	make1(np, (struct line *) 0, qdp);	/* free()'s qdp */
	time(&np->n_time);
    } else if (level == 0 && !didsomething)
	printf("%s: '%s' is up to date\n", myname, np->n_name);
    return 0;
}


make1(np, lp, qdp)
    register struct depend *qdp;
    struct line    *lp;
    struct name    *np;
{
    register struct depend *dp;


    if (dotouch)
	touch(np);
    else {
	strcpy(str1, "");
	for (dp = qdp; dp; dp = qdp) {
	    if (strlen(str1))
		strcat(str1, " ");
	    strcat(str1, dp->d_name->n_name);
	    qdp = dp->d_next;
	    free(dp);
	}
	setmacro("?", str1);
	setmacro("@", np->n_name);
	if (lp)			/* lp set if doing a :: rule */
	    docmds1(np, lp);
	else
	    docmds(np);
    }
}
#ifdef amiga
/*
 * Replace the Aztec-provided time function with one which returns something
 * easy to find and compare, namely the number of seconds since the Amiga's
 * reference date.  This is the same thing returned by modtime() above.
 */
time_t
time(v)
    time_t *v;
{
    long t[3];

    DateStamp(t);
    t[0] = t[2]/TICKS_PER_SECOND + 60*t[1] + 86400*t[0];
    if (v)
	*v = t[0];
    return t[0];
}
#endif
