mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2024-11-24 07:20:37 +00:00
ecf08f0621
dc4e6b1329
; Update copyright years in more files64b3777631
; Run set-copyright from admin.el8e1c56ae46
; Add 2024 to copyright years # Conflicts: # doc/misc/modus-themes.org # doc/misc/texinfo.tex # etc/NEWS # etc/refcards/ru-refcard.tex # etc/themes/modus-operandi-theme.el # etc/themes/modus-themes.el # etc/themes/modus-vivendi-theme.el # lib/alloca.in.h # lib/binary-io.h # lib/c-ctype.h # lib/c-strcasecmp.c # lib/c-strncasecmp.c # lib/careadlinkat.c # lib/cloexec.c # lib/close-stream.c # lib/diffseq.h # lib/dup2.c # lib/filemode.h # lib/fpending.c # lib/fpending.h # lib/fsusage.c # lib/getgroups.c # lib/getloadavg.c # lib/gettext.h # lib/gettime.c # lib/gettimeofday.c # lib/group-member.c # lib/malloc.c # lib/md5-stream.c # lib/md5.c # lib/md5.h # lib/memmem.c # lib/memrchr.c # lib/nanosleep.c # lib/save-cwd.h # lib/sha1.c # lib/sig2str.c # lib/stdlib.in.h # lib/strtoimax.c # lib/strtol.c # lib/strtoll.c # lib/time_r.c # lib/xalloc-oversized.h # lisp/auth-source-pass.el # lisp/emacs-lisp/lisp-mnt.el # lisp/emacs-lisp/timer.el # lisp/info-look.el # lisp/jit-lock.el # lisp/loadhist.el # lisp/mail/rmail.el # lisp/net/ntlm.el # lisp/net/webjump.el # lisp/progmodes/asm-mode.el # lisp/progmodes/project.el # lisp/progmodes/sh-script.el # lisp/textmodes/flyspell.el # lisp/textmodes/reftex-toc.el # lisp/textmodes/reftex.el # lisp/textmodes/tex-mode.el # lisp/url/url-gw.el # m4/alloca.m4 # m4/clock_time.m4 # m4/d-type.m4 # m4/dirent_h.m4 # m4/dup2.m4 # m4/euidaccess.m4 # m4/fchmodat.m4 # m4/filemode.m4 # m4/fsusage.m4 # m4/getgroups.m4 # m4/getloadavg.m4 # m4/getrandom.m4 # m4/gettime.m4 # m4/gettimeofday.m4 # m4/gnulib-common.m4 # m4/group-member.m4 # m4/inttypes.m4 # m4/malloc.m4 # m4/manywarnings.m4 # m4/mempcpy.m4 # m4/memrchr.m4 # m4/mkostemp.m4 # m4/mktime.m4 # m4/nproc.m4 # m4/nstrftime.m4 # m4/pathmax.m4 # m4/pipe2.m4 # m4/pselect.m4 # m4/pthread_sigmask.m4 # m4/readlink.m4 # m4/realloc.m4 # m4/sig2str.m4 # m4/ssize_t.m4 # m4/stat-time.m4 # m4/stddef_h.m4 # m4/stdint.m4 # m4/stdio_h.m4 # m4/stdlib_h.m4 # m4/stpcpy.m4 # m4/strnlen.m4 # m4/strtoimax.m4 # m4/strtoll.m4 # m4/time_h.m4 # m4/timegm.m4 # m4/timer_time.m4 # m4/timespec.m4 # m4/unistd_h.m4 # m4/warnings.m4 # nt/configure.bat # nt/preprep.c # test/lisp/register-tests.el
549 lines
18 KiB
EmacsLisp
549 lines
18 KiB
EmacsLisp
;;; rng-loc.el --- Locate the schema to use for validation -*- lexical-binding:t -*-
|
|
|
|
;; Copyright (C) 2003, 2007-2024 Free Software Foundation, Inc.
|
|
|
|
;; Author: James Clark
|
|
;; Keywords: text, hypermedia, languages, XML, RelaxNG
|
|
|
|
;; This file is part of GNU Emacs.
|
|
|
|
;; GNU Emacs is free software: you can redistribute it and/or modify
|
|
;; it under the terms of the GNU General Public License as published by
|
|
;; the Free Software Foundation, either version 3 of the License, or
|
|
;; (at your option) any later version.
|
|
|
|
;; GNU Emacs is distributed in the hope that it will be useful,
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
;; GNU General Public License for more details.
|
|
|
|
;; You should have received a copy of the GNU General Public License
|
|
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
;;; Commentary:
|
|
|
|
;;; Code:
|
|
|
|
(require 'nxml-util)
|
|
(require 'nxml-parse)
|
|
(require 'rng-parse)
|
|
(require 'rng-uri)
|
|
(require 'rng-util)
|
|
(require 'xmltok)
|
|
|
|
(defvar-local rng-current-schema-file-name nil
|
|
"Filename of schema being used for current buffer.
|
|
It is nil if using a vacuous schema.")
|
|
|
|
(defvar rng-schema-locating-files-default
|
|
(list "schemas.xml" (expand-file-name "schema/schemas.xml" data-directory))
|
|
"Default value for variable `rng-schema-locating-files'.")
|
|
|
|
(defvar rng-schema-locating-file-schema-file
|
|
(expand-file-name "schema/locate.rnc" data-directory)
|
|
"File containing schema for schema locating files.")
|
|
|
|
(defvar rng-schema-locating-file-schema nil
|
|
"Schema for schema locating files or nil if not yet loaded.")
|
|
|
|
(defcustom rng-schema-locating-files rng-schema-locating-files-default
|
|
"List of schema locating files."
|
|
:type '(repeat file)
|
|
:group 'relax-ng)
|
|
|
|
(defvar rng-schema-loader-alist '(("rnc" . rng-c-load-schema))
|
|
"Alist of schema extensions vs schema loader functions.")
|
|
|
|
(defvar rng-cached-document-element nil)
|
|
|
|
(defvar rng-document-type-history nil)
|
|
|
|
(defun rng-set-document-type (type-id)
|
|
(interactive (list (rng-read-type-id)))
|
|
(condition-case err
|
|
(when (not (string= type-id ""))
|
|
(let ((schema-file (rng-locate-schema-file type-id)))
|
|
(unless schema-file
|
|
(error "Could not locate schema for type id `%s'" type-id))
|
|
(rng-set-schema-file-1 schema-file))
|
|
(rng-save-schema-location-1 t type-id)
|
|
(rng-what-schema))
|
|
(nxml-file-parse-error
|
|
(nxml-display-file-parse-error err))))
|
|
|
|
(defun rng-read-type-id ()
|
|
(condition-case err
|
|
(let ((type-ids (rng-possible-type-ids))
|
|
(completion-ignore-case nil))
|
|
(completing-read "Document type id: "
|
|
(mapcar (lambda (x) (cons x nil))
|
|
type-ids)
|
|
nil
|
|
t
|
|
nil
|
|
'rng-document-type-history))
|
|
(nxml-file-parse-error
|
|
(nxml-display-file-parse-error err))))
|
|
|
|
(defun rng-set-schema-file (filename)
|
|
"Set the schema for the current buffer to the schema in FILENAME.
|
|
FILENAME must be the name of a file containing a schema.
|
|
The extension of FILENAME is used to determine what kind of schema it
|
|
is. The variable `rng-schema-loader-alist' maps from schema
|
|
extensions to schema loader functions. The function
|
|
`rng-c-load-schema' is the loader for RELAX NG compact syntax. The
|
|
association is between the buffer and the schema: the association is
|
|
lost when the buffer is killed."
|
|
(interactive "fSchema file: ")
|
|
(condition-case err
|
|
(progn
|
|
(rng-set-schema-file-1 filename)
|
|
(rng-save-schema-location-1 t))
|
|
(nxml-file-parse-error
|
|
(nxml-display-file-parse-error err))))
|
|
|
|
(defun rng-set-vacuous-schema ()
|
|
"Set the schema for the current buffer to allow any well-formed XML."
|
|
(interactive)
|
|
(rng-set-schema-file-1 nil)
|
|
(rng-what-schema))
|
|
|
|
(defun rng-set-schema-file-1 (filename)
|
|
(setq filename (and filename (expand-file-name filename)))
|
|
(setq rng-current-schema
|
|
(if filename
|
|
(rng-load-schema filename)
|
|
rng-any-element))
|
|
(setq rng-current-schema-file-name filename)
|
|
(run-hooks 'rng-schema-change-hook))
|
|
|
|
(defun rng-load-schema (filename)
|
|
(let* ((extension (file-name-extension filename))
|
|
(loader (cdr (assoc extension rng-schema-loader-alist))))
|
|
(or loader
|
|
(if extension
|
|
(error "No schema loader available for file extension `%s'"
|
|
extension)
|
|
(error "No schema loader available for null file extension")))
|
|
(funcall loader filename)))
|
|
|
|
(defun rng-what-schema ()
|
|
"Display a message saying what schema `rng-validate-mode' is using."
|
|
(interactive)
|
|
(if rng-current-schema-file-name
|
|
(message "Using schema %s"
|
|
(abbreviate-file-name rng-current-schema-file-name))
|
|
(message "Using vacuous schema")))
|
|
|
|
(defun rng-auto-set-schema (&optional no-display-error)
|
|
"Set the schema for this buffer based on the buffer's contents and file-name."
|
|
(interactive)
|
|
(condition-case err
|
|
(progn
|
|
(rng-set-schema-file-1 (rng-locate-schema-file))
|
|
(rng-what-schema))
|
|
(nxml-file-parse-error
|
|
(if no-display-error
|
|
(error "%s at position %s in %s"
|
|
(nth 3 err)
|
|
(nth 2 err)
|
|
(abbreviate-file-name (nth 1 err)))
|
|
(nxml-display-file-parse-error err)))))
|
|
|
|
(defun rng-locate-schema-file (&optional type-id)
|
|
"Return the file-name of the schema to use for the current buffer.
|
|
Return nil if no schema could be located.
|
|
If TYPE-ID is non-nil, then locate the schema for this TYPE-ID."
|
|
(let* ((rng-cached-document-element nil)
|
|
(schema
|
|
(if type-id
|
|
(cons type-id nil)
|
|
(rng-locate-schema-file-using rng-schema-locating-files)))
|
|
files type-ids)
|
|
(while (consp schema)
|
|
(setq files rng-schema-locating-files)
|
|
(setq type-id (car schema))
|
|
(setq schema nil)
|
|
(when (member type-id type-ids)
|
|
(error "Type-id loop for type-id `%s'" type-id))
|
|
(setq type-ids (cons type-id type-ids))
|
|
(while (and files (not schema))
|
|
(setq schema
|
|
(rng-locate-schema-file-from-type-id type-id
|
|
(car files)))
|
|
(setq files (cdr files))))
|
|
(and schema
|
|
(rng-uri-file-name schema))))
|
|
|
|
(defun rng-possible-type-ids ()
|
|
"Return a list of the known type IDs."
|
|
(let ((files rng-schema-locating-files)
|
|
type-ids)
|
|
(while files
|
|
(setq type-ids (rng-possible-type-ids-using (car files) type-ids))
|
|
(setq files (cdr files)))
|
|
(seq-uniq (sort type-ids 'string<))))
|
|
|
|
(defun rng-locate-schema-file-using (files)
|
|
"Locate a schema using the schema locating files FILES.
|
|
FILES is a list of file-names.
|
|
Return either a URI, a list (TYPE-ID) where TYPE-ID is a string,
|
|
or nil."
|
|
(let (rules
|
|
;; List of types that override normal order-based
|
|
;; priority, most important first
|
|
preferred-types
|
|
;; Best result found so far; same form as return value.
|
|
best-so-far)
|
|
(while (and (progn
|
|
(while (and (not rules) files)
|
|
(setq rules (rng-get-parsed-schema-locating-file
|
|
(car files)))
|
|
(setq files (cdr files)))
|
|
rules)
|
|
(or (not best-so-far) preferred-types))
|
|
(let* ((rule (car rules))
|
|
(rule-type (car rule))
|
|
(rule-matcher (get rule-type 'rng-rule-matcher)))
|
|
(setq rules (cdr rules))
|
|
(cond (rule-matcher
|
|
(when (and (or (not best-so-far)
|
|
(memq rule-type preferred-types)))
|
|
(setq best-so-far
|
|
(funcall rule-matcher (cdr rule)))
|
|
preferred-types)
|
|
(setq preferred-types
|
|
(nbutlast preferred-types
|
|
(length (memq rule-type preferred-types)))))
|
|
((eq rule-type 'applyFollowingRules)
|
|
(when (not best-so-far)
|
|
(let ((prefer (cdr (assq 'ruleType (cdr rule)))))
|
|
(when (and prefer
|
|
(not (memq (setq prefer (intern prefer))
|
|
preferred-types)))
|
|
(setq preferred-types
|
|
(nconc preferred-types (list prefer)))))))
|
|
((eq rule-type 'include)
|
|
(let ((uri (cdr (assq 'rules (cdr rule)))))
|
|
(when uri
|
|
(setq rules
|
|
(append (rng-get-parsed-schema-locating-file
|
|
(rng-uri-file-name uri))
|
|
rules))))))))
|
|
best-so-far))
|
|
|
|
(put 'documentElement 'rng-rule-matcher #'rng-match-document-element-rule)
|
|
(put 'namespace 'rng-rule-matcher #'rng-match-namespace-rule)
|
|
(put 'uri 'rng-rule-matcher #'rng-match-uri-rule)
|
|
(put 'transformURI 'rng-rule-matcher #'rng-match-transform-uri-rule)
|
|
(put 'default 'rng-rule-matcher #'rng-match-default-rule)
|
|
|
|
(defun rng-match-document-element-rule (props)
|
|
(let ((document-element (rng-document-element))
|
|
(prefix (cdr (assq 'prefix props)))
|
|
(local-name (cdr (assq 'localName props))))
|
|
(and (or (not prefix)
|
|
(if (= (length prefix) 0)
|
|
(not (nth 1 document-element))
|
|
(string= prefix (nth 1 document-element))))
|
|
(or (not local-name)
|
|
(string= local-name
|
|
(nth 2 document-element)))
|
|
(rng-match-default-rule props))))
|
|
|
|
(defun rng-match-namespace-rule (props)
|
|
(let ((document-element (rng-document-element))
|
|
(ns (cdr (assq 'ns props))))
|
|
(and document-element
|
|
ns
|
|
(eq (nth 0 document-element)
|
|
(if (string= ns "")
|
|
nil
|
|
(nxml-make-namespace ns)))
|
|
(rng-match-default-rule props))))
|
|
|
|
(defun rng-document-element ()
|
|
"Return a list (NS PREFIX LOCAL-NAME).
|
|
NS is t if the document has a non-nil, but not otherwise known namespace."
|
|
(or rng-cached-document-element
|
|
(setq rng-cached-document-element
|
|
(save-excursion
|
|
(save-restriction
|
|
(widen)
|
|
(goto-char (point-min))
|
|
(let (xmltok-dtd)
|
|
(xmltok-save
|
|
(xmltok-forward-prolog)
|
|
(xmltok-forward)
|
|
(when (memq xmltok-type '(start-tag
|
|
partial-start-tag
|
|
empty-element
|
|
partial-empty-element))
|
|
(list (rng-get-start-tag-namespace)
|
|
(xmltok-start-tag-prefix)
|
|
(xmltok-start-tag-local-name))))))))))
|
|
|
|
(defun rng-get-start-tag-namespace ()
|
|
(let ((prefix (xmltok-start-tag-prefix))
|
|
namespace att value)
|
|
(while xmltok-namespace-attributes
|
|
(setq att (car xmltok-namespace-attributes))
|
|
(setq xmltok-namespace-attributes (cdr xmltok-namespace-attributes))
|
|
(when (if prefix
|
|
(and (xmltok-attribute-prefix att)
|
|
(string= (xmltok-attribute-local-name att)
|
|
prefix))
|
|
(not (xmltok-attribute-prefix att)))
|
|
(setq value (xmltok-attribute-value att))
|
|
(setq namespace (if value (nxml-make-namespace value) t))))
|
|
(if (and prefix (not namespace))
|
|
t
|
|
namespace)))
|
|
|
|
(defun rng-match-transform-uri-rule (props)
|
|
(let ((from-pattern (cdr (assq 'fromPattern props)))
|
|
(to-pattern (cdr (assq 'toPattern props)))
|
|
(file-name (buffer-file-name)))
|
|
(and file-name
|
|
(setq file-name (expand-file-name file-name))
|
|
(rng-file-name-matches-uri-pattern-p file-name from-pattern)
|
|
(condition-case ()
|
|
(let ((new-file-name
|
|
(replace-match
|
|
(save-match-data
|
|
(rng-uri-pattern-file-name-replace-match to-pattern))
|
|
t
|
|
nil
|
|
file-name)))
|
|
(and (file-name-absolute-p new-file-name)
|
|
(file-exists-p new-file-name)
|
|
(rng-file-name-uri new-file-name)))
|
|
(rng-uri-error nil)))))
|
|
|
|
(defun rng-match-uri-rule (props)
|
|
(let ((resource (cdr (assq 'resource props)))
|
|
(pattern (cdr (assq 'pattern props)))
|
|
(file-name (buffer-file-name)))
|
|
(and file-name
|
|
(setq file-name (expand-file-name file-name))
|
|
(cond (resource
|
|
(condition-case ()
|
|
(eq (compare-strings (rng-uri-file-name resource)
|
|
0
|
|
nil
|
|
(expand-file-name file-name)
|
|
0
|
|
nil
|
|
nxml-file-name-ignore-case)
|
|
t)
|
|
(rng-uri-error nil)))
|
|
(pattern
|
|
(rng-file-name-matches-uri-pattern-p file-name
|
|
pattern)))
|
|
(rng-match-default-rule props))))
|
|
|
|
(defun rng-file-name-matches-uri-pattern-p (file-name pattern)
|
|
(condition-case ()
|
|
(and (let ((case-fold-search nxml-file-name-ignore-case))
|
|
(string-match (rng-uri-pattern-file-name-regexp pattern)
|
|
file-name))
|
|
t)
|
|
(rng-uri-error nil)))
|
|
|
|
(defun rng-match-default-rule (props)
|
|
(or (cdr (assq 'uri props))
|
|
(let ((type-id (cdr (assq 'typeId props))))
|
|
(and type-id
|
|
(cons (string-clean-whitespace type-id) nil)))))
|
|
|
|
(defun rng-possible-type-ids-using (file type-ids)
|
|
(let ((rules (rng-get-parsed-schema-locating-file file))
|
|
rule)
|
|
(while rules
|
|
(setq rule (car rules))
|
|
(setq rules (cdr rules))
|
|
(cond ((eq (car rule) 'typeId)
|
|
(let ((id (cdr (assq 'id (cdr rule)))))
|
|
(when id
|
|
(setq type-ids
|
|
(cons (string-clean-whitespace id)
|
|
type-ids)))))
|
|
((eq (car rule) 'include)
|
|
(let ((uri (cdr (assq 'rules (cdr rule)))))
|
|
(when uri
|
|
(setq type-ids
|
|
(rng-possible-type-ids-using
|
|
(rng-get-parsed-schema-locating-file
|
|
(rng-uri-file-name uri))
|
|
type-ids)))))))
|
|
type-ids))
|
|
|
|
(defun rng-locate-schema-file-from-type-id (type-id file)
|
|
"Locate the schema for type id TYPE-ID using schema locating file FILE.
|
|
Return either a URI, a list (TYPE-ID) where TYPE-ID is a string,
|
|
or nil."
|
|
(let ((rules (rng-get-parsed-schema-locating-file file))
|
|
schema rule)
|
|
(while (and rules (not schema))
|
|
(setq rule (car rules))
|
|
(setq rules (cdr rules))
|
|
(cond ((and (eq (car rule) 'typeId)
|
|
(let ((id (assq 'id (cdr rule))))
|
|
(and id
|
|
(string= (string-clean-whitespace (cdr id)) type-id))))
|
|
(setq schema (rng-match-default-rule (cdr rule))))
|
|
((eq (car rule) 'include)
|
|
(let ((uri (cdr (assq 'rules (cdr rule)))))
|
|
(when uri
|
|
(setq schema
|
|
(rng-locate-schema-file-from-type-id
|
|
type-id
|
|
(rng-uri-file-name uri))))))))
|
|
schema))
|
|
|
|
(defvar rng-schema-locating-file-alist nil)
|
|
|
|
(defun rng-get-parsed-schema-locating-file (file)
|
|
"Return a list of rules for the schema locating file FILE."
|
|
(setq file (expand-file-name file))
|
|
(let ((cached (assoc file rng-schema-locating-file-alist))
|
|
(mtime (file-attribute-modification-time (file-attributes file)))
|
|
parsed)
|
|
(cond ((not mtime)
|
|
(when cached
|
|
(setq rng-schema-locating-file-alist
|
|
(delq cached rng-schema-locating-file-alist)))
|
|
nil)
|
|
((and cached (time-equal-p (nth 1 cached) mtime))
|
|
(nth 2 cached))
|
|
(t
|
|
(setq parsed (rng-parse-schema-locating-file file))
|
|
(if cached
|
|
(setcdr cached (list mtime parsed))
|
|
(setq rng-schema-locating-file-alist
|
|
(cons (list file mtime parsed)
|
|
rng-schema-locating-file-alist)))
|
|
parsed))))
|
|
|
|
(defconst rng-locate-namespace-uri
|
|
(nxml-make-namespace "http://thaiopensource.com/ns/locating-rules/1.0"))
|
|
|
|
(defun rng-parse-schema-locating-file (file)
|
|
"Return list of rules.
|
|
Each rule has the form (TYPE (ATTR . VAL) ...), where
|
|
TYPE is a symbol for the element name, ATTR is a symbol for the attribute
|
|
and VAL is a string for the value.
|
|
Attribute values representing URIs are made absolute and xml:base
|
|
attributes are removed."
|
|
(when (and (not rng-schema-locating-file-schema)
|
|
rng-schema-locating-file-schema-file)
|
|
(setq rng-schema-locating-file-schema
|
|
(rng-load-schema rng-schema-locating-file-schema-file)))
|
|
(let* ((element
|
|
(if rng-schema-locating-file-schema
|
|
(rng-parse-validate-file rng-schema-locating-file-schema
|
|
file)
|
|
(nxml-parse-file file)))
|
|
(children (cddr element))
|
|
(base-uri (rng-file-name-uri file))
|
|
child name rules atts att props prop-name prop-value)
|
|
(when (equal (car element)
|
|
(cons rng-locate-namespace-uri "locatingRules"))
|
|
(while children
|
|
(setq child (car children))
|
|
(setq children (cdr children))
|
|
(when (consp child)
|
|
(setq name (car child))
|
|
(when (eq (car name) rng-locate-namespace-uri)
|
|
(setq atts (cadr child))
|
|
(setq props nil)
|
|
(while atts
|
|
(setq att (car atts))
|
|
(when (stringp (car att))
|
|
(setq prop-name (intern (car att)))
|
|
(setq prop-value (cdr att))
|
|
(when (memq prop-name '(uri rules resource))
|
|
(setq prop-value
|
|
(rng-uri-resolve prop-value base-uri)))
|
|
(setq props (cons (cons prop-name prop-value)
|
|
props)))
|
|
(setq atts (cdr atts)))
|
|
(setq rules
|
|
(cons (cons (intern (cdr name)) (nreverse props))
|
|
rules))))))
|
|
(nreverse rules)))
|
|
|
|
(defun rng-save-schema-location ()
|
|
"Save the association between the buffer's file and the current schema.
|
|
This ensures that the schema that is currently being used will be used
|
|
if the file is edited in a future session. The association will be
|
|
saved to the first writable file in `rng-schema-locating-files'."
|
|
(interactive)
|
|
(rng-save-schema-location-1 nil))
|
|
|
|
(defun rng-save-schema-location-1 (prompt &optional type-id)
|
|
(unless (or rng-current-schema-file-name type-id)
|
|
(error "Buffer is using a vacuous schema"))
|
|
(let ((files rng-schema-locating-files)
|
|
(document-file-name (buffer-file-name))
|
|
(schema-file-name rng-current-schema-file-name)
|
|
file)
|
|
(while (and files (not file))
|
|
(if (file-writable-p (car files))
|
|
(setq file (expand-file-name (car files)))
|
|
(setq files (cdr files))))
|
|
(cond ((not file)
|
|
(if prompt
|
|
nil
|
|
(error "No writable schema locating file configured")))
|
|
((not document-file-name)
|
|
(if prompt
|
|
nil
|
|
(error "Buffer does not have a filename")))
|
|
((and prompt
|
|
(not (y-or-n-p (format "Save %s to %s?"
|
|
(if type-id
|
|
"type identifier"
|
|
"schema location")
|
|
file)))))
|
|
(t
|
|
(with-current-buffer (find-file-noselect file)
|
|
(let ((modified (buffer-modified-p)))
|
|
(if (> (buffer-size) 0)
|
|
(let (xmltok-dtd)
|
|
(goto-char (point-min))
|
|
(xmltok-save
|
|
(xmltok-forward-prolog)
|
|
(xmltok-forward)
|
|
(unless (eq xmltok-type 'start-tag)
|
|
(error "Locating file `%s' invalid" file))))
|
|
(insert "<?xml version=\"1.0\"?>\n"
|
|
"<locatingRules xmlns=\""
|
|
(nxml-namespace-name rng-locate-namespace-uri)
|
|
"\">")
|
|
(let ((pos (point)))
|
|
(insert "\n</locatingRules>\n")
|
|
(goto-char pos)))
|
|
(insert "\n")
|
|
(insert (let ((locating-file-uri (rng-file-name-uri file)))
|
|
(format "<uri resource=\"%s\" %s=\"%s\"/>"
|
|
(rng-escape-string
|
|
(rng-relative-uri
|
|
(rng-file-name-uri document-file-name)
|
|
locating-file-uri))
|
|
(if type-id "typeId" "uri")
|
|
(rng-escape-string
|
|
(or type-id
|
|
(rng-relative-uri
|
|
(rng-file-name-uri schema-file-name)
|
|
locating-file-uri))))))
|
|
(indent-according-to-mode)
|
|
(when (or (not modified)
|
|
(y-or-n-p (format "Save file %s?"
|
|
(buffer-file-name))))
|
|
(save-buffer))))))))
|
|
|
|
(provide 'rng-loc)
|
|
|
|
;;; rng-loc.el ends here
|