
/*
 * REFS.C
 * (c) 1992 J.Harper
 */

#include "jed.h"
#include "jed_protos.h"

#define REF_FILE ".jrefs"
#define TEMP_REF "t:tref"
#define COPY_SIZE 1024

Prototype   VOID    initpath	(VOID);
Prototype   VOID    killpath	(VOID);
Prototype   VALUE * cmd_addpath (LONG, VALUE *);
Prototype   VALUE * cmd_rempath (LONG, VALUE *);
Prototype   VALUE * cmd_getref	(LONG, VALUE *);
Local	    BOOL    copyref	(STRPTR, LONG, LONG);

Local struct List PathList;

VOID
initpath(VOID)
{
    NewList(&PathList);
}

VOID
killpath(VOID)
{
    struct Node *thispath, *nextpath;
    thispath = PathList.lh_Head;
    while(nextpath = thispath->ln_Succ)
    {
	freestring(thispath->ln_Name);
	FreeVec(thispath);
	thispath = nextpath;
    }
}

/*
 * (addpath {`dir-name'})
 */
VALUE *
cmd_addpath(LONG argc, VALUE *argv)
{
    VALUE *res = &RES;
    BOOL rc = TRUE;
    while(rc && (argc >= 1))
    {
	if(TPLATE1(VTF_STRING))
	{
	    struct Node *newpath = AllocVec(sizeof(struct Node), MEMF_CLEAR);
	    if(newpath)
	    {
		STRPTR savedpath = savestring(ARG1.val_Value.String);
		if(savedpath)
		{
		    newpath->ln_Name = savedpath;
		    AddHead(&PathList, newpath);
		    settitlefmt("added path %s", (LONG)savedpath);
		}
		else
		{
		    FreeVec(newpath);
		    settitle(NoMemMsg);
		    rc = FALSE;
		}
	    }
	    else
	    {
		settitle(NoMemMsg);
		rc = FALSE;
	    }
	}
	else
	    goto abort;
	argc--;
	argv++;
    }
    res->val_Type = VTF_NUMBER;
    res->val_Value.Number = rc;
abort:
    return(res);
}

/*
 * (rempath {`dir-name'})
 */
VALUE *
cmd_rempath(LONG argc, VALUE *argv)
{
    VALUE *res = &RES;
    BOOL rc = TRUE;
    while(rc && (argc >= 1))
    {
	if(TPLATE1(VTF_STRING))
	{
	    struct Node *path = FindName(&PathList, ARG1.val_Value.String);
	    if(path)
	    {
		Remove(path);
		freestring(path->ln_Name);
		FreeVec(path);
		settitlefmt("removed path entry %s", (LONG)ARG1.val_Value.String);
	    }
	    else
	    {
		settitlefmt("error: can't find path entry %s", (LONG)ARG1.val_Value.String);
		rc = FALSE;
	    }
	}
	else
	    goto abort;
	argc--;
	argv++;
    }
    res->val_Type = VTF_NUMBER;
    res->val_Value.Number = rc;
abort:
    return(res);
}

/*
 * (getref `ref-name')
 * (getref) -- gets reference for word under cursor.
 */
VALUE *
cmd_getref(LONG argc, VALUE *argv)
{
    VW *vw = CurrVW;
    TX *tx = vw->vw_Tx;
    UBYTE refname[40];
    LONG rc = FALSE;
    struct Node *path;
    BOOL kg = TRUE;

    if(ARG1.val_Type == VTF_STRING)
	strncpy(refname, ARG1.val_Value.String, 39);
    else
    {
	LINE *line = tx->tx_Lines + vw->vw_CursorPos.pos_Line;
	STRPTR start = line->ln_Line + vw->vw_CursorPos.pos_Col;
	STRPTR end = start;
	UBYTE c;
	while((isalnum(c = *start--)) || (c == '_'))
	{
	    if(start == line->ln_Line - 1)
	    {
		start--;
		break;
	    }
	}
	start += 2;
	while((isalnum(c = *end++)) || (c == '_'))
	{
	    if(!c)
		break;
	}
	end--;
	if(start > end)
	{
	    settitle("error: cursor not on word");
	    setnumres(FALSE);
	    return(&RES);
	}
	memcpy(refname, start, end - start);
	refname[end - start] = 0;
    }
    path = PathList.lh_Head;
    while(kg && (path->ln_Succ))
    {
	BPTR olddir;
	if(olddir = Lock(path->ln_Name, SHARED_LOCK))
	{
	    BPTR fh;
	    olddir = CurrentDir(olddir);
	    if(fh = Open(REF_FILE, MODE_OLDFILE))
	    {
		UBYTE line[256];
		settitlefmt("scanning %s for references", (LONG)path->ln_Name);
		while(kg && FGets(fh, line, 256))
		{
		    if(line[0] == '@')
		    {
			STRPTR thisname = line + 1;
			STRPTR thisfile = line + 1;

			while(*thisfile++ != '@')
			    ;
			thisfile[-1] = 0;

			if(!strcmp(refname, thisname))
			{
			    STRPTR thispos = thisfile;
			    VW *oldvw = CurrVW;
			    kg = FALSE;
			    while(*thispos++ != '@')
				;
			    thispos[-1] = 0;

			    if(thispos[0] == '#')
			    {
				LONG startseek;
				LONG endseek = 0;

				startseek = strtol(thispos + 1, &thispos, 0);
				if(thispos[0] == '/')
				    endseek = strtol(thispos + 2, &thispos, 0);
				if(startseek < endseek)
				{
				    if(copyref(thisfile, startseek, endseek))
				    {
					rc = newfile(TEMP_REF);
					DeleteFile(TEMP_REF);
					if(rc)
					{
					    TX *newtx = CurrVW->vw_Tx;
					    freestring(newtx->tx_TitleName);
					    freestring(newtx->tx_FileName);
					    newtx->tx_FileName = savestring("");
					    newtx->tx_TitleName = savestring(refname);
					    stdtitle();
					}
				    }
				}
				else
				{
				    if(rc = activatefile(thisfile))
				    {
					moveoffset(startseek);
					resyncxy();
				    }
				}
			    }
			    else if(thispos[0] == '^')
			    {
				LONG startline = strtol(thispos + 1, &thispos, 0);
				if(rc = activatefile(thisfile))
				{
				    movelinenum(startline);
				    resyncxy();
				}
			    }
			    else
			    {
				STRPTR end = thispos;
				while(*end++ != '@')
				    ;
				end[-1] = 0;
				if(activatefile(thisfile))
				{
				    rc = findcol0(thispos);
				    resyncxy();
				}
			    }
			    if(rc)
			    {
				VW *newvw = CurrVW;
				CurrVW = oldvw;
				resettitle();
				stdtitle();
				CurrVW = newvw;
			    }
			}
		    }
		}
		Close(fh);
	    }
	    else
		settitlefmt("no .jrefs file in dir %s", (LONG)path->ln_Name);
	    olddir = CurrentDir(olddir);
	    UnLock(olddir);
	}
	if(kg)
	    path = path->ln_Succ;
    }
    if((!rc) && (!path->ln_Succ))
	settitlefmt("can't find reference %s", (LONG)refname);
    setnumres(rc);
    return(&RES);
}

Local BOOL
copyref(STRPTR fileName, LONG start, LONG end)
{
    BOOL rc = FALSE;
    STRPTR copybuff = AllocVec(COPY_SIZE, 0L);
    if(copybuff)
    {
	BPTR srcfh = Open(fileName, MODE_OLDFILE);
	if(srcfh)
	{
	    BPTR dstfh = Open(TEMP_REF, MODE_NEWFILE);
	    if(dstfh)
	    {
		if(Seek(srcfh, start, OFFSET_BEGINNING) != -1)
		{
		    LONG i = start;
		    while((end - i) > COPY_SIZE)
		    {
			Read(srcfh, copybuff, COPY_SIZE);
			Write(dstfh, copybuff, COPY_SIZE);
			i += COPY_SIZE;
		    }
		    Read(srcfh, copybuff, end - i);
		    Write(dstfh, copybuff, end - i);
		    rc = TRUE;
		}
		else
		    settitle("error: ref start offset doesn't exist in file");
		Close(dstfh);
	    }
	    else
		settitle("error: can't open temporary reference file");
	    Close(srcfh);
	}
	else
	    settitle("error: can't open reference file");
	FreeVec(copybuff);
    }
    else
	settitle(NoMemMsg);
    return(rc);
}

