]> crepu.dev Git - config.git/blame - djavu-asus/emacs/elpa/jabber-20230715.456/jabber-xml.el
Reorganización de directorios
[config.git] / djavu-asus / emacs / elpa / jabber-20230715.456 / jabber-xml.el
CommitLineData
53e6db90
DC
1;; jabber-xml.el - XML functions -*- lexical-binding: t; -*-
2
3;; Copyright (C) 2003, 2004, 2007, 2008 - Magnus Henoch - mange@freemail.hu
4;; Copyright (C) 2002, 2003, 2004 - tom berger - object@intelectronica.net
5
6;; This file is a part of jabber.el.
7
8;; This program is free software; you can redistribute it and/or modify
9;; it under the terms of the GNU General Public License as published by
10;; the Free Software Foundation; either version 2 of the License, or
11;; (at your option) any later version.
12
13;; This program is distributed in the hope that it will be useful,
14;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16;; GNU General Public License for more details.
17
18;; You should have received a copy of the GNU General Public License
19;; along with this program; if not, write to the Free Software
20;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
22(require 'xml)
23(require 'jabber-util)
24(eval-when-compile
25 (require 'cl-lib))
26
27(defsubst jabber-replace-in-string (string regexp newtext)
28 "Return STRING with all matches for REGEXP replaced with NEWTEXT.
29NEWTEXT is inserted literally, without changing its case or treating \"\\\"
30specially."
31 (replace-regexp-in-string regexp newtext string t t))
32
33(defun jabber-escape-xml (string)
34 "Escape STRING for XML."
35 (if (stringp string)
36 (let ((newstr (concat string)))
37 ;; Form feeds might appear in code you copy, etc. Nevertheless,
38 ;; it's invalid XML.
39 (setq newstr (jabber-replace-in-string newstr "\f" "\n"))
40 ;; Other control characters are also illegal, except for
41 ;; tab, CR, and LF.
42 (setq newstr (jabber-replace-in-string newstr "[\000-\010\013\014\016-\037]" " "))
43 (setq newstr (jabber-replace-in-string newstr "&" "&"))
44 (setq newstr (jabber-replace-in-string newstr "<" "&lt;"))
45 (setq newstr (jabber-replace-in-string newstr ">" "&gt;"))
46 (setq newstr (jabber-replace-in-string newstr "'" "&apos;"))
47 (setq newstr (jabber-replace-in-string newstr "\"" "&quot;"))
48 newstr)
49 string))
50
51(defun jabber-unescape-xml (string)
52 "Unescape STRING for XML."
53 ;; Eventually this can be done with `xml-substitute-special', but the
54 ;; version in xml.el of GNU Emacs 21.3 is buggy.
55 (if (stringp string)
56 (let ((newstr string))
57 (setq newstr (jabber-replace-in-string newstr "&quot;" "\""))
58 (setq newstr (jabber-replace-in-string newstr "&apos;" "'"))
59 (setq newstr (jabber-replace-in-string newstr "&gt;" ">"))
60 (setq newstr (jabber-replace-in-string newstr "&lt;" "<"))
61 (setq newstr (jabber-replace-in-string newstr "&amp;" "&"))
62 newstr)
63 string))
64
65(defun jabber-sexp2xml (sexp)
66 "Return SEXP as well-formatted XML.
67SEXP should be in the form:
68 (tagname ((attribute-name . attribute-value)...) children...)"
69 (cond
70 ((stringp sexp)
71 (jabber-escape-xml sexp))
72 ((listp (car sexp))
73 (let ((xml ""))
74 (dolist (tag sexp)
75 (setq xml (concat xml (jabber-sexp2xml tag))))
76 xml))
77 ;; work around bug in old versions of xml.el, where ("") can appear
78 ;; as children of a node
79 ((and (consp sexp)
80 (stringp (car sexp))
81 (zerop (length (car sexp))))
82 "")
83 (t
84 (let ((xml ""))
85 (setq xml (concat "<"
86 (symbol-name (car sexp))))
87 (dolist (attr (cadr sexp))
88 (if (consp attr)
89 (setq xml (concat xml
90 (format " %s='%s'"
91 (symbol-name (car attr))
92 (jabber-escape-xml (cdr attr)))))))
93 (if (cddr sexp)
94 (progn
95 (setq xml (concat xml ">"))
96 (dolist (child (cddr sexp))
97 (setq xml (concat xml
98 (jabber-sexp2xml child))))
99 (setq xml (concat xml
100 "</"
101 (symbol-name (car sexp))
102 ">")))
103 (setq xml (concat xml
104 "/>")))
105 xml))))
106
107(defun jabber-xml-skip-tag-forward (&optional dont-recurse-into-stream)
108 "Skip to end of tag or matching closing tag if present.
109Return t iff after a closing tag, otherwise throws an 'unfinished
110tag with value nil.
111If DONT-RECURSE-INTO-STREAM is non-nil, stop after an opening
112<stream:stream> tag.
113
114The version of `sgml-skip-tag-forward' in Emacs 21 isn't good
115enough for us."
116 (skip-chars-forward "^<")
117 (cond
118 ((looking-at "<!\\[CDATA\\[")
119 (if (search-forward "]]>" nil t)
120 (goto-char (match-end 0))
121 (throw 'unfinished nil)))
122 ((looking-at "<\\([^[:space:]/>]+\\)\\([[:space:]]+[^=>]+=[[:space:]]*'[^']*'\\|[[:space:]]+[^=>]+=[[:space:]]*\"[^\"]*\"\\)*")
123 (let ((node-name (match-string 1)))
124 (goto-char (match-end 0))
125 (skip-syntax-forward "\s-") ; Skip over trailing white space.
126 (cond
127 ((looking-at "/>")
128 (goto-char (match-end 0))
129 t)
130 ((looking-at ">")
131 (goto-char (match-end 0))
132 (unless (and dont-recurse-into-stream (equal node-name "stream:stream"))
133 (cl-loop
134 do (skip-chars-forward "^<")
135 until (looking-at (regexp-quote (concat "</" node-name ">")))
136 do (jabber-xml-skip-tag-forward))
137 (goto-char (match-end 0)))
138 t)
139 (t
140 (throw 'unfinished nil)))))
141 (t
142 (throw 'unfinished nil))))
143
144(defun jabber-xml-parse-next-stanza ()
145 "Parse the first XML stanza in the current buffer.
146Parse and return the first complete XML element in the buffer,
147leaving point at the end of it. If there is no complete XML
148element, return nil."
149 (and (catch 'unfinished
150 (goto-char (point-min))
151 (jabber-xml-skip-tag-forward)
152 (> (point) (point-min)))
153 (xml-parse-region (point-min) (point))))
154
155(defsubst jabber-xml-node-name (node)
156 "Return the tag associated with NODE.
157The tag is a lower-case symbol."
158 (if (listp node) (car node)))
159
160(defsubst jabber-xml-node-attributes (node)
161 "Return the list of attributes of NODE.
162The list can be nil."
163 (if (listp node) (nth 1 node)))
164
165(defsubst jabber-xml-node-children (node)
166 "Return the list of children of NODE.
167This is a list of nodes, and it can be nil."
168 (let ((children (cddr node)))
169 ;; Work around a bug in early versions of xml.el
170 (if (equal children '(("")))
171 nil
172 children)))
173
174(defun jabber-xml-get-children (node child-name)
175 "Return the children of NODE whose tag is CHILD-NAME.
176CHILD-NAME should be a lower case symbol."
177 (let ((match ()))
178 (dolist (child (jabber-xml-node-children node))
179 (if child
180 (if (equal (jabber-xml-node-name child) child-name)
181 (push child match))))
182 (nreverse match)))
183
184;; `xml-get-attribute' returns "" if the attribute is not found, which
185;; is not very useful. Therefore, we use `xml-get-attribute-or-nil'.
186(defsubst jabber-xml-get-attribute (node attribute)
187 "Get from NODE the value of ATTRIBUTE.
188Return nil if the attribute was not found."
189 (when (consp node)
190 (xml-get-attribute-or-nil node attribute)))
191
192(defsubst jabber-xml-get-xmlns (node)
193 "Get \"xmlns\" attribute of NODE, or nil if not present."
194 (jabber-xml-get-attribute node 'xmlns))
195
196(defun jabber-xml-path (xml-data path)
197 "Find sub-node of XML-DATA according to PATH.
198 PATH is a vaguely XPath-inspired list. Each element can be:
199 a symbol go to first child node with this node name
200 cons cell car is string containing namespace URI,
201 cdr is string containing node name. Find
202 first matching child node.
203 any string character data of this node."
204 (let ((node xml-data))
205 (while (and path node)
206 (let ((step (car path)))
207 (cond
208 ((symbolp step)
209 (setq node (car (jabber-xml-get-children node step))))
210 ((consp step)
211 ;; This will be easier with namespace-aware use
212 ;; of xml.el. It will also be more correct.
213 ;; Now, it only matches explicit namespace declarations.
214 (setq node
215 (cl-dolist (x (jabber-xml-get-children node (intern (cdr step))))
216 (when (string= (jabber-xml-get-attribute x 'xmlns)
217 (car step))
218 (cl-return x)))))
219 ((stringp step)
220 (setq node (car (jabber-xml-node-children node)))
221 (unless (stringp node)
222 (setq node nil)))
223 (t
224 (error "Unknown path step: %s" step))))
225 (setq path (cdr path)))
226 node))
227
228(defmacro jabber-xml-let-attributes (attributes xml-data &rest body)
229 "Evaluate BODY with ATTRIBUTES bound to their values in XML-DATA.
230ATTRIBUTES must be a list of symbols, as present in XML-DATA."
231 `(let ,(mapcar #'(lambda (attr)
232 (list attr `(jabber-xml-get-attribute ,xml-data ',attr)))
233 attributes)
234 ,@body))
235(put 'jabber-xml-let-attributes 'lisp-indent-function 2)
236
237(defun jabber-xml-resolve-namespace-prefixes (xml-data &optional default-ns prefixes)
238 (let ((node-name (jabber-xml-node-name xml-data))
239 (attrs (jabber-xml-node-attributes xml-data)))
240 (setq prefixes (jabber-xml-merge-namespace-declarations attrs prefixes))
241
242 ;; If there is an xmlns attribute, it is the new default
243 ;; namespace.
244 (let ((xmlns (jabber-xml-get-xmlns xml-data)))
245 (when xmlns
246 (setq default-ns xmlns)))
247 ;; Now, if the node name has a prefix, replace it and add an
248 ;; "xmlns" attribute. Slightly ugly, but avoids the need to
249 ;; change all the rest of jabber.el at once.
250 (let ((node-name-string (symbol-name node-name)))
251 (when (string-match "\\(.*\\):\\(.*\\)" node-name-string)
252 (let* ((prefix (match-string 1 node-name-string))
253 (unprefixed (match-string 2 node-name-string))
254 (ns (assoc prefix prefixes)))
255 (if (null ns)
256 ;; This is not supposed to happen...
257 (message "jabber-xml-resolve-namespace-prefixes: Unknown prefix in %s" node-name-string)
258 (setf (car xml-data) (intern unprefixed))
259 (setf (cadr xml-data) (cons (cons 'xmlns (cdr ns)) (delq 'xmlns attrs)))))))
260 ;; And iterate through all child elements.
261 (mapc (lambda (x)
262 (when (listp x)
263 (jabber-xml-resolve-namespace-prefixes x default-ns prefixes)))
264 (jabber-xml-node-children xml-data))
265 xml-data))
266
267(defun jabber-xml-merge-namespace-declarations (attrs prefixes)
268 ;; First find any xmlns:foo attributes..
269 (dolist (attr attrs)
270 (let ((attr-name (symbol-name (car attr))))
271 (when (string-match "xmlns:" attr-name)
272 (let ((prefix (substring attr-name (match-end 0)))
273 (ns-uri (cdr attr)))
274 ;; A slightly complicated dance to never change the
275 ;; original value of prefixes (since the caller depends on
276 ;; it), but also to avoid excessive copying (which remove
277 ;; always does). Might need to profile and tweak this for
278 ;; performance.
279 (setq prefixes
280 (cons (cons prefix ns-uri)
281 (if (assoc prefix prefixes)
282 (remove (assoc prefix prefixes) prefixes)
283 prefixes)))))))
284 prefixes)
285
286(provide 'jabber-xml)
287
288;;; arch-tag: ca206e65-7026-4ee8-9af2-ff6a9c5af98a