Files
CXML/xml/catalog.lisp
2005-03-13 18:02:10 +00:00

162 lines
5.8 KiB
Common Lisp

;;;; catalogs.lisp -- XML Catalogs -*- Mode: Lisp; readtable: runes -*-
;;;;
;;;; This file is part of the CXML parser, released under (L)LGPL.
;;;; See file COPYING for details.
;;;;
;;;; Developed 2004 for headcraft - http://headcraft.de/
;;;; Copyright: David Lichteblau
(in-package :cxml)
;;; http://www.oasis-open.org/committees/entity/spec.html
;;;
;;; Bugs:
;;; - We validate using the Catalog DTD while parsing, which is too strict
;;; and will will fail to parse files using other parser's extensions.
;;; (Jedenfalls behauptet das die Spec.)
;;; A long-term solution might be an XML Schema validator.
;;;
;;; XXX Das mit dem :dtd geht natuerlich gar nicht. Die Option muss weg.
(defvar *prefer* nil)
(defvar *catalog*
'(;; libxml standard
"/etc/xml/catalog"
;; FreeBSD
"/usr/local/share/xml/catalog.ports"))
(defparameter *catalog-dtd* nil)
(defun parse-catalog (files)
(let ((result '()))
(loop
(let ((file (pop files)))
(unless file
(return))
(multiple-value-bind (entries next) (parse-catalog-file file)
(setf result (append result entries))
(setf files (append next files)))))
result))
(defun parse-catalog-file (uri)
(handler-case
(parse-catalog-file/strict uri)
(file-error () nil)
(parser-error () nil)))
(defun parse-catalog-file/strict (uri)
(when (stringp uri)
(setf uri (puri:parse-uri uri)))
(unless *catalog-dtd*
(let ((cxml
(slot-value (asdf:find-system :cxml) 'asdf::relative-pathname)))
(setf *catalog-dtd*
(parse-dtd-file (merge-pathnames "catalog.dtd" cxml)))))
(with-open-stream (s (open (uri-to-pathname uri)
:element-type '(unsigned-byte 8)
:direction :input))
(parse-stream s
(make-instance 'catalog-parser :uri uri)
:validate t
;; XXX das geht nicht
:dtd *catalog-dtd*)))
(defclass catalog-parser ()
((entries :initform '() :accessor entries)
(next :initform '() :accessor next)
(prefer-stack :initform (list *prefer*) :accessor prefer-stack)
(base-stack :accessor base-stack)))
(defmethod initialize-instance :after
((instance catalog-parser) &key uri)
(setf (base-stack instance) (list uri)))
(defmethod prefer ((handler catalog-parser))
(car (prefer-stack handler)))
(defmethod base ((handler catalog-parser))
(car (base-stack handler)))
(defun get-attribute/lname (name attributes)
(member name attributes
:key (lambda (a)
(or (sax:attribute-local-name a)
(sax:attribute-qname a)))
:test #'rod=))
(defmethod sax:start-element ((handler catalog-parser) uri lname qname attrs)
(declare (ignore uri))
(setf lname (or lname qname))
;; we can dispatch on lnames only because we validate against the DTD,
;; which disallows other namespaces.
(push (string-or (get-attribute/lname #"prefer" attrs) (prefer handler))
(prefer-stack handler))
(push (string-or (get-attribute/lname #"base" attrs) (base handler))
(base-stack handler))
(cond
((rod= lname #"public")
(push (list :public
(get-attribute/lname #"publicId" attrs)
(puri:merge-uris
(puri:parse-uri (get-attribute/lname #"uri" attrs))
(base handler)))
(entries handler)))
((rod= lname #"system")
(push (list :system
(get-attribute/lname #"systemId" attrs)
(puri:merge-uris
(puri:parse-uri (get-attribute/lname #"uri" attrs))
(base handler)))
(entries handler)))
((rod= lname #"uri")
(push (list :uri
(get-attribute/lname #"name" attrs)
(puri:merge-uris
(puri:parse-uri (get-attribute/lname #"uri" attrs))
(base handler)))
(entries handler)))
((rod= lname #"rewriteSystem")
(push (list :rewrite-system
(get-attribute/lname #"systemIdStartString" attrs)
(get-attribute/lname #"rewritePrefix" attrs))
(entries handler)))
((rod= lname #"rewriteURI")
(push (list :rewrite-uri
(get-attribute/lname #"uriStartString" attrs)
(get-attribute/lname #"rewritePrefix" attrs))
(entries handler)))
((rod= lname #"delegatePublic")
(push (list :delegate-public
(get-attribute/lname #"publicIdStartString" attrs)
(puri:merge-uris
(puri:parse-uri (get-attribute/lname #"catalog" attrs))
(base handler)))
(entries handler)))
((rod= lname #"delegateSystem")
(push (list :delegate-system
(get-attribute/lname #"systemIdStartString" attrs)
(puri:merge-uris
(puri:parse-uri (get-attribute/lname #"catalog" attrs))
(base handler)))
(entries handler)))
((rod= lname #"delegateURI")
(push (list :delegate-uri
(get-attribute/lname #"uriStartString" attrs)
(puri:merge-uris
(puri:parse-uri (get-attribute/lname #"catalog" attrs))
(base handler)))
(entries handler)))
((rod= lname #"nextCatalog")
(push (puri:merge-uris
(puri:parse-uri (get-attribute/lname #"catalog" attrs))
(base handler))
(next handler)))))
(defmethod sax:end-element ((handler catalog-parser) uri lname qname)
(declare (ignore uri lname qname))
(pop (base-stack handler))
(pop (prefer-stack handler)))
(defmethod sax:end-document ((handler catalog-parser))
(values (reverse (entries handler)) (reverse (next handler))))