/* 
   'search' for cadaver
   Copyright (C) 2004-2006, Joe Orton <joe@manyfish.co.uk>
   Copyright (C) 2002, GRASE Lab, UCSC <grase@cse.ucsc.edu>, 
   except where otherwise indicated.
                                                                     
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
  
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
  
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "config.h"

#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif

#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include <ctype.h>
#include <time.h>

#include <ne_basic.h>
#include <ne_request.h>
#include <ne_props.h>
#include <ne_uri.h>
#include <ne_alloc.h>
#include <ne_dates.h>

#include "i18n.h"
#include "commands.h"
#include "cadaver.h"
#include "basename.h"
#include "options.h"
#include "utils.h"

/* From ne_props.c
 * Need to move util.c or something? */
#define NSPACE(x) ((x) ? (x) : "")

/*
 * The Macro definations that 'wordtype' might be.
 */
#define QUOT            101	/*''' */
#define COMMA           102	/*',' */
#define LPAR            103	/*'(' */
#define RPAR            104	/*')' */
#define EQ              105	/*'=' */
#define LE              106	/*'<=' */
#define LT              107	/*'<' */
#define GE              108	/*'>=' */
#define GT              109	/*'>' */
#define NEQ             110	/*'<>' */
#define IDEN            111	/*Integer */
#define INTEGER         112	/*identifier */
#define UNKNOWN         113	/*unknown charactor */
#define ENDBUF          114	/*End of the buffer */

#define WORDLEN 256		/*Max length of a identifier(token) in the search command */

#undef EOL
#define EOL "\n"

/* Dead prop */
typedef struct dead_prop
{
    char *name;
    char *nspace;
    char *value;
    struct dead_prop *next;
}
dead_prop;

typedef struct search_res
{
    char *href;
    /* live props */
    char *creationdate;
    char *displayname;
    char *getcontentlanguage;
    char *getcontentlength;
    char *getcontenttype;
    char *getetag;
    char *getlastmodified;
    char *lockdiscovery;
    char *resourcetype;
    char *source;
    char *supportedlock;
    char *collection;

    dead_prop *root;
    dead_prop *curr;
    int dead_prop_num;

    struct search_res *next;
}
search_res;

/* Search XML parser context */
typedef struct
{
    search_res *root;
    search_res *curr;
    int result_num;
    int start_prop;
    int err_code;

    ne_buffer *cdata;
}
search_ctx;

enum
{
    ELEM_multistatus = 1,
    ELEM_response,
    ELEM_href,
    ELEM_prop,
    ELEM_propstat,
    ELEM_status,
    ELEM_responsedescription,

    /* props from RFC 2518 , 23 Appendices 23.1 */
    ELEM_creationdate,
    ELEM_displayname,
    ELEM_getcontentlanguage,
    ELEM_getcontentlength,
    ELEM_getcontenttype,
    ELEM_getetag,
    ELEM_getlastmodified,
    ELEM_lockdiscovery,
    ELEM_resourcetype,
    ELEM_source,
    ELEM_supportedlock,
    ELEM_collection,

    ELEM_ignore
};

static const struct ne_xml_idmap search_elements[] = {
    {"DAV:", "multistatus", ELEM_multistatus},
    {"DAV:", "response", ELEM_response},
    {"DAV:", "responsedescription", ELEM_responsedescription},
    {"DAV:", "href", ELEM_href},
    {"DAV:", "propstat", ELEM_propstat},
    {"DAV:", "prop", ELEM_prop},
    {"DAV:", "status", ELEM_status},

    /* Live props */
    {"DAV:", "creationdate", ELEM_creationdate},
    {"DAV:", "displayname", ELEM_displayname},
    {"DAV:", "getcontentlanguage", ELEM_getcontentlanguage},
    {"DAV:", "getcontentlength", ELEM_getcontentlength},
    {"DAV:", "getcontenttype", ELEM_getcontenttype},
    {"DAV:", "getetag", ELEM_getetag},
    {"DAV:", "getlastmodified", ELEM_getlastmodified},
    {"DAV:", "lockdiscovery", ELEM_lockdiscovery},
    {"DAV:", "resourcetype", ELEM_resourcetype},
    {"DAV:", "source", ELEM_source},
    {"DAV:", "supportedlock", ELEM_supportedlock},
    {"DAV:", "collection", ELEM_collection},

    /* Ignore it for now */
    {"DAV:", "lockentry", ELEM_ignore},
    {"DAV:", "lockscope", ELEM_ignore},
    {"DAV:", "locktype", ELEM_ignore},
    {"DAV:", "exclusive", ELEM_ignore},
    {"DAV:", "shared", ELEM_ignore},
    {"DAV:", "read", ELEM_ignore},
    {"DAV:", "write", ELEM_ignore},
};

/* 
 * Basic search parser functions
 * return NE_OK or error_no
 * basic_search must be allcated before the function call.
 */
static int search_select_gen(const ne_propname * props,
			     ne_buffer * basic_search);
static int search_from_gen(const char *href, const int depth,
			   ne_buffer * basic_search);
static int search_where_gen(const char *query, ne_buffer * basic_search);
static int search_orderby_gen(const ne_propname * asc,
			      const ne_propname * des,
			      ne_buffer * basic_search);

/*
 * Static functions for where condition parser
 */
static int read_aword(char **string_parsed, char *word_fetched);
static int first_word_equal(const char *string_parsed,
			    const char *word_to_compare);
static int match_fetch(char **string_parsed, const char *str_expected);
static int search_condition(char **string_parsed, ne_buffer * result_buf);
static int boolean_term(char **strparsed, ne_buffer * result_buf);
static int boolean_factor(char **string_parsed, ne_buffer * result_buf);
static int boolean_primary(char **string_parsed, ne_buffer * result_buf);
static int operator_translate(const char *operator, char *XML_operator);
static int predicate(char **string_parsed, ne_buffer * result_buf);
static int contains_predicate(char **string_parsed, ne_buffer * result_buf);
static int quoted_string(char **string_parsed, ne_buffer * result_buf);
static int comparison_value(char **string_parsed, ne_buffer * result_buf);
static int word_string(char **string_parsed, ne_buffer * result_buf);

/* Set xml parser error */
static void set_xml_error(search_ctx *sctx, const char *format, ...)
{
    va_list ap;
    char buf[512];

    va_start(ap, format);
    ne_vsnprintf(buf, sizeof buf, format, ap);
    va_end(ap);

    ne_set_error(session.sess, "%s", buf);
    sctx->err_code = NE_ERROR;
}

static int start_element(void *userdata, int parent,
			 const char *nspace, 
			 const char *name, 
			 const char **atts)
{
    search_ctx *sctx = (search_ctx *) userdata;
    int state = ne_xml_mapid(search_elements, 
			     NE_XML_MAPLEN(search_elements), nspace, name);

    /* Error occured, ignore remain part */
    if (sctx->err_code != NE_OK)
	return sctx->err_code;

    ne_buffer_clear(sctx->cdata);

    switch (state) {
    case ELEM_ignore:
	break;

    case ELEM_response:	/* Start of new response */
	sctx->curr = ne_calloc(sizeof(search_res));
	sctx->result_num++;
	break;

    case ELEM_href:		/* href */
	break;

    case ELEM_propstat:	/* expecting props */
	break;

    case ELEM_prop:		/* Start of prop */
	if (sctx->curr == NULL) {
	    set_xml_error(sctx, "XML : <%s> is in the wrong place",
			  name);
	    break;
	}
	sctx->start_prop = 1;
	break;
    default:
	if (get_bool_option(opt_searchall) &&	/* check searchall option */
	    sctx->curr && sctx->start_prop == 1) {	/* It's prop */
	    search_res *res = sctx->curr;
	    res->dead_prop_num++;
	    res->curr = (dead_prop *) ne_calloc(sizeof(dead_prop));
	    res->curr->name = ne_strdup(name);
	    res->curr->nspace = ne_strdup(nspace);
	}
	break;
    }

    return state;
}

#define SEARCH_CP_ELEM(sctx, curr, name, desc, src) \
do { \
      if ((curr) == NULL) \
         set_xml_error((sctx),  "XML : </%s> is in the wrong place", (name));\
      else if (src)\
         (desc) = ne_strdup(src);\
} while (0)


static int cdata_search(void *userdata, int state, const char *buf, size_t len)
{
    search_ctx *sctx = (search_ctx *) userdata;
    ne_buffer_append(sctx->cdata, buf, len);
    return 0;
}


static int 
end_element(void *userdata, int state, const char *nspace, const char *name)
{
    search_ctx *sctx = (search_ctx *) userdata;
    const char *cdata = sctx->cdata->data;

    /* Error occured, ignore remain part */
    if (sctx->err_code != NE_OK)
	return sctx->err_code;

    switch (state) {
    case ELEM_ignore:
	break;
	
    case ELEM_response:	/* End of new response */
	/* Nothing to add */
	if (sctx->curr == NULL) {
	    set_xml_error(sctx, "XML : </%s> is in the wrong place",
			  name);
	    break;
	}

	/* No HREF */
	if (sctx->curr->href == NULL) {
	    set_xml_error(sctx, "XML : No href info in the <%s>...</%s>",
			  name, name);
	    break;
	}
	/* make link */
	sctx->curr->next = sctx->root;
	sctx->root = sctx->curr;
	sctx->curr = NULL;
	break;

    case ELEM_href:		/* href */
	SEARCH_CP_ELEM(sctx, sctx->curr, name, sctx->curr->href, cdata);
	break;

	/* live props */
    case ELEM_creationdate:
	SEARCH_CP_ELEM(sctx, sctx->curr, name,
		       sctx->curr->creationdate, cdata);
	break;

    case ELEM_displayname:
	SEARCH_CP_ELEM(sctx, sctx->curr, name,
		       sctx->curr->displayname, cdata);
	break;

    case ELEM_getcontentlanguage:
	SEARCH_CP_ELEM(sctx, sctx->curr, name,
		       sctx->curr->getcontentlanguage, cdata);
	break;

    case ELEM_getcontentlength:
	SEARCH_CP_ELEM(sctx, sctx->curr, name,
		       sctx->curr->getcontentlength, cdata);
	break;

    case ELEM_getcontenttype:
	SEARCH_CP_ELEM(sctx, sctx->curr, name,
		       sctx->curr->getcontenttype, cdata);
	break;

    case ELEM_getetag:
	SEARCH_CP_ELEM(sctx, sctx->curr, name,
		       sctx->curr->getetag, cdata);
	break;

    case ELEM_getlastmodified:
	SEARCH_CP_ELEM(sctx, sctx->curr, name,
		       sctx->curr->getlastmodified, cdata);
	break;

    case ELEM_lockdiscovery:
	SEARCH_CP_ELEM(sctx, sctx->curr, name,
		       sctx->curr->lockdiscovery, cdata);
	break;

    case ELEM_resourcetype:
	SEARCH_CP_ELEM(sctx, sctx->curr, name,
		       sctx->curr->resourcetype, cdata);
	break;

    case ELEM_source:
	SEARCH_CP_ELEM(sctx, sctx->curr, name,
		       sctx->curr->source, cdata);
	break;

    case ELEM_supportedlock:
	SEARCH_CP_ELEM(sctx, sctx->curr, name,
		       sctx->curr->supportedlock, cdata);
	break;

    case ELEM_collection:
	SEARCH_CP_ELEM(sctx, sctx->curr, name,
		       sctx->curr->collection, cdata);
	break;

    case ELEM_propstat:	/* expecting props */
	break;

    case ELEM_prop:		/* Start of prop */
	if (sctx->curr == NULL)
	    set_xml_error(sctx, "XML : </%s> is in the wrong place",
			  name);
	else			/* stop to props */
	    sctx->start_prop = 0;
	break;

    default:
	if (get_bool_option(opt_searchall) &&	/* check searchall option */
	    sctx->curr && sctx->start_prop == 1) {	/* It's dead prop */
	    search_res *res = sctx->curr;
	    res->curr->value = ne_strdup(cdata);
	    res->curr->next = res->root;
	    res->root = res->curr;
	    res->curr = NULL;
	}
	break;
    }

    return NE_OK;
}

#define RESULT_PER_PAGE 15

/* displays search results */
static int display_results(search_ctx * sctx)
{
    search_res *res;
    dead_prop *dprop;
    int i;

    if (sctx->err_code) {
	return sctx->err_code;
    }

    for (i=1, res = sctx->root; res; res = res->next, i++) {
	long modtime = res->getlastmodified ?
	    ne_httpdate_parse(res->getlastmodified) : 0;
	int size = res->getcontentlength ? atol(res->getcontentlength) : 0;
	char exec_char = ' ';

	if (i%RESULT_PER_PAGE ==1) {
	    printf("Found %d results (%d-%d)\n\n", 
		   sctx->result_num, i, 
		   sctx->result_num<i+9?sctx->result_num:i+9);
	}

	printf("[%d] %-40s%c %10d  %s <%.10s>\n", i, res->href, exec_char,
	       size, format_time(modtime), 
	       res->getcontenttype?res->getcontenttype:"");

	for (dprop = res->root;
	     get_bool_option(opt_searchall) && dprop; dprop = dprop->next)
	    printf("\t-  %s:%s = %s\n",	/* better way to show ? */
		   dprop->nspace, dprop->name, dprop->value);
	
	if (i%RESULT_PER_PAGE == 0) {
	    char buf[256];
	    printf("-- Enter to More, 'q' to QUIT --");
	    fgets(buf, 255, stdin);
	    if (*buf=='q') break;
	}
	
    }

    return sctx->err_code;
}

static void search_ctx_destroy(search_ctx * sctx)
{
    search_res *res, *res_free;
    dead_prop *dprop, *dprop_free;

    ne_buffer_destroy(sctx->cdata);

    for (res = sctx->root; res;) {
	if (res->href) ne_free(res->href);
	/* live props */
	if (res->creationdate) ne_free(res->creationdate);
	if (res->displayname) ne_free(res->displayname);
	if (res->getcontentlanguage) ne_free(res->getcontentlanguage);
	if (res->getcontentlength) ne_free(res->getcontentlength);
	if (res->getcontenttype) ne_free(res->getcontenttype);
	if (res->getetag) ne_free(res->getetag);
	if (res->getlastmodified) ne_free(res->getlastmodified);
	if (res->lockdiscovery) ne_free(res->lockdiscovery);
	if (res->resourcetype) ne_free(res->resourcetype);
	if (res->source) ne_free(res->source);
	if (res->supportedlock) ne_free(res->supportedlock);
	if (res->collection) ne_free(res->collection);

	for (dprop = res->root; dprop;) {
	    if (dprop->nspace) ne_free(dprop->nspace);
	    if (dprop->name) ne_free(dprop->name);
	    if (dprop->value) ne_free(dprop->value);

	    dprop_free = dprop;
	    dprop = dprop->next;
	    ne_free(dprop_free);
	}
	res_free = res;
	res = res->next;
	ne_free(res_free);
    }
}

/* create propname struct from searchorder setting */
static ne_propname *order_props_create(const char *str)
{
    int n;
    char *buf;
    char *tok;
    char *delm = " \t\n\r";
    int num_props = 0;
    ne_propname *props;

    /* No props */
    if (str == NULL)
	return NULL;

    buf = ne_strdup(str);
    if (strtok(buf, delm)) {
	num_props = 1;
	while (strtok(NULL, delm))
	    num_props++;
    }

    /* No props */
    if (num_props == 0)
	return NULL;

    /* One more for last NULL */
    props = ne_calloc(sizeof(ne_propname) * (num_props + 1));

    /* Set first token */
    ne_free(buf);
    buf = ne_strdup(str);
    tok = strtok(buf, delm);

    /* Other idea? */
    props[0].nspace = ne_strdup("DAV:");
    props[0].name = ne_strdup(tok);

    for (n = 1; (tok = strtok(NULL, delm)); n++) {
	props[n].nspace = ne_strdup("DAV:");
	props[n].name = ne_strdup(tok);
    }

    ne_free(buf);
    return props;
}

static void order_props_destroy(ne_propname * props)
{
    int n;

    if (props == NULL)
	return;

    for (n = 0; props[n].name != NULL; n++) {
	free((char *)props[n].name);
	free((char *)props[n].nspace);
    }

    free(props);
    props = NULL;
}

/* Run search and get the data to sctx */
static int run_search(ne_session * sess, const char *uri,
		      int depth, ne_buffer * query, search_ctx * sctx)
{
    int ret;
    ne_request *req;
    ne_buffer *basic_search = ne_buffer_create();
    ne_xml_parser *search_parser;
    const char *searchorder = (const char *) get_option(opt_searchorder);
    const char *searchdorder = (const char *) get_option(opt_searchdorder);
    ne_propname *asc = order_props_create(searchorder);
    ne_propname *des = order_props_create(searchdorder);

    /* create/prep the request */
    if ((req = ne_request_create(sess, "SEARCH", uri)) == NULL)
	return NE_ERROR;

    /* Create the request body */
    ne_buffer_zappend(basic_search,
		      "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" EOL
		      "<D:searchrequest xmlns:D=\"DAV:\"><D:basicsearch>"
		      EOL);

    if (search_select_gen(NULL, basic_search) != NE_OK)
	return NE_ERROR;

    if (search_from_gen(uri, depth, basic_search) != NE_OK)
	return NE_ERROR;

    if (search_where_gen(query->data, basic_search) != NE_OK)
	return NE_ERROR;

    if (search_orderby_gen(asc, des, basic_search) != NE_OK)
	return NE_ERROR;

    ne_buffer_zappend(basic_search, "</D:basicsearch></D:searchrequest>" EOL);

    ne_set_request_body_buffer(req, basic_search->data,
			       ne_buffer_size(basic_search));

    /* Plug our XML parser */
    search_parser = ne_xml_create();
    ne_xml_push_handler(search_parser, start_element, 
			cdata_search, end_element,
			sctx);
    
    ne_add_request_header(req, "Content-Type", NE_XML_MEDIA_TYPE);
    ne_add_depth_header(req, depth);

    ne_add_response_body_reader(req, ne_accept_207, ne_xml_parse_v,
				search_parser);

    /* run the request, see what comes back. */
    if ((ret = ne_request_dispatch(req)) != NE_OK)
	return ret;

    /* Check Errors from XML parser */
    if (sctx->err_code != NE_OK)
	return NE_ERROR;

    /* Get response code */
    if (ne_get_status(req)->code != 207)
	return NE_ERROR;

    /* destroy request, parse, and etc */
    order_props_destroy(asc);
    order_props_destroy(des);

    ne_buffer_destroy(basic_search);
    ne_request_destroy(req);
    ne_xml_destroy(search_parser);

    return NE_OK;
}

/* Main execute routine for search */
void execute_search(int count, const char **args)
{
    int ret;
    const char **pnt;
    ne_buffer *query = ne_buffer_create();
 
    search_ctx *sctx = ne_calloc(sizeof(search_ctx));
    sctx->cdata = ne_buffer_create();
 
    /* default is success */
    sctx->err_code = NE_OK;

    for (pnt = args; *pnt != NULL; pnt++) {
	/* Need quota */
	if (strchr(*pnt, ' ') || strchr(*pnt, '\t'))
	    ne_buffer_concat(query, "'", *pnt, "' ", NULL);
	else
	    ne_buffer_concat(query, *pnt, " ", NULL);
    }

    printf(_("Using query: "));
    printf("%s, ", query->data);

    /* Run search and get data to sctx */
    ret = run_search(session.sess, session.uri.path, searchdepth, query, sctx);
    if (ret == NE_OK) {
	display_results(sctx);
    }

    out_result(ret);

    search_ctx_destroy(sctx);
    ne_buffer_destroy(query);
}

/* Generate select part of the query. allprop if the props arg is NULL. */
static int search_select_gen(const ne_propname * props,
			     ne_buffer * basic_search)
{
    int n;

    if (!basic_search) {
	ne_set_error(session.sess, "select_gen: no buffer");
	return NE_ERROR;
    }

    if (props == NULL) {
	ne_buffer_zappend(basic_search,
			  "<D:select><D:allprop/></D:select>" EOL);
	return NE_OK;
    }

    ne_buffer_zappend(basic_search, "<D:select><D:prop>" EOL);

    for (n = 0; props[n].name != NULL; n++) {
	ne_buffer_concat(basic_search, "<", props[n].name, " xmlns=\"",
			 NSPACE(props[n].nspace), "\"/>" EOL, NULL);
    }

    ne_buffer_zappend(basic_search, "</D:prop></D:select>" EOL);

    return NE_OK;
}

static int search_from_gen(const char *href, const int depth,
			   ne_buffer * basic_search)
{
    const char *depth_str;

    if (!basic_search || !href) {
	ne_set_error(session.sess, "from_gen: no buffer or no href");
	return NE_ERROR;
    }

    switch (depth) {
    case NE_DEPTH_ONE:
	depth_str = "1";
	break;
    case NE_DEPTH_ZERO:
	depth_str = "0";
	break;
    default:
	depth_str = "infinity";
	break;
    }

    ne_buffer_concat(basic_search, "<D:from><D:scope><D:href>",
		     href, "</D:href><D:depth>", depth_str,
		     "</D:depth></D:scope></D:from>" EOL, NULL);

    return NE_OK;
}

/* Parse a searchquery. It will call the search_condition() function and
 * check the ending status. If the ending status is not ENDBUF, then there will
 * be a syntax error.
 * The parsing result will be saved in 'result_buf'.
 *
 * Returns:
 * NE_OK: success
 * NE_ERROR: syntax error
 * */
static int search_where_gen(const char *condition_str,
			    ne_buffer * basic_search)
{
    char identifier[WORDLEN + 1] = "";
    char *string_parsed = ne_strdup(condition_str);
    char *ptr_backup = string_parsed;
    /* The buffer storing the parsing result of search condition */
    ne_buffer *result_buf;

    if (!basic_search || !condition_str) {
	ne_set_error(session.sess, "where_gen: no buffer or no query");
	return NE_ERROR;
    }

    result_buf = ne_buffer_create();

    /* Fill boby from <D:where> to </D:where> */
    ne_buffer_zappend(basic_search, "<D:where>" EOL);

    if (search_condition(&string_parsed, result_buf) == NE_ERROR) {
	ne_free(ptr_backup);
	ne_buffer_destroy(result_buf);
	return NE_ERROR;	/*Parsing error */
    }

    /*The ending of a condition must be an ENDBUF */
    if (read_aword(&string_parsed, identifier) != ENDBUF) {
	ne_set_error(session.sess, "Syntax error in the search condition.");
	ne_free(ptr_backup);
	ne_buffer_destroy(result_buf);
	return NE_ERROR;
    }

    /*Append the parsing result of the search condition */
    ne_buffer_zappend(basic_search, result_buf->data);

    ne_buffer_zappend(basic_search, "</D:where>" EOL);

    ne_free(ptr_backup);
    ne_buffer_destroy(result_buf);

    return NE_OK;
}				/*End of ne_search_where_gen */

static int search_orderby_gen(const ne_propname * asc,
			      const ne_propname * des,
			      ne_buffer * basic_search)
{
    int n;

    if (!basic_search) {
	ne_set_error(session.sess, "orderby_gen: no buffer or no query");
	return NE_ERROR;
    }

    /* No order information */
    if (asc == NULL && des == NULL)
	return NE_OK;

    ne_buffer_zappend(basic_search, "<D:orderby>" EOL);

    for (n = 0; asc && asc[n].name != NULL; n++) {
	ne_buffer_zappend(basic_search, "<D:order><D:prop>" EOL);
	ne_buffer_concat(basic_search, "<", asc[n].name, " xmlns=\"",
			 NSPACE(asc[n].nspace), "\"/>" EOL, NULL);
	ne_buffer_zappend(basic_search,
			  "</D:prop><D:ascending/></D:order>" EOL);
    }

    for (n = 0; des && des[n].name != NULL; n++) {
	ne_buffer_zappend(basic_search, "<D:order><D:prop>" EOL);
	ne_buffer_concat(basic_search, "<", des[n].name, " xmlns=\"",
			 NSPACE(des[n].nspace), "\"/>" EOL, NULL);
	ne_buffer_zappend(basic_search,
			  "</D:prop><D:descending/></D:order>" EOL);
    }

    ne_buffer_zappend(basic_search, "</D:orderby>" EOL);

    return NE_OK;
}


/* Search command parser
 *
 * This is the Search command parser implementation. 
 * The goal of this code is to translate
 * dasl search command into XML format.
 *
 * The user interface of search command is:
 *  search resource_URI display_fields condition orderby
 *
 *  This code will parse the three parts of the search command, 
 *  dispolay_fields,
 *   
 *  condition and orderby, and translate them into XML, which is a part of a 
 *  dasl request.
 * 
 * The BNF of display_fields, condition and orderby is as below:
 *
 *<display_fields> ::= identifier {,identifier}
 *<orderby clause> ::= identifier ["asc"|"desc"] {,identifier ["asc"|"desc"]}
 *
 *<search condition> ::= <boolean term> | <search condition> " or " <boolean term>
 *<boolean term> ::= <boolean factor> | <boolean term> " and " <boolean factor>
 *<boolean factor> ::= [not] <boolean primary>
 *<boolean primary> ::= <predicate>|"("<search condition>")"
 *<predicate> ::= <comparison predicate>|<like predicate>|<isdefined predicate>
 *<comparison predicate> ::=  <column_name> <comparaison_op> 
 *                            ( <number> | <quoted_string> | <wordstring>) 
 *<like predicate> ::= <column_name> like <quoted_string> -----Formally, 
 *                      [not] like match_string
 *<contains predicate> ::= contains (<quoted_string> | <wordstring>)
 *<column_name>
 *      ::= identifier
 *<comparaison_op>
 *      ::= "=" | "<" | ">" | "<>" | "!=" | "<=" | ">="
 *<quoted_string> ::= "'" {<any_character>} "'"
 *<wordstring> ::= <any char except for blank, tab and ')'>{<any_character except for blank, tab and ')'>} 
 *<match_string>
 *      ::= "'" { <any_character> | "_" | "%" } "'"
 *<identifier> ::= <letter> { <letter>|<digital>|<underline>}
 *<number> ::= [+|-]<digital>{<digital>}
 */

/* Read a word from the buffer. The word is fetched into 'word_fetched'. 
 * Every time a word is read, '*string_parsed' is changed to the pointer
 * pointing to the char just after 'word_fetched'.
 * 
 * Parameters:
 *     string_parsed:  -- *string_parsed points to the buffer. '*string_parsed'
 *                           will be changed every time a word is fetched.
 *     word_fetched:   -- The word that is fetched.
 * Returns:
 *     ENDBUF , if the end of buffer, '\0', has been reached.
 *     UNKNOWN, if the word is invalid.
 *     According word type, if the word is valid.
 * */
static int read_aword(char **string_parsed, char *word_fetched)
{
    int i = 0;
    int ifetch = 0;
    char *stringptr = *string_parsed;

    word_fetched[0] = '\0';

    /*Find the left bound of a word */
    while (isspace(stringptr[i]))
	i++;

    if (stringptr[i] == '\0')
	return ENDBUF;

    /*
     * Check whether the next word is a identifier. An identifier may
     * be a keyword or a column name.
     */
    if (isalpha(stringptr[i])) {
	do {
	    word_fetched[ifetch++] = stringptr[i++];
	} while (isalnum(stringptr[i]) || (stringptr[i] == '_'));

	*string_parsed = *string_parsed + i;
	word_fetched[ifetch] = '\0';
	return IDEN;		/*Identifier */
    }

    /* Check whether the next word is an integer */
    /* A problem here: + , or - !!!!! PK */
    if (stringptr[i] == '+' || stringptr[i] == '-' || isdigit(stringptr[i])) {
	do {
	    word_fetched[ifetch++] = stringptr[i++];
	} while (isdigit(stringptr[i]));

	*string_parsed = *string_parsed + i;
	word_fetched[ifetch] = '\0';
	return INTEGER;		/*Identifier */
    }

    /* Check whether the next word is "<=", ">=", or "<>" */
    if ((stringptr[i] == '>') && (stringptr[i + 1] == '=')) {
	strcpy(word_fetched, ">=");
	*string_parsed = *string_parsed + i + 2;
	return GE;		/*Indicates >= */
    }

    if ((stringptr[i] == '<') && (stringptr[i + 1] == '=')) {
	strcpy(word_fetched, "<=");
	*string_parsed = *string_parsed + i + 2;
	return LE;		/*Indicates <= */
    }

    if ((stringptr[i] == '<') && (stringptr[i + 1] == '>')) {
	strcpy(word_fetched, "<>");
	*string_parsed = *string_parsed + i + 2;
	return NEQ;		/*Indicates <> */
    }

    /*Check whether the next word is "'", ",", "(", ")", ">",
     * "<", "=", */
    switch (stringptr[i]) {
    case '\'':
	strcpy(word_fetched, "'");
	*string_parsed = *string_parsed + i + 1;
	return QUOT;
    case ',':
	strcpy(word_fetched, ",");
	*string_parsed = *string_parsed + i + 1;
	return COMMA;
    case '(':
	strcpy(word_fetched, "(");
	*string_parsed = *string_parsed + i + 1;
	return LPAR;
    case ')':
	strcpy(word_fetched, ")");
	*string_parsed = *string_parsed + i + 1;
	return RPAR;
    case '>':
	strcpy(word_fetched, ">");
	*string_parsed = *string_parsed + i + 1;
	return GT;
    case '<':
	strcpy(word_fetched, "<");
	*string_parsed = *string_parsed + i + 1;
	return LT;
    case '=':
	strcpy(word_fetched, "=");
	*string_parsed = *string_parsed + i + 1;
	return EQ;
    default:
	word_fetched[0] = stringptr[i];
	word_fetched[1] = '\0';
	*string_parsed = *string_parsed + i + 1;
	return UNKNOWN;
    }				/*End of switch */
}				/*End of read_aword */

/*Read the first word from the string buffer, string_parsed. If the
 * first is equal to the 'word_to_compare', return 1, else return 0.
 * The first word will be saved to 'the_first_word', but the pointer
 * to the buffer, string_parsed, will not be changed.
 * Returns:
 * 1: The first word is equal to the word expected.
 * 0: otherwise
 * */
static int first_word_equal(const char *string_parsed,
			    const char *word_to_compare)
{
    char *string_buffer = NULL;
    char *ptr_backup = NULL;
    char first_word[WORDLEN + 1] = "";

    string_buffer = ne_strdup(string_parsed);
    ptr_backup = string_buffer;

    read_aword(&string_buffer, first_word);

    ne_free(ptr_backup);

    if (strcasecmp(first_word, word_to_compare) == 0)
	return 1;		/*equal */
    else
	return 0;		/*Not equal */
}				/*End of first_word_compare */

/*Read the first word from the string buffer, string_parsed. If the
 * first is an interger, return 1, else return 0.
 * The first word will be saved to 'the_first_word', but the pointer
 * to the buffer, string_parsed, will not be changed.
 * Returns:
 * 1: The first word is equal to the word expected.
 * 0: otherwise
 * */
static int first_word_integer(const char *string_parsed)
{
    char *string_buffer = NULL;
    char *ptr_backup = NULL;
    char first_word[WORDLEN + 1] = "";
    int ret;

    string_buffer = ne_strdup(string_parsed);
    ptr_backup = string_buffer;

    if (read_aword(&string_buffer, first_word) == INTEGER)
	ret = 1;
    else
	ret = 0;

    ne_free(ptr_backup);

    return ret;
}				/*End of first_word_compare */

/* The function reads the first word in the string buffer, *string_parsed, and
 * changes *string_parsed. 
 * Returns:
 * NE_ERROR: If the word read is equal to 'str_expected'.
 * NE_OK: Otherwise
 * */
static int match_fetch(char **string_parsed, const char *str_expected)
{
    char first_word[WORDLEN + 1] = "";

    read_aword(string_parsed, first_word);

    if (strcmp(first_word, str_expected) == 0)
	return NE_OK;
    else
	return NE_ERROR;

}				/*End of match_fetch */

/*
 * Parse the search condition
 * <search condition> ::= <boolean term> | <search condition> or <boolean term>
 *
 * The parsing result is saved into result_buf.
 * '*string_parsed' is changed to the pointer pointing to the position after
 * <search condition>.
 * Returns:
 * NE_OK: success
 * NE_ERROR: syntax error
 * */
static int search_condition(char **string_parsed, ne_buffer * result_buf)
{
    char identifier[WORDLEN + 1] = "";
    /* Indicates whether there is any 'or' in the search condition */
    int added_or = 0;
    ne_buffer *term_result;

    ne_buffer_clear(result_buf);

    term_result = ne_buffer_create();

    if (boolean_term(string_parsed, term_result) == NE_ERROR) {
	ne_buffer_destroy(term_result);	/*Free the buffer */
	return NE_ERROR;	/*parsing error */
    }

    if (first_word_equal(*string_parsed, "or") == 1) {
	added_or = 1;
	ne_buffer_concat(result_buf, "<D:or>" EOL, term_result->data, NULL);
    }
    else
	ne_buffer_zappend(result_buf, term_result->data);

    /*For or <boolean term> or <boolean term> ... */
    while (first_word_equal(*string_parsed, "or") == 1) {
	read_aword(string_parsed, identifier);	/*Read 'or' */

	if (boolean_term(string_parsed, term_result) == NE_ERROR) {
	    ne_buffer_destroy(term_result);	/*Free the buffer */
	    return NE_ERROR;	/*Parsing error */
	}
	ne_buffer_zappend(result_buf, term_result->data);
    }				/*End of while */

    if (added_or == 1)
	ne_buffer_zappend(result_buf, "</D:or>" EOL);

    ne_buffer_destroy(term_result);	/*Free the buffer */

    return NE_OK;		/*success */
}				/*End of search_condition */

/*
 * Parse a boolean term.
 * <boolean term> ::= <boolean factor> | <boolean term> and <boolean factor>
 *
 * The parsing result is saved into result_buf.
 * '*string_parsed' is changed to the pointer pointing to the position after
 * <boolean term>.
 *
 * Returns:
 * NE_OK: success
 * NE_ERROR: syntax error
 */
static int boolean_term(char **string_parsed, ne_buffer * result_buf)
{
    char identifier[WORDLEN + 1] = "";
    /*Indicates whether there is any 'and' in the boolean term. */
    int added_and = 0;
    ne_buffer *factor_result;

    ne_buffer_clear(result_buf);
    factor_result = ne_buffer_create();

    if (boolean_factor(string_parsed, factor_result) == NE_ERROR) {
	ne_buffer_destroy(factor_result);
	return NE_ERROR;	/*parsing error */
    }

    if (first_word_equal(*string_parsed, "and") == 1) {
	added_and = 1;
	ne_buffer_concat(result_buf, "<D:and>" EOL, factor_result->data,
			 NULL);
    }
    else
	ne_buffer_zappend(result_buf, factor_result->data);

    while (first_word_equal(*string_parsed, "and") == 1) {
	read_aword(string_parsed, identifier);	/*Read 'and' */

	if (boolean_factor(string_parsed, factor_result) == NE_ERROR) {
	    ne_buffer_destroy(factor_result);
	    return NE_ERROR;	/*Parsing error */
	}
	ne_buffer_zappend(result_buf, factor_result->data);
    }				/*End of while */

    if (added_and == 1)
	ne_buffer_zappend(result_buf, "</D:and>" EOL);

    ne_buffer_destroy(factor_result);

    return NE_OK;		/*success */
}				/*End of boolean_term */

/*
 * Parse a boolean factor.
 * <boolean factor> ::= [not] <boolean primary>
 *
 * The parsing result is saved into result_buf.
 * '*string_parsed' is changed to the pointer pointing to the position after
 * <boolean factor>.
 *
 * Returns:
 * NE_OK: success
 * NE_ERROR: syntax error
 */
static int boolean_factor(char **string_parsed, ne_buffer * result_buf)
{
    char identifier[WORDLEN + 1] = "";
    int added_not = 0;		/*Indicates whether there is any 'not'
				   in the search condition */
    ne_buffer *boolean_primary_result;

    ne_buffer_clear(result_buf);
    boolean_primary_result = ne_buffer_create();

    if (first_word_equal(*string_parsed, "not") == 1) {
	read_aword(string_parsed, identifier);	/*Read "not" */
	ne_buffer_zappend(result_buf, "<D:not>" EOL);
	added_not = 1;
    }

    if (boolean_primary(string_parsed, boolean_primary_result) == NE_ERROR) {
	ne_buffer_destroy(boolean_primary_result);
	return NE_ERROR;	/*parsing error */
    }

    ne_buffer_zappend(result_buf, boolean_primary_result->data);

    if (added_not == 1)
	ne_buffer_zappend(result_buf, "</D:not>" EOL);

    ne_buffer_destroy(boolean_primary_result);

    return NE_OK;		/*success */
}				/*End of boolean_factor */

/*
 * Parse a boolean primary.
 * <boolean primary> ::= <predicate> | "("<search condition>")"
 *
 * The parsing result is saved into result_buf.
 * '*string_parsed' is changed to the pointer pointing to the position after
 * <boolean primary>.
 *
 * Returns:
 * NE_OK: success
 * NE_ERROR: syntax error
 */
static int boolean_primary(char **string_parsed, ne_buffer * result_buf)
{
    char identifier[WORDLEN + 1] = "";

    ne_buffer *sub_result;

    ne_buffer_clear(result_buf);
    sub_result = ne_buffer_create();

    if (first_word_equal(*string_parsed, "(") == 1) {
	/*It is the case of "("<search condition>")" */

	read_aword(string_parsed, identifier);	/*Read "(" */

	if (search_condition(string_parsed, sub_result) == NE_ERROR) {
	    ne_buffer_destroy(sub_result);
	    return NE_ERROR;	/*Parsing error */
	}

	/*Read and match ")" */
	if (match_fetch(string_parsed, ")") == NE_ERROR) {
	    ne_set_error(session.sess,
			 "Syntax error: A ')' is expected in the search condition.");
	    ne_buffer_destroy(sub_result);
	    return NE_ERROR;	/*parsing error */
	}
	ne_buffer_zappend(result_buf, sub_result->data);
    }
    else {			/*It is the case of <predicate> */
	if (predicate(string_parsed, sub_result) == NE_ERROR) {
	    ne_buffer_destroy(sub_result);
	    return NE_ERROR;	/*parsing error */
	}
	ne_buffer_zappend(result_buf, sub_result->data);
    }

    ne_buffer_destroy(sub_result);

    return NE_OK;		/*success */
}				/*End of boolean_primary */

/*
 *  Translate comparison operator in the search condition to XML form.
 *  Returns:
 *    1: if the operator is valid.
 *    0: if the operator is not valid.
 */
static int operator_translate(const char *operator, char *XML_operator)
{
    int operator_valid = 1;

    XML_operator[0] = '\0';

    if (strcmp(operator, "=") == 0)
	strcpy(XML_operator, "eq");
    else if (strcmp(operator, ">=") == 0)
	strcpy(XML_operator, "gte");
    else if (strcmp(operator, "<=") == 0)
	strcpy(XML_operator, "lte");
    else if (strcmp(operator, ">") == 0)
	strcpy(XML_operator, "gt");
    else if (strcmp(operator, "<") == 0)
	strcpy(XML_operator, "lt");
    else if (strcmp(operator, "!=") == 0)
	strcpy(XML_operator, "not");
    else if (strcmp(operator, "<>") == 0)
	strcpy(XML_operator, "not");
    else if (strcmp(operator, "like") == 0)
	strcpy(XML_operator, "like");
    else
	operator_valid = 0;	/*Invalid operator */

    return operator_valid;
}				/*End of operator_translate */

/*
 * Parse a predicate.
 * <predicate> ::= <comparison predicate> | <like predicate> | <contains predicate>
 * <comparison predicate> ::=  <column_name> <comparaison_op> ( <number> | <quoted_string> | <wordstring>) 
 * <like predicate> ::= <column_name> like <match_string>
 * <contains predicate> ::= contains (<quoted_string> | <wordstring>)
 * <comparaison_op>
 *         ::= "=" | "<" | ">" | "<>" | "!=" | "<=" | ">="
 *   
 * The parsing result is saved into result_buf.
 * '*string_parsed' is changed to the pointer pointing to the position after
 * the predicate.
 *
 * Returns:
 * NE_OK: success
 * NE_ERROR: syntax error
 */
static int predicate(char **string_parsed, ne_buffer * result_buf)
{
    char column_name[WORDLEN + 1] = "";
    char operator[WORDLEN + 1] = "";
    char XML_operator[WORDLEN + 1] = "";
    ne_buffer *comparing_value;

    ne_buffer_clear(result_buf);

    if (first_word_equal(*string_parsed, "contains") == 1) {
	/* It is the case of <contains predicate> */
	ne_buffer *contains_result;
	contains_result = ne_buffer_create();
	if (contains_predicate(string_parsed, contains_result)
	    == NE_ERROR) {
	    ne_buffer_destroy(contains_result);
	    return NE_ERROR;	/*Parsing error */
	}
	ne_buffer_zappend(result_buf, contains_result->data);
	return NE_OK;
    }

    comparing_value = ne_buffer_create();

    /*Read the column name */
    if (read_aword(string_parsed, column_name) != IDEN) {
	ne_set_error(session.sess,
		     "A column name is expected in the search condition.");
	ne_buffer_destroy(comparing_value);
	return NE_ERROR;	/*Parsing error */
    }

    /*Read the 'operator' */
    read_aword(string_parsed, operator);

    /*Translate the operator to XML form */
    if (operator_translate(operator, XML_operator) == 0) {
	ne_set_error(session.sess,
		     "Syntax error: Invalid operator in the search condition.");
	ne_buffer_destroy(comparing_value);
	return NE_ERROR;
    }

    if (strcasecmp(operator, "like") == 0) {
	/*It is the case of like predicate and then parse the match string */
	if (first_word_equal(*string_parsed, "'")) {
	    /* For the case of a quoted string */
	    if (quoted_string(string_parsed, comparing_value) == NE_ERROR) {
		ne_buffer_destroy(comparing_value);
		return NE_ERROR;	/*Parsing error */
	    }
	}
	else {
	    /* For the case of word string. */
	    if (word_string(string_parsed, comparing_value) == NE_ERROR) {
		ne_set_error(session.sess,
			     "Syntax error: A quoted string or a word string is expected in the search condition.");
		ne_buffer_destroy(comparing_value);
		return NE_ERROR;	/*Parsing error */
	    }
	}
    }
    else {
	/* It is the case of comparison predicate and then 
	 * parse the comparison value */
	if (comparison_value(string_parsed, comparing_value) == NE_ERROR) {
	    ne_buffer_destroy(comparing_value);
	    return NE_ERROR;	/*parsing error */
	}
    }

    ne_buffer_concat(result_buf, "<D:",
		     XML_operator,
		     ">" EOL
		     "<D:prop><D:",
		     column_name,
		     "/></D:prop>" EOL
		     "<D:literal>",
		     comparing_value->data,
		     "</D:literal>" EOL "</D:", XML_operator, ">" EOL, NULL);

    ne_buffer_destroy(comparing_value);

    return NE_OK;		/*success */
}				/*End of predicate */

/*
 * Parse a contains predicate.
 * <contains predicate> ::= contains <quoted_string> 
 *
 * The parsing result is saved into result_str.
 * '*string_parsed' is changed to the pointer pointing to the position after
 * the predicate.
 *
 * Returns:
 * 1: success
 * 0: syntax error
 */
int contains_predicate(char **string_parsed, ne_buffer * result_buf)
{
    ne_buffer *contain_string;

    ne_buffer_clear(result_buf);
    contain_string = ne_buffer_create();

    /*Read 'contains' */
    if (match_fetch(string_parsed, "contains") == NE_ERROR) {
	/*The case of <contains predicate> */
	ne_set_error(session.sess,
		     "Syntax error: A 'contains' is expected in the search condition.");
	ne_buffer_destroy(contain_string);
	return NE_ERROR;
    }

    /*Now parse the containing string */
    if (first_word_equal(*string_parsed, "'") == 1) {
	/*For the case of <quoted string> */
	if (quoted_string(string_parsed, contain_string) == NE_ERROR) {
	    ne_buffer_destroy(contain_string);
	    return NE_ERROR;	/*Parsing error */
	}
    }
    else {
	/* For the case of <wordstring> */
	if (word_string(string_parsed, contain_string) == NE_ERROR) {
	    ne_set_error(session.sess,
			 "Syntax error: A quoted string or a word string is expected in the search condition.");
	    ne_buffer_destroy(contain_string);
	    return NE_ERROR;	/*Parsing error */
	}
    }

    ne_buffer_concat(result_buf, "<D:contains>" EOL,
		     contain_string->data, "</D:contains>" EOL, NULL);

    return NE_OK;		/*success */
}				/*End of contains_predicate */

/*
 * Parse a quoted string.
 * <quoted_string> ::= "'" {<any_character>} "'"
 *
 * The parsing result is saved into result_buf.
 * '*string_parsed' is changed to the pointer pointing to the position after
 * the predicate.
 *
 * Returns:
 * 1: success
 * 0: syntax error
 */
static int quoted_string(char **string_parsed, ne_buffer * result_buf)
{
    char previous_char;
    char current_char;
    char tmp_str[2] = "";
    int i = 0;

    ne_buffer_clear(result_buf);

    /*Read a quotation mark */
    if (match_fetch(string_parsed, "'") == NE_ERROR) {
	ne_set_error(session.sess,
		     "Syntax error: A ' is expected in the search condition.");
	return NE_ERROR;	/*Parsing error */
    }

    /*To parse {"any_character"}. Considering '\'' case when parsing */
    current_char = previous_char = (*string_parsed)[0];
    while ( ((current_char != '\'') && (current_char != '\0')) ||
	    ((current_char == '\'') && (previous_char == '\\')) ) {
	tmp_str[0] = current_char;
	tmp_str[1] = '\0';
	ne_buffer_zappend(result_buf, tmp_str);
	previous_char = current_char;
	i++;
	current_char = (*string_parsed)[i];
    }

    if (current_char != '\'') {	/*There should be a ending ' */
	ne_set_error(session.sess,
		     "An ending ' is expected in the search condition.");
	return NE_ERROR;	/*parsing error */
    }

    *string_parsed = *string_parsed + i + 1;	/*1 for ending ' */

    return NE_OK;		/*success */
}				/*End of auoted_string */

/*
 * Parse a word string.
 * <wordstring> ::= <any char except for blank, tab and ')'>{<any_character except for blank, tab and ')'>} 
 *
 * The parsing result is saved into result_buf.
 * '*string_parsed' is changed to the pointer pointing to the position after
 * the predicate.
 *
 * Returns:
 * 1: success
 * 0: syntax error
 */
static int word_string(char **string_parsed, ne_buffer * result_buf)
{
    char previous_char;
    char current_char;
    char tmp_str[2] = "";
    int i = 0;

    ne_buffer_clear(result_buf);

    /*Find the left bound of a word */
    while (isspace((*string_parsed)[i]))
	i++;

    if ((*string_parsed)[i] == '\0' || (*string_parsed)[i] == ')')	/* Empty string */
	return NE_ERROR;

    current_char = previous_char = (*string_parsed)[i];
    while ((current_char != ' ') &&
	   (current_char != '\t') &&
	   (current_char != ')') && (current_char != '\0')) {
	tmp_str[0] = current_char;
	tmp_str[1] = '\0';
	ne_buffer_zappend(result_buf, tmp_str);
	previous_char = current_char;
	i++;
	current_char = (*string_parsed)[i];
    }

    *string_parsed = *string_parsed + i;

    return NE_OK;		/*success */
}				/*End of auoted_string */

/*
 * Parse a comparison_value.
 *<comparison_value>
 *      ::=  <number> | <quoted_string> | <wordstring>
 *
 * The parsing result is saved into result_buf.
 * '*string_parsed' is changed to the pointer pointing to the position after
 * the predicate.
 *
 * Returns:
 * NE_OK: success
 * NE_ERROR: syntax error
 */
static int comparison_value(char **string_parsed, ne_buffer * result_buf)
{
    char identifier[WORDLEN + 1] = "";
    ne_buffer *comparing_value;

    ne_buffer_clear(result_buf);
    comparing_value = ne_buffer_create();

    if (first_word_equal(*string_parsed, "'") == 1) {
	/*It is the case of quoted string */
	if (quoted_string(string_parsed, comparing_value) == NE_ERROR) {
	    ne_buffer_destroy(comparing_value);
	    return NE_ERROR;	/*Parsing error */
	}
	ne_buffer_zappend(result_buf, comparing_value->data);
    }
    else {			/* It is the case of <number> or <wordstring> */
	/*An integer or a word string is expected */
	if (first_word_integer(*string_parsed) == 1) {
	    read_aword(string_parsed, identifier);
	    ne_buffer_zappend(result_buf, identifier);
	}
	else {			/* It is the case of a word string */
	    if (word_string(string_parsed, comparing_value) == NE_ERROR) {
		ne_set_error(session.sess,
			     "Syntax error: An integer, quoted string or word string is expected in the search condition.");
		ne_buffer_destroy(comparing_value);
		return NE_ERROR;
	    }
	}
	ne_buffer_zappend(result_buf, comparing_value->data);
    }

    ne_buffer_destroy(comparing_value);

    return NE_OK;		/*success */
}				/*End of comparison_value */
