Hello Daniel!

I imagine you'll be excited to hear that this patch series is being
worked on again!  We wanted to share the following with you:

Ihor Radchenko <[email protected]> writes:

> Morgan Smith <[email protected]> writes:
>
>> If the compat team is listening, org currently defines compatibility
>> wrappers around the following items and it would be nice if they got
>> added to compat (no pressure, just thought I'd ask):
>>
>> display-buffer-full-frame
>> buffer-text-pixel-size
>> with-undo-amalgamate
>> time-convert
>> with-connection-local-variables
>
> We can ask Daniel.
>

Org is happy to define these things in our "org-compat.el" file which we
will not be getting rid of.  However, please feel free to steal our
implementations so we can lighten the file a little.

>> I spent absolutely way too long on this but I think I did it.  Please
>> let me know what you think
>
> Thanks!
> Looks reasonable in general.
> I think we also need to update Package-Requires.

I'm pretty sure I did that in the patch "Add compat.el dependency"

>> Is the a way to conditionally include compat so newer Emacs are not bogged
>> down?
>
> Could you elaborate?
> We can simply check if something like compat-31 is available, but I am
> not sure what the problem is.
>

Sorry.  I was trying to summarize all the previous discussions and
respond to everything.  Someone had asked if there would be a
performance penality on Emacs that don't require compat.  Since newer
emacs have a stub implementation of compat there should be no
performance impacts.

>> @@ -158,6 +158,10 @@ *** Using Org's Git repository
>>  (add-to-list 'load-path "~/src/org-mode/lisp")
>>  #+end_src
>>  
>> +You must also manually install =compat= library required by Org mode.
>> +Using built-in =package.el=, you can run =M-x package-install <RET>
>> +compat <RET>=.
>
> I am wondering if we can simply switch to package-vc in the instructions.

I can't seem to get `package-vc' to work on my funky "functional"
operating system.  If someone else could write that, I would appreciate
it.  Regardless, I think updating that section of the manual is somewhat
tangential to the problem at hand.


Attached is a rebase of the previous series.



>From 899fcf58b20eb331906ca65e301ef36e8d908c94 Mon Sep 17 00:00:00 2001
From: Morgan Smith <[email protected]>
Date: Mon, 18 May 2026 23:53:11 -0400
Subject: [PATCH 1/7] mk/org-fixup.el: Remove dependency on org-compat.el

This simply isn't needed and will simplify the dependency tree
slightly.

* mk/org-fixup.el:
* mk/default.mk (MAKE_ORG_INSTALL, MAKE_ORG_VERSION, MAKE_LOCAL_MK):
* doc/Makefile (%_letter.tex):
Don't load "org-compat.el".
* lisp/org-compat.el: Run "(org-assert-version)" because we can now.
---
 doc/Makefile       | 1 -
 lisp/org-compat.el | 4 +---
 mk/default.mk      | 3 ---
 mk/org-fixup.el    | 2 --
 4 files changed, 1 insertion(+), 9 deletions(-)

diff --git a/doc/Makefile b/doc/Makefile
index 0a56dbdd9..1b32dcf2d 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -105,6 +105,5 @@ endif
 %_letter.tex:	%.tex
 	$(BATCH) 				      \
 	  --eval '(add-to-list `load-path "../lisp")' \
-	  --eval '(load "org-compat.el")' 	      \
 	  --eval '(load "../mk/org-fixup.el")' 	      \
 	  --eval '(org-make-letterformat "$(<F)" "$(@F)")'
diff --git a/lisp/org-compat.el b/lisp/org-compat.el
index 55a4e481e..5e62133ad 100644
--- a/lisp/org-compat.el
+++ b/lisp/org-compat.el
@@ -36,9 +36,7 @@
 
 (eval-when-compile (require 'subr-x))  ; Emacs < 28
 
-;; We rely on org-compat when generating Org version.  Checking Org
-;; version here will interfere with Org build process.
-;; (org-assert-version)
+(org-assert-version)
 
 (declare-function org-agenda-diary-entry "org-agenda")
 (declare-function org-agenda-maybe-redo "org-agenda" ())
diff --git a/mk/default.mk b/mk/default.mk
index 9ecda3c10..acc0a3866 100644
--- a/mk/default.mk
+++ b/mk/default.mk
@@ -129,7 +129,6 @@ BATCHO	= $(BATCH) \
 
 # How to generate local.mk
 MAKE_LOCAL_MK = $(BATCHO) \
-	  --eval '(load "org-compat.el")' \
 	  --eval '(load "../mk/org-fixup.el")' \
 	  --eval '(org-make-local-mk)'
 
@@ -139,13 +138,11 @@ BATCHL	= $(BATCH) \
 
 # How to generate org-loaddefs.el
 MAKE_ORG_INSTALL = $(BATCHL) \
-	  --eval '(load "org-compat.el")' \
 	  --eval '(load "../mk/org-fixup.el")' \
 	  --eval '(org-make-org-loaddefs)'
 
 # How to generate org-version.el
 MAKE_ORG_VERSION = $(BATCHL) \
-	  --eval '(load "org-compat.el")' \
 	  --eval '(load "../mk/org-fixup.el")' \
 	  --eval '(org-make-org-version "$(ORGVERSION)" "$(GITVERSION)")'
 
diff --git a/mk/org-fixup.el b/mk/org-fixup.el
index 451da335b..8a97abb52 100644
--- a/mk/org-fixup.el
+++ b/mk/org-fixup.el
@@ -24,8 +24,6 @@
 ;;
 ;;; Commentary:
 
-(require 'org-compat "org-compat.el")
-
 (defun org-make-manual ()
   "Generate the Texinfo file out of the Org manual."
   (require 'ox-texinfo)

base-commit: 7a92e19ed22659bda166c97ac883d1acf4f9de63
-- 
2.54.0

>From 8e234a1a61212f0dcdda1947828771e4babdcb49 Mon Sep 17 00:00:00 2001
From: Ihor Radchenko <[email protected]>
Date: Sat, 1 Apr 2023 12:00:48 +0200
Subject: [PATCH 2/7] Upgrade Org build system to handle third-party
 dependencies

* mk/org-dependencies.el: New file
(org-dependencies): The list of dependencies.
(org-dependencies-installed-p): A way to check if dependencies are
installed.
(org-install-dependencies): A way to install dependencies
* mk/default.mk (pkgdir_top): New variable holding the location of
third-party packages to be downloaded if necessary during compilation.
(EMACS_VERSION): New variable holding current Emacs version, according
to EMACS.
(pkgdir): New variable holding subdir where the third-party packages
are downloaded, according to EMACS_VERSION.
(package-install):
(EMACSQ): Update, setting default package location to pkgdir.
* mk/targets.mk (uppkg): New target to download install missing
packages.
(checkpkg): New target to ensure packages are installed.
(check test):
(repro):
(compile compile-dirty): Use the new checkpkg target.
(cleanpkg): New target cleaning up the downloaded packages.
(cleanall): Use the new target.
(.PHONY):
(CONF_DEST): Update according to the new variables and targets.
* .gitignore: Ignore the downloaded packages.

This commit paves the way towards third-party built-time dependencies
for Org.  In particular, towards including compat.el dependency.

Link: https://orgmode.org/list/87v8ks6rhf.fsf@localhost

Co-authored-by: Morgan Smith <[email protected]>
---
 .gitignore             |  1 +
 mk/default.mk          | 15 ++++++++---
 mk/org-dependencies.el | 58 ++++++++++++++++++++++++++++++++++++++++++
 mk/targets.mk          | 26 ++++++++++++++-----
 4 files changed, 90 insertions(+), 10 deletions(-)
 create mode 100644 mk/org-dependencies.el

diff --git a/.gitignore b/.gitignore
index 0eb5d834a..e12bde7b6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,6 +48,7 @@ local*.mk
 .gitattributes
 mk/x11idle
 ChangeLog
+pkg-deps/
 
 # Files generated during `make packages/org` in a clone of `elpa.git`.
 
diff --git a/mk/default.mk b/mk/default.mk
index acc0a3866..2c6f73424 100644
--- a/mk/default.mk
+++ b/mk/default.mk
@@ -6,6 +6,7 @@
 
 # Name of your emacs binary
 EMACS	= emacs
+EMACS_VERSION := $(shell $(EMACS) -Q --batch --eval '(message "%s" emacs-version)' 2>&1)
 
 # Where local software is found
 prefix	= /usr/share
@@ -31,6 +32,11 @@ GIT_BRANCH =
 TMPDIR ?= /tmp
 testdir = $(TMPDIR)/tmp-orgtest
 
+# Where to store Org dependencies
+top_builddir := $(shell pwd)
+pkgdir_top := $(top_builddir)/pkg-deps
+pkgdir := $(pkgdir_top)/$(EMACS_VERSION)
+
 # Configuration for testing
 # Verbose ERT summary by default for Emacs-28 and above.
 # To override:
@@ -117,11 +123,14 @@ REPRO = $(NOBATCH) $(BTEST_INIT) $(REPRO_INIT) $(REPRO_ARGS)
 
 # start Emacs with no user and site configuration
 # EMACSQ = -vanilla # XEmacs
-EMACSQ  = $(EMACS)  -Q
+EMACSQ  = $(EMACS)  -Q \
+	  --eval '(setq vc-handled-backends nil org-startup-folded nil org-element-cache-persistent nil)' \
+	  --eval '(make-directory "$(pkgdir)" t)' \
+	  --eval '(setq package-user-dir "$(pkgdir)")' \
+	  -f package-initialize
 
 # Using emacs in batch mode.
-BATCH	= $(EMACSQ) -batch \
-	  --eval '(setq vc-handled-backends nil org-startup-folded nil org-element-cache-persistent nil)'
+BATCH	= $(EMACSQ) -batch
 
 # Emacs must be started in toplevel directory
 BATCHO	= $(BATCH) \
diff --git a/mk/org-dependencies.el b/mk/org-dependencies.el
new file mode 100644
index 000000000..b6b060868
--- /dev/null
+++ b/mk/org-dependencies.el
@@ -0,0 +1,58 @@
+;;; org-dependencies.el --- Ensure dependencies are met in the development repository  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2026  Morgan Smith
+
+;; Author: Morgan Smith <[email protected]>
+;; Keywords:
+
+;; This file is not part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(require 'package)
+
+(defconst org-dependencies '()
+  "An alist of dependencies of org.
+Each item is of the form (PACKAGE . MINIMUM-VERSION).")
+
+(defun org-dependencies-installed-p ()
+  "Check if all the dependencies from `org-dependencies' are installed.
+Throw an error if they are not installed."
+  (let ((backtrace-on-error-noninteractive nil))
+    (dolist (dep org-dependencies)
+      (when (not (package-installed-p (car dep) (cdr-safe dep)))
+        (if (package-installed-p (car dep))
+            (error "ERROR: Package %S does not satisfy version requirement of %S
+    Consider running \"make uppkg\" to install dependencies"
+                   (car dep) (cdr dep))
+          (error "ERROR: Package %S not installed
+    Consider running \"make uppkg\" to install dependencies"
+                 (car dep)))))
+    t))
+
+(defun org-install-dependencies ()
+  "Install missing dependencies from `org-dependencies'."
+  (unless (ignore-errors (org-dependencies-installed-p))
+    (package-refresh-contents)
+    (dolist (dep org-dependencies)
+      (unless (package-installed-p (car dep) (cdr-safe dep))
+        (let ((package-install-upgrade-built-in t))
+          (package-install (car dep)))))))
+
+(provide 'org-dependencies)
+;;; org-dependencies.el ends here
diff --git a/mk/targets.mk b/mk/targets.mk
index a8bec6813..b20784e1a 100644
--- a/mk/targets.mk
+++ b/mk/targets.mk
@@ -29,18 +29,18 @@ ifneq ($(GITSTATUS),)
   GITVERSION := $(GITVERSION:.dirty=).dirty
 endif
 
-.PHONY:	all oldorg update update2 up0 up1 up2 single native $(SUBDIRS) \
+.PHONY:	all oldorg update update2 up0 up1 up2 uppkg checkpkg single native $(SUBDIRS) \
 	check test test-dirty install install-info $(INSTSUB) \
 	info html pdf card refcard doc docs \
 	autoloads cleanall clean $(CLEANDIRS:%=clean%) \
 	clean-install cleanelc cleandirs \
-	cleanlisp cleandoc cleandocs cleantest \
+	cleanlisp cleandoc cleandocs cleantest cleanpkg \
 	compile compile-dirty uncompiled \
 	config config-test config-exe config-all config-eol config-version \
 	vanilla repro
 
 CONF_BASE = EMACS DESTDIR ORGCM ORG_MAKE_DOC
-CONF_DEST = lispdir infodir datadir testdir
+CONF_DEST = lispdir infodir datadir testdir pkgdir
 CONF_TEST = BTEST_PRE BTEST_POST BTEST_OB_LANGUAGES BTEST_EXTRA BTEST_RE
 CONF_EXEC = CP MKDIR RM RMR FIND CHMOD SUDO PDFTEX TEXI2PDF TEXI2HTML MAKEINFO INSTALL_INFO
 CONF_CALL = BATCH BATCHL ELC ELN ELCDIR NOBATCH BTEST MAKE_LOCAL_MK MAKE_ORG_INSTALL MAKE_ORG_VERSION
@@ -91,7 +91,7 @@ local.mk:
 
 all compile::
 	$(foreach dir, doc lisp, $(MAKE) -C $(dir) clean;)
-compile compile-dirty::
+compile compile-dirty:: checkpkg
 	$(MAKE) -C lisp $@
 all clean-install::
 	$(foreach dir, $(SUBDIRS), $(MAKE) -C $(dir) $@;)
@@ -100,13 +100,22 @@ vanilla:
 	-@$(NOBATCH) &
 
 check test::	compile
-check test test-dirty::
+check test test-dirty:: checkpkg
 	-$(MKDIR) $(testdir)
 	TMPDIR=$(testdir) $(BTEST)
 ifeq ($(TEST_NO_AUTOCLEAN),) # define this variable to leave $(testdir) around for inspection
 	$(MAKE) cleantest
 endif
 
+checkpkg:
+	$(BATCHO) --eval '(load "../mk/org-dependencies.el")' \
+	  -f org-dependencies-installed-p
+
+uppkg:
+	$(info ========= Installing required third-party packages)
+	$(BATCHO) --eval '(load "../mk/org-dependencies.el")' \
+	  -f org-install-dependencies
+
 up0 up1 up2::
 	git checkout $(GIT_BRANCH)
 	git remote update
@@ -130,7 +139,7 @@ $(INSTSUB):
 autoloads: lisp
 	$(MAKE) -C $< $@
 
-repro: cleanall autoloads
+repro: cleanall checkpkg autoloads
 	-@$(REPRO) &
 
 $(GITDIR):
@@ -150,7 +159,7 @@ cleandirs:
 
 clean:	cleanlisp cleandoc
 
-cleanall: cleandirs cleantest cleangithooks
+cleanall: cleandirs cleantest cleangithooks cleanpkg
 	-$(FIND) . \( -name \*~ -o -name \*# -o -name .#\* \) -exec $(RM) {} +
 	-$(FIND) $(CLEANDIRS) \( -name \*~ -o -name \*.elc \) -exec $(RM) {} +
 
@@ -175,3 +184,6 @@ cleantest:
 	  $(FIND) $(testdir) -type d -exec $(CHMOD) u+w {} + && \
 	  $(RMR) $(testdir) ; \
 	}
+
+cleanpkg:
+	-$(RMR) $(pkgdir_top)
-- 
2.54.0

>From 581d644b4e9dfd822f38133e321922c8ea6bc6d2 Mon Sep 17 00:00:00 2001
From: Morgan Smith <[email protected]>
Date: Fri, 15 May 2026 16:40:48 -0400
Subject: [PATCH 3/7] Add compat.el dependency

* lisp/org.el: Add Compat to package-requires.
* mk/org-dependencies.el (org-dependencies): Add compat to dependency
list.

Co-authored-by: Ihor Radchenko <[email protected]>
---
 lisp/org.el            | 2 +-
 mk/org-dependencies.el | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/lisp/org.el b/lisp/org.el
index b418bd7ec..05614ac1e 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -7,7 +7,7 @@
 ;; Maintainer: Ihor Radchenko <[email protected]>
 ;; Keywords: outlines, hypermedia, calendar, text
 ;; URL: https://orgmode.org
-;; Package-Requires: ((emacs "28.2"))
+;; Package-Requires: ((emacs "28.2") (compat "31"))
 
 ;; Version: 10.0-pre
 
diff --git a/mk/org-dependencies.el b/mk/org-dependencies.el
index b6b060868..bd4cabed3 100644
--- a/mk/org-dependencies.el
+++ b/mk/org-dependencies.el
@@ -26,7 +26,7 @@
 
 (require 'package)
 
-(defconst org-dependencies '()
+(defconst org-dependencies '((compat . (31)))
   "An alist of dependencies of org.
 Each item is of the form (PACKAGE . MINIMUM-VERSION).")
 
-- 
2.54.0

>From e41c7d8713fcbd697fc826c05f46d0fe0f0e6853 Mon Sep 17 00:00:00 2001
From: Morgan Smith <[email protected]>
Date: Mon, 18 May 2026 23:19:26 -0400
Subject: [PATCH 4/7] lisp/org-compat.el: Use compat and obsolete old functions

* lisp/org-compat.el: Require 'compat.

Obsolete org-* compatibility functions that are already available in
compat.el: `org-file-has-changed-p', `org-string-equal-ignore-case',
`org-file-name-concat', `org-directory-empty-p',
`org-string-clean-whitespace', `org-format-prompt', `org-xor',
`org-string-distance', `org-buffer-hash', `org-list-of-strings-p',
`org-assoc-delete-all', `org--flatten-tree',
`org-completion-table-with-metadata', `org-babel-local-file-name'

Co-authored-by: Ihor Radchenko <[email protected]>
---
 lisp/org-compat.el | 254 ++++++---------------------------------------
 1 file changed, 34 insertions(+), 220 deletions(-)

diff --git a/lisp/org-compat.el b/lisp/org-compat.el
index 5e62133ad..7ad49121b 100644
--- a/lisp/org-compat.el
+++ b/lisp/org-compat.el
@@ -34,6 +34,8 @@
 (require 'seq)
 (require 'org-macs)
 
+(require 'compat)
+
 (eval-when-compile (require 'subr-x))  ; Emacs < 28
 
 (org-assert-version)
@@ -81,9 +83,6 @@
 (declare-function org-fold-show-all "org-fold" (&optional types))
 (declare-function org-fold-show-children "org-fold" (&optional level))
 (declare-function org-fold-show-entry "org-fold" (&optional hide-drawers))
-;; `org-string-equal-ignore-case' is in _this_ file but isn't at the
-;; top-level.
-(declare-function org-string-equal-ignore-case "org-compat" (string1 string2))
 (declare-function decoded-time-add "time-date" (time delta))
 
 (defvar calendar-mode-map)
@@ -97,18 +96,39 @@ org-table-tab-recognizes-table.el
 (defvar org-table1-hline-regexp)
 (defvar org-fold-core-style)
 
+;; Obsolete compatibility wrappers used before inclusion of compat.el.
+
+(define-obsolete-function-alias 'org-file-has-changed-p
+  'file-has-changed-p "10.0")
+(define-obsolete-function-alias 'org-string-equal-ignore-case
+  'string-equal-ignore-case "10.0")
+(define-obsolete-function-alias 'org-file-name-concat
+  'file-name-concat "10.0")
+(define-obsolete-function-alias 'org-directory-empty-p
+  'directory-empty-p "10.0")
+(define-obsolete-function-alias 'org-string-clean-whitespace
+  'string-clean-whitespace "10.0")
+(define-obsolete-function-alias 'org-format-prompt
+  'format-prompt "10.0")
+(define-obsolete-function-alias 'org-xor
+  'xor "10.0")
+(define-obsolete-function-alias 'org-string-distance
+  'string-distance "10.0")
+(define-obsolete-function-alias 'org-buffer-hash
+  'buffer-hash "10.0")
+(define-obsolete-function-alias 'org-list-of-strings-p
+  'list-of-strings-p "10.0")
+(define-obsolete-function-alias 'org-assoc-delete-all
+  'assoc-delete-all "10.0")
+(define-obsolete-function-alias 'org--flatten-tree
+  'flatten-tree "10.0")
+(define-obsolete-function-alias 'org-completion-table-with-metadata
+  'completion-table-with-metadata "10.0")
+(define-obsolete-function-alias 'org-babel-local-file-name
+  'file-local-name "10.0")
+
 
 ;;; Emacs < 31 compatibility
-(if (fboundp 'completion-table-with-metadata)
-    (defalias 'org-completion-table-with-metadata #'completion-table-with-metadata)
-  (defun org-completion-table-with-metadata (table metadata)
-    "Return new completion TABLE with METADATA.
-METADATA should be an alist of completion metadata.  See
-`completion-metadata' for a list of supported metadata."
-    (lambda (string pred action)
-      (if (eq action 'metadata)
-          `(metadata . ,metadata)
-        (complete-with-action action table string pred)))))
 
 ;; Broken for negative months before emacs commit bc33b70b280
 (if (version< "31" emacs-version)
@@ -147,40 +167,6 @@ org-fold-core-style
       (delete-other-windows window)
       window)))
 
-(defvar org-file-has-changed-p--hash-table (make-hash-table :test #'equal)
-  "Internal variable used by `org-file-has-changed-p'.")
-
-(if (fboundp 'file-has-changed-p)
-    (defalias 'org-file-has-changed-p #'file-has-changed-p)
-  (defun org-file-has-changed-p (file &optional tag)
-    "Return non-nil if FILE has changed.
-The size and modification time of FILE are compared to the size
-and modification time of the same FILE during a previous
-invocation of `org-file-has-changed-p'.  Thus, the first invocation
-of `org-file-has-changed-p' always returns non-nil when FILE exists.
-The optional argument TAG, which must be a symbol, can be used to
-limit the comparison to invocations with identical tags; it can be
-the symbol of the calling function, for example."
-    (let* ((file (directory-file-name (expand-file-name file)))
-           (remote-file-name-inhibit-cache t)
-           (fileattr (file-attributes file 'integer))
-	   (attr (and fileattr
-                      (cons (file-attribute-size fileattr)
-		            (file-attribute-modification-time fileattr))))
-	   (sym (concat (symbol-name tag) "@" file))
-	   (cachedattr (gethash sym org-file-has-changed-p--hash-table)))
-      (when (not (equal attr cachedattr))
-        (puthash sym attr org-file-has-changed-p--hash-table)))))
-
-(if (fboundp 'string-equal-ignore-case)
-    (defalias 'org-string-equal-ignore-case #'string-equal-ignore-case)
-  ;; From Emacs subr.el.
-  (defun org-string-equal-ignore-case (string1 string2)
-    "Like `string-equal', but case-insensitive.
-Upper-case and lower-case letters are treated as equal.
-Unibyte strings are converted to multibyte for comparison."
-    (eq t (compare-strings string1 0 nil string2 0 nil t))))
-
 (defun org-buffer-text-pixel-width ()
   "Return pixel width of text in current buffer.
 This function uses `buffer-text-pixel-size', when available, and falls
@@ -243,77 +229,6 @@ org-buffer-text-pixel-width
 Ignore optional argument."
     (get-buffer-create buffer-or-name)))
 
-(if (fboundp 'file-name-concat)
-    (defalias 'org-file-name-concat #'file-name-concat)
-  (defun org-file-name-concat (directory &rest components)
-    "Append COMPONENTS to DIRECTORY and return the resulting string.
-
-Elements in COMPONENTS must be a string or nil.
-DIRECTORY or the non-final elements in COMPONENTS may or may not end
-with a slash -- if they don't end with a slash, a slash will be
-inserted before concatenating."
-    (save-match-data
-      (mapconcat
-       #'identity
-       (delq nil
-             (mapcar
-              (lambda (str)
-                (when (and str (not (seq-empty-p str))
-                           (string-match "\\(.+?\\)/?$" str))
-                  (match-string 1 str)))
-              (cons directory components)))
-       "/"))))
-
-(if (fboundp 'directory-empty-p)
-    (defalias 'org-directory-empty-p #'directory-empty-p)
-  (defun org-directory-empty-p (dir)
-    "Return t if DIR names an existing directory containing no other files."
-    (and (file-directory-p dir)
-         (null (directory-files dir nil directory-files-no-dot-files-regexp t)))))
-
-(if (fboundp 'string-clean-whitespace)
-    (defalias 'org-string-clean-whitespace #'string-clean-whitespace)
-  ;; From Emacs subr-x.el.
-  (defun org-string-clean-whitespace (string)
-    "Clean up whitespace in STRING.
-All sequences of whitespaces in STRING are collapsed into a
-single space character, and leading/trailing whitespace is
-removed."
-    (let ((blank "[[:blank:]\r\n]+"))
-      (string-trim (replace-regexp-in-string blank " " string t t)
-                   blank blank))))
-
-(if (fboundp 'format-prompt)
-    (defalias 'org-format-prompt #'format-prompt)
-  ;; From Emacs minibuffer.el, inlining
-  ;; `minibuffer-default-prompt-format' value and replacing `length<'
-  ;; (both new in Emacs 28.1).
-  (defun org-format-prompt (prompt default &rest format-args)
-    "Compatibility substitute for `format-prompt'."
-    (concat
-     (if (null format-args)
-         prompt
-       (apply #'format prompt format-args))
-     (and default
-          (or (not (stringp default))
-              (> (length default) 0))
-          (format " (default %s)"
-                  (if (consp default)
-                      (car default)
-                    default)))
-     ": ")))
-
-(if (fboundp 'list-of-strings-p)
-    (defalias 'org-list-of-strings-p #'list-of-strings-p)
-  ;; From Emacs subr.el.
-;;;###autoload
-  (defun org-list-of-strings-p (object)
-    "Return t if OBJECT is nil or a list of strings."
-    (declare (pure t) (side-effect-free error-free))
-    (while (and (consp object) (stringp (car object)))
-      (setq object (cdr object)))
-    (null object)))
-
 
 ;;; Emacs < 27.1 compatibility
 
@@ -325,25 +240,6 @@ org-buffer-text-pixel-width
       `(progn ,@body))
   (defalias 'org-combine-change-calls 'combine-change-calls))
 
-;; `flatten-tree' was added in Emacs 27.1.
-(if (fboundp 'flatten-tree)
-    (defalias 'org--flatten-tree #'flatten-tree)
-  ;; The implementation is taken from Emacs subr.el 8664ba18c7c5.
-  (defun org--flatten-tree (tree)
-    "Return a \"flattened\" copy of TREE.
-
-A `flatten-tree' polyfill for compatibility with Emacs versions
-older than 27.1"
-    (let (elems)
-      (while (consp tree)
-        (let ((elem (pop tree)))
-          (while (consp elem)
-            (push (cdr elem) tree)
-            (setq elem (car elem)))
-          (if elem (push elem elems))))
-      (if tree (push tree elems))
-      (nreverse elems))))
-
 (with-no-warnings ; `replace-buffer-contents' is obsolete in Emacs 31
   (cond
    ((version< emacs-version "27.1")
@@ -355,22 +251,6 @@ org-buffer-text-pixel-width
     (defsubst org-replace-buffer-contents (source &optional max-secs max-costs)
       (replace-region-contents (point-min) (point-max) source max-secs max-costs)))))
 
-(unless (fboundp 'proper-list-p)
-  ;; `proper-list-p' was added in Emacs 27.1.  The function below is
-  ;; taken from Emacs subr.el 200195e824b^.
-  (defun proper-list-p (object)
-    "Return OBJECT's length if it is a proper list, nil otherwise.
-A proper list is neither circular nor dotted (i.e., its last cdr
-is nil)."
-    (and (listp object) (ignore-errors (length object)))))
-
-(if (fboundp 'xor)
-    ;; `xor' was added in Emacs 27.1.
-    (defalias 'org-xor #'xor)
-  (defsubst org-xor (a b)
-    "Exclusive `or'."
-    (if a (not b) b)))
-
 (unless (fboundp 'pcomplete-uniquify-list)
   ;; The misspelled variant was made obsolete in Emacs 27.1
   (defalias 'pcomplete-uniquify-list 'pcomplete-uniqify-list))
@@ -400,30 +280,7 @@ org--set-faces-extend
   (when (fboundp 'set-face-extend)
     (mapc (lambda (f) (set-face-extend f extend-p)) faces)))
 
-(if (fboundp 'string-distance)
-    (defalias 'org-string-distance 'string-distance)
-  (defun org-string-distance (s1 s2)
-    "Return the edit (levenshtein) distance between strings S1 S2."
-    (let* ((l1 (length s1))
-	   (l2 (length s2))
-	   (dist (vconcat (mapcar (lambda (_) (make-vector (1+ l2) nil))
-				  (number-sequence 1 (1+ l1)))))
-	   (in (lambda (i j) (aref (aref dist i) j))))
-      (setf (aref (aref dist 0) 0) 0)
-      (dolist (j (number-sequence 1 l2))
-        (setf (aref (aref dist 0) j) j))
-      (dolist (i (number-sequence 1 l1))
-        (setf (aref (aref dist i) 0) i)
-        (dolist (j (number-sequence 1 l2))
-	  (setf (aref (aref dist i) j)
-	        (min
-	         (1+ (funcall in (1- i) j))
-	         (1+ (funcall in i (1- j)))
-	         (+ (if (equal (aref s1 (1- i)) (aref s2 (1- j))) 0 1)
-		    (funcall in (1- i) (1- j)))))))
-      (funcall in l1 l2))))
-
-(define-obsolete-function-alias 'org-babel-edit-distance 'org-string-distance
+(define-obsolete-function-alias 'org-babel-edit-distance 'string-distance
   "9.5")
 
 (unless (fboundp 'with-connection-local-variables)
@@ -436,24 +293,6 @@ 'org-babel-edit-distance
     `(with-connection-local-profiles (connection-local-get-profiles nil)
        ,@body)))
 
-;; assoc-delete-all missing from 26.1
-(if (fboundp 'assoc-delete-all)
-    (defalias 'org-assoc-delete-all 'assoc-delete-all)
-  ;; from compat/compat-27.el
-  (defun org-assoc-delete-all (key alist &optional test)
-    "Delete all matching key from alist, default test equal"
-    (unless test (setq test #'equal))
-    (while (and (consp (car alist))
-		(funcall test (caar alist) key))
-      (setq alist (cdr alist)))
-    (let ((tail alist) tail-cdr)
-      (while (setq tail-cdr (cdr tail))
-	(if (and (consp (car tail-cdr))
-		 (funcall test (caar tail-cdr) key))
-            (setcdr tail (cdr tail-cdr))
-          (setq tail tail-cdr))))
-    alist))
-
 
 ;;; Emacs < 26.1 compatibility
 
@@ -461,23 +300,6 @@ 'org-babel-edit-distance
     (defalias 'org-line-number-display-width 'line-number-display-width)
   (defun org-line-number-display-width (&rest _) 0))
 
-(if (fboundp 'buffer-hash)
-    (defalias 'org-buffer-hash 'buffer-hash)
-  (defun org-buffer-hash () (md5 (current-buffer))))
-
-(unless (fboundp 'file-attribute-modification-time)
-  (defsubst file-attribute-modification-time (attributes)
-    "The modification time in ATTRIBUTES returned by `file-attributes'.
-This is the time of the last change to the file's contents, and
-is a Lisp timestamp in the same style as `current-time'."
-    (nth 5 attributes)))
-
-(unless (fboundp 'file-attribute-size)
-  (defsubst file-attribute-size (attributes)
-    "The size (in bytes) in ATTRIBUTES returned by `file-attributes'.
-This is a floating point number if the size is too large for an integer."
-    (nth 7 attributes)))
-
 
 ;;; Obsolete aliases (remove them after the next major release).
 
@@ -1626,14 +1448,6 @@ org-kill-new
                           string)
   (apply 'kill-new string args))
 
-;; `file-local-name' was added in Emacs 26.1.
-(defalias 'org-babel-local-file-name
-  (if (fboundp 'file-local-name)
-      'file-local-name
-    (lambda (file)
-      "Return the local name component of FILE."
-      (or (file-remote-p file 'localname) file))))
-
 ;;;###autoload
 (defmacro org-check-version ()
   "Try very hard to provide sensible version strings."
-- 
2.54.0

>From 025e97204c5e7b5c1ba647f5cb788c2c0f69cc88 Mon Sep 17 00:00:00 2001
From: Ihor Radchenko <[email protected]>
Date: Mon, 3 Apr 2023 10:41:50 +0200
Subject: [PATCH 5/7] Use compat.el library instead of ad-hoc compatibility
 function set

* lisp/org.el (org-fill-paragraph):
* lisp/ob-core.el (org-babel-results-keyword)
(org-babel-check-src-block, org-babel-insert-result)
(org-babel-process-file-name):
* lisp/ob-gnuplot.el (org-babel-gnuplot-process-vars):
* lisp/oc-basic.el (org-cite-basic--parse-bibliography)
(org-cite-basic--close-keys):
* lisp/oc.el (org-cite-adjust-note):
* lisp/ol-gnus.el (org-gnus-group-link, org-gnus-article-link)
(org-gnus-store-link):
* lisp/ol.el (org-store-link, org-insert-link):
* lisp/org-attach.el (org-attach-sync):
* lisp/org-capture.el (org-capture-place-item)
(org-capture-fill-template):
* lisp/org-colview.el
(org-columns--summary-types-completion-function):
* lisp/org-element.el (org-element-drawer-parser):
* lisp/org-fold-core.el (org-fold-core-next-visibility-change):
* lisp/org-lint.el (org-lint-duplicate-custom-id):
* lisp/org-num.el (org-num-skip-tags):
* lisp/org-persist.el (org-persist--load-index, org-persist-write:file)
(org-persist-write:url, org-persist-write:index)
(org-persist--merge-index-with-disk, org-persist--merge-index)
(org-persist-read, org-persist-write, org-persist-write-all)
(org-persist--gc-persist-file, org-persist--refresh-gc-lock)
(org-persist--gc-orphan-p, org-persist-gc):
* lisp/org-protocol.el (org-protocol-flatten-greedy)
(org-protocol-flatten):
* lisp/org-src.el (org-src--get-known-shells):
* lisp/org-refile.el (org-refile-get-location):
* lisp/ox.el (org-export-resolve-radio-link):
* testing/lisp/test-ol.el (test-org-link/toggle-link-display):
* testing/lisp/test-org-capture.el (test-org-capture/abort):
Use functions provided by compat.el.

Link: https://orgmode.org/list/87v8ks6rhf.fsf@localhost

Co-authored-by: Ihor Radchenko <[email protected]>
---
 lisp/ob-core.el                  | 16 ++++++------
 lisp/ob-gnuplot.el               |  2 +-
 lisp/oc-basic.el                 |  6 ++---
 lisp/oc.el                       |  2 +-
 lisp/ol-gnus.el                  |  8 +++---
 lisp/ol.el                       |  6 ++---
 lisp/org-attach.el               |  2 +-
 lisp/org-capture.el              |  8 +++---
 lisp/org-colview.el              |  2 +-
 lisp/org-element.el              |  2 +-
 lisp/org-fold-core.el            |  2 +-
 lisp/org-lint.el                 |  2 +-
 lisp/org-num.el                  |  2 +-
 lisp/org-persist.el              | 44 ++++++++++++++++----------------
 lisp/org-protocol.el             |  4 +--
 lisp/org-refile.el               |  2 +-
 lisp/org-src.el                  |  2 +-
 lisp/org.el                      |  4 +--
 lisp/ox.el                       |  6 ++---
 testing/lisp/test-ol.el          | 10 ++++----
 testing/lisp/test-org-capture.el |  2 +-
 21 files changed, 67 insertions(+), 67 deletions(-)

diff --git a/lisp/ob-core.el b/lisp/ob-core.el
index f55b91244..cbfc83685 100644
--- a/lisp/ob-core.el
+++ b/lisp/ob-core.el
@@ -144,7 +144,7 @@ org-babel-results-keyword
   :type 'string
   :safe (lambda (v)
 	  (and (stringp v)
-	       (org-string-equal-ignore-case "RESULTS" v))))
+	       (string-equal-ignore-case "RESULTS" v))))
 
 (defcustom org-babel-noweb-wrap-start "<<"
   "String used to begin a noweb reference in a code block.
@@ -1059,7 +1059,7 @@ org-babel-check-src-block
 				   (match-string 4))))))
       (dolist (name names)
 	(when (and (not (string= header name))
-		   (<= (org-string-distance header name) too-close)
+		   (<= (string-distance header name) too-close)
 		   (not (member header names)))
 	  (error "Supplied header \"%S\" is suspiciously close to \"%S\""
 		 header name))))
@@ -2746,13 +2746,13 @@ org-babel-insert-result
 			   (closing-line (concat "#+end_" type)))
 		      (cond
                        ;; Do nothing if type is "no" or "nil"
-                       ((or (org-string-equal-ignore-case type "nil")
-                            (org-string-equal-ignore-case type "no"))
+                       ((or (string-equal-ignore-case type "nil")
+                            (string-equal-ignore-case type "no"))
                         nil)
 		       ;; Escape contents from "export" wrap.  Wrap
 		       ;; inline results within an export snippet with
 		       ;; appropriate value.
-		       ((org-string-equal-ignore-case type "export")
+		       ((string-equal-ignore-case type "export")
 			(let ((backend (pcase split
 					 (`(,_) "none")
 					 (`(,_ ,b . ,_) b))))
@@ -2763,14 +2763,14 @@ org-babel-insert-result
 					   backend) "@@)}}}")))
 		       ;; Escape contents from "example" wrap.  Mark
 		       ;; inline results as verbatim.
-		       ((org-string-equal-ignore-case type "example")
+		       ((string-equal-ignore-case type "example")
 			(funcall wrap
 				 opening-line closing-line
 				 nil nil
 				 "{{{results(=" "=)}}}"))
 		       ;; Escape contents from "src" wrap.  Mark
 		       ;; inline results as inline source code.
-		       ((org-string-equal-ignore-case type "src")
+		       ((string-equal-ignore-case type "src")
 			(let ((inline-open
 			       (pcase split
 				 (`(,_)
@@ -3560,7 +3560,7 @@ org-babel-process-file-name
 remotely.  The file name is then processed by `expand-file-name'.
 Unless second argument NO-QUOTE-P is non-nil, the file name is
 additionally processed by `shell-quote-argument'."
-  (let ((f (org-babel-local-file-name (expand-file-name name))))
+  (let ((f (file-local-name (expand-file-name name))))
     (if no-quote-p f (shell-quote-argument f))))
 
 (defvar org-babel-temporary-directory
diff --git a/lisp/ob-gnuplot.el b/lisp/ob-gnuplot.el
index eda902f9e..33e2c23ef 100644
--- a/lisp/ob-gnuplot.el
+++ b/lisp/ob-gnuplot.el
@@ -109,7 +109,7 @@ org-babel-gnuplot-process-vars
 				    (org-babel-temp-directory)
 				    "/gnuplot/"
 				    (file-remote-p val 'host)
-				    (org-babel-local-file-name val))))
+				    (file-local-name val))))
 		  (if (and (file-exists-p local-name) ;; only download file if remote is newer
 			   (file-newer-than-file-p local-name val))
 		      local-name
diff --git a/lisp/oc-basic.el b/lisp/oc-basic.el
index c57e8761e..ecf084922 100644
--- a/lisp/oc-basic.el
+++ b/lisp/oc-basic.el
@@ -312,11 +312,11 @@ org-cite-basic--parse-bibliography
         (setq file (file-truename file))
         (when (file-readable-p file)
           (with-temp-buffer
-            (when (or (org-file-has-changed-p file)
+            (when (or (file-has-changed-p file)
                       (not (gethash file org-cite-basic--file-id-cache)))
               (insert-file-contents file)
               (set-visited-file-name file t)
-              (puthash file (org-buffer-hash) org-cite-basic--file-id-cache))
+              (puthash file (buffer-hash) org-cite-basic--file-id-cache))
             (condition-case nil
                 (unwind-protect
 	            (let* ((file-id (cons file (gethash file org-cite-basic--file-id-cache)))
@@ -570,7 +570,7 @@ org-cite-basic--close-keys
   "List cite keys close to KEY in terms of string distance."
   (seq-filter (lambda (k)
                 (>= org-cite-basic-max-key-distance
-                    (org-string-distance k key)))
+                   (string-distance k key)))
               keys))
 
 (defun org-cite-basic--set-keymap (beg end suggestions)
diff --git a/lisp/oc.el b/lisp/oc.el
index 31d52edbc..dda73474f 100644
--- a/lisp/oc.el
+++ b/lisp/oc.el
@@ -1049,7 +1049,7 @@ org-cite-adjust-note
                              (match-string 3 previous)))))
       ;; Bail you when there is no quote and either no punctuation, or
       ;; punctuation on both sides.
-      (when (or quote (org-xor punct final-punct))
+      (when (or quote (xor punct final-punct))
         ;; Phase 1: handle punctuation rule.
         (pcase rule
           ((guard (not quote)) nil)
diff --git a/lisp/ol-gnus.el b/lisp/ol-gnus.el
index 1964efdd7..987a7ed17 100644
--- a/lisp/ol-gnus.el
+++ b/lisp/ol-gnus.el
@@ -97,8 +97,8 @@ org-gnus-group-link
 `org-gnus-prefer-web-links' is reversed."
   (let ((unprefixed-group (replace-regexp-in-string "^[^:]+:" "" group)))
     (if (and (string-prefix-p "nntp" group) ;; Only for nntp groups
-	     (org-xor current-prefix-arg
-		      org-gnus-prefer-web-links))
+	     (xor current-prefix-arg
+		  org-gnus-prefer-web-links))
 	(concat "https://groups.google.com/group/"; unprefixed-group)
       (concat "gnus:" group))))
 
@@ -115,7 +115,7 @@ org-gnus-article-link
 
 If `org-store-link' was called with a prefix arg the meaning of
 `org-gnus-prefer-web-links' is reversed."
-  (if (and (org-xor current-prefix-arg org-gnus-prefer-web-links)
+  (if (and (xor current-prefix-arg org-gnus-prefer-web-links)
 	   newsgroups		  ;make web links only for nntp groups
 	   (not x-no-archive))	  ;and if X-No-Archive isn't set
       (format "https://groups.google.com/groups/search?as_umsgid=%s";
@@ -164,7 +164,7 @@ org-gnus-store-link
 	    newsgroups x-no-archive)
        ;; Fetching an article is an expensive operation; newsgroup and
        ;; x-no-archive are only needed for web links.
-       (when (org-xor current-prefix-arg org-gnus-prefer-web-links)
+       (when (xor current-prefix-arg org-gnus-prefer-web-links)
 	 ;; Make sure the original article buffer is up-to-date.
 	 (save-window-excursion (gnus-summary-select-article))
 	 (setq to (or to (gnus-fetch-original-field "To")))
diff --git a/lisp/ol.el b/lisp/ol.el
index 73645fb97..4a0e3a9a3 100644
--- a/lisp/ol.el
+++ b/lisp/ol.el
@@ -2561,8 +2561,8 @@ org-store-link
 	    (set-mark (point)))))
     (setq org-store-link-plist nil)
     ;; Negate `org-link-context-for-files' when given a single universal arg.
-    (let ((org-link-context-for-files (org-xor org-link-context-for-files
-                                               (equal arg '(4))))
+    (let ((org-link-context-for-files (xor org-link-context-for-files
+                                           (equal arg '(4))))
           link desc search agenda-link) ;; description
       (cond
        ;; Store a link using an external link type, if any function is
@@ -2850,7 +2850,7 @@ org-insert-link
 			 org-link--insert-history)))
 	    (setq link
 		  (org-completing-read
-                   (org-format-prompt "Insert link" (caar org-stored-links))
+                   (format-prompt "Insert link" (caar org-stored-links))
 		   (append
 		    (mapcar (lambda (x) (concat x ":")) all-prefixes)
 		    (mapcar #'car org-stored-links)
diff --git a/lisp/org-attach.el b/lisp/org-attach.el
index f44340fc7..07366afb0 100644
--- a/lisp/org-attach.el
+++ b/lisp/org-attach.el
@@ -732,7 +732,7 @@ org-attach-sync
       (let ((files (org-attach-file-list attach-dir)))
 	(org-attach-tag (not files)))
       (when org-attach-sync-delete-empty-dir
-        (when (and (org-directory-empty-p attach-dir)
+        (when (and (directory-empty-p attach-dir)
                    (if (eq 'query org-attach-sync-delete-empty-dir)
                        (yes-or-no-p "Attachment directory is empty.  Delete?")
                      t))
diff --git a/lisp/org-capture.el b/lisp/org-capture.el
index 9712244df..c9c1497e5 100644
--- a/lisp/org-capture.el
+++ b/lisp/org-capture.el
@@ -1444,9 +1444,9 @@ org-capture-place-item
 	      ;; prioritize the existing list.
 	      (when prepend?
 		(let ((ordered? (eq 'ordered (org-element-property :type item))))
-		  (when (org-xor ordered?
-				 (string-match-p "\\`[A-Za-z0-9]\\([.)]\\)"
-						 template))
+		  (when (xor ordered?
+			     (string-match-p "\\`[A-Za-z0-9]\\([.)]\\)"
+					     template))
 		    (org-cycle-list-bullet (if ordered? "1." "-")))))
 	      ;; Eventually repair the list for proper indentation and
 	      ;; bullets.
@@ -2014,7 +2014,7 @@ org-capture-fill-template
 		           (setq org-capture--prompt-history
 			         (gethash prompt org-capture--prompt-history-table))
                            (push (org-completing-read
-                                  (org-format-prompt (or prompt "Enter string") default)
+                                  (format-prompt (or prompt "Enter string") default)
 			          completions
 			          nil nil nil 'org-capture--prompt-history default)
 			         strings)
diff --git a/lisp/org-colview.el b/lisp/org-colview.el
index efee7cc6e..923c974fd 100644
--- a/lisp/org-colview.el
+++ b/lisp/org-colview.el
@@ -1042,7 +1042,7 @@ org-columns--summary-types-completion-function
 		   (doc (and doc (substring doc 0 (string-search "\n" doc)))))
 	      (if doc (format " -- %s" doc) ""))))
 	 (completion-table
-	  (org-completion-table-with-metadata
+	  (completion-table-with-metadata
 	   (lambda (str predicate action)
 	     (complete-with-action action candidates str predicate))
 	   `(metadata . ((annotation-function . ,annotation-function))))))
diff --git a/lisp/org-element.el b/lisp/org-element.el
index 313922ecc..fccd31884 100644
--- a/lisp/org-element.el
+++ b/lisp/org-element.el
@@ -1059,7 +1059,7 @@ org-element-drawer-parser
                   (and contents-begin
                        ;; We might be dealing with broken properties
                        ;; drawer. Every change inside is sensitive.
-                       (not (org-string-equal-ignore-case name "PROPERTIES"))
+                       (not (string-equal-ignore-case name "PROPERTIES"))
                        ;; Inserting blank line at contents-begin
                        ;; will trigger :pre-blank change and may not
                        ;; be robust.
diff --git a/lisp/org-fold-core.el b/lisp/org-fold-core.el
index 5aef5002f..a9222b0a1 100644
--- a/lisp/org-fold-core.el
+++ b/lisp/org-fold-core.el
@@ -868,7 +868,7 @@ org-fold-core-next-visibility-change
 			  (lambda (p) (next-single-char-property-change p 'invisible nil limit)))))
 	 (next pos))
     (while (and (funcall cmp next limit)
-		(not (org-xor
+		(not (xor
                     invisible-initially?
                     (funcall invisible-p
                              (if previous-p
diff --git a/lisp/org-lint.el b/lisp/org-lint.el
index df5c0d578..d5fe02100 100644
--- a/lisp/org-lint.el
+++ b/lisp/org-lint.el
@@ -424,7 +424,7 @@ org-lint-duplicate-custom-id
    ast
    'node-property
    (lambda (property)
-     (and (org-string-equal-ignore-case
+     (and (string-equal-ignore-case
            "CUSTOM_ID" (org-element-property :key property))
 	  (org-element-property :value property)))
    (lambda (property _) (org-element-begin property))
diff --git a/lisp/org-num.el b/lisp/org-num.el
index 7e07300ff..d2dd77a1c 100644
--- a/lisp/org-num.el
+++ b/lisp/org-num.el
@@ -140,7 +140,7 @@ org-num-skip-tags
   :group 'org-appearance
   :package-version '(Org . "9.3")
   :type '(repeat (string :tag "Tag"))
-  :safe #'org-list-of-strings-p)
+  :safe #'list-of-strings-p)
 
 ;;;###autoload
 (defcustom org-num-skip-unnumbered nil
diff --git a/lisp/org-persist.el b/lisp/org-persist.el
index 75f09b1e0..fd538b439 100644
--- a/lisp/org-persist.el
+++ b/lisp/org-persist.el
@@ -277,12 +277,12 @@ org-persist
 
 (defcustom org-persist-directory
   (expand-file-name
-   (org-file-name-concat
+   (file-name-concat
     (let ((cache-dir (when (fboundp 'xdg-cache-home)
                        (xdg-cache-home))))
       (if (or (seq-empty-p cache-dir)
               (not (file-exists-p cache-dir))
-              (file-exists-p (org-file-name-concat
+              (file-exists-p (file-name-concat
                               user-emacs-directory
                               "org-persist")))
           user-emacs-directory
@@ -746,13 +746,13 @@ 'org-persist-read:version
 
 (defun org-persist-read:file (_ path __)
   "Read file container from PATH."
-  (when (and path (file-exists-p (org-file-name-concat org-persist-directory path)))
-    (org-file-name-concat org-persist-directory path)))
+  (when (and path (file-exists-p (file-name-concat org-persist-directory path)))
+    (file-name-concat org-persist-directory path)))
 
 (defun org-persist-read:url (_ path __)
   "Read file container from PATH."
-  (when (and path (file-exists-p (org-file-name-concat org-persist-directory path)))
-    (org-file-name-concat org-persist-directory path)))
+  (when (and path (file-exists-p (file-name-concat org-persist-directory path)))
+    (file-name-concat org-persist-directory path)))
 
 (defun org-persist-read:index (cont index-file _)
   "Read index container CONT from INDEX-FILE."
@@ -822,7 +822,7 @@ org-persist--load-index
   "Load `org-persist--index'."
   (org-persist-load:index
    `(index ,org-persist--storage-version)
-   (org-file-name-concat org-persist-directory org-persist-index-file)
+   (file-name-concat org-persist-directory org-persist-index-file)
    nil))
 
 ;;;; Writing container data
@@ -883,7 +883,7 @@ org-persist-write:file
         (setq path (cadr c)))
       (let* ((persist-file (plist-get collection :persist-file))
              (ext (file-name-extension path))
-             (file-copy (org-file-name-concat
+             (file-copy (file-name-concat
                          org-persist-directory
                          (format "%s-%s.%s" persist-file (md5 path) ext))))
         (unless (file-exists-p file-copy)
@@ -899,7 +899,7 @@ org-persist-write:url
       (when (cadr c) (setq path (cadr c)))
       (let* ((persist-file (plist-get collection :persist-file))
              (ext (file-name-extension path))
-             (file-copy (org-file-name-concat
+             (file-copy (file-name-concat
                          org-persist-directory
                          (format "%s-%s.%s" persist-file (md5 path) ext))))
         (unless (file-exists-p file-copy)
@@ -938,7 +938,7 @@ org-persist-write:index
        (org-persist--check-write-access org-persist-directory))))
   (when (file-exists-p org-persist-directory)
     (let ((index-file
-           (org-file-name-concat org-persist-directory org-persist-index-file)))
+           (file-name-concat org-persist-directory org-persist-index-file)))
       (org-persist--merge-index-with-disk)
       (org-persist--write-elisp-file index-file org-persist--index t nil)
       (setq org-persist--index-age
@@ -954,7 +954,7 @@ org-persist--merge-index-with-disk
   "Merge `org-persist--index' with the current index file on disk."
   (org-persist--load-index)
   (let* ((index-file
-          (org-file-name-concat org-persist-directory org-persist-index-file))
+          (file-name-concat org-persist-directory org-persist-index-file))
          (disk-index
           (and (file-exists-p index-file)
                (org-file-newer-than-p index-file org-persist--index-age)
@@ -980,7 +980,7 @@ org-persist--merge-index
           (dolist (item (nreverse new))
             (unless (or (memq 'index (mapcar #'car (plist-get item :container)))
                         (not (file-exists-p
-                            (org-file-name-concat org-persist-directory
+                            (file-name-concat org-persist-directory
                                                   (plist-get item :persist-file))))
                         (member (plist-get item :persist-file) base-files))
               (push item combined)))
@@ -1086,7 +1086,7 @@ org-persist-read
   (let* ((collection (org-persist--find-index `(:container ,container :associated ,associated)))
          (persist-file
           (when collection
-            (org-file-name-concat
+            (file-name-concat
              org-persist-directory
              (plist-get collection :persist-file))))
          (data nil))
@@ -1176,7 +1176,7 @@ org-persist-write
              (seq-find (lambda (v)
                          (run-hook-with-args-until-success 'org-persist-before-write-hook v associated))
                        (plist-get collection :container)))
-      (let ((file (org-file-name-concat org-persist-directory (plist-get collection :persist-file)))
+      (let ((file (file-name-concat org-persist-directory (plist-get collection :persist-file)))
             (data (mapcar (lambda (c) (cons c (org-persist-write:generic c collection)))
                           (plist-get collection :container))))
         (org-persist--write-elisp-file file data)
@@ -1197,11 +1197,11 @@ org-persist-write-all
            ;; The container is an `index' container.
            (eq 'index (caar (plist-get (car org-persist--index) :container)))
            (or (not (file-exists-p org-persist-directory))
-               (org-directory-empty-p org-persist-directory)))
+               (directory-empty-p org-persist-directory)))
       ;; Do not write anything, and clear up `org-persist-directory' to reduce
       ;; clutter.
       (when (and (file-exists-p org-persist-directory)
-                 (org-directory-empty-p org-persist-directory))
+                 (directory-empty-p org-persist-directory))
         (delete-directory org-persist-directory))
     ;; Write the data.
     (let (all-containers)
@@ -1242,7 +1242,7 @@ org-persist--gc-persist-file
   "Garbage collect PERSIST-FILE."
   (when (file-exists-p persist-file)
     (delete-file persist-file)
-    (when (org-directory-empty-p (file-name-directory persist-file))
+    (when (directory-empty-p (file-name-directory persist-file))
       (delete-directory (file-name-directory persist-file)))))
 
 (defalias 'org-persist-associated-files:elisp #'ignore)
@@ -1266,7 +1266,7 @@ org-persist--refresh-gc-lock
   "Refresh session timestamp in `org-persist-gc-lock-file'.
 Remove expired sessions timestamps."
   (when org-persist--wrote-to-disk
-    (let* ((file (org-file-name-concat org-persist-directory org-persist-gc-lock-file))
+    (let* ((file (file-name-concat org-persist-directory org-persist-gc-lock-file))
            (alist (when (file-exists-p file) (org-persist--read-elisp-file file)))
            new-alist)
       (setf (alist-get before-init-time alist nil nil #'equal)
@@ -1280,9 +1280,9 @@ org-persist--refresh-gc-lock
 (defun org-persist--gc-orphan-p ()
   "Return non-nil, when orphan files should be garbage-collected.
 Remove current sessions from `org-persist-gc-lock-file'."
-  (let* ((file (org-file-name-concat org-persist-directory org-persist-gc-lock-file))
+  (let* ((file (file-name-concat org-persist-directory org-persist-gc-lock-file))
          (alist (when (file-exists-p file) (org-persist--read-elisp-file file))))
-    (setq alist (org-assoc-delete-all before-init-time alist))
+    (setq alist (assoc-delete-all before-init-time alist))
     (ignore-errors (org-persist--write-elisp-file file alist))
     ;; Only GC orphan files when there are no active sessions.
     (not alist)))
@@ -1297,7 +1297,7 @@ org-persist-gc
         (remote-files-num 0)
         (orphan-files
          (when (org-persist--gc-orphan-p) ; also removes current session from lock file.
-           (delete (org-file-name-concat org-persist-directory org-persist-index-file)
+           (delete (file-name-concat org-persist-directory org-persist-index-file)
                    (when (file-exists-p org-persist-directory)
                      (directory-files-recursively org-persist-directory ".+"))))))
     (dolist (collection org-persist--index)
@@ -1305,7 +1305,7 @@ org-persist-gc
              (web-file (and file (string-match-p "\\`https?://" file)))
              (file-remote (when file (file-remote-p file)))
              (persist-file (when (plist-get collection :persist-file)
-                             (org-file-name-concat
+                             (file-name-concat
                               org-persist-directory
                               (plist-get collection :persist-file))))
              (expired? (org-persist--gc-expired-p
diff --git a/lisp/org-protocol.el b/lisp/org-protocol.el
index 21db13e31..82203b25b 100644
--- a/lisp/org-protocol.el
+++ b/lisp/org-protocol.el
@@ -344,7 +344,7 @@ org-protocol-flatten-greedy
 `org-protocol-reverse-list-of-files' was set to t and the returned list will
 reflect that.  emacsclient's first parameter will be the first one in the
 returned list."
-  (let* ((l (org--flatten-tree (if org-protocol-reverse-list-of-files
+  (let* ((l (flatten-tree (if org-protocol-reverse-list-of-files
                               param-list
                             (reverse param-list))))
 	 (trigger (car l))
@@ -370,7 +370,7 @@ org-protocol-flatten-greedy
       l)))
 
 (define-obsolete-function-alias 'org-protocol-flatten
-  (if (fboundp 'flatten-tree) 'flatten-tree 'org--flatten-tree)
+  'flatten-tree
   "9.7"
   "Transform LIST into a flat list.
 
diff --git a/lisp/org-refile.el b/lisp/org-refile.el
index 2c966af96..f4604b2cb 100644
--- a/lisp/org-refile.el
+++ b/lisp/org-refile.el
@@ -692,7 +692,7 @@ org-refile-get-location
          (prompt (let ((default (or (car org-refile-history)
                                     (and (assoc cbnex tbl) (setq cdef cbnex)
                                          cbnex))))
-                   (org-format-prompt prompt default)))
+                   (format-prompt prompt default)))
 	 pa answ parent-target child parent old-hist)
     (setq old-hist org-refile-history)
     (setq answ (funcall cfunc prompt tbl nil (not new-nodes)
diff --git a/lisp/org-src.el b/lisp/org-src.el
index d106cc5c4..aecf708d2 100644
--- a/lisp/org-src.el
+++ b/lisp/org-src.el
@@ -212,7 +212,7 @@ org-src--get-known-shells
 The shells are associated with `sh-mode'."
   (mapcar
    (lambda (shell) (cons (symbol-name shell) 'sh))
-   (delete-dups (org--flatten-tree sh-ancestor-alist))))
+   (delete-dups (flatten-tree sh-ancestor-alist))))
 
 (defcustom org-src-lang-modes
   `(("C" . c)
diff --git a/lisp/org.el b/lisp/org.el
index 05614ac1e..ca6f17280 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -20288,7 +20288,7 @@ org-fill-paragraph
 		 (list (when current-prefix-arg 'full) t))
                org-mode)
   (let ((hash (and (not (buffer-modified-p))
-		   (org-buffer-hash))))
+		   (buffer-hash))))
     (cond
      ((and region transient-mark-mode mark-active
 	   (not (eq (region-beginning) (region-end))))
@@ -20313,7 +20313,7 @@ org-fill-paragraph
     ;; If we didn't change anything in the buffer (and the buffer was
     ;; previously unmodified), then flip the modification status back
     ;; to "unchanged".
-    (when (and hash (equal hash (org-buffer-hash)))
+    (when (and hash (equal hash (buffer-hash)))
       (set-buffer-modified-p nil))
     ;; Return non-nil.
     t))
diff --git a/lisp/ox.el b/lisp/ox.el
index e0782c1e6..2c732f933 100644
--- a/lisp/ox.el
+++ b/lisp/ox.el
@@ -4650,11 +4650,11 @@ org-export-resolve-radio-link
 
 Return value can be a radio-target object or nil.  Assume LINK
 has type \"radio\"."
-  (let ((path (org-string-clean-whitespace (org-element-property :path link))))
+  (let ((path (string-clean-whitespace (org-element-property :path link))))
     (org-element-map (plist-get info :parse-tree) 'radio-target
       (lambda (radio)
-	(and (org-string-equal-ignore-case
-	      (org-string-clean-whitespace (org-element-property :value radio))
+	(and (string-equal-ignore-case
+	      (string-clean-whitespace (org-element-property :value radio))
               path)
 	     radio))
       info 'first-match)))
diff --git a/testing/lisp/test-ol.el b/testing/lisp/test-ol.el
index c8ec09616..0a2c790d2 100644
--- a/testing/lisp/test-ol.el
+++ b/testing/lisp/test-ol.el
@@ -72,19 +72,19 @@ test-org-link/toggle-link-display
         (font-lock-ensure)
         (goto-char 1)
         (re-search-forward "\\[")
-        (should-not (org-xor org-link-descriptive (org-invisible-p)))
+        (should-not (xor org-link-descriptive (org-invisible-p)))
         (re-search-forward "example")
-        (should-not (org-xor org-link-descriptive (org-invisible-p)))
+        (should-not (xor org-link-descriptive (org-invisible-p)))
         (re-search-forward "com")
-        (should-not (org-xor org-link-descriptive (org-invisible-p)))
+        (should-not (xor org-link-descriptive (org-invisible-p)))
         (re-search-forward "]")
-        (should-not (org-xor org-link-descriptive (org-invisible-p)))
+        (should-not (xor org-link-descriptive (org-invisible-p)))
         (re-search-forward "\\[")
         (should-not (org-invisible-p))
         (re-search-forward "link")
         (should-not (org-invisible-p))
         (re-search-forward "]")
-        (should-not (org-xor org-link-descriptive (org-invisible-p)))
+        (should-not (xor org-link-descriptive (org-invisible-p)))
         (org-toggle-link-display)))))
 
 
diff --git a/testing/lisp/test-org-capture.el b/testing/lisp/test-org-capture.el
index 06177ee46..2287a479c 100644
--- a/testing/lisp/test-org-capture.el
+++ b/testing/lisp/test-org-capture.el
@@ -156,7 +156,7 @@ test-org-capture/abort
   "Test aborting a capture process."
   ;; Newly create capture buffer should not be saved.
   (let ((capture-file (make-temp-name
-                       (org-file-name-concat
+                       (file-name-concat
                         temporary-file-directory
                         "org-test"))))
     (unwind-protect
-- 
2.54.0

>From 2ecdb5325fff0615a873978af67caa16095c1dd9 Mon Sep 17 00:00:00 2001
From: Ihor Radchenko <[email protected]>
Date: Sat, 1 Apr 2023 12:18:57 +0200
Subject: [PATCH 6/7] org-manual.org: Document compat library installation

* doc/org-manual.org (Using Org's git repository): Document that users
must also install compat library when using git version of Org.
* etc/ORG-NEWS (Org mode now uses =compat.el= third-party package to
support older Emacs versions): Announce amendments to the Org
installation from git sources.
---
 doc/org-manual.org | 4 ++++
 etc/ORG-NEWS       | 7 +++++++
 2 files changed, 11 insertions(+)

diff --git a/doc/org-manual.org b/doc/org-manual.org
index dfc20b053..eebe8b2b4 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -158,6 +158,10 @@ *** Using Org's Git repository
 (add-to-list 'load-path "~/src/org-mode/lisp")
 #+end_src
 
+You must also manually install =compat= library required by Org mode.
+Using built-in =package.el=, you can run =M-x package-install <RET>
+compat <RET>=.
+
 You can also compile with =make=, generate the documentation with
 =make doc=, create a local configuration with =make config=, and
 install Org with =make install=.  Please run =make help= to get the
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 5a72b69ea..40c8acb5e 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -25,6 +25,13 @@ Please send Org bug reports to mailto:[email protected].
 # require user action for most Org mode users.
 # Sorted from most important to least important.
 
+*** Org mode now uses =compat.el= third-party package to support older Emacs versions
+
+This change is mostly technical and should not affect most users.
+However, people who install Org from git source might be affected.
+It is now necessary to manually install =compat.el= using Emacs'
+package manager.
+
 *** ~org-mouse~ tag and priority menus are now separate
 
 The "Tags and Priorities" section of the global context menu is split
-- 
2.54.0

>From a33733edc0551b40964a37f7aeb6989ea2688a35 Mon Sep 17 00:00:00 2001
From: Ihor Radchenko <[email protected]>
Date: Thu, 13 Apr 2023 14:33:16 +0200
Subject: [PATCH 7/7] * Makefile: Document new targets in make helpall

Document the newly added cleanpkg and uppkg targets.
---
 Makefile | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Makefile b/Makefile
index 813125625..5ecd51b79 100644
--- a/Makefile
+++ b/Makefile
@@ -46,6 +46,7 @@ helpall::
 	$(info Cleaning)
 	$(info ========)
 	$(info make clean          - remove built Org Elisp files and documentation)
+	$(info make cleanpkg      - remove third-party packages downloaded via make uppkg)
 	$(info make cleangithooks  - remove Git hooks)
 	$(info make cleanall       - remove everything that can be built and all remnants)
 	$(info make clean-install  - remove previous Org installation)
@@ -87,6 +88,7 @@ helpall::
 	$(info Convenience)
 	$(info ===========)
 	$(info make up0            - pull from upstream)
+	$(info make uppkg          - download third-party packages required for compilation)
 	$(info make up1            - pull from upstream, build and check)
 	$(info make up2            - pull from upstream, build, check and install)
 	$(info make update         - pull from upstream and build)
-- 
2.54.0

Reply via email to