
/*
 * MAKEREFS.C
 * (c)1992 J.Harper
 *
 * Creates reference entries from autodoc style documents, C source files and
 * C header files.
 *
 * usage: makerefs [options] reffile srcfiles...
 *
 * options,
 *	-new	don't append to reffile - create it.
 *	-full	fully expand file names in ref entries
 *
 * what it makes refs for,
 *	*.c	function definitions
 *	*.h	structure definitions
 *	*	anything else - looks for aaaa/bbbb type refs (autodocs, etc)
 */

#include <exec/types.h>
#include <exec/libraries.h>
#include <dos/dos.h>
#include <dos/dosasl.h>
#include <clib/dos_protos.h>

#include <ctype.h>
#include <string.h>

#define DOS36_MSG "Need dos.library V36+\n"
#define NAME_LEN 300

extern struct Library *DOSBase;

BOOL NewRefs =	 FALSE; /* -new */
BOOL FullName =	 FALSE; /* -full */

UWORD	main	    (UWORD, STRPTR *);
VOID	makerefs    (BPTR, struct AnchorPath *);
VOID	scanheader  (BPTR, BPTR, STRPTR);
VOID	scandoc	    (BPTR, BPTR, STRPTR);
VOID	scansource  (BPTR, BPTR, STRPTR);
VOID	copyrefname (BPTR, STRPTR);
LONG	index	    (STRPTR, STRPTR);
BOOL	mystrcmp    (STRPTR, STRPTR);
STRPTR	myFGets	    (BPTR, STRPTR, LONG);

UWORD
main(UWORD argc, STRPTR *argv)
{
    if(DOSBase->lib_Version <= 36)
    {
	Write(Output(), DOS36_MSG, sizeof(DOS36_MSG));
	return(RETURN_WARN);
    }
    else
    {
	if(argc == 1)
	{
	    PutStr("usage: makerefs [-new] [-full] reffile srcfiles...\nstandard wildcards are supported.\n");
	    return(RETURN_WARN);
	}
	else
	{
	    BPTR outfh;
	    argc--;
	    argv++;

	    while(**argv == '-')
	    {
		if(!(stricmp(*argv, "-new")))
		{
		    NewRefs = TRUE;
		    argv++;
		    argc--;
		}
		else if(!(stricmp(*argv, "-full")))
		{
		    FullName = TRUE;
		    argv++;
		    argc--;
		}
		else
		{
		    Printf("makerefs: incorrect argument %s\n", (LONG)*argv);
		    return(RETURN_WARN);
		}
	    }
	    if(outfh = Open(*argv++, NewRefs ? MODE_NEWFILE : MODE_READWRITE))
	    {
		argc--;

		Seek(outfh, 0, OFFSET_END);
		while(argc--)
		{
		    __aligned struct AnchorPath myanc;
		    clrmem(&myanc, sizeof(struct AnchorPath));

		    if(CheckSignal(SIGBREAKF_CTRL_C))
			goto broke;
		    if(!(MatchFirst(*argv++, &myanc)))
		    {
			makerefs(outfh, &myanc);
			while(!(MatchNext(&myanc)))
			{
			    if(CheckSignal(SIGBREAKF_CTRL_C))
				goto broke;
			    makerefs(outfh, &myanc);
			}
		    }
		    if(0)
broke:
			PutStr("^C\n");
		    MatchEnd(&myanc);
		}
		Close(outfh);
		return(RETURN_OK);
	    }
	    else
	    {
		Printf("makerefs: can't open %s for output\n", (LONG)argv[-1]);
		return(RETURN_WARN);
	    }
	}
    }
}

VOID
makerefs(BPTR refs, struct AnchorPath *anc)
{
    UBYTE cdname[NAME_LEN];
    if(GetCurrentDirName(cdname, NAME_LEN))
    {
	BPTR olddir = CurrentDir(anc->ap_Current->an_Lock);
	BPTR fh = Open(anc->ap_Info.fib_FileName, MODE_OLDFILE);
	if(fh)
	{
	    UBYTE fullname[NAME_LEN];
	    if(NameFromFH(fh, fullname, NAME_LEN))
	    {
		STRPTR name;
		if(FullName || mystrcmp(cdname, fullname))
		    name = fullname;
		else
		{
		    name = fullname + strlen(cdname);
		    if((*name == '/') || (*name == ':'))
			name++;
		}
		Printf("Scanning %s...\n", (LONG)anc->ap_Info.fib_FileName);
		FPrintf(refs, "\n# References from %s\n", (LONG)anc->ap_Info.fib_FileName);

		if(index(anc->ap_Info.fib_FileName, ".h") != -1)
		    scanheader(refs, fh, name);
		else if(index(anc->ap_Info.fib_FileName, ".c") != -1)
		    scansource(refs, fh, name);
		else
		    scandoc(refs, fh, name);
	    }
	    Close(fh);
	}
	else
	    Printf("makerefs: can't open %s for input\n", (LONG)anc->ap_Info.fib_FileName);
	CurrentDir(olddir);
    }
}

/*
 * Needs serious work.
 * currently just finds the structure name and references it in the file,
 * doesn't bother searching for the end.
 * also assumes all structure definitions will be in col #1.
 * in fact this is really crap.
 */
VOID
scanheader(BPTR dstfh, BPTR srcfh, STRPTR fileName)
{
    UBYTE line[256];
    LONG linenum = 0;
    while(FGets(srcfh, line, 255))
    {
	linenum++;
	if(!mystrcmp("struct", line) && isspace(line[6]))
	{
	    copyrefname(dstfh, line + 7);
	    FPrintf(dstfh, "%s@#%ld@\n", (LONG)fileName, Seek(srcfh, 0, OFFSET_CURRENT) - strlen(line));
	}
	else if(!mystrcmp("typedef", line) && isspace(line[7]) && !mystrcmp("struct", line + 8))
	{
	    LONG slinenum = linenum;
	    BOOL foundend = FALSE;
	    while(!foundend && FGets(srcfh, line, 255))
	    {
		linenum++;
		if(!mystrcmp("}", line))
		{
		    STRPTR nstart = line + 1;
		    while(isspace(*nstart++))
			;
		    nstart--;
		    copyrefname(dstfh, nstart);
		    FPrintf(dstfh, "%s@^%ld@\n", (LONG)fileName, slinenum);
		    foundend = TRUE;
		}
	    }
	}
    }
}

VOID
scandoc(BPTR dstfh, BPTR srcfh, STRPTR fileName)
{
    UBYTE line[256];
    BOOL gotline = FALSE;
    while(gotline || myFGets(srcfh, line, 255))
    {
	LONG i;
	gotline = FALSE;
	if((i = index(line, "/")) != -1)
	{
	    UBYTE name[100];
	    LONG j = ++i;
	    LONG linelen = strlen(line);
	    while((isalnum(line[j])) || (line[j] == '_'))
		j++;
	    line[j++] = 0;
	    if(index(line + j, line) != -1)
	    {
		LONG refstart = Seek(srcfh, 0, OFFSET_CURRENT) - linelen;
		LONG refend = 0;
		Printf("\tMade reference to %s\n", (LONG)line + i);
		FPrintf(dstfh, "@%s@%s@", (LONG)line + i, fileName);
		while((refend < refstart) && myFGets(srcfh, line, 255))
		{
		    if(!refend)
		    {
			if(isspace(line[0]))
			    refend = -1;
		    }
		    else
		    {
			if(!isspace(line[0]))
			    refend = Seek(srcfh, 0, OFFSET_CURRENT) - strlen(line);
		    }
		}
		if(refend < refstart)
		    refend = Seek(srcfh, 0, OFFSET_CURRENT);
		FPrintf(dstfh, "#%ld/#%ld@\n", refstart, refend);
		gotline = TRUE;
	    }
	}
    }
}

VOID
scansource(BPTR dstfh, BPTR srcfh, STRPTR fileName)
{
    UBYTE line1[256];
    UBYTE line2[256];
    STRPTR l1, l2;

    l1 = line1;
    l2 = line2;
    if(FGets(srcfh, l1, 255))
    {
	while(FGets(srcfh, l2, 255))
	{
	    LONG par1;
	    STRPTR temp;
	    if(((par1 = index(l1, "(")) != -1) && (index(l1, ")") > par1) && (*l2 == '{'))
	    {
		STRPTR ref = l1;
		copyrefname(dstfh, l1);
		while(*ref++ != '(')
		    ;
		*ref = 0;
		FPrintf(dstfh, "%s@%s@\n", (LONG)fileName, l1);
	    }
	    temp = l1;
	    l1 = l2;
	    l2 = temp;
	}
    }
}

VOID
copyrefname(BPTR fh, STRPTR name)
{
    UBYTE copy[100];
    WORD len = 0;
    UBYTE c;
    while((isalnum(c = name[len++])) || (c == '_'))
	;
    len--;
    memcpy(copy, name, len);
    copy[len] = 0;
    Printf("\tMade reference to %s\n", (LONG)copy);
    FPrintf(fh, "@%s@", (LONG)copy);
}

/*
 * Returns the offset that pattern occurs in string, or -1 if it doesn't
 */
LONG
index(STRPTR string, STRPTR pattern)
{
    STRPTR current = string;
    while(*current)
    {
	if(!(mystrcmp(pattern, current)))
	    return((LONG)(current - string));
	current++;
    }
    return(-1L);
}

/*
 * strcmp() type function but string2 doesn't have to end when string1 does to
 * match.
 */
BOOL
mystrcmp(STRPTR string1, STRPTR string2)
{
    while(*string1)
    {
	if((*string1++) != (*string2++))
	    return(1);
    }
    return(0);
}

/*
 * FGets() style function but also takes '\f' as the end of a line.
 */
STRPTR
myFGets(BPTR fh, STRPTR dst, LONG max)
{
    LONG i;
    UBYTE c;
    for(i = 0; (i < max) && ((c = (UBYTE)FGetC(fh)) != -1); i++)
    {
	dst[i] = c;
	if((c == '\n') || (c == '\f'))
	{
	    i++;
	    break;
	}
    }
    dst[i] = 0;
    if(!i || (c == -1))
	return(NULL);
    return(dst);
}

