/* 
   Copyright 1994-2003 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.  

   You may contact the author at:

   mailto::camille@bluegrass.net

   or by snail mail at:

   David Lindauer
   850 Washburn Ave Apt 99
   Louisville, KY 40222
*/
#include <stdio.h>
#include <process.h>
#include <string.h>
#include "utype.h"
#include "cmdline.h"
#include "umem.h"
#include "register.h"
#include "macros.h"
#include "maker.h"
#include "input.h"
#include "spawner.h"
#include "interp.h"
#include "phash.h"

extern BOOL prm_ignorestatus;
extern BOOL prm_keep;
extern BOOL prm_nospawn;
extern BOOL prm_suppressdisplay;
extern HASHREC **hashtable;
extern int makerv;

int tempnum = 0;
short xandand[] = { 0x26, 0x26, 0 };
short xspace[] = { 0x20, 0 };
LIST *tempfiles = 0;

FILE *CreateFile(void)
{
	FILE *rv;
	char buffer[20];
	sprintf(buffer,"MAKEFILE.%03d",tempnum++);
	rv = fopen(buffer,"w");
	if (rv) {
		short * val = AllocateMemory(strlen(buffer)*2+2);
		LIST *p = AllocateMemory(sizeof(LIST));
		ExpandFile(val,buffer);
		p->data = val;
		p->link = tempfiles;
		tempfiles = p;
	}
	return(rv);
}
void CloseFile(FILE *fh)
{
	fclose(fh);
}
void DeleteFiles(void)
{
	if (!prm_keep) {
		LIST *p = tempfiles;
		char buffer[200];
		while(p) {
			LIST * q = p->link;
			CompressFile(buffer,p->data);
			DeallocateMemory(p->data);
			DeallocateMemory(p);
			unlink(buffer);
			p = q;
		}
	}
}
int SpawnOne(short *command)
{
	int rv=0;
	char buf[130],*a;
	CompressFile(buf+3,command);
	if (!prm_suppressdisplay) {
		printf("\t%s\n",buf+3);
	}
	if (!prm_nospawn) {
		fflush(stdout);
		buf[0] = ' ';
		buf[1] = '/';
		buf[2] = 'C';
		if ((buf[3] == 'c' || buf[3] == 'C') && (buf[4] == 'd' || buf[4] == 'D')) {
			char *p=buf+5;
			while (*p == '\t' || *p == ' ')
				p++;
#ifndef TEST
		  chdir(p);
#endif
		}
		else	{
			a = getenv("COMSPEC");
			rv =spawnlp(P_WAIT,a,a,buf,0);
		}
	}
	if (prm_ignorestatus)
		rv = 0;
	return(rv);
}

void InsertString(short *buffer, short *new , short len)
{
	if (len < 0) {
		short *t,*p;
		pstrcpy(buffer,new);
		p = buffer + pstrlen(buffer);
		t = buffer - len;
		do {
			*p++ = *t++;
		} while (*(t-1));
	}
	else {
		short *t, *p;
		p = buffer + pstrlen(buffer);
		t = p + len;
		do {
			*t-- = *p--;
		} while (p >= buffer);
		while (*new)
			*buffer++ = *new++;
	}
}
void us(char *string, char data)
{
	char buf[10];
	if (string)
		strcpy(buf,string);
	else {
		string[0] = data;
		string[1] = 0;
	}
	fatal("Invalid macro expansion $%s",buf);
}
/* FIXME - if only one flag is set and we have multiple specifications on a line
 * it won't work right
 */
short *ExpandFiles(short **obuf, short **ibuf, int *len, short **files, int flags, short **implicit, BOOL onlyone)
{
  short path[200],*p, *q, *ptr = *files,*oldptr = *files;
	short num = 10000;
	if (!*files)
		return *files;
	if (onlyone)
		num = 1;
	while (num--) {
		while (*ptr == ' ')
			ptr++;
		p = *files;
		q = path;
		while (*ptr && *ptr != ' ')
			*q++ = *ptr++;
		*q = 0;
		if (!(flags & EXT)) {
			p = pstrchr(path,PERIOD);
			if (p && *(p-1) != PERIOD)
				*p = 0;
		}
		if (!(flags & NAME)) {
			p = pstrrchr(path,'\\');
			if (!p)
				*path = 0;
			else
				*(p+1) = 0;
		}
		if (!(flags & PATH)) {
			p = pstrrchr(path,'\\');
			q = path;
			if (p) {
				p++;
				while (*p)
					*q++ = *p++;
				*q = 0;
			}
		}
		if (flags & TARGEXT) {
			p = implicit+1;
			q = path + pstrlen(path);
			while (*p && *p != '.')
				p++;
			while(*p)
				*q++ = *p++;
			*q = 0;
		}
		if (path[0]) {
			short *q = path + pstrlen(path);
			while (**ibuf && **ibuf != ' ' && **ibuf != ',')
				*q++ = *(*ibuf)++;
			*q++ = 0;
		}
		if (path[0] && pstrlen(path) +1 < *len) {
			p = path;
			*(*obuf)++ = 0x20;
			while (*p)
				*(*obuf)++ = *p++;
			*len -= pstrlen(path) +1;
			oldptr = ptr ;
		}
		else
			break;
	}
 	*(*obuf) = 0;
	return oldptr ;
}
void InsertFiles(short *obuf, short *ibuf, short **files,short *itarget, BOOL oneatatime, BOOL implicit)
{
	int len = MAXLEN - pstrlen(ibuf);
	BOOL *found = FALSE;
	short *xptr = 0;
	while (*ibuf && len > 0) {
		while (*ibuf && *ibuf != MACROSTART)
			*obuf++ = *ibuf++;
		if (!*ibuf)
			break;
		else {
			found++;
			ibuf++;
			switch (*ibuf) {
				case STAR:
					if (*(ibuf+1) == STAR) {
						ibuf+=2;
						if (implicit)
							xptr = ExpandFiles(&obuf,&ibuf,&len,files,PATH | NAME | EXT,0,oneatatime);
						else
							us("**",0);
						
					}
					else {
						ibuf++;
						if (implicit)
							xptr = ExpandFiles(&obuf,&ibuf,&len,files,PATH | NAME,0,oneatatime);
						else
							xptr = ExpandFiles(&obuf,&ibuf,&len,files,PATH | NAME,0,oneatatime);
					}
					break;
				case LESS:
					ibuf++;
					if (implicit)
						xptr = ExpandFiles(&obuf,&ibuf,&len,files,PATH | NAME | EXT,0,oneatatime);
					else
						xptr = ExpandFiles(&obuf,&ibuf,&len,files,PATH | NAME | EXT,0,oneatatime);
					break;
				case COLON:
					ibuf++;
					if (implicit)
						xptr = ExpandFiles(&obuf,&ibuf,&len,files,PATH,0,oneatatime);
					else
						xptr = ExpandFiles(&obuf,&ibuf,&len,files,PATH,0,oneatatime);
					break;
				case PERIOD:
					ibuf++;
					if (implicit)
						xptr = ExpandFiles(&obuf,&ibuf,&len,files,NAME | EXT,0,oneatatime);
					else
						xptr = ExpandFiles(&obuf,&ibuf,&len,files,NAME | EXT,0,oneatatime);
					break;
				case AMPERSAND:
					ibuf++;
					if (implicit)
						xptr = ExpandFiles(&obuf,&ibuf,&len,files,NAME,0,oneatatime);
					else
						xptr = ExpandFiles(&obuf,&ibuf,&len,files,NAME,0,oneatatime);
					break;
				case AT:
					ibuf++;
					if (implicit) {
						short target[200];
						short *p,*q;
						pstrcpy(target,*files);
						p = pstrrchr(target,PERIOD);
						if (p && *(p-1) != PERIOD)
							*p = 0;
						p = target + pstrlen(target);
						q = itarget+1;
						while (*q != PERIOD)
							q++;
						pstrcat(target,q);
						xptr = ExpandFiles(&obuf,&ibuf,&len,files,NAME | TARGEXT, target, oneatatime);
					}
					else
						xptr = ExpandFiles(&obuf,&ibuf,&len,files,PATH | NAME | EXT,0,oneatatime);
					break;
				case QUESTION:
					ibuf++;
					if (implicit)
						xptr = ExpandFiles(&obuf,&ibuf,&len,files,PATH | NAME | EXT, 0, oneatatime);
					else
						xptr = ExpandFiles(&obuf,&ibuf,&len,files,NAME, 0,oneatatime);
					break;
				default:
					us(0,*ibuf);
					ibuf++;
			}
		}
	}
	if (!found || xptr && !*xptr)
		xptr = 0;
	*files = xptr;
	*obuf++ = 0;
}
int SpawnTarget(short *target, short *files, BOOL implicit)
{
	REGISTRY **r = LookupPhiHash(hashtable,target);
	short **commands;
  char buf[256];
	short *buffer,*buf2;
	buffer = AllocateMemory(INTERNALBUFSIZE * sizeof(short));
	buf2 = AllocateMemory(INTERNALBUFSIZE * sizeof(short));

	if (!r)
		fatal("Internal error #2");

	commands = (*r)->x.commands;

	while (*commands) {
		short *tfiles = files;
		short *p = *commands++, *temp;
		BOOL oneatatime = FALSE;
		if (p[0] == AT || p[0] == AMPERSAND) {
			oneatatime = TRUE;
			p++;
		}
		pstrcpy(buf2,p);
		if ((temp = pstrstr(buf2,xandand)) != 0) {
			FILE *fh = CreateFile();
			short terminator = *(temp+2);
			short *q;
			*temp = 0;
			while (*commands) {
				pstrcpy(buffer,*commands);
				ExpandString(buffer);
				q = buffer;
				while (*q && *q != terminator)
					fputc(*q++,fh);
				fputc(0x0a,fh);
				commands++;
				if (*q == terminator)
					break;
			}
			CloseFile(fh);

			pstrcat(buf2,tempfiles->data);
			if (*q)
				pstrcat(buf2,q+1);
		}
		pstrcpy(buffer,buf2);
		ExpandString(buffer);
		while (tfiles) {
			InsertFiles(buf2,buffer,&tfiles,target,oneatatime, implicit);
			if ((makerv = SpawnOne(buf2)) != 0) {
				if (!implicit) {
					CompressFile(buf2,target);
					if (!prm_suppressdisplay)
						printf("\n\n\tReturn code %d - Deleting %s\n",makerv,buf2);
					unlink(buf2);
				}
				DeallocateMemory(buffer);
				DeallocateMemory(buf2);
				return(makerv);
			}
		}
	}
	DeallocateMemory(buffer);
	DeallocateMemory(buf2);
	return(makerv);
}
void DoTargets(FILEREC *list)
{
	short *buf;
	FILEREC *p = list;
	buf = AllocateMemory(INTERNALBUFSIZE * sizeof(short));
	printf("\n");
	while (p) {
		FILEREC *r = p;
		buf[0] = 0;
		while (p && !pstrcmp(p->target,r->target)) {
			pstrcat(buf,xspace);
			if (!p->targetable)
				fatal("Internal error #1");
			if (p->implicit)
				pstrcat(buf,p->depends);
			else
				pstrcat(buf,p->target);
			p = p->link;
		}
		SpawnTarget(r->target,buf,r->implicit);
		if (makerv)
			break;
	}
	DeleteFiles();
	while (list) {
		p = list->link;
		ReleaseFile(list);
		list = p;
	}
	DeallocateMemory(buf);
}
