commit a1386fa6a7698c04902354cd5fefb39056b0a901 (HEAD, refs/remotes/origin/master) Author: Michael R. Mauger Date: Wed Apr 24 20:59:25 2019 -0400 * lisp/progmodes/sql.el (sql-is-sqli-buffer-p): New function. (sql-generate-unique-sqli-buffer-name): Refactor and use it. (sql-product-interactive): Simplify name logic. * test/lisp/progmodes/sql-tests.el (sql-tests-placeholder-filter-harness): New macro. (sql-tests-placeholder-filter-simple) (sql-tests-placeholder-filter-ampersand) (sql-tests-placeholder-filter-period): Refactored tests and use macro. (sql-tests-buffer-naming-harness): New macro. (sql-tests-buffer-naming-default) (sql-tests-buffer-naming-multiple) (sql-tests-buffer-naming-explicit) (sql-tests-buffer-naming-universal-argument) (sql-tests-buffer-naming-existing): New tests. diff --git a/lisp/progmodes/sql.el b/lisp/progmodes/sql.el index 28261ef74b..2d33b3130c 100644 --- a/lisp/progmodes/sql.el +++ b/lisp/progmodes/sql.el @@ -1423,6 +1423,15 @@ specified, it's `sql-product' or `sql-connection' must match." (and (stringp connection) (string= connection sql-connection)))))))) +(defun sql-is-sqli-buffer-p (buffer) + "Return non-nil if buffer is a SQLi buffer." + (when buffer + (setq buffer (get-buffer buffer)) + (and buffer + (buffer-live-p buffer) + (with-current-buffer buffer + (derived-mode-p 'sql-interactive-mode))))) + ;; Keymap for sql-interactive-mode. (defvar sql-interactive-mode-map @@ -3550,24 +3559,29 @@ server/database name." "Generate a new, unique buffer name for a SQLi buffer. Append a sequence number until a unique name is found." - (let ((base-name (when (stringp base) - (substring-no-properties - (or base - (sql-get-product-feature product :name) + (let ((base-name (substring-no-properties + (if base + (if (stringp base) + base + (format "%S" base)) + (or (sql-get-product-feature product :name) (symbol-name product))))) - buf-fmt-1st buf-fmt-rest) + buf-fmt-1st + buf-fmt-rest) ;; Calculate buffer format - (if base-name - (setq buf-fmt-1st (format "*SQL: %s*" base-name) - buf-fmt-rest (format "*SQL: %s-%%d*" base-name)) - (setq buf-fmt-1st "*SQL*" - buf-fmt-rest "*SQL-%d*")) + (if (string-blank-p base-name) + (setq buf-fmt-1st "*SQL*" + buf-fmt-rest "*SQL-%d*") + (setq buf-fmt-1st (format "*SQL: %s*" base-name) + buf-fmt-rest (format "*SQL: %s-%%d*" base-name))) ;; See if we can find an unused buffer (let ((buf-name buf-fmt-1st) (i 1)) - (while (sql-buffer-live-p buf-name) + (while (if (sql-is-sqli-buffer-p buf-name) + (comint-check-proc buf-name) + (buffer-live-p (get-buffer buf-name))) ;; Check a sequence number on the BASE (setq buf-name (format buf-fmt-rest i) i (1+ i))) @@ -4670,13 +4684,13 @@ the call to \\[sql-product-interactive] with (read-string "Buffer name (\"*SQL: XXX*\"; enter `XXX'): " (sql-make-alternate-buffer-name product)))) - ((or (string-prefix-p " " new-name) - (string-match-p "\\`[*].*[*]\\'" new-name)) - new-name) ((stringp new-name) - (sql-generate-unique-sqli-buffer-name product new-name)) + (if (or (string-prefix-p " " new-name) + (string-match-p "\\`[*].*[*]\\'" new-name)) + new-name + (sql-generate-unique-sqli-buffer-name product new-name))) (t - (sql-generate-unique-sqli-buffer-name product nil))))) + (sql-generate-unique-sqli-buffer-name product new-name))))) ;; Set SQLi mode. (let ((sql-interactive-product product)) diff --git a/test/lisp/progmodes/sql-tests.el b/test/lisp/progmodes/sql-tests.el index 5ac34907c2..ad1f797652 100644 --- a/test/lisp/progmodes/sql-tests.el +++ b/test/lisp/progmodes/sql-tests.el @@ -271,37 +271,142 @@ Perform ACTION and validate results" (should-not (sql-get-product-feature 'd :Z)))) ;;; SQL Oracle SCAN/DEFINE -(ert-deftest sql-tests-placeholder-filter () - "Test that placeholder relacement is as expected." - (let ((syntab (syntax-table)) - (sql-oracle-scan-on t) - (placeholder-value "")) - (set-syntax-table sql-mode-syntax-table) - - (cl-letf - (((symbol-function 'read-from-minibuffer) - (lambda (&rest _) placeholder-value))) - - (setq placeholder-value "XX") - (should (equal - (sql-placeholders-filter "select '&x' from dual;") - "select 'XX' from dual;")) - - (setq placeholder-value "&Y") - (should (equal - (sql-placeholders-filter "select '&x' from dual;") - "select '&Y' from dual;")) - (should (equal - (sql-placeholders-filter "select '&x' from dual;") - "select '&Y' from dual;")) - (should (equal - (sql-placeholders-filter "select '&x.' from dual;") - "select '&Y' from dual;")) - (should (equal - (sql-placeholders-filter "select '&x.y' from dual;") - "select '&Yy' from dual;"))) - - (set-syntax-table syntab))) +(defmacro sql-tests-placeholder-filter-harness (orig repl outp) + "Set-up and tear-down of testing of placeholder filter. + +The placeholder in ORIG will be replaced by REPL which should +yield OUTP." + + (declare (indent 0)) + `(let ((syntab (syntax-table)) + (sql-oracle-scan-on t)) + (set-syntax-table sql-mode-syntax-table) + + (cl-letf + (((symbol-function 'read-from-minibuffer) + (lambda (&rest _) ,repl))) + + (should (equal (sql-placeholders-filter ,orig) ,outp))) + + (set-syntax-table syntab))) + +(ert-deftest sql-tests-placeholder-filter-simple () + "Test that placeholder relacement of simple replacement text." + (sql-tests-placeholder-filter-harness + "select '&x' from dual;" "XX" + "select 'XX' from dual;")) + +(ert-deftest sql-tests-placeholder-filter-ampersand () + "Test that placeholder relacement of replacement text with ampersand." + (sql-tests-placeholder-filter-harness + "select '&x' from dual;" "&Y" + "select '&Y' from dual;") + + (sql-tests-placeholder-filter-harness + "select '&x' from dual;" "Y&" + "select 'Y&' from dual;") + + (sql-tests-placeholder-filter-harness + "select '&x' from dual;" "Y&Y" + "select 'Y&Y' from dual;")) + +(ert-deftest sql-tests-placeholder-filter-period () + "Test that placeholder relacement of token terminated by a period." + (sql-tests-placeholder-filter-harness + "select '&x.' from dual;" "&Y" + "select '&Y' from dual;") + + (sql-tests-placeholder-filter-harness + "select '&x.y' from dual;" "&Y" + "select '&Yy' from dual;") + + (sql-tests-placeholder-filter-harness + "select '&x..y' from dual;" "&Y" + "select '&Y.y' from dual;")) + +;; Buffer naming +(defmacro sql-tests-buffer-naming-harness (product &rest action) + "Set-up and tear-down of test of buffer naming. + +The ACTION will be tested after set-up of PRODUCT." + + (declare (indent 1)) + `(let (new-bufs) + (cl-letf + (((symbol-function 'make-comint-in-buffer) + (lambda (_name buffer _program &optional _startfile &rest _switches) + (let ((b (get-buffer-create buffer))) + (message ">>make-comint-in-buffer %S" b) + (cl-pushnew b new-bufs) ;; Keep track of what we create + b)))) + + (let (,(intern (format "sql-%s-login-params" product))) + ,@action) + + (let (kill-buffer-query-functions) ;; Kill what we create + (mapc #'kill-buffer new-bufs))))) + +(ert-deftest sql-tests-buffer-naming-default () + "Test buffer naming." + (sql-tests-buffer-naming-harness sqlite + (sql-sqlite) + (message ">> %S" (current-buffer)) + (should (equal (buffer-name) "*SQL: SQLite*")))) + +(ert-deftest sql-tests-buffer-naming-multiple () + "Test buffer naming of multiple buffers." + (sql-tests-buffer-naming-harness sqlite + (sql-sqlite) + (should (equal (buffer-name) "*SQL: SQLite*")) + + (switch-to-buffer "*scratch*") + + (sql-sqlite) + (should (equal (buffer-name) "*SQL: SQLite*")))) + +(ert-deftest sql-tests-buffer-naming-explicit () + "Test buffer naming with explicit name." + (sql-tests-buffer-naming-harness sqlite + (sql-sqlite "A") + (should (equal (buffer-name) "*SQL: A*")) + + (switch-to-buffer "*scratch*") + + (sql-sqlite "A") + (should (equal (buffer-name) "*SQL: A*")))) + +(ert-deftest sql-tests-buffer-naming-universal-argument () + "Test buffer naming with explicit name." + (sql-tests-buffer-naming-harness sqlite + (cl-letf + (((symbol-function 'read-string) + (lambda (_prompt &optional _initial-input _history _default-value _inherit-input-method) + "1"))) + (sql-sqlite '(4)) + (should (equal (buffer-name) "*SQL: 1*"))) + + (switch-to-buffer "*scratch*") + + (cl-letf + (((symbol-function 'read-string) + (lambda (_prompt &optional _initial-input _history _default-value _inherit-input-method) + "2"))) + (sql-sqlite '(16)) + (should (equal (buffer-name) "*SQL: 2*"))))) + +(ert-deftest sql-tests-buffer-naming-existing () + "Test buffer naming with an existing non-SQLi buffer." + (sql-tests-buffer-naming-harness sqlite + (get-buffer-create "*SQL: exist*") + + (cl-letf + (((symbol-function 'read-string) + (lambda (_prompt &optional _initial-input _history _default-value _inherit-input-method) + "exist"))) + (sql-sqlite '(4)) + (should (equal (buffer-name) "*SQL: exist-1*"))) + + (kill-buffer "*SQL: exist*"))) (provide 'sql-tests) commit 2bf957394cdcb93396966d3289f5e200886cb424 Author: Paul Eggert Date: Wed Apr 24 17:41:05 2019 -0700 Improve port to platforms lacking euidaccess (Bug#35406) * lib-src/emacsclient.c (set_local_socket): Use faccessat with AT_EACCESS instead of using euidaccess. * admin/merge-gnulib, lib/gnulib.mk.in, m4/gnulib-comp.m4: Revert previous change. diff --git a/admin/merge-gnulib b/admin/merge-gnulib index cd37582cc0..4a69310d83 100755 --- a/admin/merge-gnulib +++ b/admin/merge-gnulib @@ -31,7 +31,7 @@ GNULIB_MODULES=' count-leading-zeros count-one-bits count-trailing-zeros crypto/md5-buffer crypto/sha1-buffer crypto/sha256-buffer crypto/sha512-buffer d-type diffseq dosname dtoastr dtotimespec dup2 - environ euidaccess execinfo explicit_bzero faccessat + environ execinfo explicit_bzero faccessat fcntl fcntl-h fdopendir filemode filevercmp flexmember fpieee fstatat fsusage fsync getloadavg getopt-gnu gettime gettimeofday gitlog-to-changelog diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c index 5871a18ce6..fd56007b15 100644 --- a/lib-src/emacsclient.c +++ b/lib-src/emacsclient.c @@ -1472,7 +1472,7 @@ set_local_socket (char const *server_name) int sockdirnamelen = snprintf (sockdirname, sizeof sockdirname, "/run/user/%"PRIuMAX, id); if (0 <= sockdirnamelen && sockdirnamelen < sizeof sockdirname - && euidaccess (sockdirname, X_OK) == 0) + && faccessat (AT_FDCWD, sockdirname, X_OK, AT_EACCESS) == 0) message (true, ("%s: Should XDG_RUNTIME_DIR='%s' be in the environment?\n" diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in index 165405f249..ade4ff8ebd 100644 --- a/lib/gnulib.mk.in +++ b/lib/gnulib.mk.in @@ -88,7 +88,6 @@ # dtotimespec \ # dup2 \ # environ \ -# euidaccess \ # execinfo \ # explicit_bzero \ # faccessat \ @@ -1492,7 +1491,9 @@ endif ## begin gnulib module euidaccess ifeq (,$(OMIT_GNULIB_MODULE_euidaccess)) +ifneq (,$(gl_GNULIB_ENABLED_euidaccess)) +endif EXTRA_DIST += euidaccess.c EXTRA_libgnu_a_SOURCES += euidaccess.c @@ -2148,7 +2149,9 @@ endif ## begin gnulib module root-uid ifeq (,$(OMIT_GNULIB_MODULE_root-uid)) +ifneq (,$(gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c)) +endif EXTRA_DIST += root-uid.h endif diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4 index d6466a032f..f648b7a495 100644 --- a/m4/gnulib-comp.m4 +++ b/m4/gnulib-comp.m4 @@ -220,12 +220,6 @@ AC_DEFUN([gl_INIT], gl_ENVIRON gl_UNISTD_MODULE_INDICATOR([environ]) gl_HEADER_ERRNO_H - gl_FUNC_EUIDACCESS - if test $HAVE_EUIDACCESS = 0; then - AC_LIBOBJ([euidaccess]) - gl_PREREQ_EUIDACCESS - fi - gl_UNISTD_MODULE_INDICATOR([euidaccess]) gl_EXECINFO_H gl_FUNC_EXPLICIT_BZERO if test $HAVE_EXPLICIT_BZERO = 0; then @@ -441,6 +435,7 @@ AC_DEFUN([gl_INIT], gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b=false gl_gnulib_enabled_cloexec=false gl_gnulib_enabled_dirfd=false + gl_gnulib_enabled_euidaccess=false gl_gnulib_enabled_getdtablesize=false gl_gnulib_enabled_getgroups=false gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=false @@ -450,6 +445,7 @@ AC_DEFUN([gl_INIT], gl_gnulib_enabled_5264294aa0a5557541b53c8c741f7f31=false gl_gnulib_enabled_open=false gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7=false + gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=false gl_gnulib_enabled_strtoll=false gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=false func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b () @@ -480,6 +476,22 @@ AC_DEFUN([gl_INIT], gl_gnulib_enabled_dirfd=true fi } + func_gl_gnulib_m4code_euidaccess () + { + if ! $gl_gnulib_enabled_euidaccess; then + gl_FUNC_EUIDACCESS + if test $HAVE_EUIDACCESS = 0; then + AC_LIBOBJ([euidaccess]) + gl_PREREQ_EUIDACCESS + fi + gl_UNISTD_MODULE_INDICATOR([euidaccess]) + gl_gnulib_enabled_euidaccess=true + if test $HAVE_EUIDACCESS = 0; then + func_gl_gnulib_m4code_a9786850e999ae65a836a6041e8e5ed1 + fi + func_gl_gnulib_m4code_6099e9737f757db36c47fa9d9f02e88c + fi + } func_gl_gnulib_m4code_getdtablesize () { if ! $gl_gnulib_enabled_getdtablesize; then @@ -576,6 +588,12 @@ AC_DEFUN([gl_INIT], gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7=true fi } + func_gl_gnulib_m4code_6099e9737f757db36c47fa9d9f02e88c () + { + if ! $gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c; then + gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=true + fi + } func_gl_gnulib_m4code_strtoll () { if ! $gl_gnulib_enabled_strtoll; then @@ -594,12 +612,12 @@ AC_DEFUN([gl_INIT], gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=true fi } - if test $HAVE_EUIDACCESS = 0; then - func_gl_gnulib_m4code_a9786850e999ae65a836a6041e8e5ed1 - fi if test $HAVE_FACCESSAT = 0 || test $REPLACE_FACCESSAT = 1; then func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b fi + if test $HAVE_FACCESSAT = 0 || test $REPLACE_FACCESSAT = 1; then + func_gl_gnulib_m4code_euidaccess + fi if test $HAVE_FACCESSAT = 0 || test $REPLACE_FACCESSAT = 1; then func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7 fi @@ -646,6 +664,7 @@ AC_DEFUN([gl_INIT], AM_CONDITIONAL([gl_GNULIB_ENABLED_260941c0e5dc67ec9e87d1fb321c300b], [$gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b]) AM_CONDITIONAL([gl_GNULIB_ENABLED_cloexec], [$gl_gnulib_enabled_cloexec]) AM_CONDITIONAL([gl_GNULIB_ENABLED_dirfd], [$gl_gnulib_enabled_dirfd]) + AM_CONDITIONAL([gl_GNULIB_ENABLED_euidaccess], [$gl_gnulib_enabled_euidaccess]) AM_CONDITIONAL([gl_GNULIB_ENABLED_getdtablesize], [$gl_gnulib_enabled_getdtablesize]) AM_CONDITIONAL([gl_GNULIB_ENABLED_getgroups], [$gl_gnulib_enabled_getgroups]) AM_CONDITIONAL([gl_GNULIB_ENABLED_be453cec5eecf5731a274f2de7f2db36], [$gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36]) @@ -655,6 +674,7 @@ AC_DEFUN([gl_INIT], AM_CONDITIONAL([gl_GNULIB_ENABLED_5264294aa0a5557541b53c8c741f7f31], [$gl_gnulib_enabled_5264294aa0a5557541b53c8c741f7f31]) AM_CONDITIONAL([gl_GNULIB_ENABLED_open], [$gl_gnulib_enabled_open]) AM_CONDITIONAL([gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7], [$gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7]) + AM_CONDITIONAL([gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c], [$gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c]) AM_CONDITIONAL([gl_GNULIB_ENABLED_strtoll], [$gl_gnulib_enabled_strtoll]) AM_CONDITIONAL([gl_GNULIB_ENABLED_682e609604ccaac6be382e4ee3a4eaec], [$gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec]) # End of code from modules commit 445713f55561df9a9755ce13eec0c0324b1754c2 Author: Paul Eggert Date: Wed Apr 24 17:34:09 2019 -0700 Port to platforms lacking euidaccess (Bug#35406) * admin/merge-gnulib (GNULIB_MODULES): Add euidaccess. * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. diff --git a/admin/merge-gnulib b/admin/merge-gnulib index 4a69310d83..cd37582cc0 100755 --- a/admin/merge-gnulib +++ b/admin/merge-gnulib @@ -31,7 +31,7 @@ GNULIB_MODULES=' count-leading-zeros count-one-bits count-trailing-zeros crypto/md5-buffer crypto/sha1-buffer crypto/sha256-buffer crypto/sha512-buffer d-type diffseq dosname dtoastr dtotimespec dup2 - environ execinfo explicit_bzero faccessat + environ euidaccess execinfo explicit_bzero faccessat fcntl fcntl-h fdopendir filemode filevercmp flexmember fpieee fstatat fsusage fsync getloadavg getopt-gnu gettime gettimeofday gitlog-to-changelog diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in index ade4ff8ebd..165405f249 100644 --- a/lib/gnulib.mk.in +++ b/lib/gnulib.mk.in @@ -88,6 +88,7 @@ # dtotimespec \ # dup2 \ # environ \ +# euidaccess \ # execinfo \ # explicit_bzero \ # faccessat \ @@ -1491,9 +1492,7 @@ endif ## begin gnulib module euidaccess ifeq (,$(OMIT_GNULIB_MODULE_euidaccess)) -ifneq (,$(gl_GNULIB_ENABLED_euidaccess)) -endif EXTRA_DIST += euidaccess.c EXTRA_libgnu_a_SOURCES += euidaccess.c @@ -2149,9 +2148,7 @@ endif ## begin gnulib module root-uid ifeq (,$(OMIT_GNULIB_MODULE_root-uid)) -ifneq (,$(gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c)) -endif EXTRA_DIST += root-uid.h endif diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4 index f648b7a495..d6466a032f 100644 --- a/m4/gnulib-comp.m4 +++ b/m4/gnulib-comp.m4 @@ -220,6 +220,12 @@ AC_DEFUN([gl_INIT], gl_ENVIRON gl_UNISTD_MODULE_INDICATOR([environ]) gl_HEADER_ERRNO_H + gl_FUNC_EUIDACCESS + if test $HAVE_EUIDACCESS = 0; then + AC_LIBOBJ([euidaccess]) + gl_PREREQ_EUIDACCESS + fi + gl_UNISTD_MODULE_INDICATOR([euidaccess]) gl_EXECINFO_H gl_FUNC_EXPLICIT_BZERO if test $HAVE_EXPLICIT_BZERO = 0; then @@ -435,7 +441,6 @@ AC_DEFUN([gl_INIT], gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b=false gl_gnulib_enabled_cloexec=false gl_gnulib_enabled_dirfd=false - gl_gnulib_enabled_euidaccess=false gl_gnulib_enabled_getdtablesize=false gl_gnulib_enabled_getgroups=false gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=false @@ -445,7 +450,6 @@ AC_DEFUN([gl_INIT], gl_gnulib_enabled_5264294aa0a5557541b53c8c741f7f31=false gl_gnulib_enabled_open=false gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7=false - gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=false gl_gnulib_enabled_strtoll=false gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=false func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b () @@ -476,22 +480,6 @@ AC_DEFUN([gl_INIT], gl_gnulib_enabled_dirfd=true fi } - func_gl_gnulib_m4code_euidaccess () - { - if ! $gl_gnulib_enabled_euidaccess; then - gl_FUNC_EUIDACCESS - if test $HAVE_EUIDACCESS = 0; then - AC_LIBOBJ([euidaccess]) - gl_PREREQ_EUIDACCESS - fi - gl_UNISTD_MODULE_INDICATOR([euidaccess]) - gl_gnulib_enabled_euidaccess=true - if test $HAVE_EUIDACCESS = 0; then - func_gl_gnulib_m4code_a9786850e999ae65a836a6041e8e5ed1 - fi - func_gl_gnulib_m4code_6099e9737f757db36c47fa9d9f02e88c - fi - } func_gl_gnulib_m4code_getdtablesize () { if ! $gl_gnulib_enabled_getdtablesize; then @@ -588,12 +576,6 @@ AC_DEFUN([gl_INIT], gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7=true fi } - func_gl_gnulib_m4code_6099e9737f757db36c47fa9d9f02e88c () - { - if ! $gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c; then - gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=true - fi - } func_gl_gnulib_m4code_strtoll () { if ! $gl_gnulib_enabled_strtoll; then @@ -612,11 +594,11 @@ AC_DEFUN([gl_INIT], gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=true fi } - if test $HAVE_FACCESSAT = 0 || test $REPLACE_FACCESSAT = 1; then - func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b + if test $HAVE_EUIDACCESS = 0; then + func_gl_gnulib_m4code_a9786850e999ae65a836a6041e8e5ed1 fi if test $HAVE_FACCESSAT = 0 || test $REPLACE_FACCESSAT = 1; then - func_gl_gnulib_m4code_euidaccess + func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b fi if test $HAVE_FACCESSAT = 0 || test $REPLACE_FACCESSAT = 1; then func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7 @@ -664,7 +646,6 @@ AC_DEFUN([gl_INIT], AM_CONDITIONAL([gl_GNULIB_ENABLED_260941c0e5dc67ec9e87d1fb321c300b], [$gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b]) AM_CONDITIONAL([gl_GNULIB_ENABLED_cloexec], [$gl_gnulib_enabled_cloexec]) AM_CONDITIONAL([gl_GNULIB_ENABLED_dirfd], [$gl_gnulib_enabled_dirfd]) - AM_CONDITIONAL([gl_GNULIB_ENABLED_euidaccess], [$gl_gnulib_enabled_euidaccess]) AM_CONDITIONAL([gl_GNULIB_ENABLED_getdtablesize], [$gl_gnulib_enabled_getdtablesize]) AM_CONDITIONAL([gl_GNULIB_ENABLED_getgroups], [$gl_gnulib_enabled_getgroups]) AM_CONDITIONAL([gl_GNULIB_ENABLED_be453cec5eecf5731a274f2de7f2db36], [$gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36]) @@ -674,7 +655,6 @@ AC_DEFUN([gl_INIT], AM_CONDITIONAL([gl_GNULIB_ENABLED_5264294aa0a5557541b53c8c741f7f31], [$gl_gnulib_enabled_5264294aa0a5557541b53c8c741f7f31]) AM_CONDITIONAL([gl_GNULIB_ENABLED_open], [$gl_gnulib_enabled_open]) AM_CONDITIONAL([gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7], [$gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7]) - AM_CONDITIONAL([gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c], [$gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c]) AM_CONDITIONAL([gl_GNULIB_ENABLED_strtoll], [$gl_gnulib_enabled_strtoll]) AM_CONDITIONAL([gl_GNULIB_ENABLED_682e609604ccaac6be382e4ee3a4eaec], [$gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec]) # End of code from modules commit 41cc2b64904d2f59bdb32f76d898a2a1afb72ec7 Author: Paul Eggert Date: Wed Apr 24 17:29:06 2019 -0700 Merge from gnulib diff --git a/build-aux/config.sub b/build-aux/config.sub index a44fd8ae90..f53af5a2da 100755 --- a/build-aux/config.sub +++ b/build-aux/config.sub @@ -1247,7 +1247,8 @@ case $cpu-$vendor in | v70 | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \ | vax \ | visium \ - | w65 | wasm32 \ + | w65 \ + | wasm32 | wasm64 \ | we32k \ | x86 | x86_64 | xc16x | xgate | xps100 \ | xstormy16 | xtensa* \ @@ -1367,7 +1368,7 @@ case $os in | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \ | skyos* | haiku* | rdos* | toppers* | drops* | es* \ | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ - | midnightbsd* | amdhsa* | unleashed* | emscripten*) + | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi*) # Remember, each alternative MUST END IN *, to match a version number. ;; qnx*) diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in index 03160340c8..ade4ff8ebd 100644 --- a/lib/gnulib.mk.in +++ b/lib/gnulib.mk.in @@ -591,6 +591,7 @@ HAVE_OPENAT = @HAVE_OPENAT@ HAVE_OPENDIR = @HAVE_OPENDIR@ HAVE_OS_H = @HAVE_OS_H@ HAVE_PCLOSE = @HAVE_PCLOSE@ +HAVE_PDUMPER = @HAVE_PDUMPER@ HAVE_PIPE = @HAVE_PIPE@ HAVE_PIPE2 = @HAVE_PIPE2@ HAVE_POPEN = @HAVE_POPEN@ diff --git a/lib/str-two-way.h b/lib/str-two-way.h index 9155e6b560..7078c34bdc 100644 --- a/lib/str-two-way.h +++ b/lib/str-two-way.h @@ -18,7 +18,7 @@ /* Before including this file, you need to include and , and define: - RESULT_TYPE A macro that expands to the return type. + RETURN_TYPE A macro that expands to the return type. AVAILABLE(h, h_l, j, n_l) A macro that returns nonzero if there are at least N_L bytes left starting at H[J]. commit 4494789d2ca4b24e25fae1b3e97fb9743e9830a4 Author: Mark Oteiza Date: Wed Apr 24 19:44:48 2019 -0400 Fix some strings in wordstar mode * lisp/obsolete/ws-mode.el (ws-search-direction, ws-error, ws-end-block): (ws-mark-word, ws-undo, ws-goto-last-cursorposition, ws-last-error): (ws-kill-bol): Remove full stops from message string endings. Minor formatting tweaks. diff --git a/lisp/obsolete/ws-mode.el b/lisp/obsolete/ws-mode.el index 17c35176b4..3d2e968b1d 100644 --- a/lisp/obsolete/ws-mode.el +++ b/lisp/obsolete/ws-mode.el @@ -271,7 +271,7 @@ the distance between the end of the text and `fill-column'." (defvar ws-search-string nil "String of last search in WordStar mode.") (defvar ws-search-direction t - "Direction of last search in WordStar mode. t if forward, nil if backward.") + "Direction of last search in WordStar mode. t if forward, nil if backward.") (defvar ws-last-cursorposition nil "Position before last search etc. in WordStar mode.") @@ -283,8 +283,9 @@ the distance between the end of the text and `fill-column'." ;; wordstar special functions: (defun ws-error (string) - "Report error of a WordStar special function. Error message is saved -in ws-last-errormessage for recovery with C-q w." + "Report error of a WordStar special function. +Error message is saved in `ws-last-errormessage' for recovery +with C-q w." (setq ws-last-errormessage string) (error string)) @@ -375,7 +376,6 @@ in ws-last-errormessage for recovery with C-q w." (message "")) (message "Block markers not set"))) - (defun ws-indent-block () "In WordStar mode: Indent block (not yet implemented)." (interactive) @@ -390,7 +390,7 @@ in ws-last-errormessage for recovery with C-q w." (defun ws-print-block () "In WordStar mode: Print block." (interactive) - (message "Don't do this. Write block to a file (C-k w) and print this file.")) + (message "Don't do this. Write block to a file (C-k w) and print this file")) (defun ws-mark-word () "In WordStar mode: Mark current word as block." @@ -406,7 +406,7 @@ in ws-last-errormessage for recovery with C-q w." (defun ws-exdent-block () "I don't know what this (C-k u) should do." (interactive) - (ws-error "This won't be done -- not yet implemented.")) + (ws-error "This won't be done -- not yet implemented")) (defun ws-move-block () "In WordStar mode: Move block to current cursor position." @@ -577,16 +577,16 @@ in ws-last-errormessage for recovery with C-q w." "In WordStar mode: Undo and give message about undoing more changes." (interactive) (undo) - (message "Repeat C-q l to undo more changes.")) + (message "Repeat C-q l to undo more changes")) (defun ws-goto-last-cursorposition () - "In WordStar mode: " + "In WordStar mode: Go to position before last search." (interactive) (if ws-last-cursorposition (progn (setq ws-last-cursorposition (point-marker)) (goto-char ws-last-cursorposition)) - (ws-error "No last cursor position available."))) + (ws-error "No last cursor position available"))) (defun ws-last-error () "In WordStar mode: repeat last error message. @@ -594,7 +594,7 @@ This will only work for errors raised by WordStar mode functions." (interactive) (if ws-last-errormessage (message "%s" ws-last-errormessage) - (message "No WordStar error yet."))) + (message "No WordStar error yet"))) (defun ws-kill-eol () "In WordStar mode: Kill to end of line (like WordStar, not like Emacs)." @@ -604,8 +604,7 @@ This will only work for errors raised by WordStar mode functions." (kill-region p (point)))) (defun ws-kill-bol () - "In WordStar mode: Kill to beginning of line -\(like WordStar, not like Emacs)." + "In WordStar mode: Kill to beginning of line (like WordStar, not like Emacs)." (interactive) (let ((p (point))) (beginning-of-line) commit cb17e9c42e92efa526d79b717d7d4bc872153b8f Author: Mark Oteiza Date: Wed Apr 24 19:38:20 2019 -0400 Change WordStar emulation into a minor mode (Bug#35148) * lisp/obsolete/ws-mode.el: Turn on lexical-binding. Update commentary. (wordstar): New custom group. (wordstar-mode-lighter): New custom variable. (wordstar-mode): Declare with define-minor-mode. (turn-on-wordstar-mode): New function. (global-wordstar-mode): New function. Use previous new function. diff --git a/lisp/obsolete/ws-mode.el b/lisp/obsolete/ws-mode.el index 05dca959da..17c35176b4 100644 --- a/lisp/obsolete/ws-mode.el +++ b/lisp/obsolete/ws-mode.el @@ -1,4 +1,4 @@ -;;; ws-mode.el --- WordStar emulation mode for GNU Emacs +;;; ws-mode.el --- WordStar emulation mode for GNU Emacs -*- lexical-binding: t -*- ;; Copyright (C) 1991, 2001-2019 Free Software Foundation, Inc. @@ -24,9 +24,20 @@ ;;; Commentary: -;; This emulates WordStar, with a major mode. +;; This provides emulation of WordStar with a minor mode. ;;; Code: + +(defgroup wordstar nil + "WordStar emulation within Emacs." + :prefix "wordstar-" + :prefix "ws-" + :group 'emulations) + +(defcustom wordstar-mode-lighter " WordStar" + "Lighter shown in the modeline for `wordstar' mode." + :type 'string) + (defvar wordstar-C-k-map (let ((map (make-keymap))) (define-key map " " ()) @@ -98,8 +109,7 @@ (define-key map "wh" 'split-window-right) (define-key map "wo" 'other-window) (define-key map "wv" 'split-window-below) - map) - "") + map)) (defvar wordstar-C-q-map (let ((map (make-keymap))) @@ -174,12 +184,9 @@ ;; wordstar-C-j-map not yet implemented (defvar wordstar-C-j-map nil) - -(put 'wordstar-mode 'mode-class 'special) - ;;;###autoload -(define-derived-mode wordstar-mode fundamental-mode "WordStar" - "Major mode with WordStar-like key bindings. +(define-minor-mode wordstar-mode + "Minor mode with WordStar-like key bindings. BUGS: - Help menus with WordStar commands (C-j just calls help-for-help) @@ -189,8 +196,18 @@ BUGS: - Search and replace (C-q a) is only available in forward direction No key bindings beginning with ESC are installed, they will work -Emacs-like.") - +Emacs-like." + :group 'wordstar + :lighter wordstar-mode-lighter + :keymap wordstar-mode-map) + +(defun turn-on-wordstar-mode () + (when (and (not (minibufferp)) + (not wordstar-mode)) + (wordstar-mode 1))) + +(define-globalized-minor-mode global-wordstar-mode wordstar-mode + turn-on-wordstar-mode) (defun wordstar-center-paragraph () "Center each line in the paragraph at or after point. commit f0e026a8495e49379b94e04826b6b8d0dd4b1990 Author: Dmitry Gutov Date: Thu Apr 25 02:24:57 2019 +0300 Fix project-find-regexp search for '--' * lisp/progmodes/project.el (project--find-regexp-in-files): Add an explicit '-e' before the pattern. Fixing the ability to search for '--'. Reported by Juri Linkov . diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index b8a58ed317..11a2ef4009 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el @@ -391,7 +391,7 @@ pattern to search for." (status nil) (hits nil) (xrefs nil) - (command (format "xargs -0 grep %s -nHE %s" + (command (format "xargs -0 grep %s -nHE -e %s" (if (and case-fold-search (isearch-no-upper-case-p regexp t)) "-i" commit 62072bad4146598e9a88b158ef343b1d1a04a7d2 Author: Juri Linkov Date: Thu Apr 25 00:40:27 2019 +0300 * lisp/windmove.el (windmove-display-in-direction): Support consecutive calls Remember action and delete it from display-buffer-overriding-action afterwards diff --git a/lisp/windmove.el b/lisp/windmove.el index 0853f7ec7f..ab47565dfa 100644 --- a/lisp/windmove.el +++ b/lisp/windmove.el @@ -596,12 +596,25 @@ When `switch-to-buffer-obey-display-actions' is non-nil, (old-window (or (minibuffer-selected-window) (selected-window))) (new-window) (minibuffer-depth (minibuffer-depth)) - (action display-buffer-overriding-action) + (action (lambda (buffer alist) + (unless (> (minibuffer-depth) minibuffer-depth) + (let ((window (if (eq dir 'same-window) + (selected-window) + (window-in-direction + dir nil nil + (and arg (prefix-numeric-value arg)) + windmove-wrap-around))) + (type 'reuse)) + (unless window + (setq window (split-window nil nil dir) type 'window)) + (setq new-window (window--display-buffer buffer window + type alist)))))) (command this-command) (clearfun (make-symbol "clear-display-buffer-overriding-action")) (exitfun (lambda () - (setq display-buffer-overriding-action action) + (setq display-buffer-overriding-action + (delq action display-buffer-overriding-action)) (when (window-live-p (if no-select old-window new-window)) (select-window (if no-select old-window new-window))) (remove-hook 'post-command-hook clearfun)))) @@ -616,19 +629,7 @@ When `switch-to-buffer-obey-display-actions' is non-nil, (eq this-command command)) (funcall exitfun)))) (add-hook 'post-command-hook clearfun) - (push (lambda (buffer alist) - (unless (> (minibuffer-depth) minibuffer-depth) - (let ((window (if (eq dir 'same-window) - (selected-window) - (window-in-direction - dir nil nil - (and arg (prefix-numeric-value arg)) - windmove-wrap-around))) - (type 'reuse)) - (unless window - (setq window (split-window nil nil dir) type 'window)) - (setq new-window (window--display-buffer buffer window type alist))))) - display-buffer-overriding-action) + (push action display-buffer-overriding-action) (message "[display-%s]" dir))) ;;;###autoload commit 4eb7f9ef595c10df1ea78518b2f0410a0e79ec70 Author: Philipp Stephani Date: Wed Apr 24 19:35:36 2019 +0200 Unbreak build when building without GMP support. Add support for a new preprocessor macro EMACS_MODULE_HAVE_MPZ_T to emacs-module.h. If this macro is defined, assume that mpz_t is already defined and don’t include gmp.h. Don’t document the new macro for now, as it’s unclear whether we want to support this in modules outside the Emacs tree. * src/emacs-module.h.in: Allow user to prevent inclusion of gmp.h. * src/emacs-module.c: Use mini-gmp if GMP is unavailable. Don’t include gmp.h. * src/lisp.h: Don’t require gmp.h. It’s not needed for lisp.h. * test/Makefile.in (GMP_LIB, GMP_OBJ): New variables. ($(test_module)): Use them. * test/data/emacs-module/mod-test.c: Use mini-gmp if GMP is unavailable. diff --git a/src/emacs-module.c b/src/emacs-module.c index b6a1238626..0b7b3d6ffb 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -70,6 +70,11 @@ To add a new module function, proceed as follows: #include +#ifndef HAVE_GMP +#include "mini-gmp.h" +#define EMACS_MODULE_HAVE_MPZ_T +#endif + #define EMACS_MODULE_GMP #include "emacs-module.h" @@ -80,8 +85,6 @@ To add a new module function, proceed as follows: #include #include -#include - #include "lisp.h" #include "bignum.h" #include "dynlib.h" diff --git a/src/emacs-module.h.in b/src/emacs-module.h.in index e61aadfc3a..fbc62a61ef 100644 --- a/src/emacs-module.h.in +++ b/src/emacs-module.h.in @@ -28,7 +28,7 @@ along with GNU Emacs. If not, see . */ #include #endif -#ifdef EMACS_MODULE_GMP +#if defined EMACS_MODULE_GMP && !defined EMACS_MODULE_HAVE_MPZ_T #include #endif diff --git a/test/Makefile.in b/test/Makefile.in index 2282ccd951..ec20a427ba 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -257,6 +257,7 @@ endif HYBRID_MALLOC = @HYBRID_MALLOC@ LIBEGNU_ARCHIVE = ../lib/lib$(if $(HYBRID_MALLOC),e)gnu.a GMP_LIB = @GMP_LIB@ +GMP_OBJ = $(if @GMP_OBJ@, ../src/@GMP_OBJ@) # Note: emacs-module.h is generated from emacs-module.h.in, hence we # look in ../src, not $(srcdir)/../src. @@ -269,7 +270,7 @@ src/emacs-module-tests.log: $(test_module) $(test_module): $(test_module:${SO}=.c) ../src/emacs-module.h $(LIBEGNU_ARCHIVE) $(AM_V_at)${MKDIR_P} $(dir $@) $(AM_V_CCLD)$(CC) -shared $(CPPFLAGS) $(MODULE_CFLAGS) $(LDFLAGS) \ - -o $@ $< $(LIBEGNU_ARCHIVE) $(GMP_LIB) + -o $@ $< $(LIBEGNU_ARCHIVE) $(GMP_LIB) $(GMP_OBJ) endif ## Check that there is no 'automated' subdirectory, which would diff --git a/test/data/emacs-module/mod-test.c b/test/data/emacs-module/mod-test.c index 8ac08f7153..b7007bd80f 100644 --- a/test/data/emacs-module/mod-test.c +++ b/test/data/emacs-module/mod-test.c @@ -27,11 +27,16 @@ along with GNU Emacs. If not, see . */ #include #include +#ifdef HAVE_GMP +#include +#else +#include "mini-gmp.h" +#define EMACS_MODULE_HAVE_MPZ_T +#endif + #define EMACS_MODULE_GMP #include -#include - #include "timespec.h" int plugin_is_GPL_compatible; commit 553220fca670ec13180e0763bc9338fbf8ed4b30 Author: Philipp Stephani Date: Wed Apr 24 23:14:13 2019 +0200 Add missing GMP library to test module. * test/Makefile.in (GMP_LIB): Define variable. ($(test_module)): Use it. diff --git a/test/Makefile.in b/test/Makefile.in index ce6ce04b8b..2282ccd951 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -256,6 +256,7 @@ endif HYBRID_MALLOC = @HYBRID_MALLOC@ LIBEGNU_ARCHIVE = ../lib/lib$(if $(HYBRID_MALLOC),e)gnu.a +GMP_LIB = @GMP_LIB@ # Note: emacs-module.h is generated from emacs-module.h.in, hence we # look in ../src, not $(srcdir)/../src. @@ -268,7 +269,7 @@ src/emacs-module-tests.log: $(test_module) $(test_module): $(test_module:${SO}=.c) ../src/emacs-module.h $(LIBEGNU_ARCHIVE) $(AM_V_at)${MKDIR_P} $(dir $@) $(AM_V_CCLD)$(CC) -shared $(CPPFLAGS) $(MODULE_CFLAGS) $(LDFLAGS) \ - -o $@ $< $(LIBEGNU_ARCHIVE) + -o $@ $< $(LIBEGNU_ARCHIVE) $(GMP_LIB) endif ## Check that there is no 'automated' subdirectory, which would commit d2e1bac47816fa1f48482faeebf7fa562a5b0e40 Author: Philipp Stephani Date: Wed Apr 24 23:12:35 2019 +0200 Move definition of Lisp_Module_Function to emacs-module.c. * src/lisp.h: Remove include of emacs-module.h. Remove definition of Lisp_Module_Function structure. * src/emacs-module.c (module_function_documentation) (module_function_address): New accessor functions for module function fields. (emacs_subr, struct Lisp_Module_Function): Move from lisp.h. * src/print.c (print_vectorlike): * src/doc.c (Fdocumentation): Use the new accessor functions. diff --git a/src/doc.c b/src/doc.c index 372e376c62..3fa0eaac20 100644 --- a/src/doc.c +++ b/src/doc.c @@ -337,8 +337,10 @@ string is passed through `substitute-command-keys'. */) fun = XCDR (fun); if (SUBRP (fun)) doc = make_fixnum (XSUBR (fun)->doc); +#ifdef HAVE_MODULES else if (MODULE_FUNCTIONP (fun)) - doc = XMODULE_FUNCTION (fun)->documentation; + doc = module_function_documentation (XMODULE_FUNCTION (fun)); +#endif else if (COMPILEDP (fun)) { if (PVSIZE (fun) <= COMPILED_DOC_STRING) diff --git a/src/emacs-module.c b/src/emacs-module.c index 41ce9ef03e..b6a1238626 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -471,6 +471,30 @@ module_non_local_exit_throw (emacs_env *env, emacs_value tag, emacs_value value) value_to_lisp (value)); } +/* Function prototype for the module Lisp functions. */ +typedef emacs_value (*emacs_subr) (emacs_env *, ptrdiff_t, + emacs_value [], void *); + +/* Module function. */ + +/* A function environment is an auxiliary structure returned by + `module_make_function' to store information about a module + function. It is stored in a pseudovector. Its members correspond + to the arguments given to `module_make_function'. */ + +struct Lisp_Module_Function +{ + union vectorlike_header header; + + /* Fields traced by GC; these must come first. */ + Lisp_Object documentation; + + /* Fields ignored by GC. */ + ptrdiff_t min_arity, max_arity; + emacs_subr subr; + void *data; +} GCALIGNED_STRUCT; + static struct Lisp_Module_Function * allocate_module_function (void) { @@ -901,6 +925,18 @@ module_function_arity (const struct Lisp_Module_Function *const function) maxargs == MANY ? Qmany : make_fixnum (maxargs)); } +Lisp_Object +module_function_documentation (const struct Lisp_Module_Function *function) +{ + return function->documentation; +} + +void * +module_function_address (const struct Lisp_Module_Function *function) +{ + return function->subr; +} + /* Helper functions. */ diff --git a/src/lisp.h b/src/lisp.h index 70b2aa270e..8dc44291a8 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4151,32 +4151,8 @@ extern void *unexec_realloc (void *, size_t); extern void unexec_free (void *); #endif -#define EMACS_MODULE_GMP -#include "emacs-module.h" - -/* Function prototype for the module Lisp functions. */ -typedef emacs_value (*emacs_subr) (emacs_env *, ptrdiff_t, - emacs_value [], void *); - -/* Module function. */ - -/* A function environment is an auxiliary structure returned by - `module_make_function' to store information about a module - function. It is stored in a pseudovector. Its members correspond - to the arguments given to `module_make_function'. */ - -struct Lisp_Module_Function -{ - union vectorlike_header header; - - /* Fields traced by GC; these must come first. */ - Lisp_Object documentation; - - /* Fields ignored by GC. */ - ptrdiff_t min_arity, max_arity; - emacs_subr subr; - void *data; -} GCALIGNED_STRUCT; +/* The definition of Lisp_Module_Function depends on emacs-module.h, + so we don't define it here. It's defined in emacs-module.c. */ INLINE bool MODULE_FUNCTIONP (Lisp_Object o) @@ -4198,6 +4174,8 @@ extern Lisp_Object make_user_ptr (void (*finalizer) (void *), void *p); /* Defined in emacs-module.c. */ extern Lisp_Object funcall_module (Lisp_Object, ptrdiff_t, Lisp_Object *); extern Lisp_Object module_function_arity (const struct Lisp_Module_Function *); +extern Lisp_Object module_function_documentation (const struct Lisp_Module_Function *); +extern void *module_function_address (const struct Lisp_Module_Function *); extern void mark_modules (void); extern void init_module_assertions (bool); extern void syms_of_module (void); diff --git a/src/print.c b/src/print.c index 081e5574b7..8b163e3ee3 100644 --- a/src/print.c +++ b/src/print.c @@ -1787,8 +1787,8 @@ print_vectorlike (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag, case PVEC_MODULE_FUNCTION: { print_c_string ("#subr; - const char *file = NULL; + void *ptr = module_function_address (XMODULE_FUNCTION (obj)); + const char *file = NULL; const char *symbol = NULL; dynlib_addr (ptr, &file, &symbol); commit 4c90369d77d3db1cbd37df7857e4706176fd7ba2 Author: Paul Eggert Date: Wed Apr 24 13:35:14 2019 -0700 Simplify thread initialization and GC * src/lisp.h (PVECHEADERSIZE): New macro. (XSETPVECTYPESIZE): Use it. * src/search.c (syms_of_search): No need to initialize or staticpro last_thing_searched or saved_last_thing_searched, as the thread code arranges for initialization and GC. * src/thread.c (main_thread): Initialize statically. (Fmake_mutex, Fmake_condition_variable, Fmake_thread): Use ALLOCATE_ZEROED_PSEUDOVECTOR rather than zeroing by hand. (mark_one_thread): No need to mark Lisp_Object members. (init_main_thread, init_threads_once): Remove. All uses removed. diff --git a/src/emacs.c b/src/emacs.c index 8655f715e4..86d2bc65ac 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -1436,7 +1436,6 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem if (!initialized) { init_alloc_once (); - init_threads_once (); init_obarray_once (); init_eval_once (); init_charset_once (); diff --git a/src/lisp.h b/src/lisp.h index 703fe76d64..70b2aa270e 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -1279,11 +1279,11 @@ INLINE bool #define XSETPVECTYPE(v, code) \ ((v)->header.size |= PSEUDOVECTOR_FLAG | ((code) << PSEUDOVECTOR_AREA_BITS)) +#define PVECHEADERSIZE(code, lispsize, restsize) \ + (PSEUDOVECTOR_FLAG | ((code) << PSEUDOVECTOR_AREA_BITS) \ + | ((restsize) << PSEUDOVECTOR_SIZE_BITS) | (lispsize)) #define XSETPVECTYPESIZE(v, code, lispsize, restsize) \ - ((v)->header.size = (PSEUDOVECTOR_FLAG \ - | ((code) << PSEUDOVECTOR_AREA_BITS) \ - | ((restsize) << PSEUDOVECTOR_SIZE_BITS) \ - | (lispsize))) + ((v)->header.size = PVECHEADERSIZE (code, lispsize, restsize)) /* The cast to union vectorlike_header * avoids aliasing issues. */ #define XSETPSEUDOVECTOR(a, b, code) \ diff --git a/src/search.c b/src/search.c index 7a6e680685..dfbae5c962 100644 --- a/src/search.c +++ b/src/search.c @@ -3387,12 +3387,6 @@ syms_of_search (void) Fput (Qinvalid_regexp, Qerror_message, build_pure_c_string ("Invalid regexp")); - last_thing_searched = Qnil; - staticpro (&last_thing_searched); - - saved_last_thing_searched = Qnil; - staticpro (&saved_last_thing_searched); - re_match_object = Qnil; staticpro (&re_match_object); diff --git a/src/thread.c b/src/thread.c index 670680f2b0..e2deadd7a8 100644 --- a/src/thread.c +++ b/src/thread.c @@ -35,7 +35,21 @@ union aligned_thread_state }; verify (GCALIGNED (union aligned_thread_state)); -static union aligned_thread_state main_thread; +static union aligned_thread_state main_thread + = {{ + .header.size = PVECHEADERSIZE (PVEC_THREAD, + PSEUDOVECSIZE (struct thread_state, + event_object), + VECSIZE (struct thread_state)), + .m_last_thing_searched = LISPSYM_INITIALLY (Qnil), + .m_saved_last_thing_searched = LISPSYM_INITIALLY (Qnil), + .name = LISPSYM_INITIALLY (Qnil), + .function = LISPSYM_INITIALLY (Qnil), + .result = LISPSYM_INITIALLY (Qnil), + .error_symbol = LISPSYM_INITIALLY (Qnil), + .error_data = LISPSYM_INITIALLY (Qnil), + .event_object = LISPSYM_INITIALLY (Qnil), + }}; struct thread_state *current_thread = &main_thread.s; @@ -261,19 +275,15 @@ NAME, if given, is used as the name of the mutex. The name is informational only. */) (Lisp_Object name) { - struct Lisp_Mutex *mutex; - Lisp_Object result; - if (!NILP (name)) CHECK_STRING (name); - mutex = ALLOCATE_PSEUDOVECTOR (struct Lisp_Mutex, name, PVEC_MUTEX); - memset ((char *) mutex + offsetof (struct Lisp_Mutex, mutex), - 0, sizeof (struct Lisp_Mutex) - offsetof (struct Lisp_Mutex, - mutex)); + struct Lisp_Mutex *mutex + = ALLOCATE_ZEROED_PSEUDOVECTOR (struct Lisp_Mutex, name, PVEC_MUTEX); mutex->name = name; lisp_mutex_init (&mutex->mutex); + Lisp_Object result; XSETMUTEX (result, mutex); return result; } @@ -379,21 +389,17 @@ NAME, if given, is the name of this condition variable. The name is informational only. */) (Lisp_Object mutex, Lisp_Object name) { - struct Lisp_CondVar *condvar; - Lisp_Object result; - CHECK_MUTEX (mutex); if (!NILP (name)) CHECK_STRING (name); - condvar = ALLOCATE_PSEUDOVECTOR (struct Lisp_CondVar, name, PVEC_CONDVAR); - memset ((char *) condvar + offsetof (struct Lisp_CondVar, cond), - 0, sizeof (struct Lisp_CondVar) - offsetof (struct Lisp_CondVar, - cond)); + struct Lisp_CondVar *condvar + = ALLOCATE_ZEROED_PSEUDOVECTOR (struct Lisp_CondVar, name, PVEC_CONDVAR); condvar->mutex = mutex; condvar->name = name; sys_cond_init (&condvar->cond); + Lisp_Object result; XSETCONDVAR (result, condvar); return result; } @@ -637,10 +643,8 @@ mark_one_thread (struct thread_state *thread) mark_object (tem); } - mark_object (thread->m_last_thing_searched); - - if (!NILP (thread->m_saved_last_thing_searched)) - mark_object (thread->m_saved_last_thing_searched); + /* No need to mark Lisp_Object members like m_last_thing_searched, + as mark_threads_callback does that by calling mark_object. */ } static void @@ -792,12 +796,6 @@ When the function exits, the thread dies. If NAME is given, it must be a string; it names the new thread. */) (Lisp_Object function, Lisp_Object name) { - sys_thread_t thr; - struct thread_state *new_thread; - Lisp_Object result; - const char *c_name = NULL; - size_t offset = offsetof (struct thread_state, m_stack_bottom); - /* Can't start a thread in temacs. */ if (!initialized) emacs_abort (); @@ -805,20 +803,13 @@ If NAME is given, it must be a string; it names the new thread. */) if (!NILP (name)) CHECK_STRING (name); - new_thread = ALLOCATE_PSEUDOVECTOR (struct thread_state, event_object, - PVEC_THREAD); - memset ((char *) new_thread + offset, 0, - sizeof (struct thread_state) - offset); - + struct thread_state *new_thread + = ALLOCATE_ZEROED_PSEUDOVECTOR (struct thread_state, event_object, + PVEC_THREAD); new_thread->function = function; new_thread->name = name; - new_thread->m_last_thing_searched = Qnil; /* copy from parent? */ - new_thread->m_saved_last_thing_searched = Qnil; + /* Perhaps copy m_last_thing_searched from parent? */ new_thread->m_current_buffer = current_thread->m_current_buffer; - new_thread->result = Qnil; - new_thread->error_symbol = Qnil; - new_thread->error_data = Qnil; - new_thread->event_object = Qnil; new_thread->m_specpdl_size = 50; new_thread->m_specpdl = xmalloc ((1 + new_thread->m_specpdl_size) @@ -833,9 +824,8 @@ If NAME is given, it must be a string; it names the new thread. */) new_thread->next_thread = all_threads; all_threads = new_thread; - if (!NILP (name)) - c_name = SSDATA (ENCODE_UTF_8 (name)); - + char const *c_name = !NILP (name) ? SSDATA (ENCODE_UTF_8 (name)) : NULL; + sys_thread_t thr; if (! sys_thread_create (&thr, c_name, run_thread, new_thread)) { /* Restore the previous situation. */ @@ -848,6 +838,7 @@ If NAME is given, it must be a string; it names the new thread. */) } /* FIXME: race here where new thread might not be filled in? */ + Lisp_Object result; XSETTHREAD (result, new_thread); return result; } @@ -1060,22 +1051,6 @@ thread_check_current_buffer (struct buffer *buffer) -static void -init_main_thread (void) -{ - main_thread.s.header.size - = PSEUDOVECSIZE (struct thread_state, event_object); - XSETPVECTYPE (&main_thread.s, PVEC_THREAD); - main_thread.s.m_last_thing_searched = Qnil; - main_thread.s.m_saved_last_thing_searched = Qnil; - main_thread.s.name = Qnil; - main_thread.s.function = Qnil; - main_thread.s.result = Qnil; - main_thread.s.error_symbol = Qnil; - main_thread.s.error_data = Qnil; - main_thread.s.event_object = Qnil; -} - bool main_thread_p (const void *ptr) { @@ -1090,16 +1065,9 @@ in_current_thread (void) return sys_thread_equal (sys_thread_self (), current_thread->thread_id); } -void -init_threads_once (void) -{ - init_main_thread (); -} - void init_threads (void) { - init_main_thread (); sys_cond_init (&main_thread.s.thread_condvar); sys_mutex_init (&global_lock); sys_mutex_lock (&global_lock); diff --git a/src/thread.h b/src/thread.h index 0514669a87..498b9909c9 100644 --- a/src/thread.h +++ b/src/thread.h @@ -287,7 +287,6 @@ extern void finalize_one_mutex (struct Lisp_Mutex *); extern void finalize_one_condvar (struct Lisp_CondVar *); extern void maybe_reacquire_global_lock (void); -extern void init_threads_once (void); extern void init_threads (void); extern void syms_of_threads (void); extern bool main_thread_p (const void *); commit c5358e831f05cdd110f12a4260e6fb607c66c0b4 Author: Eli Zaretskii Date: Wed Apr 24 18:38:31 2019 +0300 Regenerate src/emacs-module.h when emacs-module.h.in changes * Makefile.in (CONFIG_STATUS_FILES_IN): Add src/emacs-module.h.in. diff --git a/Makefile.in b/Makefile.in index 6b99d24da4..88cbb3d46e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -320,7 +320,7 @@ SUBDIR_MAKEFILES = $(patsubst ${srcdir}/%,%,${SUBDIR_MAKEFILES_IN:.in=}) # Non-makefile files created by config.status. CONFIG_STATUS_FILES_IN = \ ${srcdir}/nt/emacs.rc.in ${srcdir}/nt/emacsclient.rc.in \ - ${srcdir}/doc/man/emacs.1.in + ${srcdir}/doc/man/emacs.1.in ${srcdir}/src/emacs-module.h.in # Subdirectories to install, and where they'll go. lib-src's and nt's # makefiles know how to install them, so we don't do that here. commit 2b3c0ae5828810c6d24f3902bade125aee8b9e9c Author: Eli Zaretskii Date: Wed Apr 24 18:13:04 2019 +0300 Fix rescheduling timers after suspension * lisp/emacs-lisp/timer.el (timer-event-handler): Fix the comparison between next invocation time and current time. diff --git a/lisp/emacs-lisp/timer.el b/lisp/emacs-lisp/timer.el index f706d9bc62..22ccc35103 100644 --- a/lisp/emacs-lisp/timer.el +++ b/lisp/emacs-lisp/timer.el @@ -281,7 +281,7 @@ This function is called, by name, directly by the C code." ;; perhaps because Emacs was suspended for a long time, ;; limit how many times things get repeated. (if (and (numberp timer-max-repeats) - (time-less-p nil (timer--time timer))) + (time-less-p (timer--time timer) nil)) (let ((repeats (/ (timer-until timer nil) (timer--repeat-delay timer)))) (if (> repeats timer-max-repeats) commit f6e6c1744be5620ce97c8429a161ae5cf733a949 Author: Eli Zaretskii Date: Wed Apr 24 15:26:54 2019 +0300 Fix posn-at-point with line-number display and display properties * src/xdisp.c (pos_visible_p): Account for line-number display width when CHARPOS is covered by display property that begins at the 2nd display element of a screen line. (Bug#35404) diff --git a/src/xdisp.c b/src/xdisp.c index a88fc698b8..825b74d539 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -1623,6 +1623,12 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y, set_iterator_to_next (&it3, false); } top_x = it3.current_x - it3.pixel_width; + /* Account for line-number display, if IT3 still + didn't. This can happen if START - 1 is the + first character on its display line. */ + if (!it3.line_number_produced_p + && it.line_number_produced_p) + top_x += it.lnum_pixel_width; /* Normally, we would exit the above loop because we found the display element whose character position is CHARPOS. For the contingency that we commit a2e9d3e33508473547ffa7c3f800fe0e3c5b5c1c Author: Eli Zaretskii Date: Wed Apr 24 15:18:18 2019 +0300 Fix a typo in a recent commit * src/timefns.c (timespec_to_lisp): Fix a typo in function definition. diff --git a/src/timefns.c b/src/timefns.c index 71280aea06..5005c73b7f 100644 --- a/src/timefns.c +++ b/src/timefns.c @@ -532,7 +532,7 @@ make_lisp_time (struct timespec t) } /* Return (TICKS . HZ) for time T. */ -struct Lisp_Object +Lisp_Object timespec_to_lisp (struct timespec t) { return Fcons (timespec_ticks (t), timespec_hz); commit b6d8d34aede02a6af7a614f32b86292ee4ba1757 Author: Philipp Stephani Date: Wed Apr 24 13:54:54 2019 +0200 * doc/lispref/internals.texi (Module Values): Add a GMP example diff --git a/doc/lispref/internals.texi b/doc/lispref/internals.texi index 3e6488a5cc..5ae71afbef 100644 --- a/doc/lispref/internals.texi +++ b/doc/lispref/internals.texi @@ -1543,6 +1543,31 @@ integral object. After you have finished using or similar. @end deftypefn +The following example uses GMP to calculate the next probable prime +after a given integer: + +@example +#include +#include + +#define EMACS_MODULE_GMP +#include + +static emacs_value +next_prime (emacs_env *env, ptrdiff_t nargs, emacs_value *args, + void *data) +@{ + assert (nargs == 1); + emacs_mpz p; + mpz_init (p.value); + env->extract_big_integer (env, args[0], &p); + mpz_nextprime (p.value, p.value); + emacs_value result = env->make_big_integer (env, &p); + mpz_clear (p.value); + return result; +@} +@end example + The @acronym{API} does not provide functions to manipulate Lisp data structures, for example, create lists with @code{cons} and @code{list} (@pxref{Building Lists}), extract list members with @code{car} and commit c4bacb1215bfdf058b374312256c27eaea1304a4 Author: Philipp Stephani Date: Wed Apr 24 13:41:05 2019 +0200 Clarify rounding mode when converting to struct timespec. * doc/lispref/internals.texi (Module Values): Clarify that the truncation is towards negative infinity. * test/data/emacs-module/mod-test.c (Fmod_test_nanoseconds): Add test function. (emacs_module_init): Define it. * test/src/emacs-module-tests.el (mod-test-nanoseconds): New unit test. diff --git a/doc/lispref/internals.texi b/doc/lispref/internals.texi index 10f49c569f..3e6488a5cc 100644 --- a/doc/lispref/internals.texi +++ b/doc/lispref/internals.texi @@ -1406,12 +1406,12 @@ billion. @xref{Elapsed Time,,,libc}. If @var{time} has higher precision than nanoseconds, then this -function truncates it to nanosecond precision. This function signals -an error if @var{time} (truncated to nanoseconds) cannot be -represented by @code{struct timespec}. For example, if @code{time_t} -is a 32-bit integral type, then a @var{time} value of ten billion -seconds would signal an error, but a @var{time} value of 600 -picoseconds would get truncated to zero. +function truncates it to nanosecond precision towards negative +infinity. This function signals an error if @var{time} (truncated to +nanoseconds) cannot be represented by @code{struct timespec}. For +example, if @code{time_t} is a 32-bit integral type, then a @var{time} +value of ten billion seconds would signal an error, but a @var{time} +value of 600 picoseconds would get truncated to zero. If you need to deal with time values that are not representable by @code{struct timespec}, or if you want higher precision, call the Lisp diff --git a/test/data/emacs-module/mod-test.c b/test/data/emacs-module/mod-test.c index 85a7f28e50..8ac08f7153 100644 --- a/test/data/emacs-module/mod-test.c +++ b/test/data/emacs-module/mod-test.c @@ -381,6 +381,22 @@ Fmod_test_add_nanosecond (emacs_env *env, ptrdiff_t nargs, emacs_value *args, return env->make_time (env, time); } +static emacs_value +Fmod_test_nanoseconds (emacs_env *env, ptrdiff_t nargs, emacs_value *args, void *data) { + assert (nargs == 1); + struct timespec time = env->extract_time (env, args[0]); + struct emacs_mpz nanoseconds; + assert (LONG_MIN <= time.tv_sec && time.tv_sec <= LONG_MAX); + mpz_init_set_si (nanoseconds.value, time.tv_sec); + static_assert (1000000000 <= ULONG_MAX, "unsupported architecture"); + mpz_mul_ui (nanoseconds.value, nanoseconds.value, 1000000000); + assert (0 <= time.tv_nsec && time.tv_nsec <= ULONG_MAX); + mpz_add_ui (nanoseconds.value, nanoseconds.value, time.tv_nsec); + emacs_value result = env->make_big_integer (env, &nanoseconds); + mpz_clear (nanoseconds.value); + return result; +} + static emacs_value Fmod_test_double (emacs_env *env, ptrdiff_t nargs, emacs_value *args, void *data) @@ -465,6 +481,7 @@ emacs_module_init (struct emacs_runtime *ert) NULL, NULL); DEFUN ("mod-test-sleep-until", Fmod_test_sleep_until, 2, 2, NULL, NULL); DEFUN ("mod-test-add-nanosecond", Fmod_test_add_nanosecond, 1, 1, NULL, NULL); + DEFUN ("mod-test-nanoseconds", Fmod_test_nanoseconds, 1, 1, NULL, NULL); DEFUN ("mod-test-double", Fmod_test_double, 1, 1, NULL, NULL); #undef DEFUN diff --git a/test/src/emacs-module-tests.el b/test/src/emacs-module-tests.el index 9eb38cd454..173b63670f 100644 --- a/test/src/emacs-module-tests.el +++ b/test/src/emacs-module-tests.el @@ -342,6 +342,25 @@ Interactively, you can try hitting \\[keyboard-quit] to quit." (ert-info ((format "input: %s" input)) (should-error (mod-test-add-nanosecond input))))) +(ert-deftest mod-test-nanoseconds () + "Test truncation when converting to `struct timespec'." + (dolist (test-case '((0 . 0) + (-1 . -1000000000) + ((1 . 1000000000) . 1) + ((-1 . 1000000000) . -1) + ((1 . 1000000000000) . 0) + ((-1 . 1000000000000) . -1) + ((999 . 1000000000000) . 0) + ((-999 . 1000000000000) . -1) + ((1000 . 1000000000000) . 1) + ((-1000 . 1000000000000) . -1) + ((0 0 0 1) . 0) + ((0 0 0 -1) . -1))) + (let ((input (car test-case)) + (expected (cdr test-case))) + (ert-info ((format "input: %S, expected result: %d" input expected)) + (should (eq (mod-test-nanoseconds input) expected)))))) + (ert-deftest mod-test-double () (dolist (input (list 0 1 2 -1 42 12345678901234567890 most-positive-fixnum (1+ most-positive-fixnum) commit 534c33cf375182c97291d2dd242f936df5953321 Author: Philipp Stephani Date: Wed Apr 24 13:17:53 2019 +0200 Fix return type of make_time. make_time is documented to return a (TICKS . HZ) pair, so we can’t use make_lisp_time. Introduce a new conversion function instead. * src/emacs-module.c (module_make_time): Use timespec_to_lisp to correct return type. * src/timefns.c (timespec_to_lisp): New function. (make_lisp_time): Use it. * test/src/emacs-module-tests.el (mod-test-add-nanosecond/valid): Check return type. diff --git a/src/emacs-module.c b/src/emacs-module.c index e203ce1d34..41ce9ef03e 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -753,7 +753,7 @@ static emacs_value module_make_time (emacs_env *env, struct timespec time) { MODULE_FUNCTION_BEGIN (NULL); - return lisp_to_value (env, make_lisp_time (time)); + return lisp_to_value (env, timespec_to_lisp (time)); } static void diff --git a/src/systime.h b/src/systime.h index 89af0c5e3d..125b2f1385 100644 --- a/src/systime.h +++ b/src/systime.h @@ -89,6 +89,7 @@ struct lisp_time /* defined in timefns.c */ extern struct timeval make_timeval (struct timespec) ATTRIBUTE_CONST; extern Lisp_Object make_lisp_time (struct timespec); +extern Lisp_Object timespec_to_lisp (struct timespec); extern bool list4_to_timespec (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, struct timespec *); extern struct timespec lisp_time_argument (Lisp_Object); diff --git a/src/timefns.c b/src/timefns.c index cb953d1b4c..71280aea06 100644 --- a/src/timefns.c +++ b/src/timefns.c @@ -528,7 +528,14 @@ make_lisp_time (struct timespec t) make_fixnum (ns / 1000), make_fixnum (ns % 1000 * 1000)); } else - return Fcons (timespec_ticks (t), timespec_hz); + return timespec_to_lisp (t); +} + +/* Return (TICKS . HZ) for time T. */ +struct Lisp_Object +timespec_to_lisp (struct timespec t) +{ + return Fcons (timespec_ticks (t), timespec_hz); } /* Convert T to a Lisp timestamp. FORM specifies the timestamp format. */ diff --git a/test/src/emacs-module-tests.el b/test/src/emacs-module-tests.el index 78f238140d..9eb38cd454 100644 --- a/test/src/emacs-module-tests.el +++ b/test/src/emacs-module-tests.el @@ -326,8 +326,12 @@ Interactively, you can try hitting \\[keyboard-quit] to quit." ;; New (TICKS . HZ) format. '(123456789 . 1000000000))) (ert-info ((format "input: %s" input)) - (should (time-equal-p (mod-test-add-nanosecond input) - (time-add input '(0 0 0 1000))))))) + (let ((result (mod-test-add-nanosecond input))) + (should (consp result)) + (should (integerp (car result))) + (should (integerp (cdr result))) + (should (cl-plusp (cdr result))) + (should (time-equal-p result (time-add input '(0 0 0 1000)))))))) (ert-deftest mod-test-add-nanosecond/nil () (should (<= (float-time (mod-test-add-nanosecond nil)) commit e290a7d1730c99010272bbff7f497c3041cef46d Author: Philipp Stephani Date: Thu Apr 18 22:38:29 2019 +0200 Add module functions to convert from and to big integers. * src/module-env-27.h: Add new module functions to convert big integers. * src/emacs-module.h.in (emacs_mpz): Define if GMP is available. * src/emacs-module.c (module_extract_big_integer) (module_make_big_integer): New functions. (initialize_environment): Use them. * test/data/emacs-module/mod-test.c (Fmod_test_double): New test function. (emacs_module_init): Define it. * test/src/emacs-module-tests.el (mod-test-double): New unit test. * doc/lispref/internals.texi (Module Values): Document new functions. diff --git a/doc/lispref/internals.texi b/doc/lispref/internals.texi index 0e7a1339e7..10f49c569f 100644 --- a/doc/lispref/internals.texi +++ b/doc/lispref/internals.texi @@ -1508,6 +1508,41 @@ function raises the @code{overflow-error} error condition if string. @end deftypefn +If you define the preprocessor macro @code{EMACS_MODULE_GMP} before +including the header @file{emacs-module.h}, you can also convert +between Emacs integers and GMP @code{mpz_t} values. @xref{GMP +Basics,,,gmp}. If @code{EMACS_MODULE_GMP} is defined, +@file{emacs-module.h} wraps @code{mpz_t} in the following structure: + +@deftp struct emacs_mpz value +struct emacs_mpz @{ mpz_t value; @}; +@end deftp + +@noindent +Then you can use the following additional functions: + +@deftypefn Function bool extract_big_integer (emacs_env *@var{env}, emacs_value @var{arg}, struct emacs_mpz *@var{result}) +This function, which is available since Emacs 27, extracts the +integral value of @var{arg} into @var{result}. @var{result} must not +be @code{NULL}. @code{@var{result}->value} must be an initialized +@code{mpz_t} object. @xref{Initializing Integers,,,gmp}. If +@var{arg} is an integer, Emacs will store its value into +@code{@var{result}->value}. After you have finished using +@code{@var{result}->value}, you should free it using @code{mpz_clear} +or similar. +@end deftypefn + +@deftypefn Function emacs_value make_big_integer (emacs_env *@var{env}, const struct emacs_mpz *@var{value}) +This function, which is available since Emacs 27, takes an +arbitrary-sized integer argument and returns a corresponding +@code{emacs_value} object. @var{value} must not be @code{NULL}. +@code{@var{value}->value} must be an initialized @code{mpz_t} object. +@xref{Initializing Integers,,,gmp}. Emacs will return a corresponding +integral object. After you have finished using +@code{@var{value}->value}, you should free it using @code{mpz_clear} +or similar. +@end deftypefn + The @acronym{API} does not provide functions to manipulate Lisp data structures, for example, create lists with @code{cons} and @code{list} (@pxref{Building Lists}), extract list members with @code{car} and diff --git a/etc/NEWS b/etc/NEWS index fc9b828baa..e861a372b1 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1913,6 +1913,10 @@ case. ** New module environment functions 'make_time' and 'extract_time' to convert between timespec structures and Emacs Lisp time values. +** New module environment functions 'make_big_integer' and +'extract_big_integer' to create and extract arbitrary-size integer +values. + * Changes in Emacs 27.1 on Non-Free Operating Systems diff --git a/src/emacs-module.c b/src/emacs-module.c index e46af30ce8..e203ce1d34 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -70,6 +70,7 @@ To add a new module function, proceed as follows: #include +#define EMACS_MODULE_GMP #include "emacs-module.h" #include @@ -79,7 +80,10 @@ To add a new module function, proceed as follows: #include #include +#include + #include "lisp.h" +#include "bignum.h" #include "dynlib.h" #include "coding.h" #include "keyboard.h" @@ -752,6 +756,27 @@ module_make_time (emacs_env *env, struct timespec time) return lisp_to_value (env, make_lisp_time (time)); } +static void +module_extract_big_integer (emacs_env *env, emacs_value value, + struct emacs_mpz *result) +{ + MODULE_FUNCTION_BEGIN (); + Lisp_Object o = value_to_lisp (value); + CHECK_INTEGER (o); + if (FIXNUMP (o)) + mpz_set_intmax (result->value, XFIXNUM (o)); + else + mpz_set (result->value, XBIGNUM (o)->value); +} + +static emacs_value +module_make_big_integer (emacs_env *env, const struct emacs_mpz *value) +{ + MODULE_FUNCTION_BEGIN (NULL); + mpz_set (mpz[0], value->value); + return lisp_to_value (env, make_integer_mpz ()); +} + /* Subroutines. */ @@ -1157,6 +1182,8 @@ initialize_environment (emacs_env *env, struct emacs_env_private *priv) env->process_input = module_process_input; env->extract_time = module_extract_time; env->make_time = module_make_time; + env->extract_big_integer = module_extract_big_integer; + env->make_big_integer = module_make_big_integer; Vmodule_environments = Fcons (make_mint_ptr (env), Vmodule_environments); return env; } diff --git a/src/emacs-module.h.in b/src/emacs-module.h.in index bfbe226dd9..e61aadfc3a 100644 --- a/src/emacs-module.h.in +++ b/src/emacs-module.h.in @@ -28,6 +28,10 @@ along with GNU Emacs. If not, see . */ #include #endif +#ifdef EMACS_MODULE_GMP +#include +#endif + #if defined __cplusplus && __cplusplus >= 201103L # define EMACS_NOEXCEPT noexcept #else @@ -94,6 +98,12 @@ enum emacs_process_input_result emacs_process_input_quit = 1 }; +#ifdef EMACS_MODULE_GMP +struct emacs_mpz { mpz_t value; }; +#else +struct emacs_mpz; /* no definition */ +#endif + struct emacs_env_25 { @module_env_snippet_25@ diff --git a/src/lisp.h b/src/lisp.h index d803f16000..703fe76d64 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4151,6 +4151,7 @@ extern void *unexec_realloc (void *, size_t); extern void unexec_free (void *); #endif +#define EMACS_MODULE_GMP #include "emacs-module.h" /* Function prototype for the module Lisp functions. */ diff --git a/src/module-env-27.h b/src/module-env-27.h index e63843f8d6..00de300900 100644 --- a/src/module-env-27.h +++ b/src/module-env-27.h @@ -8,3 +8,11 @@ emacs_value (*make_time) (emacs_env *env, struct timespec time) EMACS_ATTRIBUTE_NONNULL (1); + + void (*extract_big_integer) (emacs_env *env, emacs_value value, + struct emacs_mpz *result) + EMACS_ATTRIBUTE_NONNULL (1, 3); + + emacs_value (*make_big_integer) (emacs_env *env, + const struct emacs_mpz *value) + EMACS_ATTRIBUTE_NONNULL (1, 2); diff --git a/test/data/emacs-module/mod-test.c b/test/data/emacs-module/mod-test.c index dbdbfecfe6..85a7f28e50 100644 --- a/test/data/emacs-module/mod-test.c +++ b/test/data/emacs-module/mod-test.c @@ -27,8 +27,11 @@ along with GNU Emacs. If not, see . */ #include #include +#define EMACS_MODULE_GMP #include +#include + #include "timespec.h" int plugin_is_GPL_compatible; @@ -378,6 +381,21 @@ Fmod_test_add_nanosecond (emacs_env *env, ptrdiff_t nargs, emacs_value *args, return env->make_time (env, time); } +static emacs_value +Fmod_test_double (emacs_env *env, ptrdiff_t nargs, emacs_value *args, + void *data) +{ + assert (nargs == 1); + emacs_value arg = args[0]; + struct emacs_mpz value; + mpz_init (value.value); + env->extract_big_integer (env, arg, &value); + mpz_mul_ui (value.value, value.value, 2); + emacs_value result = env->make_big_integer (env, &value); + mpz_clear (value.value); + return result; +} + /* Lisp utilities for easier readability (simple wrappers). */ /* Provide FEATURE to Emacs. */ @@ -447,6 +465,7 @@ emacs_module_init (struct emacs_runtime *ert) NULL, NULL); DEFUN ("mod-test-sleep-until", Fmod_test_sleep_until, 2, 2, NULL, NULL); DEFUN ("mod-test-add-nanosecond", Fmod_test_add_nanosecond, 1, 1, NULL, NULL); + DEFUN ("mod-test-double", Fmod_test_double, 1, 1, NULL, NULL); #undef DEFUN diff --git a/test/src/emacs-module-tests.el b/test/src/emacs-module-tests.el index eea4c61165..78f238140d 100644 --- a/test/src/emacs-module-tests.el +++ b/test/src/emacs-module-tests.el @@ -338,4 +338,11 @@ Interactively, you can try hitting \\[keyboard-quit] to quit." (ert-info ((format "input: %s" input)) (should-error (mod-test-add-nanosecond input))))) +(ert-deftest mod-test-double () + (dolist (input (list 0 1 2 -1 42 12345678901234567890 + most-positive-fixnum (1+ most-positive-fixnum) + most-negative-fixnum (1- most-negative-fixnum))) + (ert-info ((format "input: %d" input)) + (should (= (mod-test-double input) (* 2 input)))))) + ;;; emacs-module-tests.el ends here commit bffceab6339fb4042588b893ef754c6264379e75 Author: Philipp Stephani Date: Thu Apr 18 17:42:45 2019 +0200 Add conversions to and from struct timespec to module interface. Time values are a fundamental data type, and such conversions are hard to implement within modules because of the various forms of time values in Emacs Lisp. Adding dedicated conversion functions can significantly simplify module code dealing with times. This approach uses nanosecond precision. While Emacs in theory has support for higher-precision time values, in practice most languages and standards, such as POSIX, C, Java, and Go, have settled on nanosecond-precision integers to represent time. * src/emacs-module.h.in: Add header for struct timespec. * src/module-env-27.h: Add module functions for time conversion. * src/emacs-module.c (module_extract_time, module_make_time): New functions. (initialize_environment): Use them. * test/data/emacs-module/mod-test.c (Fmod_test_add_nanosecond): New test function. (emacs_module_init): Define it. * test/src/emacs-module-tests.el (mod-test-add-nanosecond/valid) (mod-test-add-nanosecond/nil, mod-test-add-nanosecond/invalid): New unit tests. * doc/lispref/internals.texi (Module Values): Document time conversion functions. diff --git a/doc/lispref/internals.texi b/doc/lispref/internals.texi index 25892d4b57..0e7a1339e7 100644 --- a/doc/lispref/internals.texi +++ b/doc/lispref/internals.texi @@ -1387,6 +1387,38 @@ This function returns the value of a Lisp float specified by @var{arg}, as a C @code{double} value. @end deftypefn +@deftypefn Function struct timespec extract_time (emacs_env *@var{env}, emacs_value @var{time}) +This function, which is available since Emacs 27, interprets +@var{time} as an Emacs Lisp time value and returns the corresponding +@code{struct timespec}. @xref{Time of Day}. @code{struct timespec} +represents a timestamp with nanosecond precision. It has the +following members: + +@table @code +@item time_t tv_sec +Whole number of seconds. +@item long tv_nsec +Fractional seconds as number of nanoseconds, always less than one +billion. +@end table + +@noindent +@xref{Elapsed Time,,,libc}. + +If @var{time} has higher precision than nanoseconds, then this +function truncates it to nanosecond precision. This function signals +an error if @var{time} (truncated to nanoseconds) cannot be +represented by @code{struct timespec}. For example, if @code{time_t} +is a 32-bit integral type, then a @var{time} value of ten billion +seconds would signal an error, but a @var{time} value of 600 +picoseconds would get truncated to zero. + +If you need to deal with time values that are not representable by +@code{struct timespec}, or if you want higher precision, call the Lisp +function @code{encode-time} and work with its return value. +@xref{Time Conversion}. +@end deftypefn + @deftypefn Function bool copy_string_contents (emacs_env *@var{env}, emacs_value @var{arg}, char *@var{buf}, ptrdiff_t *@var{len}) This function stores the UTF-8 encoded text of a Lisp string specified by @var{arg} in the array of @code{char} pointed by @var{buf}, which @@ -1452,6 +1484,18 @@ This function takes a @code{double} argument @var{d} and returns the corresponding Emacs floating-point value. @end deftypefn +@deftypefn Function emacs_value make_time (emacs_env *@var{env}, struct timespec @var{time}) +This function, which is available since Emacs 27, takes a @code{struct +timespec} argument @var{time} and returns the corresponding Emacs +timestamp as a pair @code{(@var{ticks} . @var{hz})}. @xref{Time of +Day}. The return value represents exactly the same timestamp as +@var{time}: all input values are representable, and there is never a +loss of precision. @code{@var{time}.tv_sec} and +@code{@var{time}.tv_nsec} can be arbitrary values. In particular, +there's no requirement that @var{time} be normalized. This means that +@code{@var{time}.tv_nsec} can be negative or larger than 999,999,999. +@end deftypefn + @deftypefn Function emacs_value make_string (emacs_env *@var{env}, const char *@var{str}, ptrdiff_t @var{strlen}) This function creates an Emacs string from C text string pointed by @var{str} whose length in bytes, not including the terminating null diff --git a/etc/NEWS b/etc/NEWS index b13ab47768..fc9b828baa 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1910,6 +1910,9 @@ returns a regexp that never matches anything, which is an identity for this operation. Previously, the empty string was returned in this case. +** New module environment functions 'make_time' and 'extract_time' to +convert between timespec structures and Emacs Lisp time values. + * Changes in Emacs 27.1 on Non-Free Operating Systems diff --git a/src/emacs-module.c b/src/emacs-module.c index b812fdc2df..e46af30ce8 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -77,6 +77,7 @@ To add a new module function, proceed as follows: #include #include #include +#include #include "lisp.h" #include "dynlib.h" @@ -737,6 +738,20 @@ module_process_input (emacs_env *env) return emacs_process_input_continue; } +static struct timespec +module_extract_time (emacs_env *env, emacs_value value) +{ + MODULE_FUNCTION_BEGIN ((struct timespec) {0}); + return lisp_time_argument (value_to_lisp (value)); +} + +static emacs_value +module_make_time (emacs_env *env, struct timespec time) +{ + MODULE_FUNCTION_BEGIN (NULL); + return lisp_to_value (env, make_lisp_time (time)); +} + /* Subroutines. */ @@ -1140,6 +1155,8 @@ initialize_environment (emacs_env *env, struct emacs_env_private *priv) env->vec_size = module_vec_size; env->should_quit = module_should_quit; env->process_input = module_process_input; + env->extract_time = module_extract_time; + env->make_time = module_make_time; Vmodule_environments = Fcons (make_mint_ptr (env), Vmodule_environments); return env; } diff --git a/src/emacs-module.h.in b/src/emacs-module.h.in index 009d1583fe..bfbe226dd9 100644 --- a/src/emacs-module.h.in +++ b/src/emacs-module.h.in @@ -22,6 +22,7 @@ along with GNU Emacs. If not, see . */ #include #include +#include #ifndef __cplusplus #include diff --git a/src/module-env-27.h b/src/module-env-27.h index b491b60fbb..e63843f8d6 100644 --- a/src/module-env-27.h +++ b/src/module-env-27.h @@ -2,3 +2,9 @@ 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 value) + EMACS_ATTRIBUTE_NONNULL (1); + + emacs_value (*make_time) (emacs_env *env, struct timespec time) + EMACS_ATTRIBUTE_NONNULL (1); diff --git a/test/data/emacs-module/mod-test.c b/test/data/emacs-module/mod-test.c index a39e41afee..dbdbfecfe6 100644 --- a/test/data/emacs-module/mod-test.c +++ b/test/data/emacs-module/mod-test.c @@ -366,6 +366,18 @@ Fmod_test_sleep_until (emacs_env *env, ptrdiff_t nargs, emacs_value *args, return env->intern (env, "finished"); } +static emacs_value +Fmod_test_add_nanosecond (emacs_env *env, ptrdiff_t nargs, emacs_value *args, + void *data) +{ + assert (nargs == 1); + struct timespec time = env->extract_time (env, args[0]); + assert (time.tv_nsec >= 0); + assert (time.tv_nsec < 2000000000); /* possible leap second */ + time.tv_nsec++; + return env->make_time (env, time); +} + /* Lisp utilities for easier readability (simple wrappers). */ /* Provide FEATURE to Emacs. */ @@ -434,6 +446,7 @@ emacs_module_init (struct emacs_runtime *ert) DEFUN ("mod-test-invalid-finalizer", Fmod_test_invalid_finalizer, 0, 0, NULL, NULL); DEFUN ("mod-test-sleep-until", Fmod_test_sleep_until, 2, 2, NULL, NULL); + DEFUN ("mod-test-add-nanosecond", Fmod_test_add_nanosecond, 1, 1, NULL, NULL); #undef DEFUN diff --git a/test/src/emacs-module-tests.el b/test/src/emacs-module-tests.el index 35aaaa64b6..eea4c61165 100644 --- a/test/src/emacs-module-tests.el +++ b/test/src/emacs-module-tests.el @@ -310,4 +310,32 @@ Interactively, you can try hitting \\[keyboard-quit] to quit." 'finished)) (quit))))) +(ert-deftest mod-test-add-nanosecond/valid () + (dolist (input (list + ;; Some realistic examples. + (current-time) (time-to-seconds) + (encode-time 12 34 5 6 7 2019 t) + ;; Various legacy timestamp forms. + '(123 456) '(123 456 789) '(123 456 789 6000) + ;; Corner case: this will result in a nanosecond + ;; value of 1000000000 after addition. The module + ;; code should handle this correctly. + '(123 65535 999999 999000) + ;; Seconds since the epoch. + 123 123.45 + ;; New (TICKS . HZ) format. + '(123456789 . 1000000000))) + (ert-info ((format "input: %s" input)) + (should (time-equal-p (mod-test-add-nanosecond input) + (time-add input '(0 0 0 1000))))))) + +(ert-deftest mod-test-add-nanosecond/nil () + (should (<= (float-time (mod-test-add-nanosecond nil)) + (+ (float-time) 1e-9)))) + +(ert-deftest mod-test-add-nanosecond/invalid () + (dolist (input '(1.0e+INF 1.0e-INF 0.0e+NaN (123) (123.45 6 7) "foo" [1 2])) + (ert-info ((format "input: %s" input)) + (should-error (mod-test-add-nanosecond input))))) + ;;; emacs-module-tests.el ends here