diff lisp/psgml/psgml-dtd.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children ac2d302a0011
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/psgml/psgml-dtd.el	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,924 @@
+;;;; psgml-dtd.el --- DTD parser for SGML-editing mode with parsing support
+;; $Id: psgml-dtd.el,v 1.1.1.1 1996/12/18 03:35:19 steve Exp $
+
+;; Copyright (C) 1994 Lennart Staflin
+
+;; Author: Lennart Staflin <lenst@lysator.liu.se>
+
+;; 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 2
+;; 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, write to the Free Software
+;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+;;;; Commentary:
+
+;; Part of major mode for editing the SGML document-markup language.
+
+
+;;;; Code:
+
+(provide 'psgml-dtd)
+(require 'psgml)
+(require 'psgml-parse)
+
+
+;;;; Variables
+
+;; Variables used during doctype parsing and loading
+(defvar sgml-used-pcdata nil
+  "True if model group built is mixed")
+
+
+;;;; Constructing basic
+
+(defun sgml-copy-moves (s1 s2)
+  "Copy all moves from S1 to S2, keeping their status."
+  (let ((l (sgml-state-opts s1)))
+    (while l
+      (sgml-add-opt-move s2
+			 (sgml-move-token (car l))
+			 (sgml-move-dest (car l)))
+      (setq l (cdr l)))
+    (setq l (sgml-state-reqs s1))
+    (while l
+      (sgml-add-req-move s2
+			 (sgml-move-token (car l))
+			 (sgml-move-dest (car l)))
+      (setq l (cdr l)))))
+
+(defun sgml-copy-moves-to-opt (s1 s2)
+  "Copy all moves from S1 to S2 as optional moves."
+  (let ((l (sgml-state-opts s1)))
+    (while l
+      (sgml-add-opt-move s2
+			 (sgml-move-token (car l))
+			 (sgml-move-dest (car l)))
+      (setq l (cdr l)))
+    (setq l (sgml-state-reqs s1))
+    (while l
+      (sgml-add-opt-move s2
+			 (sgml-move-token (car l))
+			 (sgml-move-dest (car l)))
+      (setq l (cdr l)))))
+
+
+(defun sgml-some-states-of (state)
+  ;; List of some states reachable from STATE, includes all final states
+  (let* ((states (list state))
+	 (l states)
+	 s ms m)
+    (while l
+      (setq s (car l)
+	    ms (append (sgml-state-opts s) (sgml-state-reqs s)))
+      (while ms
+	(setq m (sgml-move-dest (car ms))
+	      ms (cdr ms))
+	(unless (sgml-normal-state-p m)
+	  (setq m (sgml-&node-next m)))
+	(unless (memq m states)
+	  (nconc states (list m))))
+      (setq l (cdr l)))
+    states))
+
+(defmacro sgml-for-all-final-states (s dfa &rest forms)
+  "For all final states S in DFA do FORMS.
+Syntax: var dfa-expr &body forms"
+  (` (let ((L-states (sgml-some-states-of (, dfa)))
+	   (, s))
+       (while L-states
+	 (when (sgml-state-final-p (setq (, s) (car L-states)))
+	   (,@ forms))
+	 (setq L-states (cdr L-states))))))
+
+(put 'sgml-for-all-final-states 'lisp-indent-hook 2)
+(put 'sgml-for-all-final-states 'edebug-form-hook '(symbolp &rest form))
+
+
+;;;; Optimization for the dfa building
+
+(defsubst sgml-empty-state-p (s)
+  ;; True if S hase no outgoing moves
+  (and (sgml-normal-state-p s)
+       (null (sgml-state-reqs s))
+       (null (sgml-state-opts s)))  )
+
+(defun sgml-one-final-state (s)
+  ;; Collaps all states that have no moves
+  ;; This is a safe optimization, useful for (..|..|..)
+  (sgml-debug "OPT one final: reqs %d opts %d"
+	      (length (sgml-state-reqs s))
+	      (length (sgml-state-opts s)))
+  (let ((final nil)
+	dest)
+    (loop for m in (append (sgml-state-reqs s)
+			   (sgml-state-opts s))
+	  do
+	  (setq dest (sgml-move-dest m))
+	  (when (sgml-empty-state-p dest)
+	    (cond ((null final)
+		   (setq final dest))
+		  (t
+		   (setf (sgml-move-dest m) final)))))))
+
+(defun sgml-states-equal (s1 s2)
+  (and (= (length (sgml-state-opts s1))
+	  (length (sgml-state-opts s2)))
+       (= (length (sgml-state-reqs s1))
+	  (length (sgml-state-reqs s2)))
+       (loop for m in (sgml-state-opts s1)
+	     always
+	     (eq (sgml-move-dest m)
+		 (sgml-move-dest (sgml-moves-lookup (sgml-move-token m)
+						    (sgml-state-opts s2)))))
+       (loop for m in (sgml-state-reqs s1)
+	     always
+	     (eq (sgml-move-dest m)
+		 (sgml-move-dest (sgml-moves-lookup (sgml-move-token m)
+						    (sgml-state-reqs s2)))))))
+
+(defun sgml-remove-redundant-states-1 (s)
+  ;; Remove states accessible from s with one move and equivalent to s,
+  ;; by changing the moves from s.
+  (sgml-debug "OPT redundant-1: reqs %d opts %d"
+	      (length (sgml-state-reqs s))
+	      (length (sgml-state-opts s)))
+  (let ((yes nil)
+	(no (list s))
+	(l (sgml-state-reqs s))
+	(nl (sgml-state-opts s))
+	(res s)
+	dest)
+    (while (or l (setq l (prog1 nl (setq nl nil))))
+      (cond
+       ((not (sgml-normal-state-p (setq dest (sgml-move-dest (car l))))))
+       ((memq dest no))
+       ((memq dest yes))
+       ((sgml-states-equal s dest)
+	(progn (push dest yes))))
+      (setq l (cdr l)))
+    (setq l (sgml-state-opts s)
+	  nl (sgml-state-reqs s))
+    (when yes
+      (sgml-debug "OPT redundant-1: sucess %s" (length yes))
+      (while (or l (setq l (prog1 nl (setq nl nil))))
+	(cond ((memq (sgml-move-dest (car l)) yes)
+	       (setf (sgml-move-dest (car l)) s)))
+	(setq l (cdr l))))))
+	  
+
+
+;;;; Constructing
+
+(defun sgml-make-opt (s1)
+  (when (sgml-state-reqs s1)
+    (setf (sgml-state-opts s1)
+	  (nconc (sgml-state-opts s1)
+		 (sgml-state-reqs s1)))
+    (setf (sgml-state-reqs s1) nil))
+  s1)
+
+(defun sgml-make-* (s1)
+  (setq s1 (sgml-make-+ s1))
+  (when (sgml-state-reqs s1)
+    (sgml-make-opt s1))
+  (sgml-remove-redundant-states-1 s1)
+  s1)
+
+(defun sgml-make-+ (s1)
+  (sgml-for-all-final-states s s1
+    (sgml-copy-moves-to-opt s1 s))
+  (sgml-remove-redundant-states-1 s1)	; optimize
+  s1)
+
+(defun sgml-make-conc (s1 s2)
+  (let ((moves (append (sgml-state-reqs s1) (sgml-state-opts s1))))
+    (cond
+     (;; optimize the case where all moves from s1 goes to empty states
+      (loop for m in moves
+	    always (sgml-empty-state-p (sgml-move-dest m)))
+      (loop for m in moves do (setf (sgml-move-dest m) s2))
+      (when (sgml-state-final-p s1)
+	(sgml-copy-moves s2 s1)))
+     (t					; general case
+      (sgml-for-all-final-states s s1
+	(sgml-copy-moves s2 s)
+	(sgml-remove-redundant-states-1 s)))))
+  s1)
+
+(defun sgml-make-pcdata ()
+  (sgml-make-* (sgml-make-primitive-content-token sgml-pcdata-token)))
+
+(defun sgml-reduce-, (l)
+  (while (cdr l)
+    (setcar (cdr l)
+	    (sgml-make-conc (car l) (cadr l)))
+    (setq l (cdr l)))
+  (car l))
+
+(defun sgml-reduce-| (l)
+  (while (cdr l)			; apply the binary make-alt
+    (cond ((or (sgml-state-final-p (car l))	; is result optional
+	       (sgml-state-final-p (cadr l)))
+	   (sgml-make-opt (car l))
+	   (sgml-copy-moves-to-opt (cadr l) (car l)))
+	  (t
+	   (sgml-copy-moves (cadr l) (car l))))
+    (setcdr l (cddr l)))
+  (sgml-one-final-state (car l))	; optimization
+  (car l))
+
+(defun sgml-make-& (dfas)
+  (let ((&n (sgml-make-&node dfas (sgml-make-state)))
+	(s (sgml-make-state))
+	(l dfas))
+    (while l				; For each si:
+      ;; For m in opts(si): add optional move from s to &n on token(m).
+      (loop for m in (sgml-state-opts (car l))
+	    do (sgml-add-opt-move s (sgml-move-token m) &n))
+      ;; For m in reqs(si): add required move from s to &n on token(m).
+      (loop for m in (sgml-state-reqs (car l))
+	    do (sgml-add-req-move s (sgml-move-token m) &n))
+      (setq l (cdr l)))
+    ;; Return s.
+    s))
+
+
+
+;(sgml-make-conc (sgml-make-primitive-content-token 'para) (sgml-make-primitive-content-token 'list))
+;(sgml-make-conc (sgml-make-& (list (sgml-make-primitive-content-token 'para) (sgml-make-primitive-content-token 'list))) (sgml-make-primitive-content-token 'foo))
+
+;(setq x  (sgml-some-states-of  (sgml-make-primitive-content-token 'para))) 
+;(sgml-state-final-p (car x) ) 
+;(sgml-state-final-p (cadr x)) 
+
+
+;;;; Parse doctype: General
+
+(defun sgml-skip-ts ()
+  ;; Skip over ts*
+  ;;70  ts   = 5 s | EE | 60+ parameter entity reference
+  ;;For simplicity I use ps*
+  ;;65  ps   = 5 s | EE | 60+ parameter entity reference | 92 comment
+  ;;*** some comments are accepted that shouldn't
+  (sgml-skip-ps))
+
+(defun sgml-parse-character-reference (&optional dofunchar)
+  ;; *** Actually only numerical character references
+  ;; I don't know how to handel the function character references.
+  ;; For the shortrefs let's give them numeric values.
+  (if (if dofunchar
+	  (sgml-parse-delim "CRO" (digit nmstart))
+	(sgml-parse-delim "CRO" (digit)))
+      (prog1 (if (sgml-is-delim "NULL" digit)
+		 (string-to-int (sgml-check-nametoken))
+	       (let ((spec (sgml-check-name)))
+		 (or (cdr (assoc spec '(("re" . 10)
+					("rs" . 1)
+					("tab" . 9)
+					("space" . 32))))
+		     ;; *** What to do with other names?
+		     127)))
+	(or (sgml-parse-delim "REFC")
+	    (sgml-parse-RE)))))
+
+(defun sgml-parse-parameter-literal (&optional dofunchar)
+  (let* (lita				; flag if lita
+	 (value				; accumulates literals value
+	  "")
+	 (original-buffer		; Buffer (entity) where lit started
+	  (current-buffer))
+	 temp
+	 )
+    (cond
+     ((or (sgml-parse-delim "LIT")
+	  (setq lita (sgml-parse-delim "LITA")))
+      (while (not (and (eq (current-buffer) original-buffer)
+		       (if lita
+			   (sgml-parse-delim "LITA")
+			 (sgml-parse-delim "LIT"))))
+	(cond ((eobp)
+	       (or (sgml-pop-entity)
+		   (sgml-error "Parameter literal unterminated")))
+	      ((sgml-parse-parameter-entity-ref))
+	      ((setq temp (sgml-parse-character-reference dofunchar))
+	       (setq value (concat value (format "%c" temp))))
+	      (t
+	       (setq value
+		     (concat value
+			     (buffer-substring
+			      (point)
+			      (progn (forward-char 1)
+				     (if lita
+					 (sgml-skip-upto ("LITA" "PERO" "CRO"))
+				       (sgml-skip-upto ("LIT" "PERO" "CRO")))
+				     (point)))))))
+	)
+      value))))
+
+(defun sgml-check-parameter-literal ()
+  (or (sgml-parse-parameter-literal)
+      (sgml-parse-error "Parameter literal expected")))
+
+(defsubst sgml-parse-connector ()
+  (sgml-skip-ps)
+  (cond ((sgml-parse-delim "SEQ")
+	 (function sgml-reduce-,))
+	((sgml-parse-delim "OR")
+	 (function sgml-reduce-|))
+	((sgml-parse-delim "AND")
+	 (function sgml-make-&))))
+
+(defun sgml-parse-name-group ()
+  "Parse a single name or a name group (general name case) .
+Returns a list of strings or nil."
+  (let (names)
+    (cond
+     ((sgml-parse-delim "GRPO")
+      (sgml-skip-ps)
+      (setq names (sgml-parse-name-group)) ; *** Allows more than it should
+      (while (sgml-parse-connector)
+	(sgml-skip-ps)
+	(nconc names (sgml-parse-name-group)))
+      (sgml-check-delim "GRPC")
+      names)
+     ((setq names (sgml-parse-name))
+      (list names)))))
+
+(defun sgml-check-name-group ()
+  (or (sgml-parse-name-group)
+      (sgml-parse-error "Expecting a name or a name group")))
+
+(defun sgml-check-nametoken-group ()
+  "Parse a name token group, return a list of strings.
+Case transformed for general names."
+  (sgml-skip-ps)
+  (let ((names nil))
+    (cond
+     ((sgml-parse-delim GRPO)
+      (while (progn
+	       (sgml-skip-ps)
+	       (push (sgml-general-case (sgml-check-nametoken)) names)
+	       (sgml-parse-connector)))
+      (sgml-check-delim GRPC)
+      (nreverse names))			; store in same order as declared
+     (t
+      (list (sgml-general-case (sgml-check-nametoken)))))))
+
+(defun sgml-check-element-type ()
+  "Parse and check an element type, returns list of strings."
+;;; 117  element type     =  [[30 generic identifier]]
+;;;                      |  [[69 name group]]
+;;;                      |  [[118 ranked element]]
+;;;                      |  [[119 ranked group]]
+  (cond
+   ((sgml-parse-delim GRPO)
+    (sgml-skip-ts)
+    (let ((names (list (sgml-check-name))))
+      (while (progn (sgml-skip-ts)
+		    (sgml-parse-connector))
+	(sgml-skip-ts)
+	(nconc names (list (sgml-check-name))))
+      (sgml-check-delim GRPC)
+      ;; A ranked group will have a rank suffix here
+      (sgml-skip-ps)
+      (if (sgml-is-delim "NULL" digit)
+	(let ((suffix (sgml-parse-nametoken)))
+	  (loop for n in names
+		collect (concat n suffix)))
+	names)))
+   (t					; gi/ranked element
+    (let ((name (sgml-check-name)))
+      (sgml-skip-ps)
+      (list (if (sgml-is-delim "NULL" digit)
+		(concat name (sgml-check-nametoken))
+	      name))))))
+
+
+(defun sgml-check-external ()
+  (or (sgml-parse-external)
+      (sgml-parse-error "Expecting a PUBLIC or SYSTEM")))
+
+;;;; Parse doctype: notation
+
+(defun sgml-declare-notation ()
+  ;;148  notation declaration = MDO, "NOTATION",
+  ;;                        65 ps+, 41 notation name,
+  ;;                        65 ps+, 149 notation identifier,
+  ;;                        65 ps*, MDC
+  ;;41   notation name    = 55 name
+  ;;149  notation identifier = 73 external identifier
+  (sgml-skip-ps)
+  (sgml-check-name)
+  (sgml-skip-ps)
+  (sgml-check-external))
+
+
+;;;; Parse doctype: Element
+
+(defun sgml-parse-opt ()
+  (sgml-skip-ps)
+  (cond ((or (sgml-parse-char ?o)
+	     (sgml-parse-char ?O))
+	 t)
+	((sgml-parse-char ?-)
+	 nil)))
+
+(defun sgml-parse-modifier ()
+  (cond ((sgml-parse-delim PLUS)
+	 (function sgml-make-+))
+	((sgml-parse-delim REP)
+	 (function sgml-make-*))
+	((sgml-parse-delim OPT)
+	 (function sgml-make-opt))))
+
+(defun sgml-check-primitive-content-token ()
+  (sgml-make-primitive-content-token
+   (sgml-eltype-token
+    (sgml-lookup-eltype
+     (sgml-check-name)))))
+
+(defun sgml-check-model-group ()
+  (sgml-skip-ps)
+  (let (el mod)
+    (cond
+     ((sgml-parse-delim "GRPO")
+      (let ((subs (list (sgml-check-model-group)))
+	    (con1 nil)
+	    (con2 nil))
+	(while (setq con2 (sgml-parse-connector))
+	  (cond ((and con1
+		      (not (eq con1 con2)))
+		 (sgml-parse-error "Mixed connectors")))
+	  (setq con1 con2)
+	  (setq subs (nconc subs (list (sgml-check-model-group)))))
+	(sgml-check-delim "GRPC")
+	(setq el (if con1
+		     (funcall con1 subs)
+		   (car subs)))))
+     ((sgml-parse-rni "pcdata")		; #PCDATA
+      (setq sgml-used-pcdata t)
+      (setq el (sgml-make-pcdata)))
+     ((sgml-parse-delim "DTGO")			; data tag group
+      (sgml-skip-ts)
+      (let ((tok (sgml-check-primitive-content-token)))
+	(sgml-skip-ts) (sgml-check-delim "SEQ")
+	(sgml-skip-ts) (sgml-check-data-tag-pattern)
+	(sgml-skip-ts) (sgml-check-delim "DTGC")
+	(setq el (sgml-make-conc tok (sgml-make-pcdata)))
+	(setq sgml-used-pcdata t)))
+     (t
+      (setq el (sgml-check-primitive-content-token))))
+    (setq mod (sgml-parse-modifier))
+    (if mod
+	(funcall mod el)
+      el)))
+
+(defun sgml-check-data-tag-pattern ()
+  ;; 134  data tag pattern 
+  ;; template | template group
+  (cond ((sgml-parse-delim GRPO)
+	 (sgml-skip-ts)
+	 (sgml-check-parameter-literal)	; data tag template,
+	 (while (progn (sgml-skip-ts)
+		       (sgml-parse-delim OR))
+	   (sgml-skip-ts)
+	   (sgml-check-parameter-literal)) ; data tag template
+	 (sgml-skip-ts)
+	 (sgml-check-delim GRPC))
+	(t
+	 (sgml-check-parameter-literal))) ; data tag template
+  (sgml-skip-ts)
+  (when (sgml-parse-delim SEQ)
+    (sgml-check-parameter-literal)))	; data tag padding template
+
+(defun sgml-check-content-model ()
+  (sgml-check-model-group))
+
+(defun sgml-check-content ()
+  (sgml-skip-ps)
+  (cond ((sgml-is-delim GRPO)
+	 (sgml-check-content-model))
+	(t
+	 ;; ANY, CDATA, RCDATA or EMPTY
+	 (let ((dc (intern (upcase (sgml-check-name))))) 
+	   (when (eq dc 'ANY)
+	     (setq sgml-used-pcdata t))
+	   dc))))
+
+(defun sgml-parse-exeption (type)
+  (sgml-skip-ps)
+  (if (sgml-parse-char type)
+      (mapcar (function sgml-lookup-eltype)
+	      (sgml-check-name-group))))
+
+(defun sgml-before-eltype-modification ()
+;;;  (let ((merged (sgml-dtd-merged sgml-dtd-info)))
+;;;    (when (and merged
+;;;	       (eq (sgml-dtd-eltypes sgml-dtd-info)
+;;;		   (sgml-dtd-eltypes (cdr merged))))
+;;;      (setf (sgml-dtd-eltypes sgml-dtd-info)
+;;;	    (sgml-merge-eltypes (sgml-make-eltypes-table)
+;;;				(sgml-dtd-eltypes sgml-dtd-info)))))
+  )
+
+(defun sgml-declare-element ()
+  (let* ((names (sgml-check-element-type))
+	 (stag-opt (sgml-parse-opt))
+	 (etag-opt (sgml-parse-opt))
+	 (sgml-used-pcdata nil)
+	 (model (sgml-check-content))
+	 (exclusions (sgml-parse-exeption ?-))
+	 (inclusions (sgml-parse-exeption ?+)))
+    (sgml-before-eltype-modification)
+    (while names
+      (sgml-debug "Defining element %s" (car names))
+      (let ((et (sgml-lookup-eltype (car names))))
+	(setf (sgml-eltype-stag-optional et) stag-opt
+	      (sgml-eltype-etag-optional et) etag-opt
+	      (sgml-eltype-model et) model
+	      (sgml-eltype-mixed et) sgml-used-pcdata
+	      (sgml-eltype-excludes et) exclusions
+	      (sgml-eltype-includes et) inclusions))
+      (setq names (cdr names)))
+    (sgml-lazy-message "Parsing doctype (%s elements)..."
+		       (incf sgml-no-elements))))
+
+;;;; Parse doctype: Entity
+
+(defun sgml-declare-entity ()
+  (let (name				; Name of entity
+	dest				; Entity table
+	(type 'text)			; Type of entity
+	text				; Text of entity
+	extid				; External id 
+	)
+    (cond
+     ((sgml-parse-delim "PERO")		; parameter entity declaration
+      (sgml-skip-ps)
+      (setq name (sgml-check-name t))
+      (setq dest (sgml-dtd-parameters sgml-dtd-info)))
+     (t					; normal entity declaration
+      (or (sgml-parse-rni "default")
+	  (setq name (sgml-check-name t)))
+      (setq dest (sgml-dtd-entities sgml-dtd-info))))
+    (sgml-skip-ps)
+    ;;105  entity text  = 66 parameter literal
+    ;;                 | 106 data text
+    ;;                 | 107 bracketed text
+    ;;                 | 108 external entity specification
+    (setq extid (sgml-parse-external))
+    (setq text
+	  (cond
+	   (extid			; external entity specification =
+					; 73 external identifier,
+					; (65 ps+, 109+ entity type)?
+	    (sgml-skip-ps)
+	    (setq type (or (sgml-parse-entity-type) 'text))
+	    extid)
+	   ((sgml-startnm-char-next)
+	    (let ((token (intern (sgml-check-name))))
+	      (sgml-skip-ps)
+	      (cond
+	       ((memq token '(cdata sdata)) ; data text ***
+		(setq type token)
+		(sgml-check-parameter-literal))
+	       ((eq token 'pi)
+		(concat "<?" (sgml-check-parameter-literal) ">"))
+	       ((eq token 'starttag)
+		(sgml-start-tag-of (sgml-check-parameter-literal)))
+	       ((eq token 'endtag)
+		(sgml-end-tag-of (sgml-check-parameter-literal)))	
+	       ((eq token 'ms)		; marked section
+		(concat "<![" (sgml-check-parameter-literal) "]]>"))
+	       ((eq token 'md)		; Markup declaration
+		(concat "<!" (sgml-check-parameter-literal) ">")))))
+	   ((sgml-check-parameter-literal))))
+    (when dest
+      (sgml-entity-declare name dest type text))))
+
+
+(defun sgml-parse-entity-type ()
+  ;;109+ entity type      = "SUBDOC"
+  ;;                      | (("CDATA" | "NDATA" | "SDATA"),
+  ;;                             65 ps+,
+  ;;                             41 notation name,
+  ;;                             149.2+ data attribute specification?)
+  (let ((type (sgml-parse-name)))
+    (when type
+      (setq type (intern (downcase type)))
+      (cond ((eq type 'subdoc))
+	    ((memq type '(cdata ndata sdata))
+	     (sgml-skip-ps)
+	     (sgml-check-name)
+	     ;;149.2+ data attribute specification
+	     ;;                      = 65 ps+, DSO,
+	     ;;                        31 attribute specification list,
+	     ;;                        5 s*, DSC
+	     (sgml-skip-ps)
+	     (when (sgml-parse-delim DSO)
+	       (sgml-parse-attribute-specification-list)
+	       (sgml-parse-s)
+	       (sgml-check-delim DSC)))
+	    (t (sgml-error "Illegal entity type: %s" type))))
+    type))
+
+
+;;;; Parse doctype: Attlist
+
+(defun sgml-declare-attlist ()
+  (let* ((assnot (cond ((sgml-parse-rni "notation")
+			(sgml-skip-ps)
+			t)))
+	 (assel (sgml-check-name-group))
+	 (attlist nil)			; the list
+	 (attdef nil))
+    (while (setq attdef (sgml-parse-attribute-definition))
+      (push attdef attlist))
+    (setq attlist (nreverse attlist))
+    (unless assnot
+      (sgml-before-eltype-modification)
+      (loop for elname in assel do
+	    (setf (sgml-eltype-attlist (sgml-lookup-eltype elname))
+		  attlist)))))
+
+(defun sgml-parse-attribute-definition ()
+  (sgml-skip-ps)
+  (if (sgml-is-delim MDC) ; End of attlist?
+      nil
+    (sgml-make-attdecl (sgml-check-name)
+		       (sgml-check-declared-value)
+		       (sgml-check-default-value))))
+
+(defun sgml-check-declared-value ()
+  (sgml-skip-ps)
+  (let ((type 'name-token-group)
+	(names nil))
+    (unless (eq (following-char) ?\()
+      (setq type (intern (sgml-check-name)))
+      (sgml-skip-ps))
+    (when (memq type '(name-token-group notation))
+      (setq names (sgml-check-nametoken-group)))
+    (sgml-make-declared-value type names)))
+
+(defun sgml-check-default-value ()
+  (sgml-skip-ps)
+  (let* ((rni (sgml-parse-rni))
+	 (key (if rni (intern (sgml-check-name)))))
+    (sgml-skip-ps)
+    (sgml-make-default-value
+     key
+     (if (or (not rni) (eq key 'fixed))
+	 (sgml-check-attribute-value-specification)))))
+
+
+;;;; Parse doctype: Shortref
+
+;;;150  short reference mapping declaration = MDO, "SHORTREF",
+;;;                        [[65 ps]]+, [[151 map name]],
+;;;                        ([[65 ps]]+, [[66 parameter literal]],
+;;;                        [[65 ps]]+, [[55 name]])+,
+;;;                        [[65 ps]]*, MDC
+
+(defun sgml-declare-shortref ()
+  (let ((mapname (sgml-check-name))
+	mappings literal name)
+    (while (progn
+	     (sgml-skip-ps)
+	     (setq literal (sgml-parse-parameter-literal 'dofunchar)))
+      (sgml-skip-ps)
+      (setq name (sgml-check-name t))
+      (push (cons literal name) mappings))
+    (sgml-add-shortref-map
+     (sgml-dtd-shortmaps sgml-dtd-info)
+     mapname
+     (sgml-make-shortmap mappings))))
+
+;;;152  short reference use declaration = MDO, "USEMAP",
+;;;                        [[65 ps]]+, [[153 map specification]],
+;;;                        ([[65 ps]]+, [[72 associated element type]])?,
+;;;                        [[65 ps]]*, MDC
+
+(defun sgml-do-usemap-element (mapname)
+  ;; This is called from sgml-do-usemap with the mapname
+  (sgml-before-eltype-modification)
+  (loop for e in (sgml-parse-name-group) do
+	(setf (sgml-eltype-shortmap (sgml-lookup-eltype e sgml-dtd-info))
+	      (if (null mapname)
+		  'empty
+		mapname))))
+
+
+;;;; Parse doctype
+
+(defun sgml-check-dtd-subset ()
+  (let ((sgml-parsing-dtd t)
+	(eref sgml-current-eref))
+    (while 
+	(progn
+	  (setq sgml-markup-start (point))
+	  (cond
+	   ((and (eobp) (eq sgml-current-eref eref))
+	    nil)
+	   ((sgml-parse-ds))
+	   ((sgml-parse-markup-declaration 'dtd))
+	   ((sgml-parse-delim "MS-END")))))))
+
+
+;;;; Save DTD: compute translation
+
+(defvar sgml-translate-table nil)
+
+(defun sgml-translate-node (node)
+  (assert (not (numberp node)))
+  (let ((tp (assq node sgml-translate-table)))
+    (unless tp
+      (setq tp (cons node (length sgml-translate-table)))
+      (nconc sgml-translate-table (list tp)))
+    (cdr tp)))
+
+(defun sgml-translate-moves (moves)
+  (while moves
+    (sgml-translate-node (sgml-move-dest (car moves)))
+    (setq moves (cdr moves))))
+
+(defun sgml-translate-model (model)
+  (let* ((sgml-translate-table (list (cons model 0)))
+	 (p sgml-translate-table))
+    (while p
+      (cond ((sgml-normal-state-p (caar p))
+	     (sgml-translate-moves (sgml-state-opts (caar p)))
+	     (sgml-translate-moves (sgml-state-reqs (caar p))))
+	    (t
+	     (sgml-translate-node (sgml-&node-next (caar p)))))
+      (setq p (cdr p)))
+    sgml-translate-table))
+
+;;;; Save DTD: binary coding
+
+(defvar sgml-code-token-numbers nil)
+(defvar sgml-code-xlate nil)
+
+(defsubst sgml-code-xlate (node)
+  ;;(let ((x (cdr (assq node sgml-code-xlate)))) (assert x) x)
+  (cdr (assq node sgml-code-xlate)))
+
+(defun sgml-code-number (num)
+  (if (> num sgml-max-single-octet-number)
+      (insert (+ (lsh (- num sgml-max-single-octet-number) -8)
+		 sgml-max-single-octet-number 1)
+	      (logand (- num sgml-max-single-octet-number) 255))
+    (insert num)))
+
+(defun sgml-code-token-number (token)
+  (let ((bp (assq token sgml-code-token-numbers)))
+    (unless bp
+      (setq sgml-code-token-numbers
+	    (nconc sgml-code-token-numbers
+		   (list (setq bp (cons token
+					(length sgml-code-token-numbers)))))))
+    (cdr bp)))
+
+(defun sgml-code-token (token)
+  (sgml-code-number (sgml-code-token-number token)))
+
+(defmacro sgml-code-sequence (loop-c &rest body)
+  "Produce the binary coding of a counted sequence from a list.
+Syntax: (var seq) &body forms
+FORMS should produce the binary coding of element in VAR."
+  (let ((var (car loop-c))
+	(seq (cadr loop-c)))
+    (` (let ((seq (, seq)))
+	 (sgml-code-number (length seq))       
+	 (loop for (, var) in seq 
+	       do (,@ body))))))
+
+(put 'sgml-code-sequence 'lisp-indent-hook 1)
+(put 'sgml-code-sequence 'edbug-forms-hook '(sexp &rest form))
+
+(defun sgml-code-sexp (sexp)
+  (let ((standard-output (current-buffer)))
+    (prin1 sexp)
+    (terpri)))
+
+(defun sgml-code-tokens (l)
+  (sgml-code-sequence (x l)
+    (sgml-code-token x)))
+
+(defsubst sgml-code-move (m)
+  (sgml-code-token (sgml-move-token m))
+  (insert (sgml-code-xlate (sgml-move-dest m))))
+
+(defun sgml-code-model (m)
+  (let ((sgml-code-xlate (sgml-translate-model m)))
+    (sgml-code-sequence (s sgml-code-xlate)		; s is (node . number)
+      (setq s (car s))			; s is node
+      (cond
+       ((sgml-normal-state-p s)
+	(assert (and (< (length (sgml-state-opts s)) 255)
+		     (< (length (sgml-state-reqs s)) 256)))
+	(sgml-code-sequence (x (sgml-state-opts s))
+	  (sgml-code-move x))
+	(sgml-code-sequence (x (sgml-state-reqs s))
+	  (sgml-code-move x)))
+       (t				; s is a &-node
+	(insert 255)			; Tag &-node
+	(insert (sgml-code-xlate (sgml-&node-next s)))
+	(sgml-code-sequence (m (sgml-&node-dfas s))
+	  (sgml-code-model m)))))))
+
+(defun sgml-code-element (et)
+  (sgml-code-sexp (sgml-eltype-all-miscdata et))
+  (cond
+   ((not (sgml-eltype-defined et))
+    (insert 128))
+   (t
+    (insert (sgml-eltype-flags et))
+    (let ((c (sgml-eltype-model et)))
+      (cond ((eq c sgml-cdata) (insert 0))
+	    ((eq c sgml-rcdata) (insert 1))
+	    ((eq c sgml-empty) (insert 2))
+	    ((eq c sgml-any) (insert 3))
+	    ((null c) (insert 4))
+	    (t
+	     (assert (sgml-model-group-p c))
+	     (insert 128)
+	     (sgml-code-model c))))
+    (sgml-code-tokens (sgml-eltype-includes et))
+    (sgml-code-tokens (sgml-eltype-excludes et)))))
+
+
+(defun sgml-code-dtd (dtd)
+  "Produce the binary coding of the current DTD into the current buffer."
+  (sgml-code-sexp (sgml-dtd-dependencies dtd))
+  (sgml-code-sexp (sgml-dtd-parameters dtd))
+  (sgml-code-sexp (sgml-dtd-doctype dtd))
+  (let ((done 0)			; count written elements
+	tot)
+    (setq sgml-code-token-numbers nil)
+    (sgml-code-token-number sgml-pcdata-token) ; Make #PCDATA token 0
+    (sgml-map-eltypes			; Assign numbers to all tokens
+     (function (lambda (et)
+		 (sgml-code-token-number (sgml-eltype-token et))))
+     dtd nil t)
+    (setq tot (length sgml-code-token-numbers))
+    ;; Produce the counted sequence of element type names
+    (sgml-code-sequence (pair (cdr sgml-code-token-numbers))
+      (sgml-code-sexp (sgml-eltype-name (car pair))))
+    ;; Produce the counted sequence of element types
+    (sgml-code-sequence (pair (cdr sgml-code-token-numbers))
+      (setq done (1+ done))
+      (sgml-code-element (car pair))
+      (sgml-lazy-message "Saving DTD %d%% done" (/ (* 100 done) tot)))
+    (sgml-code-sexp (sgml-dtd-entities dtd))
+    (sgml-code-sexp (sgml-dtd-shortmaps dtd))
+    (sgml-code-sexp (sgml-dtd-notations dtd))))
+
+
+;;;; Save DTD
+
+(defun sgml-save-dtd (file)
+  "Save the parsed dtd on FILE."
+  (interactive
+   (let* ((tem (expand-file-name
+		(or sgml-default-dtd-file
+		    (sgml-default-dtd-file))))
+	  (dir (file-name-directory tem))
+	  (nam (file-name-nondirectory tem)))
+     (list
+      (read-file-name "Save DTD in: " dir tem nil nam))))
+  (setq file (expand-file-name file))
+  (when (equal file (buffer-file-name))
+    (error "Would clobber current file"))
+  (sgml-need-dtd)
+  (sgml-push-to-entity (sgml-make-entity "#SAVE" nil ""))
+  (sgml-write-dtd sgml-dtd-info file)
+  (sgml-pop-entity)
+  (setq sgml-default-dtd-file
+	(if (equal (expand-file-name default-directory)
+		   (file-name-directory file))
+	    (file-name-nondirectory file)
+	  file))
+  (setq sgml-loaded-dtd file))
+
+(defun sgml-write-dtd (dtd file)
+  "Save the parsed dtd on FILE.
+Construct the binary coded DTD (bdtd) in the current buffer."
+  (insert
+   ";;; This file was created by psgml on " (current-time-string) "\n"
+   "(sgml-saved-dtd-version 6)\n")
+  (sgml-code-dtd dtd)
+  (setq file-type 1)
+  (write-region (point-min) (point-max) file))
+
+
+;;; psgml-dtd.el ends here