+(defalias 'gnus-read-shell-command
+ (if (fboundp 'read-shell-command) 'read-shell-command 'read-string))
+
+(defmacro gnus-put-display-table (range value display-table)
+ "Set the value for char RANGE to VALUE in DISPLAY-TABLE. "
+ (if (featurep 'xemacs)
+ (progn
+ `(if (fboundp 'put-display-table)
+ (put-display-table ,range ,value ,display-table)
+ (if (sequencep ,display-table)
+ (aset ,display-table ,range ,value)
+ (put-char-table ,range ,value ,display-table))))
+ `(aset ,display-table ,range ,value)))
+
+(defmacro gnus-get-display-table (character display-table)
+ "Find value for CHARACTER in DISPLAY-TABLE. "
+ (if (featurep 'xemacs)
+ `(if (fboundp 'get-display-table)
+ (get-display-table ,character ,display-table)
+ (if (sequencep ,display-table)
+ (aref ,display-table ,character)
+ (get-char-table ,character ,display-table)))
+ `(aref ,display-table ,character)))
+
+(declare-function image-size "image.c" (spec &optional pixels frame))
+
+(defun gnus-rescale-image (image size)
+ "Rescale IMAGE to SIZE if possible.
+SIZE is in format (WIDTH . HEIGHT). Return a new image.
+Sizes are in pixels."
+ (if (or (not (fboundp 'imagemagick-types))
+ (not (get-buffer-window (current-buffer))))
+ image
+ (let ((new-width (car size))
+ (new-height (cdr size)))
+ (when (> (cdr (image-size image t)) new-height)
+ (setq image (or (create-image (plist-get (cdr image) :data) 'imagemagick t
+ :height new-height)
+ image)))
+ (when (> (car (image-size image t)) new-width)
+ (setq image (or
+ (create-image (plist-get (cdr image) :data) 'imagemagick t
+ :width new-width)
+ image)))
+ image)))
+
+(eval-when-compile (require 'gmm-utils))
+(defun gnus-recursive-directory-files (dir)
+ "Return all regular files below DIR.
+The first found will be returned if a file has hard or symbolic links."
+ (let (files attr attrs)
+ (gmm-labels
+ ((fn (directory)
+ (dolist (file (directory-files directory t))
+ (setq attr (file-attributes (file-truename file)))
+ (when (and (not (member attr attrs))
+ (not (member (file-name-nondirectory file)
+ '("." "..")))
+ (file-readable-p file))
+ (push attr attrs)
+ (cond ((file-regular-p file)
+ (push file files))
+ ((file-directory-p file)
+ (fn file)))))))
+ (fn dir))
+ files))
+
+(defun gnus-list-memq-of-list (elements list)
+ "Return non-nil if any of the members of ELEMENTS are in LIST."
+ (let ((found nil))
+ (dolist (elem elements)
+ (setq found (or found
+ (memq elem list))))
+ found))
+
+(eval-and-compile
+ (cond
+ ((fboundp 'match-substitute-replacement)
+ (defalias 'gnus-match-substitute-replacement 'match-substitute-replacement))
+ (t
+ (defun gnus-match-substitute-replacement (replacement &optional fixedcase literal string subexp)
+ "Return REPLACEMENT as it will be inserted by `replace-match'.
+In other words, all back-references in the form `\\&' and `\\N'
+are substituted with actual strings matched by the last search.
+Optional FIXEDCASE, LITERAL, STRING and SUBEXP have the same
+meaning as for `replace-match'.
+
+This is the definition of match-substitute-replacement in subr.el from GNU Emacs."
+ (let ((match (match-string 0 string)))
+ (save-match-data
+ (set-match-data (mapcar (lambda (x)
+ (if (numberp x)
+ (- x (match-beginning 0))
+ x))
+ (match-data t)))
+ (replace-match replacement fixedcase literal match subexp)))))))
+
+(if (fboundp 'string-match-p)
+ (defalias 'gnus-string-match-p 'string-match-p)
+ (defsubst gnus-string-match-p (regexp string &optional start)
+ "\
+Same as `string-match' except this function does not change the match data."
+ (save-match-data
+ (string-match regexp string start))))
+
+(if (fboundp 'string-prefix-p)
+ (defalias 'gnus-string-prefix-p 'string-prefix-p)
+ (defun gnus-string-prefix-p (str1 str2 &optional ignore-case)
+ "Return non-nil if STR1 is a prefix of STR2.
+If IGNORE-CASE is non-nil, the comparison is done without paying attention
+to case differences."
+ (and (<= (length str1) (length str2))
+ (let ((prefix (substring str2 0 (length str1))))
+ (if ignore-case
+ (string-equal (downcase str1) (downcase prefix))
+ (string-equal str1 prefix))))))
+
+(defalias 'gnus-format-message
+ (if (fboundp 'format-message) 'format-message
+ ;; for Emacs < 25, and XEmacs, don't worry about quote translation.
+ 'format))
+
+;; Simple check: can be a macro but this way, although slow, it's really clear.
+;; We don't use `bound-and-true-p' because it's not in XEmacs.
+(defun gnus-bound-and-true-p (sym)
+ (and (boundp sym) (symbol-value sym)))
+
+(if (fboundp 'timer--function)
+ (defalias 'gnus-timer--function 'timer--function)
+ (defun gnus-timer--function (timer)
+ (elt timer 5)))
+
+(defun gnus-test-list (list predicate)
+ "To each element of LIST apply PREDICATE.
+Return nil if LIST is no list or is empty or some test returns nil;
+otherwise, return t."
+ (when (and list (listp list))
+ (let ((result (mapcar predicate list)))
+ (not (memq nil result)))))
+
+(defun gnus-subsetp (list1 list2)
+ "Return t if LIST1 is a subset of LIST2.
+Similar to `subsetp' but use member for element test so that this works for
+lists of strings."
+ (when (and (listp list1) (listp list2))
+ (if list1
+ (and (member (car list1) list2)
+ (gnus-subsetp (cdr list1) list2))
+ t)))
+
+(defun gnus-setdiff (list1 list2)
+ "Return member-based set difference of LIST1 and LIST2."
+ (when (and list1 (listp list1) (listp list2))
+ (if (member (car list1) list2)
+ (gnus-setdiff (cdr list1) list2)
+ (cons (car list1) (gnus-setdiff (cdr list1) list2)))))