
/*
 *  libmake.c
 *
 *  libmake  -n -oobjdir -l library file [-clean]
 *
 *  loads 'files' list and compiles out of date files accordingly, putting
 *  objects in an alternate directory if requested.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <exec/types.h>
#include <libraries/dos.h>

#define EF_OOD	    0x01
#define EF_COMP     0x02

typedef struct {
    char *SrcName;
    char *ObjName;
    long Flags;
} NameNode;

typedef struct DateStamp DateStamp;

char Prefix[64];
char *CtlFile = "files";
char *ObjDir;
char *DestLib;
NameNode *NList;
short	NumFiles;
short	DccExec;
short	DryRun;
short	Verbose;
short	Broke;
short	Clean;
static char Buf[128];

char *MungeFile();
char *GetIndex();
void TimeCompareFiles();
void help();

int
brk()
{
    Broke = 1;
    return(0);
}

main(ac, av)
char *av[];
{
    char *nameBuf;

    onbreak(brk);

    /*
     *	calculate prefix
     */

#ifdef _DCC
    DccExec = 1;
#else
    DccExec = 0;
#endif

    {
	char *ptr = av[0];
	short len = strlen(ptr);

	while (len >= 0 && ptr[len] != '/' && ptr[len] != ':')
	    --len;
	ptr = ptr + len + 1;	/*  file part	*/

	for (len = 0; ptr[len] && ptr[len] != '_'; ++len);
	if (ptr[len] == '_')
	    ++len;
	else
	    len = 0;
	strncpy(Prefix, ptr, len);
	Prefix[len] = 0;
    }
    {
	short i;

	for (i = 1; i < ac; ++i) {
	    char *ptr = av[i];
	    if (*ptr != '-') {
		CtlFile = ptr;
		continue;
	    }
	    ptr += 2;
	    switch(ptr[-1]) {
	    case 'v':
		Verbose = 1;
		break;
	    case 'n':
		DryRun = 1;
		break;
	    case 'o':
		if (*ptr)
		    ObjDir = ptr;
		else
		    ObjDir = av[++i];
		break;
	    case 'l':
		if (*ptr)
		    DestLib = ptr;
		else
		    DestLib = av[++i];
		break;
	    case 'c':
		Clean = 1;
		DestLib = "";
		break;
	    default:
		help();
		exit(1);
	    }
	}
	if (i > ac) {
	    help();
	    exit(1);
	}
    }
    if (ObjDir == NULL) {
	puts("must specify an output directory for objects (-o)");
	exit(1);
    }
    if (DestLib == NULL) {
	puts("must specify output library (-l)");
	exit(1);
    }
    {
	FILE *fi = fopen(CtlFile, "r");
	long siz;

	if (fi == NULL) {
	    perror("fopen");
	    exit(1);
	}
	fseek(fi, 0L, 2);
	siz = ftell(fi);
	if (siz < 0) {
	    perror("fseek");
	    exit(1);
	}
	fseek(fi, 0L, 0);
	nameBuf = malloc(siz + 1);
	if (fread(nameBuf, siz, 1, fi) != 1) {
	    perror("fread");
	    exit(1);
	}
	fclose(fi);
	nameBuf[siz] = 0;
    }
    {
	char *namePtr;
	char *getIdx;
	long files = 0;
	NameNode *nn;

	getIdx = nameBuf;

	for (namePtr = GetIndex(&getIdx); *namePtr; namePtr = GetIndex(&getIdx))
	    ++files;
	NList = malloc(sizeof(NameNode) * files);

	nn = NList;

	getIdx = nameBuf;
	NumFiles = 0;
	for (namePtr = GetIndex(&getIdx); *namePtr; namePtr = GetIndex(&getIdx)) {
	    if (*namePtr == ';')
		continue;
	    nn->Flags = 0;
	    nn->SrcName = namePtr;
	    nn->ObjName = MungeFile(nn->SrcName, ObjDir);
	    ++nn;
	    ++NumFiles;
	}
    }

    if (Clean) {
	NameNode *nn;
	short i;

	for (i = 0, nn = NList; i < NumFiles; ++i, ++nn) {
	    printf("%s\n", nn->ObjName);
	    remove(nn->ObjName);
	}
    } else {
	TimeCompareFiles();
	if (CompileFiles() == 0) {
	    ALibMake();
	}
    }
    return(0);
}

void
help()
{
    fprintf(stderr, "libmake [-n] files -o objdir/ -l library [-clean]\n");
}

void
TimeCompareFiles()
{
    NameNode *nn;
    short i;
    DateStamp d1;
    DateStamp d2;

    for (i = 0, nn = NList; i < NumFiles; ++i, ++nn) {
	if (GetFileTime(nn->SrcName, &d1)) {
	    fprintf(stderr, "unable to find %s\n", nn->SrcName);
	    continue;
	}
	if (GetFileTime(nn->ObjName, &d2) == 0) {
	    if (d1.ds_Days < d2.ds_Days)
		continue;
	    if (d1.ds_Days == d2.ds_Days) {
		if (d1.ds_Minute < d2.ds_Minute)
		    continue;
		if (d1.ds_Minute == d2.ds_Minute) {
		    if (d1.ds_Tick < d2.ds_Tick)
			continue;
		}
	    }
	}
	nn->Flags |= EF_OOD;
    }
}

CompileFiles()
{
    NameNode *nn;
    short i;
    int errs = 0;

    for (i = 0, nn = NList; i < NumFiles; ++i, ++nn) {
	if (nn->Flags & EF_OOD) {
	    short j;
	    for (j = strlen(nn->SrcName) - 1; j >= 0 && nn->SrcName[j] != '.'; --j);
	    ++j;
	    if (j > 0 && (nn->SrcName[j]|0x20) == 'a') {
		if (nn->SrcName[j+1] == '6')
		    errs += AssembleA68K(nn->SrcName, nn->ObjName);
		else
		    errs += Assemble(nn->SrcName, nn->ObjName);
	    } else {
		errs += Compile(nn->SrcName, nn->ObjName);
	    }
	}
    }
    return(errs);
}

Assemble(src, obj)
char *src;
char *obj;
{
    sprintf(Buf, "%sdas -o%s %s", Prefix, obj, src);
    return(run_cmd(Buf, DccExec));
}

AssembleA68K(src, obj)
char *src;
char *obj;
{
    sprintf(Buf, "a68k -o%s %s -q", obj, src);
    return(run_cmd(Buf, 0));
}


Compile(src, obj)
char *src;
char *obj;
{
    char *verb = (Verbose) ? "-v" : "";

    sprintf(Buf, "%sdcc -c %s -o %s %s -S -r", Prefix, src, obj, verb);
    return(run_cmd(Buf, DccExec));
}

ALibMake()
{
    NameNode *nn;
    short i;
    FILE *fo;

    if (DryRun)
	return(0);

    printf("Creating alink 'join' type libray %s\n", DestLib);
    if (fo = fopen(DestLib, "w")) {
	for (i = 0, nn = NList; i < NumFiles; ++i, ++nn) {
	    FILE *fi;
	    if (fi = fopen(nn->ObjName, "r")) {
		int c;
		while ((c = getc(fi)) != EOF)
		    putc(c, fo);
		fclose(fi);
	    } else {
		printf("Couldn't read %s\n", nn->ObjName);
	    }
	}
	fclose(fo);
    } else {
	printf("Unable to create %s\n", DestLib);
    }
    return(0);
}



char *
MungeFile(name, prefix)
char *name;
char *prefix;
{
    char *str;
    char *postfix = ".o";
    short i;

    for (i = strlen(name) - 1; i >= 0 && name[i] != '.'; --i);
    if (i < 0)
	i = strlen(name);
    str = malloc(strlen(prefix) + i + strlen(postfix) + 1);
    strcpy(str, prefix);
    sprintf(str + strlen(str), "%.*s%s", (long)i, name, postfix);
    return(str);
}

char *
GetIndex(ip)
char **ip;
{
    char *ptr = *ip;
    char *rp;

    while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n')
	++ptr;
    if (*ptr == 0)
	return(ptr);
    rp = ptr;
    while (*ptr && *ptr != '\n')
	++ptr;
    *ptr = 0;
    *ip = ptr + 1;
    return(rp);
}

run_cmd(buf, dccExec)
char *buf;
{
    printf("%s\n", buf);
    if (Broke) {
	printf("^C\n");
	exit(1);
    }
    if (DryRun)
	return(0);

#ifdef _DCC
    if (dccExec) {
	short i;
	long r;

	for (i = 0; buf[i] && buf[i] != ' '; ++i);
	if (buf[i] == ' ') {
	    buf[i] = 0;
	    for (++i; buf[i] == ' '; ++i);
	}
	r = exec_dcc(buf, buf + i);
	if (r) {
	    printf("Exit code %d\n", r);
	    exit(1);
	}
	return(r);
    }
#endif

    Execute(buf, NULL, Output());
    return(0);
}

GetFileTime(file, ds)
char *file;
DateStamp *ds;
{
    static struct FileInfoBlock *Fib;
    long rv = -1;
    APTR lock;

    if (Fib == NULL)
	Fib = malloc(sizeof(struct FileInfoBlock));
    if (lock = (APTR)Lock(file, SHARED_LOCK)) {
	if (Examine(lock, Fib)) {
	    *ds = Fib->fib_Date;
	    rv = 0;
	}
	UnLock(lock);
    }
    return(rv);
}

