emacs-elpa-diffs
[Top][All Lists]
Advanced

[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;
+}



reply via email to

[Prev in Thread] Current Thread [Next in Thread]