Switch to side-by-side view

--- a/src/python/recoll/pyrecoll.cpp
+++ b/src/python/recoll/pyrecoll.cpp
@@ -74,30 +74,46 @@
     return (PyObject *)self;
 }
 
+PyDoc_STRVAR(doc_SearchDataObject,
+"SearchData([type=AND|OR], [stemlang=somelanguage|null])\n"
+"\n"
+"A SearchData object describes a query. It has a number of global\n"
+"parameters and a chain of search clauses.\n"
+);
+
 static int
 SearchData_init(recoll_SearchDataObject *self, PyObject *args, PyObject *kwargs)
 {
     LOGDEB(("SearchData_init\n"));
-    static const char* kwlist[] = {"type", NULL};
+    static const char* kwlist[] = {"type", "stemlang", NULL};
     char *stp = 0;
-
-    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s", (char**)kwlist, &stp))
+    char *steml = 0;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|sz", (char**)kwlist, 
+				     &stp, &steml))
 	return -1;
     Rcl::SClType tp = Rcl::SCLT_AND;
 
     if (stp && strcasecmp(stp, "or")) {
 	tp = Rcl::SCLT_OR;
     }
-    self->sd = RefCntr<Rcl::SearchData>(new Rcl::SearchData(tp, "english"));
+    string stemlang;
+    if (steml) {
+	stemlang = steml;
+    } else {
+	stemlang = "english";
+    }
+    self->sd = RefCntr<Rcl::SearchData>(new Rcl::SearchData(tp, stemlang));
     return 0;
 }
 
 /* Note: addclause necessite And/Or vient du fait que le string peut avoir
    plusieurs mots. A transferer dans l'i/f Python ou pas ? */
 PyDoc_STRVAR(doc_addclause,
-"addclause(type='and'|'or'|'excl'|'phrase'|'near'|'sub',\n"
+"addclause(type='and'|'or'|'filename'|'phrase'|'near'|'path'|'sub',\n"
 "          qstring=string, slack=int, field=string, stemming=1|0,\n"
-"          subSearch=SearchData)\n"
+"          subSearch=SearchData, exclude=0|1, anchorstart=0|1, anchorend=0|1,\n"
+"          casesens=0|1, diacsens=0|1)\n"
 "Adds a simple clause to the SearchData And/Or chain, or a subquery\n"
 "defined by another SearchData object\n"
 );
@@ -106,8 +122,6 @@
 static PyObject *
 SearchData_addclause(recoll_SearchDataObject* self, PyObject *args, 
 		     PyObject *kwargs);
-
-
 
 static PyMethodDef SearchData_methods[] = {
     {"addclause", (PyCFunction)SearchData_addclause, METH_VARARGS|METH_KEYWORDS,
@@ -115,12 +129,6 @@
     {NULL}  /* Sentinel */
 };
 
-PyDoc_STRVAR(doc_SearchDataObject,
-"SearchData()\n"
-"\n"
-"A SearchData object describes a query. It has a number of global\n"
-"parameters and a chain of search clauses.\n"
-);
 static PyTypeObject recoll_SearchDataType = {
     PyObject_HEAD_INIT(NULL)
     0,                         /*ob_size*/
@@ -174,17 +182,29 @@
         return 0;
     }
     static const char *kwlist[] = {"type", "qstring", "slack", "field", 
-                                   "stemming", "subsearch", NULL};
+                                   "stemming", "subsearch", 
+				   "exclude", "anchorstart", "anchorend",
+				   "casesens", "diacsens",
+				   NULL};
     char *tp = 0;
     char *qs = 0; // needs freeing
     int slack = 0;
     char *fld = 0; // needs freeing
-    int  dostem = 1;
+    PyObject  *dostem = 0;
     recoll_SearchDataObject *sub = 0;
-    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ses|iesiO!", (char**)kwlist,
+    PyObject *exclude = 0;
+    PyObject *anchorstart = 0;
+    PyObject *anchorend = 0;
+    PyObject *casesens = 0;
+    PyObject *diacsens = 0;
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ses|iesOO!OOOOO", 
+				     (char**)kwlist,
 				     &tp, "utf-8", &qs, &slack,
 				     "utf-8", &fld, &dostem,
-				     &recoll_SearchDataType, &sub))
+				     &recoll_SearchDataType, &sub,
+				     &exclude, &anchorstart, &anchorend,
+				     &casesens, &diacsens
+	    ))
 	return 0;
 
     Rcl::SearchDataClause *cl = 0;
@@ -196,18 +216,17 @@
 	    goto defaultcase;
 	cl = new Rcl::SearchDataClauseSimple(Rcl::SCLT_AND, qs, fld?fld:"");
 	break;
+    case 'f':
+    case 'F':
+	if (strcasecmp(tp, "filename"))
+	    goto defaultcase;
+	cl = new Rcl::SearchDataClauseFilename(qs);
+	break;
     case 'o':
     case 'O':
 	if (strcasecmp(tp, "or"))
 	    goto defaultcase;
 	cl = new Rcl::SearchDataClauseSimple(Rcl::SCLT_OR, qs, fld?fld:"");
-	break;
-    case 'e':
-    case 'E':
-	if (strcasecmp(tp, "excl"))
-	    goto defaultcase;
-	cl = new Rcl::SearchDataClauseSimple(Rcl::SCLT_OR, qs, fld?fld:"");
-	cl->setexclude(true);
 	break;
     case 'n':
     case 'N':
@@ -218,10 +237,14 @@
 	break;
     case 'p':
     case 'P':
-	if (strcasecmp(tp, "phrase"))
+	if (!strcasecmp(tp, "phrase")) {
+	    cl = new Rcl::SearchDataClauseDist(Rcl::SCLT_PHRASE, qs, slack, 
+					       fld ? fld : "");
+	} else if (!strcasecmp(tp, "path")) {
+	    cl = new Rcl::SearchDataClausePath(qs);
+	} else {
 	    goto defaultcase;
-	cl = new Rcl::SearchDataClauseDist(Rcl::SCLT_PHRASE, qs, slack, 
-					   fld ? fld : "");
+	}
 	break;
     case 's':
     case 'S':
@@ -234,12 +257,27 @@
         PyErr_SetString(PyExc_AttributeError, "Bad tp arg");
 	return 0;
     }
-    if (dostem == 0) {
-	cl->setModifiers(Rcl::SearchDataClause::SDCM_NOSTEMMING);
-    }
-
     PyMem_Free(qs);
     PyMem_Free(fld);
+
+    if (dostem != 0 && !PyObject_IsTrue(dostem)) {
+	cl->addModifier(Rcl::SearchDataClause::SDCM_NOSTEMMING);
+    }
+    if (exclude != 0 && !PyObject_IsTrue(exclude)) {
+	cl->setexclude(true);
+    }
+    if (anchorstart && PyObject_IsTrue(anchorstart)) {
+	cl->addModifier(Rcl::SearchDataClause::SDCM_ANCHORSTART);
+    }
+    if (anchorend && PyObject_IsTrue(anchorend)) {
+	cl->addModifier(Rcl::SearchDataClause::SDCM_ANCHOREND);
+    }
+    if (casesens && PyObject_IsTrue(casesens)) {
+	cl->addModifier(Rcl::SearchDataClause::SDCM_CASESENS);
+    }
+    if (diacsens && PyObject_IsTrue(diacsens)) {
+	cl->addModifier(Rcl::SearchDataClause::SDCM_DIACSENS);
+    }
     self->sd->addClause(cl);
     Py_RETURN_NONE;
 }
@@ -1426,7 +1464,6 @@
 static PyObject *
 Db_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 {
-    LOGDEB(("Db_new\n"));
     recoll_DbObject *self;
 
     self = (recoll_DbObject *)type->tp_alloc(type, 0);
@@ -1439,7 +1476,6 @@
 static int
 Db_init(recoll_DbObject *self, PyObject *args, PyObject *kwargs)
 {
-    LOGDEB(("Db_init\n"));
     static const char *kwlist[] = {"confdir", "extra_dbs", "writable", NULL};
     PyObject *extradbs = 0;
     char *confdir = 0;
@@ -1459,6 +1495,8 @@
     } else {
 	rclconfig = recollinit(0, 0, reason, 0);
     }
+    LOGDEB(("Db_init\n"));
+
     if (rclconfig == 0) {
 	PyErr_SetString(PyExc_EnvironmentError, reason.c_str());
 	return -1;
@@ -1786,7 +1824,6 @@
 static PyObject *
 recoll_connect(PyObject *self, PyObject *args, PyObject *kwargs)
 {
-    LOGDEB(("recoll_connect\n"));
     recoll_DbObject *db = (recoll_DbObject *)
 	PyObject_Call((PyObject *)&recoll_DbType, args, kwargs);
     return (PyObject *)db;
@@ -1820,21 +1857,13 @@
 PyMODINIT_FUNC
 initrecoll(void)
 {
-    string reason;
-    rclconfig = recollinit(0, 0, reason, 0);
-    if (rclconfig == 0) {
-	PyErr_SetString(PyExc_EnvironmentError, reason.c_str());
-	return;
-    }
-    if (!rclconfig->ok()) {
-	PyErr_SetString(PyExc_EnvironmentError, 
-			"Recoll init error: bad environment ?");
-	return;
-    }
+    // Note: we can't call recollinit here, because the confdir is only really
+    // known when the first db object is created (it is an optional parameter).
+    // Using a default here may end up with variables such as stripchars being
+    // wrong
 
     PyObject* m;
-    m = Py_InitModule3("recoll", recollMethods,
-                       "Recoll extension module.");
+    m = Py_InitModule3("recoll", recollMethods, "Recoll extension module.");
     
     if (PyType_Ready(&recoll_DbType) < 0)
         return;