/******************************************************************************

    MODUL
	refs.c

    DESCRIPTION
	Bringup a cross reference editor window.  The file S:dme.refs and
	dme.refs in the current directory are searched for the reference.
	If found, the file specified is searched and the segment specified
	loaded as a new file in a new window.

    NOTES

    BUGS

    TODO

    EXAMPLES

    SEE ALSO

    INDEX

    HISTORY
	25. Nov 1992	ada created

******************************************************************************/

/**************************************
		Includes
**************************************/
#include "defs.h"


/**************************************
	    Globale Variable
**************************************/
Prototype void do_addpath (void);
Prototype void do_rempath (void);
Prototype void do_ctags   (void);
Prototype void do_refs	  (void);
Prototype int searchref   (char *, char *, char **, char **, int *, char **);
Prototype int dirpart	  (char *);

Prototype LIST PBase;

LIST	PBase;			/*  special XDME paths	 */


/**************************************
      Interne Defines & Strukturen
**************************************/
typedef struct _PEN
{
    NODE    Node;
    char  * path;
} PEN;


/**************************************
	    Interne Variable
**************************************/


/**************************************
	   Interne Prototypes
**************************************/



/*
 *  Special XDME paths for REF and CTAGS
 */

#ifndef NO_DO2

void do_addpath (void)
{
    PEN * pen;
    short len = strlen (av[1]);

    for (pen = (PEN *)GetHead(&PBase); pen; pen=(PEN *)GetSucc((struct Node *)pen))
    {
	if (stricmp (av[1], pen->path) == 0)
	    return;
    }

    if (pen = malloc (sizeof (PEN) + len + 2))
    {
	pen->path = (char *) (pen + 1);
	strcpy (pen->path, av[1]);

	switch (pen->path[len - 1])
	{
	    case ':':
	    case '/':
		break;
	    default:
		strcat (pen->path, "/");
	}
    }

    AddTail ((LIST *) & PBase, (NODE *) pen);
    pen->Node.ln_Name = pen->path;
} /* do_addpath */


void do_rempath (void)
{
    PEN * pen;

    for (pen = (PEN *)GetHead(&PBase); pen; pen = (PEN *)GetSucc((struct Node *)pen))
    {
	if (!stricmp (av[1], pen->path))
	{
	    Remove ((NODE *) pen);
	    free (pen);
	}
    }
} /* do_rempath */

#endif


#ifndef NO_DO_CTAGS

/*
 *  Implement ctags
 */

void do_ctags (void)
{
    char    str[64];
    char    path[128];
    char    buf[128];
    char    sbuf[128];
    short   xlen;
    short   slen;
    short   dbaselen;
    BPTR    oldlock = CurrentDir (Ep->dirlock);
    ED	  * ed;

#ifdef PATCH_NULL
    if (av[1] != NULL)
    {
	strcpy(str, av[1]);
	xlen = strlen(str);
    } else
#endif /* PATCH_NULL */
#ifdef PATCH_SCANF
    {
	strcpy(str, current_word());
	xlen = strlen(str);
    }
#else
    {
	short	i,
		j;

	for (i = Ep->column; Current[i] == ' '; ++i);

	for (j = i;; ++j)
	{
	    short   c = Current[j];

	    if (isalnum (c) || (c == '_'))
		continue;
	    break;
	}

	j -= i;

	if (j > 63)
	    j = 63;

	movmem (Current + i, str, j);

	str[j] = 0;
	xlen = j;
    }
#endif /* PATCH_SCANF */

    if (!Ep->iconmode)
	title ("search tags");

    {
	FILE * fi;
	PEN  * pen;
	long   i;
	short  j,
	       len;

	dbaselen = dirpart (Ep->name);
	movmem (Ep->name, path, dbaselen);
	strcpy (path + dbaselen, "tags");

	/*
	 *  Note: pen not used first pass and set to list head, so next
	 *  pass it will be the first element.
	 *
	 *  Note2:  The file path depends on several factors.  (1) tags in
	 *	    'current' directory, use path to name of current window.
	 *	    (2) tags in directory in XDME special path, use special
	 *	    path.  (3) tag entry is a full path name, override
	 *	    previous directories.
	 */

	for (pen = (PEN *)GetHead(&PBase); TRUE; )
	{
	    mountrequest (0);

	    if (fi = fopen (path, "r"))
	    {
		mountrequest (1);

		while ((len = xefgets (fi, buf, 128)) >= 0)
		{
		    for (j = 0; buf[j] && buf[j] != ' '; ++j);

		    if (j == 0 || buf[0] == '#')
			continue;

		    if (j == xlen && strncmp (str, buf, j) == 0)
		    {
			while (buf[j] == ' ')
			    ++j;

			/*
			 *  Extract the file name into str.  If the
			 *  filename does not contain an absolute path,
			 *  prepend it with such.
			 */
			{
			    char prep = 1;

			    for (i = 0; buf[j] && buf[j] != ' '; ++i, ++j)
			    {
				str[i] = buf[j];
				if (str[i] == ':')
				    prep = 0;
			    }

			    if (prep)
			    {
				movmem (str, str + dbaselen, i);
				movmem (path, str, dbaselen);

				i += dbaselen;
			    }
			}

			str[i] = 0;

			while (buf[j] && buf[j] != '^') /*  SEARCH ARG */
			    ++j;

			fclose (fi);

			if (buf[j] != '^')
			{
			    error ("ctags:\nError in tags-file");
			    goto done;
			}

			++j;	/* Skip ^ */

			/* UN*X FIX: Dme works for Aztec ctags, which has the format:	*/
			/*	<tag>	<file>	/^<pattern>				*/
			/* However, ctags under UN*X has the following format:		*/
			/*	<tag>	<file>	/^<pattern>[$]/ 			*/
			/* We just ignore the '$' and '/' so that both foramts work     */
			/* Thomas Rolfs 27/3/91 					*/

			{
			    short i = 0;

			    while (buf[j] && buf[j] != '$' && buf[j] != '/')
			    {
				sbuf[i++] = buf[j++];
			    }

			    sbuf[i] = 0;
			}

			/* End of fix.	*/

			slen = strlen (sbuf);

			if ((ed = finded (str, 0)) == NULL)
			{
			    strcpy (buf, "newwindow newfile `");
			    strcat (buf, str);
			    strcat (buf, "'");
			    do_command (buf);

			    /* remove path */
			    strcpy (str, FilePart (str));

			    ed = finded (str, 0);
			} else
			{
			    WindowToFront (ed->win);
			    ActivateWindow (ed->win);
			}

			if (ed == NULL)
			{
			    error ("ctags:\nunable to load file\n`%s'", str);
			    goto done;
			}

			switch_ed (ed);
			text_cursor (0);

			for (i = 0; i < ed->lines; ++i)
			{
			    if (strncmp (ed->list[i], sbuf, slen) == 0)
				break;
			}

			sprintf (buf, "first goto %ld", i + 1);
			do_command (buf);
			text_cursor (1);    /* turn cursor on */

			goto done;
		    }
		}

		fclose (fi);
	    } else
	    {
		mountrequest (1);
	    }

	    if (!pen) break;

	    pen = (PEN *)GetSucc ((struct Node *)pen);

	    if (pen)
	    {
		strcpy (path, pen->path);
		strcat (path, "tags");
		dbaselen = strlen (pen->path);
	    }
	}
	error ("ctags:\ntag `%s'\nnot found", str);
    }
done:
    CurrentDir (oldlock);
} /* do_ctags */

#endif


#ifndef NO_DO_REFS

/*
 *  Implement references
 */

void do_refs (void)
{
    static char str[MAXLINELEN];
    static char tmpfile[128];
    static char path[128];
    char      * srch;
    char      * file;
    char      * estr;
    long	len;
    int 	bcnt = 10;
    short	i,
		j;
    short	slen,
		elen;
    FILE      * fi,
	      * fj;
    short	tmph,
		tmpw;
    BPTR	oldlock = CurrentDir (Ep->dirlock);

#ifdef PATCH_NULL
    if (av[1] != NULL)
    {
	strcpy (str, av[1]);
    }
#endif /* PATCH_NULL */
#ifdef PATCH_SCANF
    else
	strcpy (str, current_word ());

#else
    {
	char	c;

	for (i = Ep->column; Current[i] == ' '; ++i);       /*  skip spaces     */

	for (j = 0; c=Current[i] && j<sizeof(str)-1; ++i, ++j)
	{
	    str[j] = c;

	    if (!isalnum (c) && c != '_')
		break;
	}

	str[j] = 0;
    }
#endif /* PATCH_SCANF */

    strcpy (tmpfile, "t:ref_");
    strncat (tmpfile, str, sizeof (tmpfile) - 32);

    title ("search .refs");

    {
	PEN * pen;

	strcpy (path, "dme.refs");      /*  warning, am assuming 8 char name */
	mountrequest (0);

	/* search local refs */
	if (searchref (path, str, &srch, &file, &len, &estr))
	{
	    mountrequest (1);
	    goto found;
	}

	/* search all paths */
	for (pen = (PEN *)GetHead(&PBase); pen; pen=(PEN *)GetSucc(pen))
	{
	    strcpy (path, pen->path);
	    strcat (path, "dme.refs");

	    if (searchref (path, str, &srch, &file, &len, &estr))
	    {
		mountrequest (1);
		goto found;
	    }
	}

	error ("refs:\nReference `%s'\nnot found", str);
	mountrequest (1);
	goto done;
    }

found:
    title ("search file");
    slen = strlen (srch);

    if (estr)
	elen = strlen (estr);

    fi = fopen (file, "r");

    if (fi == NULL)
    {		/*  try using path prefix   */
	strcpy (str, path);
	strcpy (str + strlen (str) - 8, file);
	fi = fopen (str, "r");
    }

    if (fi)
    {
	short lenstr;

	if (srch[0] == '@' && srch[1] == '@')
	{
	    fseek (fi, atoi (srch + 2), 0);
	    if ((lenstr = xefgets (fi, str, MAXLINELEN)) >= 0)
		goto autoseek;
	}

	while ((lenstr = xefgets (fi, str, MAXLINELEN)) >= 0)
	{
	    if (strncmp (str, srch, slen) == 0)
	    {
autoseek:
		title ("load..");
		if (fj = fopen (tmpfile, "w"))
		{
		    tmph = 0;
		    tmpw = 0;

		    do
		    {
			if (lenstr > tmpw)
			    tmpw = strlen (str);

			tmph ++;

			fputs (str, fj);
			fputc ('\n', fj);

			if (estr && strncmp (str, estr, elen) == 0)
			    break;

			len --;
		    }

		    while ((lenstr = xefgets (fi, str, MAXLINELEN)) >= 0 && len);

		    fclose (fj);

		    if (tmph > 10)
			tmph = 10;
		    if (tmpw > 80)
			tmpw = 80;

		    sprintf (str, "openwindow +0+0+%d+%d newfile %s", (tmpw << 3) + 24, (tmph << 3) + 24, tmpfile);
		    do_command (str);
		    text_cursor (1);    /* turn cursor on */
		    unlink (tmpfile);
		} else
		{
		    error ("refs:\nUnable to open\n`%s'\nfor write", tmpfile);
		}

		fclose (fi);
		free (srch);
		free (file);

		if (estr)
		    free (estr);

		goto done;
	    }
	    if (--bcnt == 0)
	    {  /* check break every so so  */
		bcnt = 50;

		if (breakcheck ())
		    break;
	    }
	}
	fclose (fi);

	error ("refs:\nSearch for `%s'\nfailed", str);
    } else
    {
	error ("refs:\nUnable to open\nsub document");
    }

    free (srch);
    free (file);

    if (estr)
	free (estr);
done:
    CurrentDir (oldlock);
} /* do_refs */


/*
 *  Reference file format:
 *
 *  `key' `lines' `file' `searchstring'
 *
 *  where `lines' can be a string instead ... like a read-until, otherwise
 *  the number of lines to read from the reference.
 */

int searchref (char * file, char * find, char ** psstr, char ** pfile, int * plines, char **pestr)
{
    FILE  * fi;
    char    buf[MAXLINELEN];
    char  * ptr,
	  * base;
    char  * b1,
	  * b2,
	  * b3,
	  * b4;
    char    quoted;

    /* short findlen = strlen(find); TODO */

    if (fi = fopen (file, "r"))
    {
	while (xefgets (fi, (base = buf), MAXLINELEN) >= 0)
	{
	    if (buf[0] == '#')
		continue;

	    ptr = breakout (&base, &quoted, &b1);

	    /* TODO if (ptr && *ptr && strncmp(ptr, find, findlen) == 0) */
	    if (ptr && *ptr && strcmp (ptr, find) == 0)
	    {
		if (ptr = breakout (&base, &quoted, &b2))
		{
		    *pestr = NULL;
		    *plines = atoi (ptr);

		    if (*plines == 0)
		    {
			*pestr = (char *) malloc (strlen (ptr) + 1);
			strcpy (*pestr, ptr);
		    }

		    if (ptr = breakout (&base, &quoted, &b3))
		    {
			*pfile = (char *) malloc (strlen (ptr) + 1);
			strcpy (*pfile, ptr);

			if (ptr = breakout (&base, &quoted, &b4))
			{
			    *psstr = (char *) malloc (strlen (ptr) + 1);

			    strcpy (*psstr, ptr);
			    fclose (fi);

			    if (b1)
				free (b1);
			    if (b2)
				free (b2);
			    if (b3)
				free (b3);
			    if (b4)
				free (b4);

			    return (1);
			}

			free (*pfile);

			if (b4)
			    free (b4);
		    }

		    if (pestr)
			free (*pestr);
		    if (b3)
			free (b3);
		}

		if (b2)
		    free (b2);
	    }

	    if (b1)
		free (b1);
	}

	fclose (fi);
    }

    return (0);
} /* searchref */

#endif

#ifndef NO_DO_CTAGS

int dirpart (char * str)
{
    short   i;

    for (i = strlen (str) - 1; i >= 0; --i)
    {
	if (str[i] == '/' || str[i] == ':')
	    break;
    }

    return (i + 1);
} /* dirpart */

#endif


/******************************************************************************
*****  ENDE refs.c
******************************************************************************/
