/*************************************************************************
 *
 *  m a k e :   r u l e s . c
 *
 *  Control of the implicit suffix rules
 *========================================================================
 * Edition history
 *
 *  #    Date                         Comments                       By
 * --- -------- ---------------------------------------------------- ---
 *   1    ??                                                         ??
 *   2 01.07.89 $<,$* bugs fixed, impl. r. ending in expl. r. added  RAL
 *   3 23.08.89 suffix as macro, testname intr., algorithem to find
 *              source dep. made more intelligent (see Readme3)      RAL
 *   4 30.08.89 indention changed                                    PSH,RAL
 *   5 03.09.89 fixed LZ eliminated                                  RAL
 *   6 07.09.89 rules of type '.c', .DEFAULT added, dep. search impr.RAL
 * ------------ Version 2.0 released ------------------------------- RAL
 * 2.1 22.02.91 table-driven implicit rules, SOFTRULES for TOS       TRH
 * 2.2 11.12.91 OOPS! implicit rules should be read BEFORE environment
 *              macros are set, so reinstate make_rules().           TRH
 *************************************************************************/

#include "h.h"


/*
 *	Dynamic dependency.  This routine applies the suffis rules
 *	to try and find a source and a set of rules for a missing
 *	target.  If found, np is made into a target with the implicit
 *	source name, and rules.  Returns TRUE if np was made into
 *	a target.
 */
bool dyndep(np,pbasename,pinputname)
struct name  *np;
char        **pbasename;		/*  Name without suffix  */
char        **pinputname;
{
  register char *p;
  register char *q;
  register char *suff;				/*  Old suffix  */
  struct name   *op = (struct name *)0,*optmp;	/*  New dependent  */
  struct name   *sp;				/*  Suffix  */
  struct line   *lp,*nlp;
  struct depend *dp,*ndp;
  struct cmd    *cmdp;
  char          *newsuff;
  bool           depexists = FALSE;


  p = str1;
  q = np->n_name;
  suff = suffix(q);
  while (*q && (q < suff || !suff)) *p++ = *q++;
  *p = '\0';
  if ((*pbasename = malloc(strlen(str1)+1)) == (char *)0 )
     nomem("basename");
  strcpy(*pbasename,str1);
  if ( !suff) suff = p - str1 + *pbasename;  /* set suffix to nullstring */

  if (!((sp = newname(".SUFFIXES"))->n_flag & N_TARG))  return FALSE;

  /* search all .SUFFIXES lines */
  for (lp = sp->n_line; lp; lp = lp->l_next)
     /* try all suffixes */
     for (dp = lp->l_dep; dp; dp = dp->d_next) {
        /* compose implicit rule name (.c.o)...*/
        newsuff = dp->d_name->n_name;
        while (strlen(suff)+strlen(newsuff)+1 >= str1s.len) strrealloc(&str1s);
        p = str1;
        q = newsuff;
        while (*p++ = *q++) ;
        p--;
        q = suff;
        while (*p++ = *q++) ;
        /* look if the rule exists */
        sp = newname(str1);
        if (sp->n_flag & N_TARG) {
           /* compose resulting dependency name */
           while (strlen(*pbasename) + strlen(newsuff)+1 >= str1s.len)
              strrealloc(&str1s);
           q = *pbasename;
           p = str1;
           while (*p++ = *q++) ;
           p--;
           q = newsuff;
           while (*p++ = *q++) ;
           /* test if dependency file or an explicit rule exists */
           if ((optmp= testname(str1)) != (struct name *)0) {
              /* store first possible dependency as default */
              if ( op == (struct name *)0) {
                 op = optmp;
                 cmdp = sp->n_line->l_cmd;
              }
              /* check if testname is an explicit dependency */
              for ( nlp=np->n_line; nlp; nlp=nlp->l_next) {
                 for( ndp=nlp->l_dep; ndp; ndp=ndp->d_next) {
                    if ( strcmp( ndp->d_name->n_name, str1) == 0) {
                       op = optmp;
                       cmdp = sp->n_line->l_cmd;
                       ndp = (struct depend *) 0;
                       goto found2;
                    }
                    depexists = TRUE;
                 }
              }
              /* if no explicit dependencies : accept testname */
              if (!depexists)  goto found;
           }
        }
     }

  if ( op == (struct name *)0) {
     if( np->n_flag & N_TARG) {     /* DEFAULT handling */
        if (!((sp = newname(".DEFAULT"))->n_flag & N_TARG))  return FALSE;
        if (!(sp->n_line)) return FALSE;
        cmdp = sp->n_line->l_cmd;
        for ( nlp=np->n_line; nlp; nlp=nlp->l_next) {
           if ( ndp=nlp->l_dep) {
              op = ndp->d_name;
              ndp = (struct depend *)0;
              goto found2;
           }
        }
        newline(np, (struct depend *)0, cmdp, 0);
        *pinputname = (char *)0;
        *pbasename  = (char *)0;
        return TRUE;
     }
  else return FALSE;
  }

found:
  ndp = newdep(op, (struct depend *)0);
found2:
  newline(np, ndp, cmdp, 0);
  *pinputname = op->n_name;
  return TRUE;
}

/*
 *	Make the default rules
 */
void makerules()
{
#ifndef RULETABLE
  struct cmd    *cp;
  struct name   *np;
  struct depend *dp;


#ifdef eon
  setmacro("BDSCC", "asm");
  /*	setmacro("BDSCFLAGS", "");	*/
  cp = newcmd("$(BDSCC) $(BDSCFLAGS) -n $<", (struct cmd *)0);
  np = newname(".c.o");
  newline(np, (struct depend *)0, cp, 0);

  setmacro("CC", "c");
  setmacro("CFLAGS", "-O");
  cp = newcmd("$(CC) $(CFLAGS) -c $<", (struct cmd *)0);
  np = newname(".c.obj");
  newline(np, (struct depend *)0, cp, 0);

  setmacro("M80", "asm -n");
  /*	setmacro("M80FLAGS", "");	*/
  cp = newcmd("$(M80) $(M80FLAGS) $<", (struct cmd *)0);
  np = newname(".mac.o");
  newline(np, (struct depend *)0, cp, 0);

  setmacro("AS", "zas");
  /*	setmacro("ASFLAGS", "");	*/
  cp = newcmd("$(ZAS) $(ASFLAGS) -o $@ $<", (struct cmd *)0);
  np = newname(".as.obj");
  newline(np, (struct depend *)0, cp, 0);

  np = newname(".as");
  dp = newdep(np, (struct depend *)0);
  np = newname(".obj");
  dp = newdep(np, dp);
  np = newname(".c");
  dp = newdep(np, dp);
  np = newname(".o");
  dp = newdep(np, dp);
  np = newname(".mac");
  dp = newdep(np, dp);
  np = newname(".SUFFIXES");
  newline(np, dp, (struct cmd *)0, 0);
#endif

#ifdef tos
#define unix
#endif

/*
 *	Some of the UNIX implicit rules
 */

#ifdef unix

  setmacro("CC", "cc");
  setmacro("CFLAGS", "-O");
#ifdef MINIXPC
  cp = newcmd("$(CC) -S $(CFLAGS) $<", (struct cmd *)0);
  np = newname(".c.s");
#else
  cp = newcmd("$(CC) -c $(CFLAGS) $<", (struct cmd *)0);
  np = newname(".c.o");
#endif MINIXPC
  newline(np, (struct depend *)0, cp, 0);

#ifdef MINIXPC
  cp = newcmd("$(CC) $(CFLAGS) -i -o $@ $<", (struct cmd *)0);
#else
  cp = newcmd("$(CC) $(CFLAGS) -o $@ $<", (struct cmd *)0);
#endif MINIXPC
  np = newname(".c");
  newline(np, (struct depend *)0, cp, 0);

  setmacro("AS", "as");
  cp = newcmd("$(AS) -o $@ $<", (struct cmd *)0);
  np = newname(".s.o");
  newline(np, (struct depend *)0, cp, 0);

  setmacro("YACC", "yacc");
  /*setmacro("YFLAGS", "");	*/
  cp = newcmd("$(YACC) $(YFLAGS) $<", (struct cmd *)0);
  cp = newcmd("mv y.tab.c $@", cp);
  np = newname(".y.c");
  newline(np, (struct depend *)0, cp, 0);

  cp = newcmd("$(YACC) $(YFLAGS) $<", (struct cmd *)0);
#ifdef MINIXPC
  cp = newcmd("$(CC) $(CFLAGS) -S y.tab.c", cp);
  cp = newcmd("mv y.tab.s $@", cp);
  np = newname(".y.s");
#else
  cp = newcmd("$(CC) $(CFLAGS) -c y.tab.c", cp);
  cp = newcmd("mv y.tab.o $@", cp);
  np = newname(".y.o");
#endif MINIXPC
  cp = newcmd("rm y.tab.c", cp);
  newline(np, (struct depend *)0, cp, 0);

  setmacro("FLEX", "flex");
  cp = newcmd("$(FLEX) $(FLEX_FLAGS) $<", (struct cmd *)0);
  cp = newcmd("mv lex.yy.c $@", cp);
  np = newname(".l.c");
  newline(np, (struct depend *)0, cp, 0);

  cp = newcmd("$(FLEX) $(FLEX_FLAGS) $<", (struct cmd *)0);
#ifdef MINIXPC
  cp = newcmd("$(CC) $(CFLAGS) -S lex.yy.s", cp);
  cp = newcmd("mv lex.yy.s $@", cp);
  np = newname(".l.s");
#else
  cp = newcmd("$(CC) $(CFLAGS) -c lex.yy.c", cp);
  cp = newcmd("mv lex.yy.o $@", cp);
  np = newname(".l.o");
#endif MINIXPC
  cp = newcmd("rm lex.yy.c", cp);
  newline(np, (struct depend *)0, cp, 0);

  np = newname(".o");
  dp = newdep(np, (struct depend *)0);
  np = newname(".s");
  dp = newdep(np, dp);
  np = newname(".c");
  dp = newdep(np, dp);
  np = newname(".y");
  dp = newdep(np, dp);
  np = newname(".l");
  dp = newdep(np, dp);
  np = newname(".SUFFIXES");
  newline(np, dp, (struct cmd *)0, 0);

#endif unix


#ifdef os9
/*
 *	Fairlight use an enhanced version of the C sub-system.
 *	They have a specialised macro pre-processor.
 */
  setmacro("CC", "cc");
  setmacro("CFLAGS", "-z");
  cp = newcmd("$(CC) $(CFLAGS) -r $<", (struct cmd *)0);

  np = newname(".c.r");
  newline(np, (struct depend *)0, cp, 0);
  np = newname(".ca.r");
  newline(np, (struct depend *)0, cp, 0);
  np = newname(".a.r");
  newline(np, (struct depend *)0, cp, 0);
  np = newname(".o.r");
  newline(np, (struct depend *)0, cp, 0);
  np = newname(".mc.r");
  newline(np, (struct depend *)0, cp, 0);
  np = newname(".mca.r");
  newline(np, (struct depend *)0, cp, 0);
  np = newname(".ma.r");
  newline(np, (struct depend *)0, cp, 0);
  np = newname(".mo.r");
  newline(np, (struct depend *)0, cp, 0);

  np = newname(".r");
  dp = newdep(np, (struct depend *)0);
  np = newname(".mc");
  dp = newdep(np, dp);
  np = newname(".mca");
  dp = newdep(np, dp);
  np = newname(".c");
  dp = newdep(np, dp);
  np = newname(".ca");
  dp = newdep(np, dp);
  np = newname(".ma");
  dp = newdep(np, dp);
  np = newname(".mo");
  dp = newdep(np, dp);
  np = newname(".o");
  dp = newdep(np, dp);
  np = newname(".a");
  dp = newdep(np, dp);
  np = newname(".SUFFIXES");
  newline(np, dp, (struct cmd *)0, 0);
#endif /* os9 */

#else /* RULETABLE */
  input(stdout); /* cheat! reads builtin rule-base only,
		    and fails on first read attempt on stdout. */
  /* {{The royal way would be to read from /dev/null, but unfortunately
       that is also system-dependent...}} */
#endif /* RULETABLE */
}

/* TRH table-driven implicit rule base is SO much easier to maintain... */

#ifdef RULETABLE
#ifndef SOFTRULES

char *builtin[] = {

#ifdef eon

".SUFFIXES=.as .obj .c .o .mac",

"BDSCC=asm",
/* "BDSCFLAGS=", */
".c.o:\n\
 $(BDSCC) $(BDSCFLAGS) -n $<",

"CC=c",
"CFLAGS=-O",
".c.obj:\n\
 $(CC) $(CFLAGS) -c $<",

"M80=asm -n",
/* "M80FLAGS=", */
".mac.o:\n\
 $(M80) $(M80FLAGS) $<",

"AS=zas",
/* "ASFLAGS=", */
".as.obj:\n\
 $(ZAS) $(ASFLAGS) -o $@ $<",

#endif /* eon */


#if __TURBOC__ && __MSDOS__
	/* [TRH 10-Apr-92] added C++ rules */
#   undef MSDOS
#   undef unix

".SUFFIXES: .exe .o .obj .asm .c .cpp",
"AS=tasm",
"CC=bcc",
"RC=rc",
"LD=tlink",
"RM=rm",
"CP=cp",
"MV=mv",
"AFLAGS=",
"CFLAGS=",
"LDFLAGS=",
"RCFLAGS=",
"LOADLIBES=",

".c.obj .c.o .cpp.obj .cpp.o:\n\
	$(CC) $(CFLAGS) -c -o$@ $<",

".asm.obj:\n\
	$(AS) $(AFLAGS) $<",

".c.exe .cpp.exe .obj.exe .o.exe:\n\
	$(CC) $(CFLAGS) $(LDFLAGS) -e$@ $< $(LOADLIBES)",

#endif /* __TURBOC__ && __MSDOS__*/


#ifdef MSDOS
#   undef unix	

".SUFFIXES: .exe .o .obj .asm .c .cpp",
"AS=masm",
"CC=cl",
"RC=rc",
"LD=link",
"RM=rm",
"CP=cp",
"MV=mv",
"AFLAGS=",
"CFLAGS=-AS",
"LDFLAGS=",
"RCFLAGS=",
"LOADLIBES=",

".c.obj .c.o .cpp.obj .cpp.o:\n\
	$(CC) $(CFLAGS) -c -Fo$@ $<",

".asm.obj:\n\
	$(AS) $(AFLAGS) $<;",

".c.exe .cpp.exe .obj.exe .o.exe:\n\
	$(CC) $(CFLAGS) $(LDFLAGS) $< $(LOADLIBES)",

#endif /* MSDOS */


#ifdef tos
#   define unix
#endif


#ifdef unix

".SUFFIXES: .o .s .c .y .l",
#ifdef BISON
"YACC=bison -y",
"YFLAGS=",
#else
"YACC=yacc",
"YFLAGS=",
#endif
#ifdef FLEX
"LEX=flex",
"LFLAGS=",
#else
"LEX=lex",
"LFLAGS=",
#endif
"CC=cc",
"AS=as",
"CFLAGS=",
"LOADLIBES=",

#ifdef MINIXPC

"LDFLAGS=-i",

".c.s:\n\
	$(CC) $(CFLAGS) -S $<",

".y.s:\n\
	$(YACC) $(YFLAGS) $<\n\
	$(CC) $(CFLAGS) -S y.tab.c\n\
	mv y.tab.s $@\n\
	@-rm y.tab.c",

".l.s:\n\
	$(LEX) $(LFLAGS) $<\n\
	$(CC) $(CFLAGS) -S lex.yy.c\n\
	mv lex.yy.s $@\n\
	@-rm lex.yy.c",

#else /* !MINIXPC */

"LDFLAGS=",

".c.o:\n\
	$(CC) $(CFLAGS) -c $<",

".s.o:\n\
	$(AS) -o $@ $<",

".y.o:\n\
	$(YACC) $(YFLAGS) $<\n\
	$(CC) $(CFLAGS) -c y.tab.c\n\
	mv y.tab.o $@\n\
	@-rm y.tab.c",

".l.o:\n\
	$(LEX) $(LFLAGS) $<\n\
	$(CC) $(CFLAGS) -c lex.yy.c\n\
	mv lex.yy.o $@\n\
	@-rm lex.yy.c",

#endif /* MINIXPC */

".y.c:\n\
	$(YACC) $(YFLAGS) $<\n\
	mv y.tab.c $@",

".l.c:\n\
	$(LEX) $(LFLAGS) $<\n\
	mv lex.yy.c $@",

".s .c .o:\n\
	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LOADLIBES)",

".y:\n\
	$(YACC) $(YFLAGS) $<\n\
	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ y.tab.c $(LOADLIBES) -ly\n\
	@-rm y.tab.c",

".l:\n\
	$(LEX) $(LFLAGS) $<\n\
	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ lex.yy.c $(LOADLIBES) -ll\n\
	@-rm lex.yy.c",
#endif /* unix */


#ifdef os9
/*
 *    Fairlight use an enhanced version of the C sub-system.
 *    They have a specialised macro pre-processor.
 */
"CC=cc",
"CFLAGS=-z",

".c.r .ca.r .a.r .o.r .mc.r .mca.r .ma.r .mo.r:\n\
	$(CC) $(CFLAGS) -r $<",

".SUFFIXES: .r .mc .mca .c .ca .ma .mo .o .a"
#endif /* os9 */

NULL		/* terminated with a NULL pointer */
};

#else /* SOFTRULES */

/*	guarantee minimum size for rule base buffer */

#   if (SOFTRULES - 1024 < 0)
#	define SOFTRULES 1024
#   endif

char rule_buf[SOFTRULES] = 
"junk:\n\
	@$(ECHO) 'run \"make -f <rule-file> -c\" to install default rules'";

char *builtin[] = {
"CP=cp",
"RM=rm",
"ECHO=echo",
rule_buf,
NULL
};

/* load a new set of default rules (from the make file)
 */
void load_rules(fd)
    register FILE *fd;
{
    register int	c;
    register char *	p = rule_buf;
    register int	size_left = sizeof(rule_buf);

    while ((c = getc(fd)) != EOF) {
	if (--size_left == 0) {
	    fatal("%s (max %d bytes)", "No space for default rules",
		  sizeof(rule_buf));
	}
	*p++ = c;
    }
    *p = '\0';
} /* load_rules */


/* patch default rule base back to executable 
 * this is *very* system- and compiler dependent.
 * and is currently only supported from the TOS Lattice/Turbo compiler.
 */
#ifdef TRHTOS
#   define SZ_BASEPAGE		0x100
#   define SZ_EXEC_HEADER	0x1C
#else
"SOFTRULES not supported"
/* this causes an error (not all compilers support #error directive yet) */
#endif

#ifdef __TURBOC__
#   define _pbase	_BasPag
#endif
extern void *_pbase;

/*
 * [TRH] There IS a way to dig the name of the program out of the system.
 * I do not know how `official' it is, but it works all TOS ROM versions
 * up to and including 1.4. (I guess Atari will send its death squads
 * after me because of this:-)  The dirty work is done by my extended 
 * startup module, which extracts the executable's name and puts a pointer
 * to it in the variable _pname.
 * (If you do not use that startup mudule, we just make an educated guess
 *  about the program's name.)
 */
char *_pname;

void
patch_rules()
{
    register long	base = (long) _pbase + SZ_BASEPAGE - SZ_EXEC_HEADER;
    register FILE	*fd, *tty;
    int			answer;

    if (_pname == NULL)
	_pname = "make.ttp";

    /* ask confirmation */
    if ((tty = fopen("con:", "r+")) == NULL)
	fatal("cannot open tty", (char *)0, 0);

    fprintf(tty, "Patch default rules in \"%s\", are you sure? ", _pname);
    answer = getc(tty);
    fclose(tty);

    if (strchr("yYjJ", answer)) {
	if ((fd = fopen(_pname, "rb+")) &&
	    fseek(fd, (long) rule_buf - base, 0) == 0 &&
	    fwrite(rule_buf, sizeof(rule_buf), 1, fd) == 1 &&
	    fclose (fd) == 0) {
		fatal("Default rules replaced", (char *)0, 0);
	} else {
	    fatal("Cannot replace default rules", (char *)0, 0);
	}
    }
} /* patch_rules */

#endif /* SOFTRULES */

/*
 * Get the next line from the implicit rule base.
 * return FALSE if the rule base has been read completely.
 */
bool rule_gets(buf, size)
char		*buf;
register int	size;            
{
  register char	*s, *d;
  static char	**rule_p;	/* where we were left in rule base */
  static char	*rule_cp;	/* where we were left in rule line */
				/* (can contain embedded newlines) */

  if (!rule_p) {		/* once-only initialisation */
	rule_p = builtin;
	rule_cp = *rule_p++;
  }
  if ((s = rule_cp) == (char *)0)
	return FALSE;

  d = buf;

  /* skip until end of buffer, or newline, or end-of-string reached.
   * size is decremented first to leave room for terminating null-byte.
   * PRE: size > 1
   */
  while ((--size > 0) && ((*d = *s++) != '\0') && (*d++ != '\n')) ;

  if (*--s == '\0') {		/* end of string reached */
	*d++ = '\n';		/* implicit newline */
	rule_cp = *rule_p++;	/* next string from array */
  } else {
	rule_cp = ++s;		/* remember where we left off */
  }
  *d = '\0';
  return TRUE;
}

#endif /* RULETABLE */
