#include <stdio.h>
#include <ctype.h>

#define FALSE	0
#define TRUE	1
#define NULL_ADDRESS	0	/* get_addr return codes */
#define SINGLE_ADDRESS	1
#define COMMA_ADDRESS	2
#define SEMI_ADDRESS	3
#define REG_FAIL	4
#define ADDR_FAIL	5
#define FOUND		0	/* search_file return codes */
#define NOT_FOUND	1
#define REGERROR	2
#define MAXLINE		256
#define NL		'\n'
#define DEBUG(l, f, s)	if (Debug >= l) fprintf(stderr, f, s)

extern char *gap_end, *gap_start, *buffer_end, *the_bp;
extern int Debug;
static int  buffer_size, evalsw, li, le, temp_reg;

/***
 *	get_addr(atp,ati,ate,ali,ale,api,ape,acode);
 *      char *tp;
 *      int *ati, *ate, *ali, *ale, *api, *ape, *acode;
 *
 *	get_addr - interpret address given in tp, ati, ate
 *	using current line in ali, ale to return addressed
 *	line in api, ape.
 */

get_addr(tp,ati,ate,ali,ale,api,ape,acode)
char *tp;
int  *ati,*ate,*ali,*ale,*api,*ape,*acode;
{
    int ti, te, code;
    int i, j, num, dummy_flag;
    char ch;
    int relsw, negsw;

    *acode = NULL_ADDRESS;
    ti = *ati;		/* copy parameters */
    te = *ate;
    li = *ali;
    le = *ale;
    relsw = negsw = evalsw = FALSE;
    temp_reg = 0;
    buffer_size = (buffer_end - the_bp) - (gap_end - gap_start);
    goto scan2;

scan:
    *acode = SINGLE_ADDRESS;
    relsw = TRUE;
scan1:
    ti++;
scan2:
    if (ti > te) {
bad_addr: printf ("Address syntax error.\n");
	  goto fail;
    }

    ch = tp[ti];
    if (ch == ' ') goto scan1;
    if (ch == '/') goto reg;
    if (ch == '$') goto last;
    if (ch == '-') goto neg;
    if (ch == '+') goto pos;
    if (ch == '.') goto scan;
    if (isdigit(ch)) goto get_num;
    if (ch == ',') {
	ti++;
	*acode = COMMA_ADDRESS;
    }

    if (ch == ';') {
	ti++;
	*acode = SEMI_ADDRESS;
    }

    if (evalsw)
	if (!eval()) goto fail;

    *api = li;
    *ape = le;
    *ati = ti;
    DEBUG(7, "getaddr success - api %d", li);
    DEBUG(7, " ape %d", le);
    DEBUG(7, " acode %d\n", *acode);
    return;

reg_fail:
    *acode = REG_FAIL;
    return;

fail:
    *acode = ADDR_FAIL;
    return;

reg:
    if (evalsw)
	if (!eval()) goto fail;

    for (i= ++ti;ti<=te;ti++) {
	if (tp[ti] == '/') goto reg1;
	if (tp[ti] == '\\') ti++;
    }

    printf ("Syntax error in regular expression\n");
    goto fail;

reg1:
    DEBUG(4,"getaddr reg - %s", &tp[i]);
    j = ti - i;
    search_file (tp,i,j,le+1,buffer_size-1,&li,&le,&dummy_flag,&dummy_flag,&code);
    if (code == FOUND) goto reg2;
    if (code == REGERROR) goto fail;
    search_file (tp,i,0,0,le,&li,&le,&dummy_flag,&dummy_flag,&code);
    if (code != FOUND) goto reg_fail;

reg2:
    find_line (li, &li, &le);
    goto scan;

last:
    if (buffer_size <= 0) goto scan;
    le = buffer_size-1;
    find_bol (&li, &le);
    goto scan;

neg:
    negsw = TRUE;
    goto scan;

pos:
    negsw = FALSE;
    goto scan;

get_num:
    num = 0;
    for (; ti <= te && num <= 100000; ti++) {
	ch = tp[ti];
	if (!(isdigit(ch))) goto end_num;
	num = num * 10 + ch - '0';
    }
    goto bad_addr;

end_num:
    DEBUG(4, "getaddr - num %d\n", num);
    ti--;
    evalsw = TRUE;
    if (!relsw) {
	li = -1;
	le = -1;
	if (num == 0) li = 0;
    }
    if (negsw) {
	negsw = FALSE;
	temp_reg -= num;
    }
    else
	temp_reg += num;
    goto scan;
}

/***
 *	eval () - Evaluae numberical addresses and return
 *	character indices of beginning and end of addressed line 
 *
 */

eval ()
{
    int i;

    evalsw = FALSE;
    if (buffer_size <= 0 && temp_reg != 0) {
	printf ("Buffer empty.\n");
	return (FALSE);
    }

    if (temp_reg > 0) {
	for (i=1;i<=temp_reg;i++)
	    if (!advance_line (&li, &le)) {
		printf ("Address out of buffer (too big).\n");
		return (FALSE);
	    }
    }
    else {
	for (i=1;i<=-temp_reg;i++)
	    if (!backup_line (&li, &le)) {
		printf ("Address out of buffer (negative address).\n");
		return (FALSE);
	    }
    }

    temp_reg = 0;
    return (TRUE);
}

/***
 *	advance_line (ali, ale)
 *	int *ali, *ale;
 *
 *	advance_line - advance to next line.
 *
 *	return codes
 *		TRUE - ok.
 *		FALSE - failed
 */
advance_line(ali,ale)
int *ali,*ale;
{
    if (*ale >= buffer_size-1) return (FALSE);

    find_line (*ale+1,ali,ale);
    return (TRUE);
}

/***
 *	backup_line (ali, ale)
 *	integer *ali,*ale;
 *
 *	backup_line - backup to previous line.
 *
 *	return codes
 *		TRUE - ok
 *		FALSE - failed
 */
backup_line (ali,ale)
int *ali,*ale;
{
    if (*ali <= 1) return (FALSE);

    find_line (*ali-1,ali,ale);
    return (TRUE);
}
