Login | Register
My pages Projects Community openCollabNet

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Catacomb] A patch for cadaver-dasl



Hello Joe,

This is a DASL patch for cadaver. In this patch, we didn't change neon at all. We just added search module in cadaver on the top of neon library like subversion. The functions of the patch can be summarized as those:

 - search command added
 - parser for the search query and generate DASL query
 - parser DASL response and display
 - several set values for search (The variable name is not sweet. :-)
     * searchall : weather search and display all dead props
     * searchdepth : depth for search
     * searchorder : prop(s) for ascending order
     * searchdorder : prop(s) for descending order

The search.c includes all search module including parser, so it seems a bit big.
Maybe we can separate it to two files: one for search main module, the other for search parser.

Would you like to give some advice on the patch.

Have a nice day.

PS - I attached a sample cadaver-dasl screen shot.
PS2 - For test, you can connet to http://ocean.cse.ucsc.edu/repos.

--
Sung Kim <hunkim@cse.ucsc.edu>
http://www.cse.ucsc.edu/~hunkim

 "Dreams become reality!"

Script started on Mon Aug 26 20:31:45 2002
[root@dhcppc4 cadaver]# ./cadaver http://localhost/repos
dav:/repos/> set
Options:
        tolerant: off
       overwrite: on
       expect100: off
            utf8: off
           quiet: on
       searchall: on
       lockowner: mailto:root@dhcppc4
       lockstore: /root/.cadaver-locks
          editor: unset
            cert: unset
         certkey: unset
       namespace: http://webdav.org/cadaver/custom-properties/
           pager: unset
           proxy: unset
     searchorder: unset
    searchdorder: unset
      proxy-port: unset
           debug: {}
       lockscope: exclusive
       lockdepth: infinite
     searchdepth: infinity
dav:/repos/> ls
Listing collection `/repos/': succeeded.
Coll:  hanna                                   0    (unknown) 
       jj22                                    0    (unknown) 
       kk                                   2888    (unknown) 
dav:/repos/> search aa=aa
Using query: aa=aa , Found 2 results

/repos/jj22                              0  Dec 31  1969 
	-  aa:aa = aa
/repos/kk                             2888  Dec 31  1969 
	-  cc:aa = aa
	-  aa:aa = aa
 succeeded.
dav:/repos/> search content
Using query: content ,  failed:
Syntax error: Invalid operator in the search condition.
dav:/repos/> search getcontentlength < 10
Using query: getcontentlength < 10 , Found 3 results

/repos/hanna                             0  Dec 31  1969 
/repos/hanna/aa                          0  Dec 31  1969 
/repos/jj22                              0  Dec 31  1969 
	-  aa:aa = aa
 succeeded.
dav:/repos/> search getcontentlength > 100
Using query: getcontentlength > 100 , Found 2 results

/repos/kk                             2888  Dec 31  1969 
	-  cc:aa = aa
	-  aa:aa = aa
/repos/hanna/java                     2888  Dec 31  1969 
 succeeded.
dav:/repos/> unset searchall
dav:/repos/> search getcontentlength > 100
Using query: getcontentlength > 100 , Found 2 results

/repos/kk                             2888  Dec 31  1969 
/repos/hanna/java                     2888  Dec 31  1969 
 succeeded.
dav:/repos/> quit
Connection to `localhost' closed.
[root@dhcppc4 cadaver]# 
Script done on Mon Aug 26 20:33:15 2002
Index: Makefile.in
===================================================================
RCS file: /home/cvs/cadaver/Makefile.in,v
retrieving revision 1.45
diff -u -r1.45 Makefile.in
--- Makefile.in	2002/08/03 21:38:19	1.45
+++ Makefile.in	2002/08/27 03:22:54
@@ -36,7 +36,7 @@
 TARGET = $(PACKAGE)
 SUBDIRS = libneon @SUBDIRS@
 OBJECTS = src/cadaver.o src/common.o src/commands.o src/ls.o	\
-	 src/cmdline.o src/options.o src/utils.o src/edit.o
+	 src/cmdline.o src/options.o src/utils.o src/edit.o src/search.o
 LIBOBJS = lib/basename.o lib/dirname.o lib/rpmatch.o lib/yesno.o	\
 	lib/glob.o lib/getpass.o lib/tempname.o lib/mkstemp.o \
 	@LIBOBJS@
@@ -89,6 +89,7 @@
 src/edit.o: src/edit.c src/cadaver.h src/options.h src/common.h
 src/common.o: src/common.c src/common.h config.h
 src/ls.o: src/ls.c src/commands.h src/cadaver.h config.h
+src/search.o: src/search.c src/commands.h src/cadaver.h config.h
 src/utils.o: src/utils.c src/utils.h src/cadaver.h config.h
 lib/netrc.o: lib/netrc.c lib/netrc.h config.h
 lib/getpass.o: lib/getpass.c lib/getpass.h config.h
Index: src/cadaver.c
===================================================================
RCS file: /home/cvs/cadaver/src/cadaver.c,v
retrieving revision 1.141
diff -u -r1.141 cadaver.c
--- src/cadaver.c	2002/08/21 14:57:38	1.141
+++ src/cadaver.c	2002/08/27 03:22:57
@@ -793,8 +793,10 @@
     set_option(opt_namespace, ne_strdup(DEFAULT_NAMESPACE));
     set_bool_option(opt_overwrite, 1);
     set_bool_option(opt_quiet, 1);
+    set_bool_option(opt_searchall, 1);
     lockdepth = NE_DEPTH_INFINITE;
     lockscope = ne_lockscope_exclusive;
+    searchdepth = NE_DEPTH_INFINITE;
 
     /* This is what Markus Kahn says we should do. */
     if ((tmp = getenv("LC_ALL")) ||
Index: src/cadaver.h
===================================================================
RCS file: /home/cvs/cadaver/src/cadaver.h,v
retrieving revision 1.36
diff -u -r1.36 cadaver.h
--- src/cadaver.h	2002/03/07 19:59:10	1.36
+++ src/cadaver.h	2002/08/27 03:22:57
@@ -57,6 +57,7 @@
     cmd_echo, cmd_set, cmd_unset, cmd_rmcol, cmd_lock, cmd_unlock,
     cmd_steal, cmd_discover, cmd_showlocks, cmd_propedit, cmd_propnames,
     cmd_propget, cmd_propset, cmd_propdel, cmd_chexec, cmd_edit, cmd_logout,
+    cmd_search,
     cmd_unknown
 /* DON'T FORGET TO ADD A NEW COMMAND ALIAS WHEN YOU ADD A NEW COMMAND */
 };
@@ -137,6 +138,8 @@
 
 void execute_ls(const char *remote);
 void execute_edit(const char *remote);
+
+void execute_search(int count, const char **args);
 
 void free_resource(struct resource *res);
 void free_resource_list(struct resource *res);
Index: src/commands.c
===================================================================
RCS file: /home/cvs/cadaver/src/commands.c,v
retrieving revision 1.115
diff -u -r1.115 commands.c
--- src/commands.c	2002/08/08 22:29:55	1.115
+++ src/commands.c	2002/08/27 03:23:00
@@ -92,6 +92,7 @@
     C(less), C(cat), C(lpwd), C(lcd), C(lls), C(echo), C(quit),
     C(mget), C(mput), C(rmcol), C(lock), C(unlock), C(discover), C(steal),
     C(chexec), C(showlocks), C(version), C(propget), C(propset), C(propdel),
+    C(search),
 #if 0
 C(propedit), 
 #endif
@@ -1304,6 +1305,23 @@
       N_("propset res propname value"),
       N_("Set property on resource") },
 
+    { cmd_search, true, 1, CMD_VARY, parmscope_remote, TV(execute_search),
+      N_("search query"), 
+      N_("DASL Search resource in current directory" EOL EOL
+	 " Examples : " EOL
+	 "     - search content length is smaller than 100" EOL
+	 "         search getcontentlength < 100" EOL 
+         "     - search author is Sung or Orton" EOL
+         "         search author = Sung or author = Orton" EOL EOL
+	 " Available operations and keywords :" EOL
+	 "     - and, or , (, ), =, <, >, <=, >=, like" EOL EOL
+	 " Set variables (use set command):" EOL
+	 "     - searchdepth : Depth for search" EOL
+         "     - searchorder : Ascending Order property" EOL 
+	 "       ex: set searchorder author" EOL
+         "           set searchorder \"author getcontenttype\"" EOL
+	 "     - searchdorder : Descneding order property" EOL ) },
+    
     { cmd_set, false, 0, 2, parmscope_none, T2(execute_set), 
       N_("set [option] [value]"), N_("Set an option, or display options") },
     { cmd_open, false, 1, 1, parmscope_none, T1(open_connection), 
Index: src/ls.c
===================================================================
RCS file: /home/cvs/cadaver/src/ls.c,v
retrieving revision 1.26
diff -u -r1.26 ls.c
--- src/ls.c	2002/03/07 19:59:10	1.26
+++ src/ls.c	2002/08/27 03:23:01
@@ -41,8 +41,6 @@
 #include "cadaver.h"
 #include "basename.h"
 
-static time_t current_time;
-
 struct fetch_context {
     struct resource **list;
     const char *target; /* Request-URI of the PROPFIND */
@@ -67,42 +65,6 @@
     { NULL }
 };
 
-static char *format_time(time_t when)
-{
-    const char *fmt;
-    static char ret[256];
-    struct tm *local;
-
-    if (when == (time_t)-1) {
-	/* Happens on lock-null resources */
-	return "  (unknown) ";
-    }
-
-    /* from GNU fileutils... this section is 
-     *  Copyright (C) 85, 88, 90, 91, 1995-1999 Free Software Foundation, Inc.
-     */
-    if (current_time > when + 6L * 30L * 24L * 60L * 60L	/* Old. */
-	|| current_time < when - 60L * 60L) {
-	/* The file is fairly old or in the future.
-	   POSIX says the cutoff is 6 months old;
-	   approximate this by 6*30 days.
-	   Allow a 1 hour slop factor for what is considered "the future",
-	   to allow for NFS server/client clock disagreement.
-	   Show the year instead of the time of day.  */
-	fmt = "%b %e  %Y";
-    } else {
-	fmt = "%b %e %H:%M";
-    }
-    /* end FSF copyrighted section. */
-    local = localtime(&when);
-    if (local != NULL) {
-	if (strftime(ret, 256, fmt, local)) {
-	    return ret;
-	}
-    }
-    return "???";
-}
-
 static int compare_resource(const struct resource *r1, 
 			    const struct resource *r2)
 {
@@ -181,7 +143,6 @@
 	    output(o_finish, _("collection is empty.\n"));
 	} else {
 	    out_success();
-	    current_time = time(NULL);
 	    for (current = reslist; current!=NULL; current = next) {
 		next = current->next;
 		if (strlen(current->uri) > strlen(real_remote)) {
Index: src/options.c
===================================================================
RCS file: /home/cvs/cadaver/src/options.c,v
retrieving revision 1.37
diff -u -r1.37 options.c
--- src/options.c	2002/08/25 23:13:39	1.37
+++ src/options.c	2002/08/27 03:23:02
@@ -52,13 +52,20 @@
 static void unset_lockdepth(const char *new);
 static void disp_lockdepth(void);
 
+static void set_searchdepth(const char *new);
+static void unset_searchdepth(const char *new);
+static void disp_searchdepth(void);
+
 /* Option holders */
 
-static int enable_expect, presume_utf8, overwrite, quiet;
+static int enable_expect, presume_utf8, overwrite, quiet, searchall;
 
 enum ne_lock_scope lockscope;
 int lockdepth;
 
+/* search option global */
+int searchdepth;
+
 static struct {
     const char *name;
     enum option_id id;
@@ -84,6 +91,7 @@
     B(expect100, &enable_expect, "Enable use of 'Expect: 100-continue' header"),
     B(utf8, &presume_utf8, "Presume filenames etc are UTF-8 encoded"),
     B(quiet, &quiet, "Whether to display connection status messages"),
+    B(searchall, &searchall, "Whether to search and display all props including dead props"),
 #undef B
 #define S(x,h) { #x, opt_##x, NULL, opt_string, NULL, NULL, NULL, h, NULL }
     S(lockowner, "Lock owner URI"),
@@ -94,6 +102,8 @@
     S(namespace, "Namespace to use for propset/propget commands."),
     S(pager, "Command to run for less/more commands."),
     S(proxy, "Hostname of proxy server"),
+    S(searchorder,  "Search ascending props options"),
+    S(searchdorder, "Search descending props options"),
     { "proxy-port", opt_proxy_port, NULL, opt_string, NULL, NULL, NULL,
       "Port to use on proxy server", NULL },
 #undef S
@@ -110,6 +120,13 @@
       set_lockdepth, unset_lockdepth, disp_lockdepth, "Lock depth options",
       "The lockdepth value must be 0 or infinity."
     },
+
+    /* Several options for search */
+    { "searchdepth", opt_searchdepth, NULL, opt_handled,
+      set_searchdepth, unset_searchdepth, disp_searchdepth, "Search depth options",
+      "The searchdepth value must be 0, 1 or infinity."
+    },
+
     { NULL, 0 }
 };
 
@@ -138,18 +155,18 @@
 	int *val = (int *)options[n].holder;
 	switch (options[n].type) {
 	case opt_bool:
-	    printf(" %10s: %s\n", options[n].name, *val?"on":"off");
+	    printf(" %15s: %s\n", options[n].name, *val?"on":"off");
 	    break;
 	case opt_string:
 	    if (options[n].holder == NULL) {
-		printf(" %10s: unset\n", options[n].name);
+		printf(" %15s: unset\n", options[n].name);
 	    } else {
-		printf(" %10s: %s\n", options[n].name, 
+		printf(" %15s: %s\n", options[n].name, 
 		       (char *)options[n].holder);
 	    }
 	    break;
 	case opt_handled:
-	    printf(" %10s: ", options[n].name);
+	    printf(" %15s: ", options[n].name);
 	    (*options[n].display)();
 	    printf("\n");
 	    break;
@@ -263,6 +280,34 @@
 	printf("infinite");
     else
 	printf("illegal value");
+}
+
+
+static void set_searchdepth(const char *set)
+{
+    if (strcmp(set, "0") == 0 ||
+	strcasecmp(set, "zero") == 0)
+	searchdepth = NE_DEPTH_ZERO;
+    else if (strcasecmp(set, "1") == 0 ||
+	     strcasecmp(set, "one") == 0)
+	searchdepth = NE_DEPTH_ONE;
+    else
+	searchdepth = NE_DEPTH_INFINITE;
+}
+
+static void unset_searchdepth(const char *s)
+{
+    searchdepth = NE_DEPTH_INFINITE;
+}
+
+static void disp_searchdepth(void)
+{
+    if (searchdepth == NE_DEPTH_ZERO)
+	printf("0");
+    else if (searchdepth == NE_DEPTH_ONE)
+	printf("1");
+    else
+	printf("infinity");
 }
 
 void execute_set(const char *opt, const char *newv)
Index: src/options.h
===================================================================
RCS file: /home/cvs/cadaver/src/options.h,v
retrieving revision 1.13
diff -u -r1.13 options.h
--- src/options.h	2002/03/14 22:10:44	1.13
+++ src/options.h	2002/08/27 03:23:02
@@ -39,10 +39,16 @@
     opt_lockstore,
     opt_lockdepth,
     opt_lockscope,
-    opt_pager
+    opt_pager,
+   
+    opt_searchdepth,
+    opt_searchorder,
+    opt_searchdorder,
+    opt_searchall,
 };
 
 extern int lockdepth; /* current lock depth setting. */
+extern int searchdepth; /* current search depth setting. */
 extern enum ne_lock_scope lockscope; /* current lock scope setting. */
 
 void execute_set( const char *opt, const char * );
Index: src/search.c
===================================================================
RCS file: search.c
diff -N search.c
--- /dev/null	Tue May  5 13:32:27 1998
+++ search.c	Mon Aug 26 20:23:05 2002
@@ -0,0 +1,1423 @@
+/* 
+   'search' for cadaver
+   Copyright (C) 2000-2001, Joe Orton <joe@manyfish.co.uk>, 
+   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 <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"
+
+/* 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 */
+
+/* Dead prop */
+typedef struct dead_prop_t {
+    char *name;
+    char *nspace;
+    char *value;
+    struct dead_prop_t *next;
+} dead_prop;
+
+typedef struct resource_t {
+    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;
+    
+    dead_prop *root;
+    dead_prop *curr;
+    int dead_prop_num;
+    
+    struct resource_t *next;
+} resource;
+
+/* Search XML parser context */
+typedef struct {
+    resource *root;
+    resource *curr;
+    int result_num;
+    int start_prop;
+    int err_code;
+} search_ctx;
+
+enum {
+    ELEM_multistatus = NE_ELM_207_UNUSED,
+    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_ignore,
+};
+
+static const struct ne_xml_elm search_elements[] =
+{
+    { "DAV:", "multistatus", ELEM_multistatus, 0 },
+    { "DAV:", "response", ELEM_response, 0 },
+    { "DAV:", "responsedescription", ELEM_responsedescription, 
+      NE_XML_CDATA },
+    { "DAV:", "href", ELEM_href, NE_XML_CDATA },
+    { "DAV:", "propstat", ELEM_propstat, 0 },
+    { "DAV:", "prop", ELEM_prop, 0 },
+    { "DAV:", "status", ELEM_status, NE_XML_CDATA },
+    
+    /* Live props */
+    { "DAV:", "creationdate", ELEM_creationdate, NE_XML_CDATA},
+    { "DAV:", "displayname", ELEM_displayname, NE_XML_CDATA},
+    { "DAV:", "getcontentlanguage", ELEM_getcontentlanguage, NE_XML_CDATA},
+    { "DAV:", "getcontentlength", ELEM_getcontentlength, NE_XML_CDATA},
+    { "DAV:", "getcontenttype", ELEM_getcontenttype, NE_XML_CDATA},
+    { "DAV:", "getetag", ELEM_getetag, NE_XML_CDATA},
+    { "DAV:", "getlastmodified", ELEM_getlastmodified, NE_XML_CDATA},
+    { "DAV:", "lockdiscovery", ELEM_lockdiscovery, NE_XML_CDATA},
+    { "DAV:", "resourcetype", ELEM_resourcetype, NE_XML_CDATA},
+    { "DAV:", "source", ELEM_source, NE_XML_CDATA},
+    { "DAV:", "supportedlock", ELEM_supportedlock, NE_XML_CDATA},
+    
+    /* Ignore it for now */
+    { "DAV:", "lockentry", ELEM_ignore, NE_XML_CDATA},
+    { "DAV:", "lockscope", ELEM_ignore, NE_XML_CDATA},
+    { "DAV:", "locktype", ELEM_ignore, NE_XML_CDATA},
+    { "DAV:", "exclusive", ELEM_ignore, NE_XML_CDATA},
+    { "DAV:", "shared", ELEM_ignore, NE_XML_CDATA},
+    { "DAV:", "read", ELEM_ignore, NE_XML_CDATA},
+    { "DAV:", "write", ELEM_ignore, NE_XML_CDATA},
+    
+    /* It deals all unknown elements */
+    { "", "", NE_ELM_unknown, NE_XML_COLLECT },
+    { NULL }
+};
+
+/* 
+ * 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 int search_limit_gen(const char *limit, 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 validate_search_elements(void *userdata,
+                                   ne_xml_elmid parent,
+                                   ne_xml_elmid child)
+{
+    return NE_XML_VALID;
+}
+
+static int set_xml_error(search_ctx *sctx, const char *format, ...) {
+    sctx->err_code = NE_ERROR;
+    ne_set_error(session, format);
+}
+
+static int start_element(void *userdata, const struct ne_xml_elm *elm,
+                             const char **atts)
+{
+    search_ctx *sctx = (search_ctx*) userdata;
+    
+    /* Error occured, ignore remain part */
+    if(sctx->err_code!=NE_OK) 
+	return sctx->err_code;
+    
+    switch(elm->id) {
+    case ELEM_ignore:
+	break;
+
+    case ELEM_response : /* Start of new response */
+	sctx->curr = ne_calloc(sizeof(resource));
+	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", elm->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 */
+	    resource *res = sctx->curr;
+	    res->dead_prop_num++;
+	    res->curr = (dead_prop*)ne_calloc(sizeof(dead_prop));
+	    res->curr->name = ne_strdup(elm->name);
+	    res->curr->nspace = ne_strdup(elm->nspace);
+	}
+	break;
+    }
+
+    return NE_XML_VALID;
+}
+
+static int end_element(void *userdata, const struct ne_xml_elm *elm,
+                           const char *cdata)
+{
+    search_ctx *sctx = (search_ctx*) userdata;
+    
+    /* Error occured, ignore remain part */
+    if(sctx->err_code!=NE_OK) 
+	return sctx->err_code;
+    
+    switch(elm->id) {
+    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", elm->name);
+	    break;
+	}
+
+	/* No HREF */
+	if(sctx->curr->href==NULL) {
+	    set_xml_error(sctx, "XML : No href info in the <%s>...</%s>", 
+			  elm->name, elm->name);
+	    break;
+	}   
+	/* make link */
+	sctx->curr->next = sctx->root;
+	sctx->root = sctx->curr;
+	sctx->curr = NULL;
+	break;
+
+    case ELEM_href : /* href */
+	if(sctx->curr==NULL) 
+	    set_xml_error(sctx, "XML : </%s> is in the wrong place", elm->name);
+	else /* copy href */
+	    sctx->curr->href = ne_strdup(cdata);
+	
+	break;
+	
+	/* live props */
+    case ELEM_creationdate:
+	if(sctx->curr==NULL)
+	    set_xml_error(sctx, "XML : </%s> is in the wrong place", elm->name);
+	else /* copy creationdate */
+	    sctx->curr->creationdate = ne_strdup(cdata);
+	break;
+	
+    case ELEM_displayname:
+	if(sctx->curr==NULL)
+	    set_xml_error(sctx, "XML : </%s> is in the wrong place", elm->name);
+	else /* copy displayname */
+	    sctx->curr->displayname = ne_strdup(cdata);
+	break;
+
+    case ELEM_getcontentlanguage:
+	if(sctx->curr==NULL)
+	    set_xml_error(sctx, "XML : </%s> is in the wrong place", elm->name);
+	else /* copy getcontentlanguage */
+	    sctx->curr->getcontentlanguage = ne_strdup(cdata);
+	break;
+	
+    case ELEM_getcontentlength:
+	if(sctx->curr==NULL)
+	    set_xml_error(sctx, "XML : </%s> is in the wrong place", elm->name);
+	else /* copy getcontentlength */
+	    sctx->curr->getcontentlength = ne_strdup(cdata);
+	break;
+
+    case ELEM_getcontenttype:
+	if(sctx->curr==NULL)
+	    set_xml_error(sctx, "XML : </%s> is in the wrong place", elm->name);
+	else/* copy getcontenttype */
+	    sctx->curr->getcontenttype = ne_strdup(cdata);
+	break;
+
+    case ELEM_getetag:
+	if(sctx->curr==NULL)
+	    set_xml_error(sctx, "XML : </%s> is in the wrong place", elm->name);
+	else/* copy getetag */
+	    sctx->curr->getetag = ne_strdup(cdata);
+	break;
+	
+    case ELEM_getlastmodified:
+    	if(sctx->curr==NULL)
+	    set_xml_error(sctx, "XML : </%s> is in the wrong place", elm->name);
+	else/* copy getlastmodified */
+	    sctx->curr->getlastmodified = ne_strdup(cdata);
+	break;
+
+    case ELEM_lockdiscovery:
+	if(sctx->curr==NULL)
+	    set_xml_error(sctx, "XML : </%s> is in the wrong place", elm->name);
+	else/* copy lockdiscovery */
+	    sctx->curr->lockdiscovery = ne_strdup(cdata);
+	break;
+	
+    case ELEM_resourcetype:
+	if(sctx->curr==NULL)
+	    set_xml_error(sctx, "XML : </%s> is in the wrong place", elm->name);
+	else/* copy resourcetype */
+	    sctx->curr->resourcetype = ne_strdup(cdata);
+	break;
+
+    case ELEM_source:
+	if(sctx->curr==NULL)
+	    set_xml_error(sctx, "XML : </%s> is in the wrong place", elm->name);
+	else/* copy source */
+	    sctx->curr->source = ne_strdup(cdata);
+	break;
+
+    case ELEM_supportedlock:
+	if(sctx->curr==NULL)
+	    set_xml_error(sctx, "XML : </%s> is in the wrong place", elm->name);
+	else/* copy supportedlock */
+	    sctx->curr->supportedlock = ne_strdup(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", elm->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 */
+	    resource *res = sctx->curr;
+	    res->curr->value = ne_strdup(cdata);
+	    res->curr->next = res->root;
+	    res->root = res->curr;
+	    res->curr = NULL;
+	}
+	break;
+    }
+    
+    return NE_XML_VALID;
+}
+
+/* Custom function of type ne_accept_response. */
+static int search_accepter(void *userdata,
+			   ne_request *req,
+			   const ne_status *st)
+{
+    return (st->code == 207);
+}
+
+/* displays search results */
+static int display_results(search_ctx *sctx) {
+    resource *res;
+    dead_prop *dprop;
+    
+    if(sctx->err_code) {
+	return sctx->err_code;
+    }
+    
+    printf("Found %d results\n\n", sctx->result_num);
+    for(res=sctx->root; res; res = res->next) {
+	long modtime = res->getlastmodified ? atol(res->getlastmodified):0;
+	int size = res->getcontentlength ? atol(res->getcontentlength):0;
+	char exec_char = ' ';
+	
+	printf("%-30s%c %10d  %s \n", res->href, exec_char, 
+	       size, format_time(modtime));
+
+	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);
+    }
+    
+    return sctx->err_code;
+}
+
+static void search_ctx_destroy(search_ctx *sctx) {
+    resource *res, *res_free;
+    dead_prop *dprop, *dprop_free;
+        
+    for (res=sctx->root; res; ) {
+	NE_FREE(res->href);
+        /* live props */
+	NE_FREE(res->creationdate);
+	NE_FREE(res->displayname);
+	NE_FREE(res->getcontentlanguage);
+	NE_FREE(res->getcontentlength);
+	NE_FREE(res->getcontenttype);
+	NE_FREE(res->getetag);
+	NE_FREE(res->getlastmodified);
+	NE_FREE(res->lockdiscovery);
+	NE_FREE(res->resourcetype);
+	NE_FREE(res->source);
+	NE_FREE(res->supportedlock);
+    
+	for (dprop=res->root; dprop; ) { 
+	    NE_FREE(dprop->nspace);
+	    NE_FREE(dprop->name);
+	    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++) {
+	NE_FREE((char*)props[n].name);
+	NE_FREE((char*)props[n].nspace);
+    }
+    
+    NE_FREE(props);
+}
+
+/* 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, search_elements, validate_search_elements,
+			start_element, 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, search_accepter, 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;
+    search_ctx *sctx = ne_calloc(sizeof(search_ctx));
+    ne_buffer *query = 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, 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, "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, "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;
+    ne_buffer *result_buf;	/*The buffer storing the parsing result of search condition */
+
+    if (!basic_search || !condition_str) {
+	ne_set_error(session, "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, "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, "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>
+ *<column_name>
+ *      ::= identifier
+ *<comparaison_op>
+ *      ::= "=" | "<" | ">" | "<>" | "!=" | "<=" | ">="
+ *<quoted_string> ::= "'" {<any_character>} "'"
+ *<wordstring> ::= {<any character except for blank and tab}
+ *<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 */
+
+/* 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] = "";
+    int added_or = 0;		/*Indicates whether there is any 'or' in the search condition */
+    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] = "";
+    int added_and = 0;		/*Indicates whether there is any 'and' in the boolean term. */
+    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] = "";
+    int added_not = 0;		/*Indicates whether there is any 'not' in the search condition */
+    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,
+			 "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>
+ * <comparison predicate> ::=  <column_name> <comparaison_op> ( <number> | <quoted_string> | <wordstring>) 
+ * <like predicate> ::= <column_name> like <match_string>
+ * <contains predicate> ::= contains <quoted_string>
+ * <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);
+    comparing_value = ne_buffer_create();
+
+    /*Read the column name */
+    if (read_aword(string_parsed, column_name) != IDEN) {
+	ne_set_error(session,
+		     "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,
+		     "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 (quoted_string(string_parsed, comparing_value) == NE_ERROR) {
+	    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)
+{
+    char identifier[WORDLEN + 1] = "";
+    ne_buffer *contain_string;
+
+    ne_buffer_clear(result_buf);
+    contain_string = ne_buffer_create();
+
+    /*Read 'contains' */
+    if (match_fetch(string_parsed, "contains") == 0) {
+	/*The case of <contains predicate> */
+	ne_set_error(session,
+		     "Syntax error: A 'contains' is expected in the search condition.");
+	ne_buffer_destroy(contain_string);
+	return NE_ERROR;
+    }
+
+    /*Now parse the containing string */
+    if (quoted_string(string_parsed, contain_string) == NE_ERROR) {
+	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 identifier[WORDLEN + 1] = "";
+    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,
+		     "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,
+		     "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 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 (read_aword(string_parsed, identifier) == ENDBUF) {
+	    ne_set_error(session,
+			 "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, identifier);
+    }
+
+    ne_buffer_destroy(comparing_value);
+
+    return NE_OK;		/*success */
+}				/*End of comparison_value */
Index: src/utils.c
===================================================================
RCS file: /home/cvs/cadaver/src/utils.c,v
retrieving revision 1.2
diff -u -r1.2 utils.c
--- src/utils.c	2002/03/07 19:59:10	1.2
+++ src/utils.c	2002/08/27 03:23:05
@@ -21,6 +21,7 @@
 
 #include "config.h"
 
+#include <time.h>
 #include <sys/types.h>
 
 #include <ne_basic.h>
@@ -52,5 +53,44 @@
     }
     free_resource_list(res);
     return ret;
+}
+
+
+char *format_time(time_t when)
+{
+    const char *fmt;
+    static char ret[256];
+    struct tm *local;
+    time_t current_time;
+    
+    if (when == (time_t)-1) {
+	/* Happens on lock-null resources */
+	return "  (unknown) ";
+    }
+
+    /* from GNU fileutils... this section is 
+     *  Copyright (C) 85, 88, 90, 91, 1995-1999 Free Software Foundation, Inc.
+     */
+    current_time = time(NULL);
+    if (current_time > when + 6L * 30L * 24L * 60L * 60L	/* Old. */
+	|| current_time < when - 60L * 60L) {
+	/* The file is fairly old or in the future.
+	   POSIX says the cutoff is 6 months old;
+	   approximate this by 6*30 days.
+	   Allow a 1 hour slop factor for what is considered "the future",
+	   to allow for NFS server/client clock disagreement.
+	   Show the year instead of the time of day.  */
+	fmt = "%b %e  %Y";
+    } else {
+	fmt = "%b %e %H:%M";
+    }
+    /* end FSF copyrighted section. */
+    local = localtime(&when);
+    if (local != NULL) {
+	if (strftime(ret, 256, fmt, local)) {
+	    return ret;
+	}
+    }
+    return "???";
 }
 
Index: src/utils.h
===================================================================
RCS file: /home/cvs/cadaver/src/utils.h,v
retrieving revision 1.1
diff -u -r1.1 utils.h
--- src/utils.h	2002/01/14 20:00:33	1.1
+++ src/utils.h	2002/08/27 03:23:05
@@ -24,3 +24,6 @@
 /* Returns resource type of resource with given URI; where resr_error
  * means "resource not found." */
 enum resource_type getrestype(const char *uri);
+
+/* Returns time to display */
+char *format_time(time_t when);