#ifndef lint
static char sccsid[] = "@(#)pagelist.c	3.1\t11/16/89";
#endif lint

#include <stdio.h>
#include <strings.h>
#include "struct.h"
#include "defs.h"

#define NOLIST (pagelist *)0

extern void Fatal(), Warning();
extern char *malloc();

extern int C_firstpage, C_lastpage;
extern char C_pageargs[];

struct p_l {
    int   first;
    int   by;
    int   last;
    struct p_l *next;
};
typedef struct p_l pagelist;

pagelist *plhead, *pltail;

/**********************************************************************/
/**************************  Forward Definitions  *********************/
/**********************************************************************/
void  AddPage();
void  AddRange();
void  AddSkipRange();
void  AnalyzePageList();
void  ExcludePage();
bool InPageList();
void  Process();


/**********************************************************************/
/*******************************  AddPage  ****************************/
/**********************************************************************/
void
AddPage(n)
int   n;
{
    pagelist *pl, *tpl;
    int   new;

    if (InPageList(n))
	return;
    pl = NEW(pagelist);
    pl->first = pl->last = n;
    pl->by = 1;
    if (n < plhead->first) {
	pl->next = plhead;
	plhead = pl;
	return;
    }
    if (n > pltail->last) {
	pltail->next = pl;
	pl->next = NULL;
	pltail = pl;
	return;
    }
    tpl = plhead;
    while (n > tpl->last) {
	pl->next = tpl;
	tpl = tpl->next;
    }
    if (n < tpl->first) {
	pl->next->next = pl;
	pl->next = tpl;
	return;
    }
    tpl = NEW(pagelist);
    new = pl->next->first;
    while (new < n)
	new += pl->next->by;
    tpl->first = new;
    tpl->by = pl->next->by;
    tpl->last = pl->next->last;
    tpl->next = pl->next->next;
    pl->next->last = new - pl->next->by;
    pl->next->next = pl;
    pl->next = tpl;
    return;
}


/**********************************************************************/
/******************************  AddRange  ****************************/
/**********************************************************************/
#define MIN(a,b) ((a<b?a:b))
#define MAX(a,b) ((a<b?b:a))

void
AddRange(from, to)
int   from, to;
{
    pagelist *pl, *tpl;
    int   old, new;

    pl = NEW(pagelist);
    pl->first = from;
    pl->last = to;
    pl->by = 1;
    if (to < plhead->first) {
	pl->next = plhead;
	plhead = pl;
	return;
    }
    if (from > pltail->last) {
	pltail->next = pl;
	pl->next = NULL;
	pltail = pl;
	return;
    }
    tpl = plhead;
    while (from > tpl->last) {
	pl->next = tpl;
	tpl = tpl->next;
    }
    if (to < tpl->first) {
	pl->next->next = pl;
	pl->next = tpl;
	return;
    }
    if (tpl->by == 1 || (from <= tpl->first && tpl->last <= to)) {
	tpl->first = MIN(tpl->first, from);
	tpl->last = MAX(tpl->last, to);
	free((char *) pl);
	return;
    }
    if (tpl->first < from && tpl->last <= to) {
	new = tpl->last;
	while (new > from)
	    new -= tpl->by;
	tpl->last = new;
	pl->next = tpl->next;
	tpl->next = pl;
	return;
    }
    if (tpl->first < from && to < tpl->last) {
	new = tpl->last;
	while (new > from)
	    new -= tpl->by;
	old = tpl->last;
	tpl->last = new;
	new = tpl->by;
	pl->next = tpl->next;
	tpl->next = pl;
	tpl = NEW(pagelist);
	tpl->by = new;
	tpl->last = old;
	while (old > pl->last)
	    old -= new;
	old += new;
	tpl->first = old;
	tpl->next = pl->next;
	pl->next = tpl;
	return;
    }
    if (from <= tpl->first && to < tpl->last) {
	new = tpl->first;
	while (new < to)
	    new += tpl->by;
	tpl->first = new;
	pl->next->next = pl;
	pl->next = tpl;
	return;
    }
}


/**********************************************************************/
/****************************  AddSkipRange  **************************/
/**********************************************************************/
void
AddSkipRange(from, by, to)
int   from, by, to;
{
    pagelist *pl, *tpl;
    int   new;

    pl = NEW(pagelist);
    pl->first = from;
    pl->by = by;
    pl->last = to;
    if (to < plhead->first) {
	pl->next = plhead;
	plhead = pl;
	return;
    }
    if (from > pltail->last) {
	pltail->next = pl;
	pl->next = NULL;
	pltail = pl;
	return;
    }
    tpl = plhead;
    while (from > tpl->last) {
	pl->next = tpl;
	tpl = tpl->next;
    }
    if (to < tpl->first) {
	pl->next->next = pl;
	pl->next = tpl;
	return;
    }
    if (tpl->by != 1) {
	Warning("Overlapping skip ranges. Ignoring %d:%d:%d.\n",
	  from, by, to);
	free((char *) pl);
	return;
    }
    if (tpl->first <= from && to <= tpl->last) {
	free((char *) pl);
	return;
    }
    if (from <= tpl->first && to <= tpl->last) {
	while (to >= tpl->first)
	    to -= by;
	pl->last = to;
	pl->next->next = pl;
	pl->next = tpl;
	return;
    }
    if (tpl->first <= from && tpl->last <= to) {
	while (from <= tpl->last)
	    from += by;
	pl->first = from;
	pl->next = tpl->next;
	tpl->next = pl;
	return;
    }
    if (from < tpl->first && tpl->last <= to) {
	new = to;
	while (to >= tpl->first)
	    to -= by;
	pl->last = to;
	pl->next->next = pl;
	pl->next = tpl;
	tpl = NEW(pagelist);
	tpl->last = new;
	tpl->by = by;
	tpl->first = new;
	while (tpl->first > pl->next->last)
	    tpl->first -= by;
	tpl->first += by;
	tpl->next = pl->next;
	pl->next = tpl;
	return;
    }
}


/**********************************************************************/
/***************************  AnalyzePageList  ************************/
/**********************************************************************/
void
AnalyzePageList()
{
    char *tcp;
    char *nextarg;

    pltail = plhead = NEW(pagelist);
    plhead->first = C_firstpage;
    plhead->last = C_lastpage;
    plhead->by = 1;
    plhead->next = NULL;

    if (index(C_pageargs, ',') == NULL)
	return;
    nextarg = C_pageargs;
    if (C_firstpage == FIRST_DEF && C_lastpage == LAST_DEF)
	plhead->first = LAST_DEF;
    while ((tcp = index(nextarg, ',')) != NULL) {
	*tcp = '\0';
	Process(nextarg);
	nextarg = tcp + 1;
    }
    return;
}


/**********************************************************************/
/*****************************  ExcludePage  **************************/
/**********************************************************************/
void
ExcludePage(n)
int   n;
{
}


/**********************************************************************/
/******************************  InPageList  **************************/
/**********************************************************************/
bool
InPageList(n)
int   n;
{
    register pagelist *p = plhead;

    while (p != NULL && n > p->last)
	p = p->next;
    if (p == NOLIST || n < p->first || (n - p->first) % p->by != 0)
	return (FALSE);
    return (TRUE);
}


/**********************************************************************/
/******************************  Process  *****************************/
/**********************************************************************/
void
Process(pages)
char *pages;
{
    int   from, by, to, temp;
    char *tcp;

    if (*pages == '^') {
	if (sscanf(pages + 1, "%d", &from) != 1)
	    Fatal("Invalid argument to -x\n");
	ExcludePage(from);
    }
    else if ((tcp = index(pages, ':')) == NULL) {
	if (sscanf(pages, "%d", &from) != 1)
	    Fatal("Invalid argument to -x\n");
	AddPage(from);
    }
    else {
	*tcp = '@';
	if (index(pages, ':') == NULL) {
	    if (sscanf(pages, "%d@%d", &from, &to) != 2)
		Fatal("Invalid argument to -x\n");
	    if (to < from) {
		Warning("Incorrect page list argument. Ignoring %d:%d\n", 
			from, to);
	    }
	    else
		AddRange(from, to);
	}
	else {
	    if (sscanf(pages, "%d@%d:%d", &from, &by, &to) != 3)
		Fatal("Invalid argument to -x\n");
	    if (to < from) {
		Warning("Invalid page list argument. Ignoring %d:%d:%d\n", 
			from, by, to);
	    }
	    else {
		if ((temp = (to - from) % by) != 0) {
		    Warning("Invalid page list argument %d:%d:%d.\n", 
			from, by, to);
		    to = to - temp;
		    Warning("Using %d:%d:%d instead.\n", from, by, to);
		}
		AddSkipRange(from, by, to);
	    }
	}
    }
}
