static char rcsid[] = "index.c,v 1.14 1996/01/04 04:07:06 duane Exp";
/* 
 *  index.c -- Nebula stuff
 *
 *   William G. Camargo, Penn State Univ.
 *  Chanda Dharap,  Penn State Univ.  
 *  Darren Hardy, Univ. of Colorado - Boulder
 *
 *  ----------------------------------------------------------------------
 *  Copyright (c) 1994, 1995.  All rights reserved.
 *  
 *    The Harvest software was developed by the Internet Research Task
 *    Force Research Group on Resource Discovery (IRTF-RD):
 *  
 *          Mic Bowman of Transarc Corporation.
 *          Peter Danzig of the University of Southern California.
 *          Darren R. Hardy of the University of Colorado at Boulder.
 *          Udi Manber of the University of Arizona.
 *          Michael F. Schwartz of the University of Colorado at Boulder.
 *          Duane Wessels of the University of Colorado at Boulder.
 *  
 *    This copyright notice applies to software in the Harvest
 *    ``src/'' directory only.  Users should consult the individual
 *    copyright notices in the ``components/'' subdirectories for
 *    copyright information about other software bundled with the
 *    Harvest source code distribution.
 *  
 *  TERMS OF USE
 *    
 *    The Harvest software may be used and re-distributed without
 *    charge, provided that the software origin and research team are
 *    cited in any use of the system.  Most commonly this is
 *    accomplished by including a link to the Harvest Home Page
 *    (http://harvest.cs.colorado.edu/) from the query page of any
 *    Broker you deploy, as well as in the query result pages.  These
 *    links are generated automatically by the standard Broker
 *    software distribution.
 *    
 *    The Harvest software is provided ``as is'', without express or
 *    implied warranty, and with no support nor obligation to assist
 *    in its use, correction, modification or enhancement.  We assume
 *    no liability with respect to the infringement of copyrights,
 *    trade secrets, or any patents, and are not responsible for
 *    consequential damages.  Proper use of the Harvest software is
 *    entirely the responsibility of the user.
 *  
 *  DERIVATIVE WORKS
 *  
 *    Users may make derivative works from the Harvest software, subject 
 *    to the following constraints:
 *  
 *      - You must include the above copyright notice and these 
 *        accompanying paragraphs in all forms of derivative works, 
 *        and any documentation and other materials related to such 
 *        distribution and use acknowledge that the software was 
 *        developed at the above institutions.
 *  
 *      - You must notify IRTF-RD regarding your distribution of 
 *        the derivative work.
 *  
 *      - You must clearly notify users that your are distributing 
 *        a modified version and not the original Harvest software.
 *  
 *      - Any derivative product is also subject to these copyright 
 *        and use restrictions.
 *  
 *    Note that the Harvest software is NOT in the public domain.  We
 *    retain copyright, as specified above.
 *  
 *  HISTORY OF FREE SOFTWARE STATUS
 *  
 *    Originally we required sites to license the software in cases
 *    where they were going to build commercial products/services
 *    around Harvest.  In June 1995 we changed this policy.  We now
 *    allow people to use the core Harvest software (the code found in
 *    the Harvest ``src/'' directory) for free.  We made this change
 *    in the interest of encouraging the widest possible deployment of
 *    the technology.  The Harvest software is really a reference
 *    implementation of a set of protocols and formats, some of which
 *    we intend to standardize.  We encourage commercial
 *    re-implementations of code complying to this set of standards.  
 *  
 */
#include "broker.h"
#include "log.h"
#include "index.h"

#define CLIENT 1

#include "unp.h"

/* Global variables */
extern char *DIRpath;
extern char *brk_obj_url;
extern int IndexType;

extern char *unpi_error;

typedef struct NEBQ {
    char *fname;
    struct NEBQ *next;
} NEBq;

LOCAL NEBq *NEB_indexq = NULL;
LOCAL NEBq *NEB_deleteq = NULL;
LOCAL UNPI *uptr = NULL;
LOCAL POINTER NEB_context = NIL;

/* configuration variables */
LOCAL char *NEB_cname;
LOCAL char *NEB_host;
LOCAL int NEB_port = 0;
LOCAL int NEB_key = 0;

/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX *
 * 
 * PRIVATE FUNCTIONS
 * 
 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */

/* ----------------------------------------------------------------- *
 * NEB_initcon -- initialize the connection to the Nebula server.
 * ----------------------------------------------------------------- */
int NEB_initcon()
{
    char msg[STRMAX];
    int context;

    if ((uptr = unpi_connect(NEB_host, NEB_port)) == NULL) {
	if (unpi_error != NULL) {
	    sprintf(msg, "Unable to connect to Nebula server <%s>\n", unpi_error);
	    free(unpi_error);
	    fatal(msg);
	} else {
	    fatal("Connection error.\n");
	}
    }
    if (!unpi_getcontext(uptr, NEB_cname, &context)) {
#if DEBUG2
	fprintf(stdout, "Unable to locate broker context.\n");
#endif
	return (NEB_Destroy_Full());	/* creates a new broker context */
    }
    NEB_context = CVUNID(uptr->address, context);
}


/* ----------------------------------------------------------------- *
 * NEB_Index_Object -- index a single object with the Nebula
 * harvest::index-object function.
 * ----------------------------------------------------------------- */
LOCAL int NEB_Index_Object(entry)
     reg_t *entry;
{
    static int fclass = -1;
    static int fhandle = -1;
    char *fn;
    POINTER ptr1, ptr2;

    /* ----- Initialization ----- */
    unpi_error = NULL;
    if (uptr == NULL)
	NEB_initcon();

    if (fclass == -1) {
	if (!unpi_getfunction(uptr, "harvest::index-object", &fclass, &fhandle))
	    return ERROR;
#if DEBUG2
	fprintf(stdout, "harvest::index-object: %d %d\n", fclass, fhandle);
#endif
    }
    /* ----- Create argument list ----- */
    fn = SM_Get_Obj_Filename(entry->FD);
    ptr1 = CVLIST(CVSTRING(fn), NIL);

    /* ----- Process RPC ----- */
    ptr2 = ccall(uptr->channel, fclass, fhandle, NEB_key, NEB_context, ptr1, NIL);
    if ((ptr2 == NIL) && (unpi_error != NULL)) {
	fprintf(stderr, "Nebula index-object failed: %s\n", (char *) unpi_error);
	FREEP(ptr1);
	free(unpi_error);
	return ERROR;
    }
#if DEBUG2
    fprintf(stdout, "Successfully indexed object:\n");
    writeexp(stdout, ptr2);
    unpputc(stdout, '\n');
#endif

    /* ----- Clean up ----- */
    FREEP(ptr1);
    FREEP(ptr2);
    free(fn);

    return (SUCCESS);
}

/* ----------------------------------------------------------------- *
 * NEB_Index_Batch -- incrementally index the current batch of objects
 * using the Nebula function harvest::index-batch.
 * ----------------------------------------------------------------- */
LOCAL int NEB_Index_Batch()
{
    static int fclass = -1;
    static int fhandle = -1;
    NEBq *qptr = NULL, *tptr = NULL;
    POINTER ptr1, ptr2;

    /* ----- Initialization ----- */
    unpi_error = NULL;
    if (uptr == NULL)
	NEB_initcon();

    if (fclass == -1) {
	if (!unpi_getfunction(uptr, "harvest::index-batch", &fclass, &fhandle))
	    return ERROR;
#if DEBUG2
	fprintf(stdout, "harvest::index-batch: %d %d\n", fclass, fhandle);
#endif
    }
    /* ----- Create argument list ----- */
    for (ptr1 = NIL, qptr = NEB_indexq; qptr != NULL; qptr = qptr->next)
	ptr1 = CVLIST(CVSTRING(qptr->fname), ptr1);
    ptr1 = CVLIST(ptr1, NIL);

    /* ----- Process RPC ----- */
    ptr2 = ccall(uptr->channel, fclass, fhandle, NEB_key, NEB_context, ptr1, NIL);
    if ((ptr2 == NIL) && (unpi_error != NULL)) {
	fprintf(stderr, "Nebula index-batch failed: %s\n", (char *) unpi_error);
	FREEP(ptr1);
	free(unpi_error);
	return ERROR;
    }
#if DEBUG2
    fprintf(stdout, "Successfully indexed objects:\n");
    writeexp(stdout, ptr2);
    unpputc(stdout, '\n');
#endif

    /* ----- Clean up ----- */
    FREEP(ptr1);
    FREEP(ptr2);

    for (qptr = NEB_indexq; qptr != NULL; qptr = tptr) {
	tptr = qptr->next;
	free(qptr->fname);
	free(qptr);
    }

    NEB_indexq = NULL;

    return (SUCCESS);
}

/* ----------------------------------------------------------------- *
 * NEB_Index_Full -- remove the existing database and force a full
 * reindexing; this may take awhile.  This uses the Nebula function
 * harvest::index-full.
 * ----------------------------------------------------------------- */
LOCAL int NEB_Index_Full()
{
    char buf[STRMAX];
    static int fclass = -1;
    static int fhandle = -1;
    POINTER ptr1, ptr2;
    NEBq *qptr, *tptr;

    /* ----- Initialization ----- */
    unpi_error = NULL;
    if (uptr == NULL)
	NEB_initcon();

    if (fclass == -1) {
	if (!unpi_getfunction(uptr, "harvest::index-full", &fclass, &fhandle)) {
	    if (unpi_error != NULL) {
		fprintf(stderr, "Nebula Error: %s\n", (char *) unpi_error);
		free(unpi_error);
	    }
	    return ERROR;
	}
#if DEBUG2
	fprintf(stdout, "harvest::index-full: %d %d\n", fclass, fhandle);
#endif
    }
    /* ----- Remove the old context ----- */
    if (NEB_Destroy_Full() != SUCCESS)
	return ERROR;

    /* ----- Create argument list ----- */
    sprintf(buf, "%s/objects", DIRpath);
    ptr1 = CVLIST(CVSTRING(buf), NIL);

    /* ----- Process RPC ----- */
    unpi_error = NULL;
    ptr2 = ccall(uptr->channel, fclass, fhandle, NEB_key, NEB_context, ptr1, NIL);
    if ((ptr2 == NIL) && (unpi_error != NULL)) {
	fprintf(stderr, "Nebula index-full failed: %s\n", (char *) unpi_error);
	FREEP(ptr1);
	free(unpi_error);
	return ERROR;
    }
#if DEBUG2
    fprintf(stdout, "Successfully reindexed database:\n");
    writeexp(stdout, ptr2);
    unpputc(stdout, '\n');
#endif

    /* ----- Clean up ----- */
    FREEP(ptr1);
    FREEP(ptr2);

    for (qptr = NEB_indexq; qptr != NULL; qptr = tptr) {
	tptr = qptr->next;
	free(qptr->fname);
	free(qptr);
    }

    return (SUCCESS);
}

/* ----------------------------------------------------------------- *
 * NEB_Destroy_Object -- remove a single object from the Nebula
 * server using the function harvest::destroy-object.
 * ----------------------------------------------------------------- */
LOCAL int NEB_Destroy_Object(entry)
     reg_t *entry;
{
    static int fclass = -1;
    static int fhandle = -1;
    char *fn;
    POINTER ptr1, ptr2;

    /* ----- Initialization ----- */
    unpi_error = NULL;
    if (uptr == NULL)
	NEB_initcon();

    if (fclass == -1) {
	if (!unpi_getfunction(uptr, "harvest::destroy-object", &fclass, &fhandle))
	    return ERROR;
#if DEBUG2
	fprintf(stdout, "harvest::destroy-object: %d %d\n", fclass, fhandle);
#endif
    }
    /* ----- Create argument list ----- */
    fn = SM_Get_Obj_Filename(entry->FD);
    ptr1 = CVLIST(CVSTRING(fn), NIL);

    /* ----- Process RPC ----- */
    ptr2 = ccall(uptr->channel, fclass, fhandle, NEB_key, NEB_context, ptr1, NIL);
    if ((ptr2 == NIL) && (unpi_error != NULL)) {
	fprintf(stderr, "Nebula destroy-object failed: %s\n", (char *) unpi_error);
	FREEP(ptr1);
	free(unpi_error);
	return ERROR;
    }
#if DEBUG2
    fprintf(stdout, "Successfully removed object:\n");
    writeexp(stdout, ptr2);
    unpputc(stdout, '\n');
#endif

    /* ----- Clean up ----- */
    FREEP(ptr1);
    FREEP(ptr2);
    free(fn);

    return (SUCCESS);
}

/* ----------------------------------------------------------------- *
 * NEB_Destroy_Batch -- incrementally index the current batch of objects.
 * ----------------------------------------------------------------- */
LOCAL int NEB_Destroy_Batch()
{
    static int fclass = -1;
    static int fhandle = -1;
    NEBq *qptr = NULL, *tptr = NULL;
    POINTER ptr1, ptr2;

    /* ----- Initialization ----- */
    unpi_error = NULL;
    if (uptr == NULL)
	NEB_initcon();

    if (fclass == -1) {
#if DEBUG3
	fprintf(stdout, "Initializing harvest::destroy-batch.\n");
#endif
	if (!unpi_getfunction(uptr, "harvest::destroy-batch", &fclass, &fhandle))
	    return ERROR;
#if DEBUG3
	fprintf(stdout, "harvest::destroy-batch: %d %d\n", fclass, fhandle);
#endif
    }
    /* ----- Create argument list ----- */
    for (ptr1 = NIL, qptr = NEB_deleteq; qptr != NULL; qptr = qptr->next)
	ptr1 = CVLIST(CVSTRING(qptr->fname), ptr1);
    ptr1 = CVLIST(ptr1, NIL);

    /* ----- Process RPC ----- */
    ptr2 = ccall(uptr->channel, fclass, fhandle, NEB_key, NEB_context, ptr1, NIL);
    if ((ptr2 == NIL) && (unpi_error != NULL)) {
	fprintf(stderr, "Nebula destroy-batch failed: %s\n", (char *) unpi_error);
	FREEP(ptr1);
	free(unpi_error);
	return ERROR;
    }
#if DEBUG2
    fprintf(stdout, "Successfully removed objects:\n");
    writeexp(stdout, ptr2);
    unpputc(stdout, '\n');
#endif

    /* ----- Clean up ----- */
    FREEP(ptr1);
    FREEP(ptr2);

    for (qptr = NEB_deleteq; qptr != NULL; qptr = tptr) {
	tptr = qptr->next;
	free(qptr->fname);
	free(qptr);
    }

    NEB_deleteq = NULL;

    return (SUCCESS);
}

/* ----------------------------------------------------------------- *
 * NEB_Destroy_Full -- remove the existing database using the Nebula
 * harvest::destroy-full function.  This will force the creation of
 * a new broker context is saved in NEB_context.
 * ----------------------------------------------------------------- */
LOCAL int NEB_Destroy_Full()
{
    static int fclass = -1;
    static int fhandle = -1;
    POINTER ptr1, ptr2, cptr;

    /* ----- Initialization ----- */
    unpi_error = NULL;
    if (uptr == NULL)
	NEB_initcon();

    if (fclass == -1) {
	if (!unpi_getfunction(uptr, "harvest::destroy-full", &fclass, &fhandle))
	    return ERROR;
#if DEBUG2
	fprintf(stdout, "harvest::destroy-full: %d %d\n", fclass, fhandle);
#endif
    }
    /* ----- Create argument list ----- */
    ptr1 = CVLIST(CVSTRING(NEB_cname), NIL);
    cptr = CVUNID(uptr->address, 4);

    /* ----- Process RPC ----- */
    ptr2 = ccall(uptr->channel, fclass, fhandle, NEB_key, cptr, ptr1, NIL);
    if ((ptr2 == NIL) && (unpi_error != NULL)) {
	fprintf(stderr, "Nebula destroy-full failed: %s\n", (char *) unpi_error);
	FREEP(ptr1);
	free(unpi_error);
	return ERROR;
    }
#if DEBUG2
    fprintf(stdout, "Successfully removed database:\n");
    writeexp(stdout, ptr2);
    unpputc(stdout, '\n');
#endif

    /* ----- Clean up ----- */
    FREEP(ptr1);
    FREEP(NEB_context);
    NEB_context = ptr2;

    return (SUCCESS);
}

/* ----------------------------------------------------------------- *
 * NEB_bulk_query -- process a bulk query, return full SOIF objects.
 * ----------------------------------------------------------------- */
LOCAL int NEB_bulk_query(rsock, ptime, head)
     int rsock;
     time_t ptime;
     POINTER head;
{
    FILE *fp;
    POINTER ptr;
    fd_t fd;
    reg_t *entry;

    if ((fp = fdopen(rsock, "w")) == NULL) {
	perror("fdopen");
	QM_send_bulk_err(rsock);
	return ERROR;
    }
    QM_send_bulk_begin(rsock);

    for (ptr = head; ptr != NIL; ptr = CDR(ptr)) {
	fd = GETFIXNUM(CAR(ptr));
	if ((entry = RG_Get_Entry(fd)) != NULL)
	    if (entry->update_time >= ptime)
		QM_send_bulk_fd(fd, fp, entry);
    }
    fflush(fp);
    QM_send_bulk_end(rsock);

    fclose(fp);
    return SUCCESS;
}

/* ----------------------------------------------------------------- *
 * NEB_del_query -- delete all objects that match the query.
 * ----------------------------------------------------------------- */
LOCAL int NEB_del_query(rsock, head)
     int rsock;
     POINTER head;
{
    POINTER ptr;
    fd_t fd;
    reg_t *entry;

    for (ptr = head; ptr != NIL; ptr = CDR(ptr)) {
	fd = GETFIXNUM(CAR(ptr));
	if ((entry = RG_Get_Entry(fd) != NULL)) {
	    COL_DEL_Obj(entry);
	}
	return SUCCESS;
    }
}

/* ----------------------------------------------------------------- *
 * NEB_user_query -- Read the output of the Nebula query from the pointer
 * head then send to rsock via protocol.
 * ----------------------------------------------------------------- */
LOCAL int NEB_user_query(rsock, head)
     int rsock;
     POINTER head;
{
    POINTER ptr;
    fd_t fd;
    int obcnt = 0;

    for (ptr = head; ptr != NIL; ptr = CDR(ptr)) {
	fd = GETFIXNUM(CAR(ptr));
	QM_user_object(rsock, fd, 0, NULL);
	obcnt++;
    }

    QM_user_done(rsock, obcnt);

    return SUCCESS;
}

/* ----------------------------------------------------------------- *
 * NEB_qprocess -- Recursive function to build a query from the list.
 * ----------------------------------------------------------------- */
LOCAL POINTER NEB_qbuild(ql)
     qlist_t *ql;
{
    POINTER ptr;

    if (ql == NULL)
	return (NIL);

    switch (ql->type) {
    case LOGICAL:
	switch (ql->op) {
	case AND:
	    ptr = CVLIST(NEB_qbuild(ql->rlist), NIL);
	    ptr = CVLIST(NEB_qbuild(ql->llist), ptr);
	    ptr = CVLIST(CVSYM("INTERSECT-SET"), ptr);
	    return (ptr);

	case OR:
	    ptr = CVLIST(NEB_qbuild(ql->rlist), NIL);
	    ptr = CVLIST(NEB_qbuild(ql->llist), ptr);
	    ptr = CVLIST(CVSYM("UNION-SET"), ptr);
	    return (ptr);

	case NOT:
	    ptr = CVLIST(NEB_qbuild(ql->llist), NIL);
	    ptr = CVLIST(CVSYM("NOT"), ptr);
	    return (ptr);

	case EXCEPT:
	    ptr = CVLIST(NEB_qbuild(ql->rlist), NIL);
	    ptr = CVLIST(NEB_qbuild(ql->llist), ptr);
	    ptr = CVLIST(CVSYM("DIFF-SET"), ptr);
	    return (ptr);

	default:
#if DEBUG0
	    fprintf(stderr, "Unknown logical operation %d.\n", (int) ql->op);
#endif
	    return (NIL);
	}

    case SELECT:
	switch (ql->op) {
	case EXACT:
	    ptr = CVLIST(CVSTRING(ql->rlist), NIL);
	    if (ql->llist != NULL)
		ptr = CVLIST(CVSTRING(ql->llist), ptr);
	    else
		ptr = CVLIST(CVSTRING("*keys*"), ptr);
	    ptr = CVLIST(CVSYM("HARVEST-EQUAL"), ptr);
	    return (ptr);

	case REGEX:
	    ptr = CVLIST(CVSTRING(ql->rlist), NIL);
	    if (ql->llist != NULL)
		ptr = CVLIST(CVSTRING(ql->llist), ptr);
	    else
		ptr = CVLIST(CVSTRING("*keys*"), ptr);
	    ptr = CVLIST(CVSYM("HARVEST-REGEX"), ptr);
	    return (ptr);

	case GTT:
	    ptr = CVLIST(CVSTRING(ql->rlist), NIL);
	    if (ql->llist != NULL)
		ptr = CVLIST(CVSTRING(ql->llist), ptr);
	    else
		ptr = CVLIST(CVSTRING("*keys*"), ptr);
	    ptr = CVLIST(CVSYM("HARVEST-GT"), ptr);
	    return (ptr);

	case LST:
	    ptr = CVLIST(CVSTRING(ql->rlist), NIL);
	    if (ql->llist != NULL)
		ptr = CVLIST(CVSTRING(ql->llist), ptr);
	    else
		ptr = CVLIST(CVSTRING("*keys*"), ptr);
	    ptr = CVLIST(CVSYM("HARVEST-LT"), ptr);
	    return (ptr);

	default:
#if DEBUG0
	    fprintf(stderr, "Unknown select operation %d.\n", (int) ql->op);
#endif
	    return (NIL);
	}

    default:
#if DEBUG0
	fprintf(stderr, "Unknown operation type %d.\n", (int) ql->type);
#endif
	return (NIL);
    }
}

/* ----------------------------------------------------------------- *
 * NEB_qprocess -- Process the query.
 * ----------------------------------------------------------------- */
POINTER NEB_qprocess(query)
     POINTER query;
{
    static int fclass = -1;
    static int fhandle = -1;
    POINTER ptr1, ptr2;

    /* ----- Initialization ----- */
    unpi_error = NULL;
    if (uptr == NULL)
	NEB_initcon();

    if (fclass == -1) {
	if (!unpi_getfunction(uptr, "harvest::process-query", &fclass, &fhandle))
	    return NIL;
#if DEBUG2
	fprintf(stdout, "harvest::process-query: %d %d\n", fclass, fhandle);
#endif
    }
    /* ----- Create argument list ----- */
    ptr1 = CVLIST(query, NIL);

    /* ----- Process RPC ----- */
    ptr2 = ccall(uptr->channel, fclass, fhandle, NEB_key, NEB_context, ptr1, NIL);
    if ((ptr2 == NIL) && (unpi_error != NULL)) {
	fprintf(stderr, "Nebula process-query failed: %s\n", (char *) unpi_error);
	FREEP(ptr1);
	free(unpi_error);
	return NIL;
    }
#if DEBUG2
    fprintf(stdout, "Successfully processed query:\n");
    writeexp(stdout, ptr2);
    unpputc(stdout, '\n');
#endif

    /* ----- Clean up ----- */
    FREEP(ptr1);

    return (ptr2);
}


/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX *
 * 
 * PUBLIC FUNCTIONS
 * 
 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */

/* ----------------------------------------------------------------- *
 * info -- UNP info function
 * ----------------------------------------------------------------- */
info(fmt, msg)
     char *fmt, *msg;
{
    extern char *unpi_error;
    static char buf[STRMAX];

    sprintf(buf, fmt, msg);
#if DEBUG2
    fprintf(stderr, "%s\n", buf);
#endif
    unpi_error = (char *) strdup(buf);
}


/* ----------------------------------------------------------------- *
 * IND_New_Object -- index a new object
 * ----------------------------------------------------------------- */
int Nebula_IND_New_Object(entry)
     reg_t *entry;
{
    NEBq *qn;

    switch (IndexType) {
    case I_FULL:
	/* Ignore, we'll get it later. */
	return (SUCCESS);

    case I_INCR:
	qn = (NEBq *) xmalloc(sizeof(NEBq));
	qn->fname = SM_Get_Obj_Filename(entry->FD);
	qn->next = NEB_indexq;
	NEB_indexq = qn;
	return (SUCCESS);

    case I_PER_OBJ:
	return (NEB_Index_Object(entry));

    default:
	/* SHOULDN'T HAPPEN!! */
	return (ERROR);
    }
}

/* ----------------------------------------------------------------- *
 * INDIndex_Full -- builds an index of all of the object files.
 * ----------------------------------------------------------------- */
int Nebula_IND_Index_Full()
{
    return (NEB_Index_Full());
}
/* ----------------------------------------------------------------- *
 * IND_Index_Incremental -- do a full incremental update of the index.
 * ----------------------------------------------------------------- */
int Nebula_IND_Index_Incremental()
{
    return (NEB_Index_Batch());
}

/* ----------------------------------------------------------------- *
 * IND_Index_Start -- prepare for indexing a stream of objects.
 * ----------------------------------------------------------------- */
int Nebula_IND_Index_Start()
{
    NEB_indexq = NEB_deleteq = NULL;
}

/* ----------------------------------------------------------------- *
 * IND_Index_Flush -- finish indexing a stream of objects.
 * ----------------------------------------------------------------- */
int Nebula_IND_Index_Flush()
{
    int stat = SUCCESS;

    switch (IndexType) {
    case I_FULL:
	return (NEB_Index_Full());

    case I_INCR:
	if (NEB_deleteq != NULL)
	    if (NEB_Destroy_Batch() == ERROR)
		stat = ERROR;
	if (NEB_indexq != NULL)
	    if (NEB_Index_Batch() == ERROR)
		stat = ERROR;
	return (stat);

    case I_PER_OBJ:
	return (SUCCESS);

    default:
	/* SHOULDN'T HAPPEN!! */
	return (ERROR);
    }
}

/* ----------------------------------------------------------------- *
 * IND_Destroy_Obj -- remove an object from the indexer.
 * ----------------------------------------------------------------- */
int Nebula_IND_Destroy_Obj(entry)
     reg_t *entry;
{
    NEBq *qn;

    switch (IndexType) {
    case I_FULL:
	/* Ignore, we'll get it later. */
	return (SUCCESS);

    case I_INCR:
	qn = (NEBq *) xmalloc(sizeof(NEBq));
	qn->fname = SM_Get_Obj_Filename(entry->FD);
	qn->next = NEB_deleteq;
	NEB_deleteq = qn;
	return (SUCCESS);

    case I_PER_OBJ:
	return (NEB_Destroy_Object(entry));

    default:
	/* SHOULDN'T HAPPEN!! */
	return (ERROR);
    }
}

/* ----------------------------------------------------------------- *
 * IND_initialize -- initialize interface to indexer
 * ----------------------------------------------------------------- */
int Nebula_IND_initialize()
{
    extern char *HName;
    int context;

    if ((NEB_cname = strdup("broker")) == NULL)
	fatal("strdup failed");

    if ((NEB_host = strdup(HName)) == NULL)
	fatal("strdup failed");

    return (SUCCESS);
}

/* ----------------------------------------------------------------- *
 * IND_Init_Flags -- initialize parser flags
 * ----------------------------------------------------------------- */
void Nebula_IND_Init_Flags()
{
}

/* ----------------------------------------------------------------- *
 * IND_Set_Flags -- set query parser flag
 * ----------------------------------------------------------------- */
void Nebula_IND_Set_Flags(flag, val)
     char *flag;
     char *val;
{
}

/* ----------------------------------------------------------------- *
 * IND_config -- configure indexer specific variables
 * ----------------------------------------------------------------- */
int Nebula_IND_config(value, tag)
     char *value;
     char *tag;
{
#if DEBUG1
    if (tag != NULL)
	printf("Nebula Configuration: %s %s\n", value, tag);
#endif

    if (tag != NULL) {
	if (strcasecmp(tag, NEB_HOST) == 0) {
	    if ((NEB_host = strdup(value)) == NULL)
		fatal("strdup failed");
	    return SUCCESS;
	} else if (strcasecmp(tag, NEB_PORT) == 0) {
	    if (sscanf(value, "%d", &NEB_port) < 1) {
		fprintf(stderr, "Could not convert Nebula port. \n");
		return ERROR;
	    }
	    if (NEB_port < 0) {
		fprintf(stderr, "Invalid Nebula port number %d.\n", NEB_port);
		NEB_port = 0;
		return ERROR;
	    }
	    return SUCCESS;
	} else if (strcasecmp(tag, NEB_KEY) == 0) {
	    if (sscanf(value, "%d", &NEB_key) < 1) {
		fprintf(stderr, "Count not convert Nebula key.\n");
		return ERROR;
	    }
	    return SUCCESS;
	} else if (strcasecmp(tag, NEB_CNAME) == 0) {
	    if ((NEB_cname = strdup(value)) == NULL)
		fatal("strdup failed");
	    return SUCCESS;
	}
    }				/* tag != NULL */
    return ERROR;
}

/* ----------------------------------------------------------------- *
 * IND_do_query -- construct a query based upon the query list 
 * structure.
 * ----------------------------------------------------------------- */
int Nebula_IND_do_query(ql, rsock, qflag, ptime)
     qlist_t *ql;
     int rsock;
     int qflag;
     time_t ptime;
{
    POINTER ptr1, ptr2;

    if ((ptr1 = NEB_qbuild(ql)) != NIL)
	ptr2 = NEB_qprocess(ptr1);
    else
	ptr2 = NIL;

    switch (qflag) {
    case UQUERY:
	return (NEB_user_query(rsock, ptr2));

    case QBULK:
	return (NEB_bulk_query(rsock, ptime, ptr2));

    case QDELETE:
	return (NEB_del_query(rsock, ptr2));

    default:
#if DEBUG0
	fprintf(stderr, "Invalid query type %d.\n", qflag);
#endif
	return ERROR;
    }
}
