
/*	SC	A Spreadsheet Calculator
 *		Range Manipulations
 *
 *              Robert Bond, 4/87
 *
 *		$Revision: 6.21 A $
 */

#include <sys/types.h>
#if defined(BSD42) || defined(BSD43)
#include <strings.h>
#else
#ifndef SYSIII
#include <string.h>
#endif
#endif

#include <stdio.h>
#ifdef DOINGX
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#else
#include <curses.h>
#endif
#include <ctype.h>
#include "sc.h"

static struct range *rng_base;

void
add_range(name, left, right, is_range)
char *name;
struct ent_ptr left, right;
int is_range;
{
    struct range *r;
    register char *p;
    int len;
    int minr,minc,maxr,maxc;
    int minrf, mincf, maxrf, maxcf;
    register struct ent *rcp;

    if (left.vp->row < right.vp->row) {
	minr = left.vp->row; minrf = left.vf & FIX_ROW;
	maxr = right.vp->row; maxrf = right.vf & FIX_ROW;
    } else {
	minr = right.vp->row; minrf = right.vf & FIX_ROW;
	maxr = left.vp->row; maxrf = right.vf & FIX_ROW;
    } 

    if (left.vp->col < right.vp->col) {
	minc = left.vp->col; mincf = left.vf & FIX_COL;
	maxc = right.vp->col; maxcf = right.vf & FIX_COL;
    } else {
	minc = right.vp->col; mincf = right.vf & FIX_COL;
	maxc = left.vp->col; maxcf = left.vf & FIX_COL;
    } 

    left.vp = lookat(minr, minc);
    left.vf = minrf | mincf;
    right.vp = lookat(maxr, maxc);
    right.vf = maxrf | maxcf;

    if (find_range(name, strlen(name), (struct ent *)0, (struct ent *)0)) {
	error("Error: range name already defined");
	scxfree(name);
	return;
    }

    if (strlen(name) <= (unsigned)2) {
	error("Invalid range name - too short");
	scxfree(name);
	return;
    }

    for(p=name, len=0; *p; p++, len++)
	if (!((isalpha(*p) && (len<=2)) ||
	    ((isdigit(*p) || isalpha(*p) || (*p == '_')) && (len>2)))) {
	    error("Invalid range name - illegal combination");
	    scxfree(name);
	    return;
	}
 
    if(autolabel && minc>0 && !is_range) {
	rcp = lookat(minr,minc-1);
	if (rcp->label==0 && rcp->expr==0 && rcp->v==0)
		label(rcp, name, 0);
    }

    r = (struct range *)scxmalloc((unsigned)sizeof(struct range));
    r->r_name = name;
    r->r_left = left;
    r->r_right = right;
    r->r_next = rng_base;
    r->r_prev = (struct range *)0;
    r->r_is_range = is_range;
    if (rng_base)
        rng_base->r_prev = r;
    rng_base = r;
}

void
del_range(left, right)
struct ent *left, *right;
{
    register struct range *r;
    int minr,minc,maxr,maxc;

    minr = left->row < right->row ? left->row : right->row;
    minc = left->col < right->col ? left->col : right->col;
    maxr = left->row > right->row ? left->row : right->row;
    maxc = left->col > right->col ? left->col : right->col;

    left = lookat(minr, minc);
    right = lookat(maxr, maxc);

    if (!(r = find_range((char *)0, 0, left, right))) 
	return;

    if (r->r_next)
        r->r_next->r_prev = r->r_prev;
    if (r->r_prev)
        r->r_prev->r_next = r->r_next;
    else
	rng_base = r->r_next;
    scxfree((char *)(r->r_name));
    scxfree((char *)r);
}

void
clean_range()
{
    register struct range *r;
    register struct range *nextr;

    r = rng_base;
    rng_base = (struct range *)0;

    while (r) {
	nextr = r->r_next;
	scxfree((char *)(r->r_name));
	scxfree((char *)r);
	r = nextr;
    }
}

/* Match on name or lmatch, rmatch */

struct range *
find_range(name, len, lmatch, rmatch)
char *name;
int len;
struct ent *lmatch;
struct ent *rmatch;
{
    struct range *r;
    register char *rp, *np;
    register int c;

    if (name) {
	for (r = rng_base; r; r = r->r_next) {
	    for (np = name, rp = r->r_name, c = len;
		 c && *rp && (*rp == *np);
		 rp++, np++, c--) /* */;
	    if (!c && !*rp)
		return(r);
	}
	return((struct range *)0);
    }

    for (r = rng_base; r; r= r->r_next) {
	if ((lmatch == r->r_left.vp) && (rmatch == r->r_right.vp)) 
	    return(r);
    }
    return((struct range *)0);
}

void
sync_ranges()
{
    register struct range *r;

    r = rng_base;
    while(r) {
	r->r_left.vp = lookat(r->r_left.vp->row, r->r_left.vp->col);
	r->r_right.vp = lookat(r->r_right.vp->row, r->r_right.vp->col);
	r = r->r_next;
    }
}

void
write_range(f)
FILE *f;
{
    register struct range *r;

    for (r = rng_base; r; r = r->r_next) {
	(void) fprintf(f, "define \"%s\" %s%s%s%d",
			r->r_name,
			r->r_left.vf & FIX_COL ? "$":"",
			coltoa(r->r_left.vp->col), 
			r->r_left.vf & FIX_ROW ? "$":"",
			r->r_left.vp->row);
	if (r->r_is_range)
	    (void) fprintf(f, ":%s%s%s%d\n",
			    r->r_right.vf & FIX_COL ? "$":"",
			    coltoa(r->r_right.vp->col), 
			    r->r_right.vf & FIX_ROW ? "$":"",
			    r->r_right.vp->row);
	else
	    (void) fprintf(f, "\n");
    }
}

void
list_range(f)
FILE *f;
{
    register struct range *r;

    (void) fprintf(f, "%-30s %s\n\n","Name","Definition");

    for (r = rng_base; r; r = r->r_next) {
	(void) fprintf(f, "%-30s %s%s%s%d",
			    r->r_name,
			    r->r_left.vf & FIX_COL ? "$":"",
			    coltoa(r->r_left.vp->col), 
			    r->r_left.vf & FIX_ROW ? "$":"",
			    r->r_left.vp->row);
	if (r->r_is_range)
	    (void) fprintf(f, ":%s%s%s%d\n",
			    r->r_right.vf & FIX_COL ? "$":"",
			    coltoa(r->r_right.vp->col), 
			    r->r_right.vf & FIX_ROW ? "$":"",
			    r->r_right.vp->row);
	else
	    (void) fprintf(f, "\n");
    }
}

char *
v_name(row, col)
int row, col;
{
    struct ent *v;
    struct range *r;
    static char buf[20];

    v = lookat(row, col);
    if (r = find_range((char *)0, 0, v, v)) {
	return(r->r_name);
    } else {
        (void) sprintf(buf, "%s%d", coltoa(col), row);
	return(buf);
    }
}

char *
r_name(r1, c1, r2, c2)
int r1, c1, r2, c2;
{
    struct ent *v1, *v2;
    struct range *r;
    static char buf[100];

    v1 = lookat(r1, c1);
    v2 = lookat(r2, c2);
    if (r = find_range((char *)0, 0, v1, v2)) {
	return(r->r_name);
    } else {
        (void) sprintf(buf, "%s", v_name(r1, c1));
	(void) sprintf(buf+strlen(buf), ":%s", v_name(r2, c2));
	return(buf);
    }
}

int
are_ranges()
{
	return (rng_base != 0);
}
