[
Date Prev][
Date Next][
Thread Prev][
Thread Next][
Date Index][
Thread Index]
Re: [Catacomb] Re: A patch for cadaver-dasl
Hello,
I appreciate your comments and attach a new patch for you.
On Fri, 30 Aug 2002, Joe Orton wrote:
> > +++ 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>,
>
> I'm still not really happy about this, since I didn't write the code.
> Without a formal copyright assignment I'd prefer you owned the copyright
> (or UCSC, or whatever).
Changed it to Grase Lab, UCSC.
> > +typedef struct dead_prop_t {
>
> POSIX owns the "*_t" namespace, I'd prefer to not invade that... you
> probably don't need the tag name for this typedef anyway, so can you
> just drop the "dead_prop_t" out of this declaration, and similarly with
> resource_t.
Done.
> Declaring a "resource" or a "struct resource_t" is also confusing, since
> cadaver.h already defines a "struct resource", I'm not sure what can be
> done about that though.
Changed it to search_res.
> > + case ELEM_href : /* href */
> > + if(sctx->curr==NULL)
> > + set_xml_error(sctx, "XML : </%s> is in the wrong place", elm->name);
> ...
> > + if(sctx->curr==NULL)
> > + set_xml_error(sctx, "XML : </%s> is in the wrong place", elm->name);
> ...
> > + if(sctx->curr==NULL)
> > + set_xml_error(sctx, "XML : </%s> is in the wrong place", elm->name);
>
> Way too much duplicated code in there!
Added a new MACRO and replaced them to the macro.
> This is equivalent to ne_accept_207 from ne_207.h.
Done.
Again, I appreciate your help.
--
Sung Kim
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/31 02:22:55
@@ -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.142
diff -u -r1.142 cadaver.c
--- src/cadaver.c 2002/08/30 22:49:47 1.142
+++ src/cadaver.c 2002/08/31 02: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/31 02: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/31 02: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/31 02: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/31 02:23:01
@@ -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/31 02:23:01
@@ -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 Fri Aug 30 19:23:05 2002
@@ -0,0 +1,1562 @@
+/*
+ 'search' for cadaver
+ Copyright (C) 2002, GRASE Lab, UCSC <grase@mcse.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 <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
+{
+ 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;
+}
+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_collection,
+
+ 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},
+ {"DAV:", "collection", ELEM_collection, 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 word_string(char **string_parsed, ne_buffer * result_buf);
+
+/* We do not validate at this piint */
+static int validate_search_elements(void *userdata,
+ ne_xml_elmid parent, ne_xml_elmid child)
+{
+ return NE_XML_VALID;
+}
+
+/* Set xml parser error */
+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(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",
+ 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 */
+ search_res *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;
+}
+
+#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 \
+ (desc) = ne_strdup(src);\
+} while (0)
+
+
+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 */
+ SEARCH_CP_ELEM(sctx, sctx->curr, elm->name, sctx->curr->href, cdata);
+ break;
+
+ /* live props */
+ case ELEM_creationdate:
+ SEARCH_CP_ELEM(sctx, sctx->curr, elm->name,
+ sctx->curr->creationdate, cdata);
+ break;
+
+ case ELEM_displayname:
+ SEARCH_CP_ELEM(sctx, sctx->curr, elm->name,
+ sctx->curr->displayname, cdata);
+ break;
+
+ case ELEM_getcontentlanguage:
+ SEARCH_CP_ELEM(sctx, sctx->curr, elm->name,
+ sctx->curr->getcontentlanguage, cdata);
+ break;
+
+ case ELEM_getcontentlength:
+ SEARCH_CP_ELEM(sctx, sctx->curr, elm->name,
+ sctx->curr->getcontentlength, cdata);
+ break;
+
+ case ELEM_getcontenttype:
+ SEARCH_CP_ELEM(sctx, sctx->curr, elm->name,
+ sctx->curr->getcontenttype, cdata);
+ break;
+
+ case ELEM_getetag:
+ SEARCH_CP_ELEM(sctx, sctx->curr, elm->name,
+ sctx->curr->getetag, cdata);
+ break;
+
+ case ELEM_getlastmodified:
+ SEARCH_CP_ELEM(sctx, sctx->curr, elm->name,
+ sctx->curr->getlastmodified, cdata);
+ break;
+
+ case ELEM_lockdiscovery:
+ SEARCH_CP_ELEM(sctx, sctx->curr, elm->name,
+ sctx->curr->lockdiscovery, cdata);
+ break;
+
+ case ELEM_resourcetype:
+ SEARCH_CP_ELEM(sctx, sctx->curr, elm->name,
+ sctx->curr->resourcetype, cdata);
+ break;
+
+ case ELEM_source:
+ SEARCH_CP_ELEM(sctx, sctx->curr, elm->name,
+ sctx->curr->source, cdata);
+ break;
+
+ case ELEM_supportedlock:
+ SEARCH_CP_ELEM(sctx, sctx->curr, elm->name,
+ sctx->curr->supportedlock, cdata);
+ break;
+
+ case ELEM_collection:
+ SEARCH_CP_ELEM(sctx, sctx->curr, elm->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",
+ 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 */
+ 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_XML_VALID;
+}
+
+/* displays search results */
+static int display_results(search_ctx * sctx)
+{
+ search_res *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 ?
+ ne_httpdate_parse(res->getlastmodified) : 0;
+ int size = res->getcontentlength ? atol(res->getcontentlength) : 0;
+ char exec_char = ' ';
+
+ printf("%-40s%c %10d %s <%.10s>\n", res->href, exec_char,
+ size, format_time(modtime), 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);
+ }
+
+ return sctx->err_code;
+}
+
+static void search_ctx_destroy(search_ctx * sctx)
+{
+ search_res *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);
+ NE_FREE(res->collection);
+
+ 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, 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;
+ 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;
+ /* The buffer storing the parsing result of search condition */
+ ne_buffer *result_buf;
+
+ 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> | <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] = "";
+ /*Indicates whether there is any 'not' in the search condition */
+ int added_not = 0;
+ 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> | <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,
+ "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 (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,
+ "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,
+ "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,
+ "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 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 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 identifier[WORDLEN + 1] = "";
+ 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,
+ "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 */
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/31 02: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/31 02: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);