[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/xeft babe67496a 52/55: Add xapian-lite source
From: |
ELPA Syncer |
Subject: |
[elpa] externals/xeft babe67496a 52/55: Add xapian-lite source |
Date: |
Fri, 13 Jan 2023 23:58:40 -0500 (EST) |
branch: externals/xeft
commit babe67496a753a214265aa9ec7740297acca7cd1
Author: Yuan Fu <casouri@gmail.com>
Commit: Yuan Fu <casouri@gmail.com>
Add xapian-lite source
This way the user don't need to clone the git repo of xapian-lite.
* Makefile:
* emacs-module-prelude.h:
* emacs-module.h:
* xapian-lite.cc: Copied straight from xapian-lite.
---
Makefile | 19 +-
emacs-module-prelude.h | 163 +++++++++++
emacs-module.h | 763 +++++++++++++++++++++++++++++++++++++++++++++++++
xapian-lite.cc | 505 ++++++++++++++++++++++++++++++++
4 files changed, 1438 insertions(+), 12 deletions(-)
diff --git a/Makefile b/Makefile
index 8a623b43ba..45d2c036b1 100644
--- a/Makefile
+++ b/Makefile
@@ -1,10 +1,9 @@
.POSIX:
-# Even if this is unnecessary, it doesn’t hurt.
-PREFIX=/usr/local
-CXX=g++
-CXXFLAGS=-fPIC -I$(PREFIX)/include -std=c++11
-LDFLAGS=-L$(PREFIX)/lib
-LDLIBS=-lxapian
+PREFIX ?= /usr/local
+CXX ?= g++
+CXXFLAGS = -fPIC -I$(PREFIX)/include -std=c++11
+LDFLAGS = -L$(PREFIX)/lib
+LDLIBS = -lxapian
# Dylib extensions.
ifeq ($(OS),Windows_NT)
@@ -15,12 +14,8 @@ else
SOEXT = so
endif
-xapian-lite.$(SOEXT): module/xapian-lite.cc
+xapian-lite.$(SOEXT): xapian-lite.cc
$(CXX) $< -o $@ -shared $(CXXFLAGS) $(LDFLAGS) $(LDLIBS)
-module/xapian-lite.cc:
- git clone https://github.com/casouri/xapian-lite module --depth=1
-
clean:
- rm -f *.so *.o
- rm -rf module
+ rm -f *.so *.o *.dylib *.dll
diff --git a/emacs-module-prelude.h b/emacs-module-prelude.h
new file mode 100644
index 0000000000..a4587652be
--- /dev/null
+++ b/emacs-module-prelude.h
@@ -0,0 +1,163 @@
+#include "emacs-module.h"
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#ifndef EMACS_MODULE_PRELUDE_H
+#define EMACS_MODULE_PRELUDE_H
+
+#define EMP_MAJOR_VERSION 1
+#define EMP_MINOR_VERSION 0
+#define EMP_PATCH_VERSION 0
+
+
+/*
+ Copy a Lisp string VALUE into BUFFER, and store the string size in
+ SIZE. A user doesn’t need to allocate BUFFER, but it is the user’s
+ responsibility to free it.
+ */
+bool
+emp_copy_string_contents
+(emacs_env *env, emacs_value value, char **buffer, size_t *size)
+/* Copied from Pillipp’s document. I commented out assertions. */
+{
+ ptrdiff_t buffer_size;
+ if (!env->copy_string_contents (env, value, NULL, &buffer_size))
+ return false;
+ /* assert (env->non_local_exit_check (env) == emacs_funcall_exit_return); */
+ /* assert (buffer_size > 0); */
+ *buffer = (char*) malloc ((size_t) buffer_size);
+ if (*buffer == NULL)
+ {
+ env->non_local_exit_signal (env, env->intern (env, "memory-full"),
+ env->intern (env, "nil"));
+ return false;
+ }
+ ptrdiff_t old_buffer_size = buffer_size;
+ if (!env->copy_string_contents (env, value, *buffer, &buffer_size))
+ {
+ free (*buffer);
+ *buffer = NULL;
+ return false;
+ }
+ /* assert (env->non_local_exit_check (env) == emacs_funcall_exit_return); */
+ /* assert (buffer_size == old_buffer_size); */
+ *size = (size_t) (buffer_size - 1);
+ return true;
+}
+
+/*
+ Return a Lisp string. This is basically env->make_string except that
+ it calls strlen for you.
+ */
+emacs_value
+emp_build_string (emacs_env *env, const char *string)
+{
+ return env->make_string (env, string, strlen (string));
+}
+
+/*
+ Intern NAME to a symbol. NAME has to be all-ASCII.
+ */
+emacs_value
+emp_intern (emacs_env *env, const char *name)
+{
+ return env->intern (env, name);
+}
+
+/*
+ Call a function named FN which takes NARGS number of arguments.
+ Example: funcall (env, "cons", 2, car, cdr);
+ */
+emacs_value
+emp_funcall (emacs_env *env, const char* fn, ptrdiff_t nargs, ...)
+{
+ va_list argv;
+ va_start (argv, nargs);
+ emacs_value *args = (emacs_value *) malloc(nargs * sizeof(emacs_value));
+ for (int idx = 0; idx < nargs; idx++)
+ {
+ args[idx] = va_arg (argv, emacs_value);
+ }
+ va_end (argv);
+ emacs_value val = env->funcall (env, emp_intern (env, fn), nargs, args);
+ free (args);
+ return val;
+}
+
+/*
+ Provide FEATURE like ‘provide’ in Lisp.
+*/
+void
+emp_provide (emacs_env *env, const char *feature)
+{
+ emp_funcall (env, "provide", 1, emp_intern (env, feature));
+}
+
+/*
+ Raise a signal where NAME is the signal name and MESSAGE is the
+ error message.
+ */
+void
+emp_signal_message1
+(emacs_env *env, const char *name, const char *message)
+{
+ env->non_local_exit_signal
+ (env, env->intern (env, name),
+ emp_funcall (env, "cons", 2,
+ env->make_string (env, message, strlen (message)),
+ emp_intern (env, "nil")));
+}
+
+/*
+ Define an error like ‘define-error’.
+ */
+void
+emp_define_error
+(emacs_env *env, const char *name,
+ const char *description, const char *parent)
+{
+ emp_funcall (env, "define-error", 3,
+ emp_intern (env, name),
+ env->make_string (env, description, strlen (description)),
+ emp_intern (env, parent));
+}
+
+/*
+ Return true if VAL is symbol nil.
+ */
+bool
+emp_nilp (emacs_env *env, emacs_value val)
+{
+ return !env->is_not_nil (env, val);
+}
+
+/*
+ Define a function NAME. The number of arguments that the function
+ takes is between MIN_ARITY and MAX_ARITY. FUNCTION is a function
+ with signature
+
+ static emacs_value
+ function
+ (emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data)
+ EMACS_NOEXCEPT
+
+ DOCUMENTATION is the docstring for FUNCTION.
+ */
+void
+emp_define_function
+(emacs_env *env, const char *name, ptrdiff_t min_arity,
+ ptrdiff_t max_arity,
+ emacs_value (*function) (emacs_env *env,
+ ptrdiff_t nargs,
+ emacs_value* args,
+ void *data) EMACS_NOEXCEPT,
+ const char *documentation)
+{
+ emacs_value fn = env->make_function
+ (env, min_arity, max_arity, function, documentation, NULL);
+ emp_funcall (env, "fset", 2, emp_intern (env, name), fn);
+}
+
+#endif /* EMACS_MODULE_PRELUDE_H */
diff --git a/emacs-module.h b/emacs-module.h
new file mode 100644
index 0000000000..1185c06f45
--- /dev/null
+++ b/emacs-module.h
@@ -0,0 +1,763 @@
+/* emacs-module.h - GNU Emacs module API.
+
+Copyright (C) 2015-2021 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs 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 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs 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 GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+/*
+This file defines the Emacs module API. Please see the chapter
+`Dynamic Modules' in the GNU Emacs Lisp Reference Manual for
+information how to write modules and use this header file.
+*/
+
+#ifndef EMACS_MODULE_H
+#define EMACS_MODULE_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <time.h>
+
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif
+
+#define EMACS_MAJOR_VERSION 28
+
+#if defined __cplusplus && __cplusplus >= 201103L
+# define EMACS_NOEXCEPT noexcept
+#else
+# define EMACS_NOEXCEPT
+#endif
+
+#if defined __cplusplus && __cplusplus >= 201703L
+# define EMACS_NOEXCEPT_TYPEDEF noexcept
+#else
+# define EMACS_NOEXCEPT_TYPEDEF
+#endif
+
+#if 3 < __GNUC__ + (3 <= __GNUC_MINOR__)
+# define EMACS_ATTRIBUTE_NONNULL(...) \
+ __attribute__ ((__nonnull__ (__VA_ARGS__)))
+#elif (defined __has_attribute \
+ && (!defined __clang_minor__ \
+ || 3 < __clang_major__ + (5 <= __clang_minor__)))
+# if __has_attribute (__nonnull__)
+# define EMACS_ATTRIBUTE_NONNULL(...) \
+ __attribute__ ((__nonnull__ (__VA_ARGS__)))
+# endif
+#endif
+#ifndef EMACS_ATTRIBUTE_NONNULL
+# define EMACS_ATTRIBUTE_NONNULL(...)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Current environment. */
+typedef struct emacs_env_28 emacs_env;
+
+/* Opaque pointer representing an Emacs Lisp value.
+ BEWARE: Do not assume NULL is a valid value! */
+typedef struct emacs_value_tag *emacs_value;
+
+enum { emacs_variadic_function = -2 };
+
+/* Struct passed to a module init function (emacs_module_init). */
+struct emacs_runtime
+{
+ /* Structure size (for version checking). */
+ ptrdiff_t size;
+
+ /* Private data; users should not touch this. */
+ struct emacs_runtime_private *private_members;
+
+ /* Return an environment pointer. */
+ emacs_env *(*get_environment) (struct emacs_runtime *runtime)
+ EMACS_ATTRIBUTE_NONNULL (1);
+};
+
+/* Type aliases for function pointer types used in the module API.
+ Note that we don't use these aliases directly in the API to be able
+ to mark the function arguments as 'noexcept' before C++20.
+ However, users can use them if they want. */
+
+/* Function prototype for the module Lisp functions. These must not
+ throw C++ exceptions. */
+typedef emacs_value (*emacs_function) (emacs_env *env, ptrdiff_t nargs,
+ emacs_value *args,
+ void *data)
+ EMACS_NOEXCEPT_TYPEDEF EMACS_ATTRIBUTE_NONNULL (1);
+
+/* Function prototype for module user-pointer and function finalizers.
+ These must not throw C++ exceptions. */
+typedef void (*emacs_finalizer) (void *data) EMACS_NOEXCEPT_TYPEDEF;
+
+/* Possible Emacs function call outcomes. */
+enum emacs_funcall_exit
+{
+ /* Function has returned normally. */
+ emacs_funcall_exit_return = 0,
+
+ /* Function has signaled an error using `signal'. */
+ emacs_funcall_exit_signal = 1,
+
+ /* Function has exit using `throw'. */
+ emacs_funcall_exit_throw = 2
+};
+
+/* Possible return values for emacs_env.process_input. */
+enum emacs_process_input_result
+{
+ /* Module code may continue */
+ emacs_process_input_continue = 0,
+
+ /* Module code should return control to Emacs as soon as possible. */
+ emacs_process_input_quit = 1
+};
+
+/* Define emacs_limb_t so that it is likely to match GMP's mp_limb_t.
+ This micro-optimization can help modules that use mpz_export and
+ mpz_import, which operate more efficiently on mp_limb_t. It's OK
+ (if perhaps a bit slower) if the two types do not match, and
+ modules shouldn't rely on the two types matching. */
+typedef size_t emacs_limb_t;
+#define EMACS_LIMB_MAX SIZE_MAX
+
+struct emacs_env_25
+{
+ /* Structure size (for version checking). */
+ ptrdiff_t size;
+
+ /* Private data; users should not touch this. */
+ struct emacs_env_private *private_members;
+
+ /* Memory management. */
+
+ emacs_value (*make_global_ref) (emacs_env *env, emacs_value value)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ void (*free_global_ref) (emacs_env *env, emacs_value global_value)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ /* Non-local exit handling. */
+
+ enum emacs_funcall_exit (*non_local_exit_check) (emacs_env *env)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ void (*non_local_exit_clear) (emacs_env *env)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ enum emacs_funcall_exit (*non_local_exit_get)
+ (emacs_env *env, emacs_value *symbol, emacs_value *data)
+ EMACS_ATTRIBUTE_NONNULL(1, 2, 3);
+
+ void (*non_local_exit_signal) (emacs_env *env,
+ emacs_value symbol, emacs_value data)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ void (*non_local_exit_throw) (emacs_env *env,
+ emacs_value tag, emacs_value value)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ /* Function registration. */
+
+ emacs_value (*make_function) (emacs_env *env,
+ ptrdiff_t min_arity,
+ ptrdiff_t max_arity,
+ emacs_value (*func) (emacs_env *env,
+ ptrdiff_t nargs,
+ emacs_value* args,
+ void *data)
+ EMACS_NOEXCEPT
+ EMACS_ATTRIBUTE_NONNULL(1),
+ const char *docstring,
+ void *data)
+ EMACS_ATTRIBUTE_NONNULL(1, 4);
+
+ emacs_value (*funcall) (emacs_env *env,
+ emacs_value func,
+ ptrdiff_t nargs,
+ emacs_value* args)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ emacs_value (*intern) (emacs_env *env, const char *name)
+ EMACS_ATTRIBUTE_NONNULL(1, 2);
+
+ /* Type conversion. */
+
+ emacs_value (*type_of) (emacs_env *env, emacs_value arg)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ bool (*is_not_nil) (emacs_env *env, emacs_value arg)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ bool (*eq) (emacs_env *env, emacs_value a, emacs_value b)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ intmax_t (*extract_integer) (emacs_env *env, emacs_value arg)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ emacs_value (*make_integer) (emacs_env *env, intmax_t n)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ double (*extract_float) (emacs_env *env, emacs_value arg)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ emacs_value (*make_float) (emacs_env *env, double d)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ /* Copy the content of the Lisp string VALUE to BUFFER as an utf8
+ null-terminated string.
+
+ SIZE must point to the total size of the buffer. If BUFFER is
+ NULL or if SIZE is not big enough, write the required buffer size
+ to SIZE and return true.
+
+ Note that SIZE must include the last null byte (e.g. "abc" needs
+ a buffer of size 4).
+
+ Return true if the string was successfully copied. */
+
+ bool (*copy_string_contents) (emacs_env *env,
+ emacs_value value,
+ char *buf,
+ ptrdiff_t *len)
+ EMACS_ATTRIBUTE_NONNULL(1, 4);
+
+ /* Create a Lisp string from a utf8 encoded string. */
+ emacs_value (*make_string) (emacs_env *env,
+ const char *str, ptrdiff_t len)
+ EMACS_ATTRIBUTE_NONNULL(1, 2);
+
+ /* Embedded pointer type. */
+ emacs_value (*make_user_ptr) (emacs_env *env,
+ void (*fin) (void *) EMACS_NOEXCEPT,
+ void *ptr)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ void *(*get_user_ptr) (emacs_env *env, emacs_value arg)
+ EMACS_ATTRIBUTE_NONNULL(1);
+ void (*set_user_ptr) (emacs_env *env, emacs_value arg, void *ptr)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ void (*(*get_user_finalizer) (emacs_env *env, emacs_value uptr))
+ (void *) EMACS_NOEXCEPT EMACS_ATTRIBUTE_NONNULL(1);
+ void (*set_user_finalizer) (emacs_env *env, emacs_value arg,
+ void (*fin) (void *) EMACS_NOEXCEPT)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ /* Vector functions. */
+ emacs_value (*vec_get) (emacs_env *env, emacs_value vector, ptrdiff_t index)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ void (*vec_set) (emacs_env *env, emacs_value vector, ptrdiff_t index,
+ emacs_value value)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ ptrdiff_t (*vec_size) (emacs_env *env, emacs_value vector)
+ EMACS_ATTRIBUTE_NONNULL(1);
+};
+
+struct emacs_env_26
+{
+ /* Structure size (for version checking). */
+ ptrdiff_t size;
+
+ /* Private data; users should not touch this. */
+ struct emacs_env_private *private_members;
+
+ /* Memory management. */
+
+ emacs_value (*make_global_ref) (emacs_env *env, emacs_value value)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ void (*free_global_ref) (emacs_env *env, emacs_value global_value)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ /* Non-local exit handling. */
+
+ enum emacs_funcall_exit (*non_local_exit_check) (emacs_env *env)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ void (*non_local_exit_clear) (emacs_env *env)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ enum emacs_funcall_exit (*non_local_exit_get)
+ (emacs_env *env, emacs_value *symbol, emacs_value *data)
+ EMACS_ATTRIBUTE_NONNULL(1, 2, 3);
+
+ void (*non_local_exit_signal) (emacs_env *env,
+ emacs_value symbol, emacs_value data)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ void (*non_local_exit_throw) (emacs_env *env,
+ emacs_value tag, emacs_value value)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ /* Function registration. */
+
+ emacs_value (*make_function) (emacs_env *env,
+ ptrdiff_t min_arity,
+ ptrdiff_t max_arity,
+ emacs_value (*func) (emacs_env *env,
+ ptrdiff_t nargs,
+ emacs_value* args,
+ void *data)
+ EMACS_NOEXCEPT
+ EMACS_ATTRIBUTE_NONNULL(1),
+ const char *docstring,
+ void *data)
+ EMACS_ATTRIBUTE_NONNULL(1, 4);
+
+ emacs_value (*funcall) (emacs_env *env,
+ emacs_value func,
+ ptrdiff_t nargs,
+ emacs_value* args)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ emacs_value (*intern) (emacs_env *env, const char *name)
+ EMACS_ATTRIBUTE_NONNULL(1, 2);
+
+ /* Type conversion. */
+
+ emacs_value (*type_of) (emacs_env *env, emacs_value arg)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ bool (*is_not_nil) (emacs_env *env, emacs_value arg)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ bool (*eq) (emacs_env *env, emacs_value a, emacs_value b)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ intmax_t (*extract_integer) (emacs_env *env, emacs_value arg)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ emacs_value (*make_integer) (emacs_env *env, intmax_t n)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ double (*extract_float) (emacs_env *env, emacs_value arg)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ emacs_value (*make_float) (emacs_env *env, double d)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ /* Copy the content of the Lisp string VALUE to BUFFER as an utf8
+ null-terminated string.
+
+ SIZE must point to the total size of the buffer. If BUFFER is
+ NULL or if SIZE is not big enough, write the required buffer size
+ to SIZE and return true.
+
+ Note that SIZE must include the last null byte (e.g. "abc" needs
+ a buffer of size 4).
+
+ Return true if the string was successfully copied. */
+
+ bool (*copy_string_contents) (emacs_env *env,
+ emacs_value value,
+ char *buf,
+ ptrdiff_t *len)
+ EMACS_ATTRIBUTE_NONNULL(1, 4);
+
+ /* Create a Lisp string from a utf8 encoded string. */
+ emacs_value (*make_string) (emacs_env *env,
+ const char *str, ptrdiff_t len)
+ EMACS_ATTRIBUTE_NONNULL(1, 2);
+
+ /* Embedded pointer type. */
+ emacs_value (*make_user_ptr) (emacs_env *env,
+ void (*fin) (void *) EMACS_NOEXCEPT,
+ void *ptr)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ void *(*get_user_ptr) (emacs_env *env, emacs_value arg)
+ EMACS_ATTRIBUTE_NONNULL(1);
+ void (*set_user_ptr) (emacs_env *env, emacs_value arg, void *ptr)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ void (*(*get_user_finalizer) (emacs_env *env, emacs_value uptr))
+ (void *) EMACS_NOEXCEPT EMACS_ATTRIBUTE_NONNULL(1);
+ void (*set_user_finalizer) (emacs_env *env, emacs_value arg,
+ void (*fin) (void *) EMACS_NOEXCEPT)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ /* Vector functions. */
+ emacs_value (*vec_get) (emacs_env *env, emacs_value vector, ptrdiff_t index)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ void (*vec_set) (emacs_env *env, emacs_value vector, ptrdiff_t index,
+ emacs_value value)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ ptrdiff_t (*vec_size) (emacs_env *env, emacs_value vector)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ /* Returns whether a quit is pending. */
+ bool (*should_quit) (emacs_env *env)
+ EMACS_ATTRIBUTE_NONNULL(1);
+};
+
+struct emacs_env_27
+{
+ /* Structure size (for version checking). */
+ ptrdiff_t size;
+
+ /* Private data; users should not touch this. */
+ struct emacs_env_private *private_members;
+
+ /* Memory management. */
+
+ emacs_value (*make_global_ref) (emacs_env *env, emacs_value value)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ void (*free_global_ref) (emacs_env *env, emacs_value global_value)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ /* Non-local exit handling. */
+
+ enum emacs_funcall_exit (*non_local_exit_check) (emacs_env *env)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ void (*non_local_exit_clear) (emacs_env *env)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ enum emacs_funcall_exit (*non_local_exit_get)
+ (emacs_env *env, emacs_value *symbol, emacs_value *data)
+ EMACS_ATTRIBUTE_NONNULL(1, 2, 3);
+
+ void (*non_local_exit_signal) (emacs_env *env,
+ emacs_value symbol, emacs_value data)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ void (*non_local_exit_throw) (emacs_env *env,
+ emacs_value tag, emacs_value value)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ /* Function registration. */
+
+ emacs_value (*make_function) (emacs_env *env,
+ ptrdiff_t min_arity,
+ ptrdiff_t max_arity,
+ emacs_value (*func) (emacs_env *env,
+ ptrdiff_t nargs,
+ emacs_value* args,
+ void *data)
+ EMACS_NOEXCEPT
+ EMACS_ATTRIBUTE_NONNULL(1),
+ const char *docstring,
+ void *data)
+ EMACS_ATTRIBUTE_NONNULL(1, 4);
+
+ emacs_value (*funcall) (emacs_env *env,
+ emacs_value func,
+ ptrdiff_t nargs,
+ emacs_value* args)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ emacs_value (*intern) (emacs_env *env, const char *name)
+ EMACS_ATTRIBUTE_NONNULL(1, 2);
+
+ /* Type conversion. */
+
+ emacs_value (*type_of) (emacs_env *env, emacs_value arg)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ bool (*is_not_nil) (emacs_env *env, emacs_value arg)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ bool (*eq) (emacs_env *env, emacs_value a, emacs_value b)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ intmax_t (*extract_integer) (emacs_env *env, emacs_value arg)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ emacs_value (*make_integer) (emacs_env *env, intmax_t n)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ double (*extract_float) (emacs_env *env, emacs_value arg)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ emacs_value (*make_float) (emacs_env *env, double d)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ /* Copy the content of the Lisp string VALUE to BUFFER as an utf8
+ null-terminated string.
+
+ SIZE must point to the total size of the buffer. If BUFFER is
+ NULL or if SIZE is not big enough, write the required buffer size
+ to SIZE and return true.
+
+ Note that SIZE must include the last null byte (e.g. "abc" needs
+ a buffer of size 4).
+
+ Return true if the string was successfully copied. */
+
+ bool (*copy_string_contents) (emacs_env *env,
+ emacs_value value,
+ char *buf,
+ ptrdiff_t *len)
+ EMACS_ATTRIBUTE_NONNULL(1, 4);
+
+ /* Create a Lisp string from a utf8 encoded string. */
+ emacs_value (*make_string) (emacs_env *env,
+ const char *str, ptrdiff_t len)
+ EMACS_ATTRIBUTE_NONNULL(1, 2);
+
+ /* Embedded pointer type. */
+ emacs_value (*make_user_ptr) (emacs_env *env,
+ void (*fin) (void *) EMACS_NOEXCEPT,
+ void *ptr)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ void *(*get_user_ptr) (emacs_env *env, emacs_value arg)
+ EMACS_ATTRIBUTE_NONNULL(1);
+ void (*set_user_ptr) (emacs_env *env, emacs_value arg, void *ptr)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ void (*(*get_user_finalizer) (emacs_env *env, emacs_value uptr))
+ (void *) EMACS_NOEXCEPT EMACS_ATTRIBUTE_NONNULL(1);
+ void (*set_user_finalizer) (emacs_env *env, emacs_value arg,
+ void (*fin) (void *) EMACS_NOEXCEPT)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ /* Vector functions. */
+ emacs_value (*vec_get) (emacs_env *env, emacs_value vector, ptrdiff_t index)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ void (*vec_set) (emacs_env *env, emacs_value vector, ptrdiff_t index,
+ emacs_value value)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ ptrdiff_t (*vec_size) (emacs_env *env, emacs_value vector)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ /* Returns whether a quit is pending. */
+ bool (*should_quit) (emacs_env *env)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ /* Processes pending input events and returns whether the module
+ function should quit. */
+ enum emacs_process_input_result (*process_input) (emacs_env *env)
+ EMACS_ATTRIBUTE_NONNULL (1);
+
+ struct timespec (*extract_time) (emacs_env *env, emacs_value arg)
+ EMACS_ATTRIBUTE_NONNULL (1);
+
+ emacs_value (*make_time) (emacs_env *env, struct timespec time)
+ EMACS_ATTRIBUTE_NONNULL (1);
+
+ bool (*extract_big_integer) (emacs_env *env, emacs_value arg, int *sign,
+ ptrdiff_t *count, emacs_limb_t *magnitude)
+ EMACS_ATTRIBUTE_NONNULL (1);
+
+ emacs_value (*make_big_integer) (emacs_env *env, int sign, ptrdiff_t count,
+ const emacs_limb_t *magnitude)
+ EMACS_ATTRIBUTE_NONNULL (1);
+};
+
+struct emacs_env_28
+{
+ /* Structure size (for version checking). */
+ ptrdiff_t size;
+
+ /* Private data; users should not touch this. */
+ struct emacs_env_private *private_members;
+
+ /* Memory management. */
+
+ emacs_value (*make_global_ref) (emacs_env *env, emacs_value value)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ void (*free_global_ref) (emacs_env *env, emacs_value global_value)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ /* Non-local exit handling. */
+
+ enum emacs_funcall_exit (*non_local_exit_check) (emacs_env *env)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ void (*non_local_exit_clear) (emacs_env *env)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ enum emacs_funcall_exit (*non_local_exit_get)
+ (emacs_env *env, emacs_value *symbol, emacs_value *data)
+ EMACS_ATTRIBUTE_NONNULL(1, 2, 3);
+
+ void (*non_local_exit_signal) (emacs_env *env,
+ emacs_value symbol, emacs_value data)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ void (*non_local_exit_throw) (emacs_env *env,
+ emacs_value tag, emacs_value value)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ /* Function registration. */
+
+ emacs_value (*make_function) (emacs_env *env,
+ ptrdiff_t min_arity,
+ ptrdiff_t max_arity,
+ emacs_value (*func) (emacs_env *env,
+ ptrdiff_t nargs,
+ emacs_value* args,
+ void *data)
+ EMACS_NOEXCEPT
+ EMACS_ATTRIBUTE_NONNULL(1),
+ const char *docstring,
+ void *data)
+ EMACS_ATTRIBUTE_NONNULL(1, 4);
+
+ emacs_value (*funcall) (emacs_env *env,
+ emacs_value func,
+ ptrdiff_t nargs,
+ emacs_value* args)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ emacs_value (*intern) (emacs_env *env, const char *name)
+ EMACS_ATTRIBUTE_NONNULL(1, 2);
+
+ /* Type conversion. */
+
+ emacs_value (*type_of) (emacs_env *env, emacs_value arg)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ bool (*is_not_nil) (emacs_env *env, emacs_value arg)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ bool (*eq) (emacs_env *env, emacs_value a, emacs_value b)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ intmax_t (*extract_integer) (emacs_env *env, emacs_value arg)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ emacs_value (*make_integer) (emacs_env *env, intmax_t n)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ double (*extract_float) (emacs_env *env, emacs_value arg)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ emacs_value (*make_float) (emacs_env *env, double d)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ /* Copy the content of the Lisp string VALUE to BUFFER as an utf8
+ null-terminated string.
+
+ SIZE must point to the total size of the buffer. If BUFFER is
+ NULL or if SIZE is not big enough, write the required buffer size
+ to SIZE and return true.
+
+ Note that SIZE must include the last null byte (e.g. "abc" needs
+ a buffer of size 4).
+
+ Return true if the string was successfully copied. */
+
+ bool (*copy_string_contents) (emacs_env *env,
+ emacs_value value,
+ char *buf,
+ ptrdiff_t *len)
+ EMACS_ATTRIBUTE_NONNULL(1, 4);
+
+ /* Create a Lisp string from a utf8 encoded string. */
+ emacs_value (*make_string) (emacs_env *env,
+ const char *str, ptrdiff_t len)
+ EMACS_ATTRIBUTE_NONNULL(1, 2);
+
+ /* Embedded pointer type. */
+ emacs_value (*make_user_ptr) (emacs_env *env,
+ void (*fin) (void *) EMACS_NOEXCEPT,
+ void *ptr)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ void *(*get_user_ptr) (emacs_env *env, emacs_value arg)
+ EMACS_ATTRIBUTE_NONNULL(1);
+ void (*set_user_ptr) (emacs_env *env, emacs_value arg, void *ptr)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ void (*(*get_user_finalizer) (emacs_env *env, emacs_value uptr))
+ (void *) EMACS_NOEXCEPT EMACS_ATTRIBUTE_NONNULL(1);
+ void (*set_user_finalizer) (emacs_env *env, emacs_value arg,
+ void (*fin) (void *) EMACS_NOEXCEPT)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ /* Vector functions. */
+ emacs_value (*vec_get) (emacs_env *env, emacs_value vector, ptrdiff_t index)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ void (*vec_set) (emacs_env *env, emacs_value vector, ptrdiff_t index,
+ emacs_value value)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ ptrdiff_t (*vec_size) (emacs_env *env, emacs_value vector)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ /* Returns whether a quit is pending. */
+ bool (*should_quit) (emacs_env *env)
+ EMACS_ATTRIBUTE_NONNULL(1);
+
+ /* Processes pending input events and returns whether the module
+ function should quit. */
+ enum emacs_process_input_result (*process_input) (emacs_env *env)
+ EMACS_ATTRIBUTE_NONNULL (1);
+
+ struct timespec (*extract_time) (emacs_env *env, emacs_value arg)
+ EMACS_ATTRIBUTE_NONNULL (1);
+
+ emacs_value (*make_time) (emacs_env *env, struct timespec time)
+ EMACS_ATTRIBUTE_NONNULL (1);
+
+ bool (*extract_big_integer) (emacs_env *env, emacs_value arg, int *sign,
+ ptrdiff_t *count, emacs_limb_t *magnitude)
+ EMACS_ATTRIBUTE_NONNULL (1);
+
+ emacs_value (*make_big_integer) (emacs_env *env, int sign, ptrdiff_t count,
+ const emacs_limb_t *magnitude)
+ EMACS_ATTRIBUTE_NONNULL (1);
+
+ /* Add module environment functions newly added in Emacs 28 here.
+ Before Emacs 28 is released, remove this comment and start
+ module-env-29.h on the master branch. */
+
+ void (*(*EMACS_ATTRIBUTE_NONNULL (1)
+ get_function_finalizer) (emacs_env *env,
+ emacs_value arg)) (void *) EMACS_NOEXCEPT;
+
+ void (*set_function_finalizer) (emacs_env *env, emacs_value arg,
+ void (*fin) (void *) EMACS_NOEXCEPT)
+ EMACS_ATTRIBUTE_NONNULL (1);
+
+ int (*open_channel) (emacs_env *env, emacs_value pipe_process)
+ EMACS_ATTRIBUTE_NONNULL (1);
+
+ void (*make_interactive) (emacs_env *env, emacs_value function,
+ emacs_value spec)
+ EMACS_ATTRIBUTE_NONNULL (1);
+
+ /* Create a unibyte Lisp string from a string. */
+ emacs_value (*make_unibyte_string) (emacs_env *env,
+ const char *str, ptrdiff_t len)
+ EMACS_ATTRIBUTE_NONNULL(1, 2);
+};
+
+/* Every module should define a function as follows. */
+extern int emacs_module_init (struct emacs_runtime *runtime)
+ EMACS_NOEXCEPT
+ EMACS_ATTRIBUTE_NONNULL (1);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EMACS_MODULE_H */
diff --git a/xapian-lite.cc b/xapian-lite.cc
new file mode 100644
index 0000000000..539c15046c
--- /dev/null
+++ b/xapian-lite.cc
@@ -0,0 +1,505 @@
+/* Xapian-lite.
+
+Copyright (C) 2021-2023 Free Software Foundation, Inc.
+
+Maintainer: Yuan Fu <casouri@gmail.com>
+
+This file is part of GNU Emacs.
+
+GNU Emacs 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 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs 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 GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+#include <string>
+#include <cstring>
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include <exception>
+#include <iterator>
+#include <cstdarg>
+
+#include <stdlib.h>
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <xapian.h>
+
+#include "emacs-module.h"
+#include "emacs-module-prelude.h"
+
+using namespace std;
+
+int plugin_is_GPL_compatible;
+
+#if defined __cplusplus && __cplusplus >= 201103L
+# define EMACS_NOEXCEPT noexcept
+#else
+# define EMACS_NOEXCEPT
+#endif
+
+#define CHECK_EXIT(env) \
+ if (env->non_local_exit_check (env) \
+ != emacs_funcall_exit_return) \
+ { return NULL; }
+
+/* A few notes: The database we use, WritableDatabase, will not throw
+ DatabaseModifiedError, so we don’t need to handle that. For query,
+ we first try to parse it with special syntax enabled, i.e., with
+ AND, OR, +/-, etc. If that doesn’t parse, we’ll just parse it as
+ plain text.
+
+ REF:
https://lists.xapian.org/pipermail/xapian-discuss/2021-August/009906.html
+
+ To find a Xapian document by a path, we assign each document a
+ special term QV<path>. But since Xapian has a limit on the length of
+ a term, we hash long paths, and the actual term becomes QH<hash>.
+ */
+
+/*** Xapian stuff */
+
+// The index of the document value that stores the mtime.
+static const Xapian::valueno DOC_MTIME = 0;
+// The index of the document value that store the file path.
+static const Xapian::valueno DOC_FILEPATH = 1;
+
+static Xapian::WritableDatabase database;
+static string cached_dbpath = "";
+
+class xapian_lite_cannot_open_file: public exception {};
+
+// Return the hash of KEY.
+static uint64_t
+fingerprint (string key)
+{
+ // Polynomial rolling hash.
+ // http://web.cs.unlv.edu/larmore/Courses/CSC477/F14/Assignments/horners.pdf
+ //
https://begriffs.com/posts/2014-03-28-magic-numbers-in-polynomial-hash.html
+ const uint64_t prime = 31;
+ uint64_t hash = 0;
+ for (int idx = 0; idx < key.length(); idx++)
+ {
+ hash = key[idx] + hash * prime; // mod 2^64.
+ }
+ return hash;
+}
+
+// Return the hash string of PATH. The length limit of a term is ~245
+// bytes, but we don’t have to use that as the threshold.
+static string
+hash_path (string path)
+{
+ if (path.length() < 50)
+ {
+ return "QV" + path;
+ }
+ else
+ {
+ return "QH" + to_string (fingerprint (path));
+ }
+}
+
+// Reindex the file at PATH, using database at DBPATH. Throws
+// cannot_open_file. Both path must be absolute. Normally only reindex
+// if file has change since last index, if FORCE is true, always
+// reindex. Return true if re-indexed, return false if didn’t.
+// LANG is the language used by the stemmer.
+// Possible langauges:
+// https://xapian.org/docs/apidoc/html/classXapian_1_1Stem.html
+static bool
+reindex_file
+(string path, string dbpath, string lang = "en", bool force = false)
+{
+ // Check for mtime.
+ struct stat st;
+ time_t file_mtime;
+ off_t file_size;
+ if (stat (path.c_str(), &st) == 0)
+ {
+ file_mtime = st.st_mtime;
+ file_size = st.st_size;
+ }
+ else
+ {
+ throw xapian_lite_cannot_open_file();
+ }
+
+ // Even though the document says that database object only carries a
+ // pointer to the actual object, it is still not cheap enough. By
+ // using this cache, we get much better performance when reindexing
+ // hundreds of files, which most are no-op because they hasn’t been
+ // modified.
+ if (dbpath != cached_dbpath)
+ {
+ database = Xapian::WritableDatabase
+ (dbpath, Xapian::DB_CREATE_OR_OPEN);
+ cached_dbpath = dbpath;
+ }
+ // Track doc with file path as "id". See
+ //
https://getting-started-with-xapian.readthedocs.io/en/latest/practical_example/indexing/updating_the_database.html
+ // Also https://trac.xapian.org/wiki/FAQ/UniqueIds
+ string termID = hash_path (path);
+ Xapian::PostingIterator it_begin = database.postlist_begin (termID);
+ Xapian::PostingIterator it_end = database.postlist_end (termID);
+ bool has_doc = it_begin != it_end;
+ time_t db_mtime;
+ if (has_doc)
+ {
+ // sortable_serialise is for double and we can’t really use it.
+ Xapian::Document db_doc = database.get_document(*it_begin);
+ db_mtime = (time_t) stoi (db_doc.get_value (DOC_MTIME));
+ }
+
+ // Need re-index.
+ if (!has_doc || (has_doc && db_mtime < file_mtime) || force)
+ {
+ // Get the file content.
+ // REF:
https://stackoverflow.com/questions/2912520/read-file-contents-into-a-string-in-c
+ ifstream infile (path);
+ string content ((istreambuf_iterator<char>(infile)),
+ (istreambuf_iterator<char>()));
+ // Create the indexer.
+ Xapian::TermGenerator indexer;
+ Xapian::Stem stemmer (lang);
+ indexer.set_stemmer (stemmer);
+ indexer.set_stemming_strategy
+ (Xapian::TermGenerator::STEM_SOME);
+ // Support CJK.
+ indexer.set_flags (Xapian::TermGenerator::FLAG_CJK_NGRAM);
+ // Index file content.
+ Xapian::Document new_doc;
+ indexer.set_document (new_doc);
+ indexer.index_text (content);
+ // Set doc info.
+ new_doc.add_boolean_term (termID);
+ // We store the path in value, no need to use set_data.
+ new_doc.add_value (DOC_FILEPATH, path);
+ new_doc.add_value (DOC_MTIME, (string) to_string (file_mtime));
+ database.replace_document (termID, new_doc);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+// Query TERM in the databse at DBPATH. OFFSET and PAGE_SIZE is for
+// paging, see the docstring for the lisp function. If a file in the
+// result doesn’t exist anymore, it is removed from the database.
+// LANG is the language used by the stemmer.
+// Possible langauges:
+// https://xapian.org/docs/apidoc/html/classXapian_1_1Stem.html
+static vector<string>
+query_term
+(string term, string dbpath, int offset, int page_size,
+ string lang = "en")
+{
+ // See reindex_file for the reason for caching the database object.
+ if (dbpath != cached_dbpath)
+ {
+ database = Xapian::WritableDatabase
+ (dbpath, Xapian::DB_CREATE_OR_OPEN);
+ cached_dbpath = dbpath;
+ }
+
+ Xapian::QueryParser parser;
+ Xapian::Stem stemmer (lang);
+ parser.set_stemmer (stemmer);
+ parser.set_stemming_strategy (Xapian::QueryParser::STEM_SOME);
+ // Partial match (FLAG_PARTIAL) needs the database to expand
+ // wildcards.
+ parser.set_database(database);
+
+ Xapian::Query query;
+ try
+ {
+ query = parser.parse_query
+ // CJK_NGRAM is the flag for CJK support. PARTIAL makes
+ // interactive search more stable. DEFAULT enables AND OR and
+ // +/-.
+ (term, Xapian::QueryParser::FLAG_CJK_NGRAM
+ | Xapian::QueryParser::FLAG_PARTIAL
+ | Xapian::QueryParser::FLAG_DEFAULT);
+ }
+ // If the syntax is syntactically wrong, Xapian throws this error.
+ // Try again without enabling any special syntax.
+ catch (Xapian::QueryParserError &e)
+ {
+ query = parser.parse_query
+ (term, Xapian::QueryParser::FLAG_CJK_NGRAM
+ | Xapian::QueryParser::FLAG_PARTIAL);
+ }
+
+ Xapian::Enquire enquire (database);
+ enquire.set_query (query);
+
+ Xapian::MSet mset = enquire.get_mset (offset, page_size);
+ vector<string> result (0);
+ for (Xapian::MSetIterator it = mset.begin(); it != mset.end(); it++)
+ {
+ Xapian::Document doc = it.get_document();
+ string path = doc.get_value(DOC_FILEPATH);
+ // If the file doesn’t exists anymore, remove it.
+ struct stat st;
+ if (stat (path.c_str(), &st) == 0)
+ {
+ result.push_back (doc.get_value (DOC_FILEPATH));
+ }
+ else
+ {
+ database.delete_document (doc.get_docid());
+ }
+ }
+ return result;
+}
+
+/*** Module definition */
+
+static string
+copy_string (emacs_env *env, emacs_value value)
+{
+ char* char_buffer;
+ size_t size;
+ if (emp_copy_string_contents (env, value, &char_buffer, &size))
+ {
+ string str = (string) char_buffer;
+ free (char_buffer);
+ return str;
+ }
+ else
+ {
+ emp_signal_message1 (env, "xapian-lite-error",
+ "Error turning lisp string to C++ string");
+ return "";
+ }
+}
+
+static bool
+NILP (emacs_env *env, emacs_value val)
+{
+ return !env->is_not_nil (env, val);
+}
+
+static const char* xapian_lite_reindex_file_doc =
+ "Refindex file at PATH with database at DBPATH\n"
+ "Both paths has to be absolute. Normally, this function only\n"
+ "reindex a file if it has been modified since last indexed,\n"
+ "but if FORCE is non-nil, this function will always reindex.\n"
+ "Return non-nil if actually reindexed the file, return nil if not.\n"
+ "\n"
+ "LANG is the language used by the indexer, it tells Xapian how to\n"
+ "reduce words to word stems, e.g., apples <-> apple.\n"
+ "A full list of possible languages can be found at\n"
+ "https://xapian.org/docs/apidoc/html/classXapian_1_1Stem.html.\n"
+ "By default, LANG is \"en\".\n"
+ "\n"
+ "(fn PATH DBPATH &optional LANG FORCE)";
+
+static emacs_value
+Fxapian_lite_reindex_file
+(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data)
+ EMACS_NOEXCEPT
+{
+
+ // Decode arguments.
+ emacs_value lisp_path = args[0];
+ emacs_value lisp_dbpath = args[1];
+
+ if (NILP (env, emp_funcall (env, "file-name-absolute-p", 1, lisp_path)))
+ {
+ emp_signal_message1 (env, "xapian-lite-file-error",
+ "PATH is not a absolute path");
+ return NULL;
+ }
+ if (NILP (env,
+ emp_funcall (env, "file-name-absolute-p", 1, lisp_dbpath)))
+ {
+ emp_signal_message1 (env, "xapian-lite-file-error",
+ "DBPATH is not a absolute path");
+ return NULL;
+ }
+
+ // Expand "~" in the filename.
+ emacs_value lisp_args[] = {lisp_path};
+ lisp_path = emp_funcall (env, "expand-file-name", 1, lisp_path);
+ lisp_dbpath = emp_funcall (env, "expand-file-name", 1, lisp_dbpath);
+
+ emacs_value lisp_lang = nargs < 3 ? emp_intern (env, "nil") : args[2];
+ emacs_value lisp_force = nargs < 4 ? emp_intern (env, "nil") : args[3];
+
+ string path = copy_string (env, lisp_path);
+ string dbpath = copy_string (env, lisp_dbpath);
+ bool force = !NILP (env, lisp_force);
+ CHECK_EXIT (env);
+ string lang = NILP (env, lisp_lang) ?
+ "en" : copy_string (env, lisp_lang);
+ CHECK_EXIT (env);
+
+ // Do the work.
+ bool indexed;
+ try
+ {
+ indexed = reindex_file (path, dbpath, lang, force);
+ return indexed ? emp_intern (env, "t") : emp_intern (env, "nil");
+ }
+ catch (xapian_lite_cannot_open_file &e)
+ {
+ emp_signal_message1 (env, "xapian-lite-file-error",
+ "Cannot open the file");
+ return NULL;
+ }
+ catch (Xapian::DatabaseCorruptError &e)
+ {
+ emp_signal_message1 (env, "xapian-lite-database-corrupt-error",
+ e.get_description().c_str());
+ return NULL;
+ }
+ catch (Xapian::DatabaseLockError &e)
+ {
+ emp_signal_message1 (env, "xapian-lite-database-lock-error",
+ e.get_description().c_str());
+ return NULL;
+ }
+ catch (Xapian::Error &e)
+ {
+ emp_signal_message1 (env, "xapian-lite-lib-error",
+ e.get_description().c_str());
+ return NULL;
+ }
+ catch (exception &e)
+ {
+ emp_signal_message1 (env, "xapian-lite-error",
+ "Something went wrong");
+ return NULL;
+ }
+}
+
+static const char *xapian_lite_query_term_doc =
+ "Query for TERM in database at DBPATH.\n"
+ "Paging is supported by OFFSET and PAGE-SIZE. OFFSET specifies page\n"
+ "start, and PAGE-SIZE the size. For example, if a page is 10 entries,\n"
+ "OFFSET and PAGE-SIZE would be first 0 and 10, then 10 and 10, and\n"
+ "so on.\n"
+ "\n"
+ "If a file in the result doesn't exist anymore, it is removed from\n"
+ "the database, and is not included in the return value.\n"
+ "\n"
+ "LANG is the language used by the indexer, it tells Xapian how to\n"
+ "reduce words to word stems, e.g., apples <-> apple.\n"
+ "A full list of possible languages can be found at\n"
+ "https://xapian.org/docs/apidoc/html/classXapian_1_1Stem.html.\n"
+ "By default, LANG is \"en\".\n"
+ "\n"
+ "TERM can use common Xapian syntax like AND, OR, and +/-.\n"
+ "Specifically, this function supports:\n"
+ "\n"
+ " Boolean operators: AND, OR, XOR, NOT\n"
+ " Parenthesized expression: ()\n"
+ " Love/hate terms: +/-\n"
+ " Exact match: \"\"\n"
+ "\n"
+ "If TERM contains syntactic errors, like \"a AND AND b\",\n"
+ "it is treated as a plain term.\n"
+ "\n"
+ "(fn TERM DBPATH OFFSET PAGE-SIZE &optional LANG)";
+
+static emacs_value
+Fxapian_lite_query_term
+(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data)
+ EMACS_NOEXCEPT
+{
+ // Decode arguments.
+ emacs_value lisp_term = args[0];
+ emacs_value lisp_dbpath = args[1];
+ emacs_value lisp_offset = args[2];
+ emacs_value lisp_page_size = args[3];
+
+ if (NILP (env,
+ emp_funcall (env, "file-name-absolute-p", 1, lisp_dbpath)))
+ {
+ emp_signal_message1 (env, "xapian-lite-file-error",
+ "DBPATH is not a absolute path");
+ return NULL;
+ }
+
+ lisp_dbpath = emp_funcall (env, "expand-file-name", 1, lisp_dbpath);
+
+ string term = copy_string (env, lisp_term);
+ string dbpath = copy_string (env, lisp_dbpath);
+ int offset = env->extract_integer (env, lisp_offset);
+ int page_size = env->extract_integer (env, lisp_page_size);
+ CHECK_EXIT (env);
+
+ vector<string> result;
+ try
+ {
+ result = query_term (term, dbpath, offset, page_size);
+ }
+ catch (Xapian::Error &e)
+ {
+ emp_signal_message1 (env, "xapian-lite-lib-error",
+ e.get_description().c_str());
+ return NULL;
+ }
+ catch (exception &e)
+ {
+ emp_signal_message1 (env, "xapian-lite-error",
+ "Something went wrong");
+ return NULL;
+ }
+
+ vector<string>::iterator it;
+ emacs_value ret = emp_intern (env, "nil");
+ for (it = result.begin(); it != result.end(); it++) {
+ ret = emp_funcall (env, "cons", 2,
+ env->make_string
+ (env, it->c_str(), strlen(it->c_str())),
+ ret);
+ CHECK_EXIT (env);
+ }
+ return emp_funcall (env, "reverse", 1, ret);
+}
+
+int
+emacs_module_init (struct emacs_runtime *ert) EMACS_NOEXCEPT
+{
+ emacs_env *env = ert->get_environment (ert);
+
+ emp_define_error (env, "xapian-lite-error",
+ "Generic xapian-lite error", "error");
+ emp_define_error (env, "xapian-lite-lib-error",
+ "Xapian library error", "xapian-lite-error");
+ emp_define_error (env, "xapian-lite-database-corrupt-error",
+ "Xapian library error", "xapian-lite-lib-error");
+ emp_define_error (env, "xapian-lite-database-lock-error",
+ "Xapian library error", "xapian-lite-lib-error");
+ emp_define_error (env, "xapian-lite-file-error",
+ "Cannot open file", "xapian-lite-error");
+
+ emp_define_function(env, "xapian-lite-reindex-file", 2, 3,
+ &Fxapian_lite_reindex_file,
+ xapian_lite_reindex_file_doc);
+ emp_define_function(env, "xapian-lite-query-term", 4, 4,
+ &Fxapian_lite_query_term,
+ xapian_lite_query_term_doc);
+
+ emp_provide (env, "xapian-lite");
+
+ /* Return 0 to indicate module loaded successfully. */
+ return 0;
+}
- [elpa] externals/xeft 26389942e9 37/55: Minor change, (continued)
- [elpa] externals/xeft 26389942e9 37/55: Minor change, ELPA Syncer, 2023/01/13
- [elpa] externals/xeft 6efff4f20d 12/55: Add conexcept qualifier, ELPA Syncer, 2023/01/13
- [elpa] externals/xeft 5c8adb7c51 28/55: * Makefile: Fix PREFIX., ELPA Syncer, 2023/01/13
- [elpa] externals/xeft 78bf82b507 31/55: * xeft.el (xeft-refresh): Remove workaround for emacs-rime., ELPA Syncer, 2023/01/13
- [elpa] externals/xeft 6d27d1a7ab 32/55: Replace xeft-module with xapian-lite, ELPA Syncer, 2023/01/13
- [elpa] externals/xeft 4f16722a47 38/55: Handle database errors, ELPA Syncer, 2023/01/13
- [elpa] externals/xeft 321dd84b64 39/55: * Makefile: Add support for windows dylib extension, ELPA Syncer, 2023/01/13
- [elpa] externals/xeft 361ee04913 54/55: * xeft.el (xeft--download-module): Fix., ELPA Syncer, 2023/01/13
- [elpa] externals/xeft f193d6135e 42/55: Ignore dot directory and make file function customizable, ELPA Syncer, 2023/01/13
- [elpa] externals/xeft 4bdb052d81 55/55: * xeft.el: Bump to version 3.2., ELPA Syncer, 2023/01/13
- [elpa] externals/xeft babe67496a 52/55: Add xapian-lite source,
ELPA Syncer <=
- [elpa] externals/xeft 2fd4dd6ef8 41/55: Update xapian-lite.cc, ELPA Syncer, 2023/01/13