(U+9F8D): Use `->subsumptive'.
[chise/xemacs-chise.git.1] / lisp / utf-2000 / char-db-util.el
1 ;;; char-db-util.el --- Character Database utility -*- coding: utf-8-er; -*-
2
3 ;; Copyright (C) 1998,1999,2000,2001,2002,2003,2004,2005 MORIOKA Tomohiko.
4
5 ;; Author: MORIOKA Tomohiko <tomo@kanji.zinbun.kyoto-u.ac.jp>
6 ;; Keywords: CHISE, Character Database, ISO/IEC 10646, UCS, Unicode, MULE.
7
8 ;; This file is part of XEmacs CHISE.
9
10 ;; XEmacs CHISE is free software; you can redistribute it and/or
11 ;; modify it under the terms of the GNU General Public License as
12 ;; published by the Free Software Foundation; either version 2, or (at
13 ;; your option) any later version.
14
15 ;; XEmacs CHISE is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 ;; General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with XEmacs CHISE; see the file COPYING.  If not, write to
22 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
24
25 ;;; Code:
26
27 (require 'alist)
28
29 (defconst unidata-normative-category-alist
30   '(("Lu" letter        uppercase)
31     ("Ll" letter        lowercase)
32     ("Lt" letter        titlecase)
33     ("Mn" mark          non-spacing)
34     ("Mc" mark          spacing-combining)
35     ("Me" mark          enclosing)
36     ("Nd" number        decimal-digit)
37     ("Nl" number        letter)
38     ("No" number        other)
39     ("Zs" separator     space)
40     ("Zl" separator     line)
41     ("Zp" separator     paragraph)
42     ("Cc" other         control)
43     ("Cf" other         format)
44     ("Cs" other         surrogate)
45     ("Co" other         private-use)
46     ("Cn" other         not-assigned)))
47
48 (defconst unidata-informative-category-alist
49   '(("Lm" letter        modifier)
50     ("Lo" letter        other)
51     ("Pc" punctuation   connector)
52     ("Pd" punctuation   dash)
53     ("Ps" punctuation   open)
54     ("Pe" punctuation   close)
55     ("Pi" punctuation   initial-quote)
56     ("Pf" punctuation   final-quote)
57     ("Po" punctuation   other)
58     ("Sm" symbol        math)
59     ("Sc" symbol        currency)
60     ("Sk" symbol        modifier)
61     ("So" symbol        other)
62     ))
63
64 (defconst ideographic-radicals
65   (let ((v (make-vector 215 nil))
66         (i 1))
67     (while (< i 215)
68       (aset v i (decode-char '=ucs (+ #x2EFF i)))
69       (setq i (1+ i)))
70     v))
71
72 (defun ideographic-radical (number)
73   (aref ideographic-radicals number))
74
75 (defconst shuowen-radicals
76   [?一 ?上 ?示 ?三 ?王 ?玉 ?玨 ?气 ?士 ?丨 ?屮 ?艸 ?茻])
77
78 (defun shuowen-radical (number)
79   (aref shuowen-radicals (1- number)))
80
81 (defvar char-db-file-coding-system 'utf-8-mcs-er)
82
83 (defvar char-db-feature-domains
84   '(ucs daikanwa cns gt jis jis/alt jis/a jis/b
85         jis-x0212 jis-x0213 cdp shinjigen misc unknown))
86
87 (defvar char-db-ignored-attributes '(ideographic-products))
88
89 (defun char-attribute-name< (ka kb)
90   (cond
91    ((eq '->denotational kb)
92     t)
93    ((eq '->subsumptive kb)
94     (not (eq '->denotational ka)))
95    ((eq '->denotational ka)
96     nil)
97    ((eq '->subsumptive ka)
98     nil)
99    ((and (symbolp ka)
100          (string-match "^->" (symbol-name ka)))
101     (cond ((and (symbolp kb)
102                 (string-match "^->" (symbol-name kb)))
103            (string< (symbol-name ka)
104                     (symbol-name kb))
105            ))
106     )
107    ((and (symbolp kb)
108          (string-match "^->" (symbol-name kb)))
109     t)
110    ((find-charset ka)
111     (if (find-charset kb)
112         (if (<= (charset-id ka) 1)
113             (if (<= (charset-id kb) 1)
114                 (cond
115                  ((= (charset-dimension ka)
116                      (charset-dimension kb))
117                   (> (charset-id ka)(charset-id kb)))
118                  (t
119                   (> (charset-dimension ka)
120                      (charset-dimension kb))
121                   ))
122               t)
123           (if (<= (charset-id kb) 1)
124               nil
125             (< (charset-id ka)(charset-id kb))))
126       nil))
127    ((find-charset kb)
128     t)
129    ((symbolp ka)
130     (cond ((symbolp kb)
131            (string< (symbol-name ka)
132                     (symbol-name kb)))
133           (t)))
134    ((symbolp kb)
135     nil)))
136
137 (defvar char-db-coded-charset-priority-list
138   '(ascii
139     control-1
140     latin-iso8859-1
141     latin-iso8859-2
142     latin-iso8859-3
143     latin-iso8859-4
144     latin-iso8859-9
145     latin-jisx0201
146     cyrillic-iso8859-5
147     greek-iso8859-7
148     thai-tis620
149     =jis-x0208
150     =jis-x0208@1978
151     =jis-x0208@1983
152     japanese-jisx0212
153     chinese-gb2312
154     chinese-cns11643-1
155     chinese-cns11643-2
156     chinese-cns11643-3
157     chinese-cns11643-4
158     chinese-cns11643-5
159     chinese-cns11643-6
160     chinese-cns11643-7
161     =jis-x0208@1990
162     =jis-x0213-1-2000
163     =jis-x0213-2-2000
164     korean-ksc5601
165     chinese-isoir165
166     katakana-jisx0201
167     hebrew-iso8859-8
168     chinese-gb12345
169     latin-viscii
170     ethiopic-ucs
171     =big5-cdp
172     =gt
173     ideograph-daikanwa-2
174     ideograph-daikanwa
175     =cbeta
176     ideograph-hanziku-1
177     ideograph-hanziku-2
178     ideograph-hanziku-3
179     ideograph-hanziku-4
180     ideograph-hanziku-5
181     ideograph-hanziku-6
182     ideograph-hanziku-7
183     ideograph-hanziku-8
184     ideograph-hanziku-9
185     ideograph-hanziku-10
186     ideograph-hanziku-11
187     ideograph-hanziku-12
188     =gt-k
189     =ucs@iso
190     =ucs@unicode
191     =big5
192     =big5-eten
193     =jis-x0208@1997
194     =zinbun-oracle
195     =ruimoku-v6
196     =jef-china3))
197
198 (defun char-db-make-char-spec (char)
199   (let (ret char-spec)
200     (cond ((characterp char)
201            (cond ((and (setq ret (encode-char char '=ucs 'defined-only))
202                        (not (and (<= #xE000 ret)(<= ret #xF8FF))))
203                   (setq char-spec (list (cons '=ucs ret)))
204                   (cond ((setq ret (get-char-attribute char 'name))
205                          (setq char-spec (cons (cons 'name ret) char-spec))
206                          )
207                         ((setq ret (get-char-attribute char 'name*))
208                          (setq char-spec (cons (cons 'name* ret) char-spec))
209                          ))
210                   )
211                  ((setq ret
212                         (catch 'tag
213                           (let ((rest char-db-coded-charset-priority-list)
214                                 ccs)
215                             (while rest
216                               (setq ccs (charset-name
217                                          (find-charset (car rest))))
218                               (if (setq ret
219                                         (encode-char char ccs
220                                                      'defined-only))
221                                   (throw 'tag (cons ccs ret)))
222                               (setq rest (cdr rest))))))
223                   (setq char-spec (list ret))
224                   (dolist (ccs (delq (car ret) (charset-list)))
225                     (if (and (or (charset-iso-final-char ccs)
226                                  (memq ccs
227                                        '(=daikanwa
228                                          =daikanwa@rev2
229                                          ;; =gt-k
230                                          )))
231                              (setq ccs (charset-name ccs))
232                              (null (assq ccs char-spec))
233                              (setq ret (encode-char char ccs 'defined-only)))
234                         (setq char-spec (cons (cons ccs ret) char-spec))))
235                   (if (null char-spec)
236                       (setq char-spec (split-char char)))
237                   (cond ((setq ret (get-char-attribute char 'name))
238                          (setq char-spec (cons (cons 'name ret) char-spec))
239                          )
240                         ((setq ret (get-char-attribute char 'name*))
241                          (setq char-spec (cons (cons 'name* ret) char-spec))
242                          ))
243                   ))
244            char-spec)
245           ((consp char)
246            char))))
247     
248 (defun char-db-insert-char-spec (char &optional readable column
249                                       required-features)
250   (unless column
251     (setq column (current-column)))
252   (let (char-spec temp-char)
253     (setq char-spec (char-db-make-char-spec char))
254     (unless (or (characterp char) ; char
255                 (condition-case nil
256                     (setq char (find-char char-spec))
257                   (error nil)))
258       ;; define temporary character
259       ;;   Current implementation is dirty.
260       (setq temp-char (define-char (cons '(ideograph-daikanwa . 0)
261                                          char-spec)))
262       (remove-char-attribute temp-char 'ideograph-daikanwa)
263       (setq char temp-char))
264     (insert-char-attributes char
265                             readable
266                             (union (mapcar #'car char-spec)
267                                    required-features))
268     (when temp-char
269       ;; undefine temporary character
270       ;;   Current implementation is dirty.
271       (setq char-spec (char-attribute-alist temp-char))
272       (while char-spec
273         (remove-char-attribute temp-char (car (car char-spec)))
274         (setq char-spec (cdr char-spec))))))
275
276 (defun char-db-insert-alist (alist &optional readable column)
277   (unless column
278     (setq column (current-column)))
279   (let ((line-breaking
280          (concat "\n" (make-string (1+ column) ?\ )))
281         name value
282         ret al ; cal
283         key
284         lbs cell rest separator)
285     (insert "(")
286     (while alist
287       (setq name (car (car alist))
288             value (cdr (car alist)))
289       (cond ((eq name 'char)
290              (insert "(char . ")
291              (if (setq ret (condition-case nil
292                                (find-char value)
293                              (error nil)))
294                  (progn
295                    (setq al nil
296                          ;; cal nil
297                          )
298                    (while value
299                      (setq key (car (car value)))
300                      ;; (if (find-charset key)
301                      ;;     (setq cal (cons key cal))
302                      (setq al (cons key al))
303                      ;; )
304                      (setq value (cdr value)))
305                    (insert-char-attributes ret
306                                            readable
307                                            (or al 'none) ; cal
308                                            ))
309                (insert (prin1-to-string value)))
310              (insert ")")
311              (insert line-breaking))
312             ((consp value)
313              (insert (format "(%-18s " name))
314              (setq lbs (concat "\n" (make-string (current-column) ?\ )))
315              (while (consp value)
316                (setq cell (car value))
317                (if (and (consp cell)
318                         (consp (car cell))
319                         (setq ret (condition-case nil
320                                       (find-char cell)
321                                     (error nil)))
322                         )
323                    (progn
324                      (setq rest cell
325                            al nil
326                            ;; cal nil
327                            )
328                      (while rest
329                        (setq key (car (car rest)))
330                        ;; (if (find-charset key)
331                        ;;     (setq cal (cons key cal))
332                        (setq al (cons key al))
333                        ;; )
334                        (setq rest (cdr rest)))
335                      (if separator
336                          (insert lbs))
337                      (insert-char-attributes ret
338                                              readable
339                                              al ; cal
340                                              )
341                      (setq separator lbs))
342                  (if separator
343                      (insert separator))
344                  (insert (prin1-to-string cell))
345                  (setq separator " "))
346                (setq value (cdr value)))
347              (insert ")")
348              (insert line-breaking))
349             (t
350              (insert (format "(%-18s . %S)%s"
351                              name value
352                              line-breaking))))
353       (setq alist (cdr alist))))
354   (insert ")"))
355
356 (defun char-db-insert-char-reference (plist &optional readable column)
357   (unless column
358     (setq column (current-column)))
359   (let ((line-breaking
360          (concat "\n" (make-string (1+ column) ?\ )))
361         (separator "")
362         name value)
363     (insert "(")
364     (while plist
365       (setq name (pop plist))
366       (setq value (pop plist))
367       (cond ((eq name :char)
368              (insert separator)
369              (insert ":char\t")
370              (cond ((numberp value)
371                     (setq value (decode-char '=ucs value)))
372                    ;; ((consp value)
373                    ;;  (setq value (or (find-char value)
374                    ;;                  value)))
375                    )
376              (char-db-insert-char-spec value readable)
377              (insert line-breaking)
378              (setq separator ""))
379             ((eq name :radical)
380              (insert (format "%s%s\t%d ; %c%s"
381                              separator
382                              name value
383                              (ideographic-radical value)
384                              line-breaking))
385              (setq separator ""))
386             (t
387              (insert (format "%s%s\t%S" separator name value))
388              (setq separator line-breaking)))
389       ))
390   (insert ")"))
391
392 (defun char-db-decode-isolated-char (ccs code-point)
393   (let (ret)
394     (setq ret
395           (cond ((eq ccs 'arabic-iso8859-6)
396                  (decode-char ccs code-point))
397                 ((and (memq ccs '(=gt-pj-1
398                                   =gt-pj-2
399                                   =gt-pj-3
400                                   =gt-pj-4
401                                   =gt-pj-5
402                                   =gt-pj-6
403                                   =gt-pj-7
404                                   =gt-pj-8
405                                   =gt-pj-9
406                                   =gt-pj-10
407                                   =gt-pj-11))
408                       (setq ret (decode-char ccs code-point))
409                       (setq ret (encode-char ret '=gt 'defined-only)))
410                  (decode-builtin-char '=gt ret))
411                 (t
412                  (decode-builtin-char ccs code-point))))
413     (cond ((and (<= 0 (char-int ret))
414                 (<= (char-int ret) #x1F))
415            (decode-char '=ucs (+ #x2400 (char-int ret))))
416           ((= (char-int ret) #x7F)
417            ?\u2421)
418           (t ret))))
419
420 (defvar char-db-convert-obsolete-format t)
421
422 (defun char-db-insert-ccs-feature (name value line-breaking)
423   (insert
424    (format
425     (cond ((memq name '(=daikanwa
426                         =daikanwa@rev1 =daikanwa@rev2
427                         =gt =gt-k =cbeta =zinbun-oracle))
428            "(%-18s . %05d)\t; %c")
429           ((eq name 'mojikyo)
430            "(%-18s . %06d)\t; %c")
431           ((>= (charset-dimension name) 2)
432            "(%-18s . #x%04X)\t; %c")
433           (t
434            "(%-18s . #x%02X)\t; %c"))
435     name
436     (if (= (charset-iso-graphic-plane name) 1)
437         (logior value
438                 (cond ((= (charset-dimension name) 1)
439                        #x80)
440                       ((= (charset-dimension name) 2)
441                        #x8080)
442                       ((= (charset-dimension name) 3)
443                        #x808080)
444                       (t 0)))
445       value)
446     (char-db-decode-isolated-char name value)))
447   (if (and (= (charset-chars name) 94)
448            (= (charset-dimension name) 2))
449       (insert (format " [%02d-%02d]"
450                       (- (lsh value -8) 32)
451                       (- (logand value 255) 32))))
452   (insert line-breaking))
453
454 (defun char-db-insert-relation-feature (char name value line-breaking
455                                              ccss readable)
456   (insert (format "(%-18s%s " name line-breaking))
457   (let ((lbs (concat "\n" (make-string (current-column) ?\ )))
458         separator cell sources required-features
459         ret)
460     (while (consp value)
461       (setq cell (car value))
462       (if (integerp cell)
463           (setq cell (decode-char '=ucs cell)))
464       (cond
465        ((eq name '->subsumptive)
466         (when (or (not (some (lambda (atr)
467                                (get-char-attribute cell atr))
468                              char-db-ignored-attributes))
469                   (some (lambda (ccs)
470                           (encode-char cell ccs 'defined-only))
471                         ccss))
472           (if separator
473               (insert lbs))
474           (let ((char-db-ignored-attributes
475                  (cons '<-subsumptive
476                        char-db-ignored-attributes)))
477             (insert-char-attributes cell readable))
478           (setq separator lbs))
479         )
480        ((characterp cell)
481         (setq sources
482               (get-char-attribute
483                char (intern (format "%s*sources" name))))
484         (setq required-features nil)
485         (dolist (source sources)
486           (cond
487            ((memq source '(JP JP/Jouyou shinjigen-1))
488             (setq required-features
489                   (union required-features
490                          '(=jis-x0208
491                            =jis-x0208@1990
492                            =jis-x0213-1-2000
493                            =jis-x0213-2-2000
494                            =jis-x0212
495                            =jis-x0208@1983
496                            =jis-x0208@1978))))
497            ((eq source 'CN)
498             (setq required-features
499                   (union required-features
500                          '(=gb2312
501                            =gb12345
502                            =iso-ir165)))))
503           (cond
504            ((find-charset (setq ret (intern (format "=%s" source))))
505             (setq required-features
506                   (cons ret required-features)))
507            (t (setq required-features
508                     (cons source required-features)))))
509         (cond ((string-match "@JP" (symbol-name name))
510                (setq required-features
511                      (union required-features
512                             '(=jis-x0208
513                               =jis-x0208@1990
514                               =jis-x0213-1-2000
515                               =jis-x0213-2-2000
516                               =jis-x0212
517                               =jis-x0208@1983
518                               =jis-x0208@1978))))
519               ((string-match "@CN" (symbol-name name))
520                (setq required-features
521                      (union required-features
522                             '(=gb2312
523                               =gb12345
524                               =iso-ir165)))))
525         (if separator
526             (insert lbs))
527         (if readable
528             (insert (format "%S" cell))
529           (char-db-insert-char-spec cell readable
530                                     nil
531                                     required-features))
532         (setq separator lbs))
533        ((consp cell)
534         (if separator
535             (insert lbs))
536         (if (consp (car cell))
537             (char-db-insert-char-spec cell readable)
538           (char-db-insert-char-reference cell readable))
539         (setq separator lbs))
540        (t
541         (if separator
542             (insert separator))
543         (insert (prin1-to-string cell))
544         (setq separator " ")))
545       (setq value (cdr value)))
546     (insert ")")
547     (insert line-breaking)))
548
549 (defun insert-char-attributes (char &optional readable attributes column)
550   (unless column
551     (setq column (current-column)))
552   (let (name value ; has-long-ccs-name
553         rest
554         radical strokes
555         (line-breaking
556          (concat "\n" (make-string (1+ column) ?\ )))
557         lbs cell separator ret
558         key al cal
559         dest-ccss ; sources required-features
560         ccss)
561     (let (atr-d)
562       (setq attributes
563             (sort (if attributes
564                       (if (consp attributes)
565                           (progn
566                             (dolist (name attributes)
567                               (unless (memq name char-db-ignored-attributes)
568                                 (if (find-charset name)
569                                     (push name ccss))
570                                 (push name atr-d)))
571                             atr-d))
572                     (dolist (name (char-attribute-list))
573                       (unless (memq name char-db-ignored-attributes)
574                         (if (find-charset name)
575                             (push name ccss))
576                         (push name atr-d)))
577                     atr-d)
578                   #'char-attribute-name<)))
579     (insert "(")
580     (when (memq '<-subsumptive attributes)
581       (when readable
582         (when (setq value (get-char-attribute char '<-subsumptive))
583           (char-db-insert-relation-feature char '<-subsumptive value
584                                            line-breaking
585                                            ccss readable)))
586       (setq attributes (delq '<-subsumptive attributes)))
587     (when (and (memq '<-denotational attributes)
588                (setq value (get-char-attribute char '<-denotational)))
589       (char-db-insert-relation-feature char '<-denotational value
590                                        line-breaking
591                                        ccss readable)
592       (setq attributes (delq '<-denotational attributes)))
593     (when (and (memq 'name attributes)
594                (setq value (get-char-attribute char 'name)))
595       (insert (format
596                (if (> (+ (current-column) (length value)) 48)
597                    "(name . %S)%s"
598                  "(name               . %S)%s")
599                value line-breaking))
600       (setq attributes (delq 'name attributes))
601       )
602     (when (and (memq 'name* attributes)
603                (setq value (get-char-attribute char 'name*)))
604       (insert (format
605                (if (> (+ (current-column) (length value)) 48)
606                    "(name* . %S)%s"
607                  "(name*              . %S)%s")
608                value line-breaking))
609       (setq attributes (delq 'name* attributes))
610       )
611     (when (and (memq 'script attributes)
612                (setq value (get-char-attribute char 'script)))
613       (insert (format "(script\t\t%s)%s"
614                       (mapconcat (function prin1-to-string)
615                                  value " ")
616                       line-breaking))
617       (setq attributes (delq 'script attributes))
618       )
619     (dolist (name '(=>ucs =>ucs*))
620       (when (and (memq name attributes)
621                  (setq value (get-char-attribute char name)))
622         (insert (format "(%-18s . #x%04X)\t; %c%s"
623                         name value (decode-char '=ucs value)
624                         line-breaking))
625         (setq attributes (delq name attributes))))
626     (dolist (name '(=>ucs@gb =>ucs@cns =>ucs@jis =>ucs@ks =>ucs@big5))
627       (when (and (memq name attributes)
628                  (setq value (get-char-attribute char name)))
629         (insert (format "(%-18s . #x%04X)\t; %c%s"
630                         name value
631                         (decode-char (intern
632                                       (concat "="
633                                               (substring
634                                                (symbol-name name) 2)))
635                                      value)
636                         line-breaking))
637         (setq attributes (delq name attributes))
638         ))
639     (dolist (name '(=>daikanwa))
640       (when (and (memq name attributes)
641                  (setq value (get-char-attribute char name)))
642         (insert
643          (if (integerp value)
644              (format "(%-18s . %05d)\t; %c%s"
645                      name value (decode-char '=daikanwa value)
646                      line-breaking)
647            (format "(%-18s %s)\t; %c%s"
648                    name
649                    (mapconcat (function prin1-to-string)
650                               value " ")
651                    (char-representative-of-daikanwa char)
652                    line-breaking)))
653         (setq attributes (delq name attributes))))
654     (when (and (memq 'general-category attributes)
655                (setq value (get-char-attribute char 'general-category)))
656       (insert (format
657                "(general-category\t%s) ; %s%s"
658                (mapconcat (lambda (cell)
659                             (format "%S" cell))
660                           value " ")
661                (cond ((rassoc value unidata-normative-category-alist)
662                       "Normative Category")
663                      ((rassoc value unidata-informative-category-alist)
664                       "Informative Category")
665                      (t
666                       "Unknown Category"))
667                line-breaking))
668       (setq attributes (delq 'general-category attributes))
669       )
670     (when (and (memq 'bidi-category attributes)
671                (setq value (get-char-attribute char 'bidi-category)))
672       (insert (format "(bidi-category\t. %S)%s"
673                       value
674                       line-breaking))
675       (setq attributes (delq 'bidi-category attributes))
676       )
677     (unless (or (not (memq 'mirrored attributes))
678                 (eq (setq value (get-char-attribute char 'mirrored 'empty))
679                     'empty))
680       (insert (format "(mirrored\t\t. %S)%s"
681                       value
682                       line-breaking))
683       (setq attributes (delq 'mirrored attributes))
684       )
685     (cond
686      ((and (memq 'decimal-digit-value attributes)
687            (setq value (get-char-attribute char 'decimal-digit-value)))
688       (insert (format "(decimal-digit-value . %S)%s"
689                       value
690                       line-breaking))
691       (setq attributes (delq 'decimal-digit-value attributes))
692       (when (and (memq 'digit-value attributes)
693                  (setq value (get-char-attribute char 'digit-value)))
694         (insert (format "(digit-value\t . %S)%s"
695                         value
696                         line-breaking))
697         (setq attributes (delq 'digit-value attributes))
698         )
699       (when (and (memq 'numeric-value attributes)
700                  (setq value (get-char-attribute char 'numeric-value)))
701         (insert (format "(numeric-value\t . %S)%s"
702                         value
703                         line-breaking))
704         (setq attributes (delq 'numeric-value attributes))
705         )
706       )
707      (t
708       (when (and (memq 'digit-value attributes)
709                  (setq value (get-char-attribute char 'digit-value)))
710         (insert (format "(digit-value\t. %S)%s"
711                         value
712                         line-breaking))
713         (setq attributes (delq 'digit-value attributes))
714         )
715       (when (and (memq 'numeric-value attributes)
716                  (setq value (get-char-attribute char 'numeric-value)))
717         (insert (format "(numeric-value\t. %S)%s"
718                         value
719                         line-breaking))
720         (setq attributes (delq 'numeric-value attributes))
721         )))
722     (when (and (memq 'iso-10646-comment attributes)
723                (setq value (get-char-attribute char 'iso-10646-comment)))
724       (insert (format "(iso-10646-comment\t. %S)%s"
725                       value
726                       line-breaking))
727       (setq attributes (delq 'iso-10646-comment attributes))
728       )
729     (when (and (memq 'morohashi-daikanwa attributes)
730                (setq value (get-char-attribute char 'morohashi-daikanwa)))
731       (insert (format "(morohashi-daikanwa\t%s)%s"
732                       (mapconcat (function prin1-to-string) value " ")
733                       line-breaking))
734       (setq attributes (delq 'morohashi-daikanwa attributes))
735       )
736     (setq radical nil
737           strokes nil)
738     (when (and (memq 'ideographic-radical attributes)
739                (setq value (get-char-attribute char 'ideographic-radical)))
740       (setq radical value)
741       (insert (format "(ideographic-radical . %S)\t; %c%s"
742                       radical
743                       (ideographic-radical radical)
744                       line-breaking))
745       (setq attributes (delq 'ideographic-radical attributes))
746       )
747     (when (and (memq 'shuowen-radical attributes)
748                (setq value (get-char-attribute char 'shuowen-radical)))
749       (insert (format "(shuowen-radical\t. %S)\t; %c%s"
750                       value
751                       (shuowen-radical value)
752                       line-breaking))
753       (setq attributes (delq 'shuowen-radical attributes))
754       )
755     (let (key)
756       (dolist (domain
757                (append
758                 char-db-feature-domains
759                 (let (dest domain)
760                   (dolist (feature (char-attribute-list))
761                     (setq feature (symbol-name feature))
762                     (when (string-match
763                            "\\(radical\\|strokes\\)@\\([^@*]+\\)\\(\\*\\|$\\)"
764                            feature)
765                       (setq domain (intern (match-string 2 feature)))
766                      (unless (memq domain dest)
767                        (setq dest (cons domain dest)))))
768                   (sort dest #'string<))))
769         (setq key (intern (format "%s@%s" 'ideographic-radical domain)))
770         (when (and (memq key attributes)
771                    (setq value (get-char-attribute char key)))
772           (setq radical value)
773           (insert (format "(%s . %S)\t; %c%s"
774                           key
775                           radical
776                           (ideographic-radical radical)
777                           line-breaking))
778           (setq attributes (delq key attributes))
779           )
780         (setq key (intern (format "%s@%s" 'ideographic-strokes domain)))
781         (when (and (memq key attributes)
782                    (setq value (get-char-attribute char key)))
783           (setq strokes value)
784           (insert (format "(%s . %S)%s"
785                           key
786                           strokes
787                           line-breaking))
788           (setq attributes (delq key attributes))
789           )
790         (setq key (intern (format "%s@%s" 'total-strokes domain)))
791         (when (and (memq key attributes)
792                    (setq value (get-char-attribute char key)))
793           (insert (format "(%s       . %S)%s"
794                           key
795                           value
796                           line-breaking))
797           (setq attributes (delq key attributes))
798           )
799         (dolist (feature '(ideographic-radical
800                            ideographic-strokes
801                            total-strokes))
802           (setq key (intern (format "%s@%s*sources" feature domain)))
803           (when (and (memq key attributes)
804                      (setq value (get-char-attribute char key)))
805             (insert (format "(%s%s" key line-breaking))
806             (dolist (cell value)
807               (insert (format " %s" cell)))
808             (insert ")")
809             (insert line-breaking)
810             (setq attributes (delq key attributes))
811             ))
812         ))
813     (when (and (memq 'ideographic-strokes attributes)
814                (setq value (get-char-attribute char 'ideographic-strokes)))
815       (setq strokes value)
816       (insert (format "(ideographic-strokes . %S)%s"
817                       strokes
818                       line-breaking))
819       (setq attributes (delq 'ideographic-strokes attributes))
820       )
821     (when (and (memq 'kangxi-radical attributes)
822                (setq value (get-char-attribute char 'kangxi-radical)))
823       (unless (eq value radical)
824         (insert (format "(kangxi-radical\t . %S)\t; %c%s"
825                         value
826                         (ideographic-radical value)
827                         line-breaking))
828         (or radical
829             (setq radical value)))
830       (setq attributes (delq 'kangxi-radical attributes))
831       )
832     (when (and (memq 'kangxi-strokes attributes)
833                (setq value (get-char-attribute char 'kangxi-strokes)))
834       (unless (eq value strokes)
835         (insert (format "(kangxi-strokes\t . %S)%s"
836                         value
837                         line-breaking))
838         (or strokes
839             (setq strokes value)))
840       (setq attributes (delq 'kangxi-strokes attributes))
841       )
842     (when (and (memq 'japanese-radical attributes)
843                (setq value (get-char-attribute char 'japanese-radical)))
844       (unless (eq value radical)
845         (insert (format "(japanese-radical\t . %S)\t; %c%s"
846                         value
847                         (ideographic-radical value)
848                         line-breaking))
849         (or radical
850             (setq radical value)))
851       (setq attributes (delq 'japanese-radical attributes))
852       )
853     (when (and (memq 'japanese-strokes attributes)
854                (setq value (get-char-attribute char 'japanese-strokes)))
855       (unless (eq value strokes)
856         (insert (format "(japanese-strokes\t . %S)%s"
857                         value
858                         line-breaking))
859         (or strokes
860             (setq strokes value)))
861       (setq attributes (delq 'japanese-strokes attributes))
862       )
863     (when (and (memq 'cns-radical attributes)
864                (setq value (get-char-attribute char 'cns-radical)))
865       (insert (format "(cns-radical\t . %S)\t; %c%s"
866                       value
867                       (ideographic-radical value)
868                       line-breaking))
869       (setq attributes (delq 'cns-radical attributes))
870       )
871     (when (and (memq 'cns-strokes attributes)
872                (setq value (get-char-attribute char 'cns-strokes)))
873       (unless (eq value strokes)
874         (insert (format "(cns-strokes\t . %S)%s"
875                         value
876                         line-breaking))
877         (or strokes
878             (setq strokes value)))
879       (setq attributes (delq 'cns-strokes attributes))
880       )
881     (when (and (memq 'shinjigen-1-radical attributes)
882                (setq value (get-char-attribute char 'shinjigen-1-radical)))
883       (unless (eq value radical)
884         (insert (format "(shinjigen-1-radical . %S)\t; %c%s"
885                         value
886                         (ideographic-radical value)
887                         line-breaking))
888         (or radical
889             (setq radical value)))
890       (setq attributes (delq 'shinjigen-1-radical attributes))
891       )
892     (when (and (memq 'ideographic- attributes)
893                (setq value (get-char-attribute char 'ideographic-)))
894       (insert "(ideographic-       ")
895       (setq lbs (concat "\n" (make-string (current-column) ?\ ))
896             separator nil)
897       (while (consp value)
898         (setq cell (car value))
899         (if (integerp cell)
900             (setq cell (decode-char '=ucs cell)))
901         (cond ((characterp cell)
902                (if separator
903                    (insert lbs))
904                (if readable
905                    (insert (format "%S" cell))
906                  (char-db-insert-char-spec cell readable))
907                (setq separator lbs))
908               ((consp cell)
909                (if separator
910                    (insert lbs))
911                (if (consp (car cell))
912                    (char-db-insert-char-spec cell readable)
913                  (char-db-insert-char-reference cell readable))
914                (setq separator lbs))
915               (t
916                (if separator
917                    (insert separator))
918                (insert (prin1-to-string cell))
919                (setq separator " ")))
920         (setq value (cdr value)))
921       (insert ")")
922       (insert line-breaking)
923       (setq attributes (delq 'ideographic- attributes)))
924     (when (and (memq 'total-strokes attributes)
925                (setq value (get-char-attribute char 'total-strokes)))
926       (insert (format "(total-strokes       . %S)%s"
927                       value
928                       line-breaking))
929       (setq attributes (delq 'total-strokes attributes))
930       )
931     (when (and (memq '->ideograph attributes)
932                (setq value (get-char-attribute char '->ideograph)))
933       (insert (format "(->ideograph\t%s)%s"
934                       (mapconcat (lambda (code)
935                                    (cond ((symbolp code)
936                                           (symbol-name code))
937                                          ((integerp code)
938                                           (format "#x%04X" code))
939                                          (t
940                                           (format "%s %S"
941                                                   line-breaking code))))
942                                  value " ")
943                       line-breaking))
944       (setq attributes (delq '->ideograph attributes))
945       )
946     ;; (when (and (memq '->decomposition attributes)
947     ;;            (setq value (get-char-attribute char '->decomposition)))
948     ;;   (insert (format "(->decomposition\t%s)%s"
949     ;;                   (mapconcat (lambda (code)
950     ;;                                (cond ((symbolp code)
951     ;;                                       (symbol-name code))
952     ;;                                      ((characterp code)
953     ;;                                       (if readable
954     ;;                                           (format "%S" code)
955     ;;                                         (format "#x%04X"
956     ;;                                                 (char-int code))
957     ;;                                         ))
958     ;;                                      ((integerp code)
959     ;;                                       (format "#x%04X" code))
960     ;;                                      (t
961     ;;                                       (format "%s%S" line-breaking code))))
962     ;;                              value " ")
963     ;;                   line-breaking))
964     ;;   (setq attributes (delq '->decomposition attributes))
965     ;;   )
966     (if (equal (get-char-attribute char '->titlecase)
967                (get-char-attribute char '->uppercase))
968         (setq attributes (delq '->titlecase attributes)))
969     (when (and (memq '->mojikyo attributes)
970                (setq value (get-char-attribute char '->mojikyo)))
971       (insert (format "(->mojikyo\t\t. %06d)\t; %c%s"
972                       value (decode-char 'mojikyo value)
973                       line-breaking))
974       (setq attributes (delq '->mojikyo attributes))
975       )
976     (when (and (memq 'hanyu-dazidian-vol attributes)
977                (setq value (get-char-attribute char 'hanyu-dazidian-vol)))
978       (insert (format "(hanyu-dazidian-vol  . %d)%s"
979                       value line-breaking))
980       (setq attributes (delq 'hanyu-dazidian-vol attributes))
981       )
982     (when (and (memq 'hanyu-dazidian-page attributes)
983                (setq value (get-char-attribute char 'hanyu-dazidian-page)))
984       (insert (format "(hanyu-dazidian-page . %d)%s"
985                       value line-breaking))
986       (setq attributes (delq 'hanyu-dazidian-page attributes))
987       )
988     (when (and (memq 'hanyu-dazidian-char attributes)
989                (setq value (get-char-attribute char 'hanyu-dazidian-char)))
990       (insert (format "(hanyu-dazidian-char . %d)%s"
991                       value line-breaking))
992       (setq attributes (delq 'hanyu-dazidian-char attributes))
993       )
994     (unless readable
995       (dolist (ignored '(composition
996                          ->denotational <-subsumptive ->ucs-unified
997                          ->ideographic-component-forms))
998         (setq attributes (delq ignored attributes))))
999     (while attributes
1000       (setq name (car attributes))
1001       (if (setq value (get-char-attribute char name))
1002           (cond ((setq ret (find-charset name))
1003                  (setq name (charset-name ret))
1004                  (if (and (not (memq name dest-ccss))
1005                           (prog1
1006                               (setq value (get-char-attribute char name))
1007                             (setq dest-ccss (cons name dest-ccss))))
1008                      (char-db-insert-ccs-feature name value line-breaking))
1009                  )
1010                 ((string-match "^=>ucs@" (symbol-name name))
1011                  (insert (format "(%-18s . #x%04X)\t; %c%s"
1012                                  name value (decode-char '=ucs value)
1013                                  line-breaking))
1014                  )
1015                 ((eq name 'jisx0208-1978/4X)
1016                  (insert (format "(%-18s . #x%04X)%s"
1017                                  name value
1018                                  line-breaking))
1019                  )
1020                 ((and
1021                   (not readable)
1022                   (not (eq name '->subsumptive))
1023                   (not (eq name '->bopomofo))
1024                   (not (eq name '->mistakable))
1025                   (not (eq name '->ideographic-variants))
1026                   (not (eq name '->canonical))
1027                   (null (get-char-attribute
1028                          char (intern (format "%s*sources" name))))
1029                   (not (string-match "\\*sources$" (symbol-name name)))
1030                   (or (eq name '<-identical)
1031                       (eq name '<-canonical)
1032                       (eq name '<-ideographic-variants)
1033                       ;; (eq name '<-synonyms)
1034                       (string-match "^<-synonyms" (symbol-name name))
1035                       (eq name '<-mistakable)
1036                       (when (string-match "^->" (symbol-name name))
1037                         (cond
1038                          ((string-match "^->fullwidth" (symbol-name name))
1039                           (not (and (consp value)
1040                                     (characterp (car value))
1041                                     (encode-char
1042                                      (car value) '=ucs 'defined-only)))
1043                           )
1044                          (t)))
1045                       ))
1046                  )
1047                 ((or (eq name 'ideographic-structure)
1048                      (eq name 'ideographic-combination)
1049                      (eq name 'ideographic-)
1050                      (eq name '=decomposition)
1051                      (string-match "^=>decomposition" (symbol-name name))
1052                      (string-match "^\\(->\\|<-\\)" (symbol-name name)))
1053                  (char-db-insert-relation-feature char name value
1054                                                   line-breaking
1055                                                   ccss readable))
1056                 ((memq name '(ideograph=
1057                               original-ideograph-of
1058                               ancient-ideograph-of
1059                               vulgar-ideograph-of
1060                               wrong-ideograph-of
1061                               ;; simplified-ideograph-of
1062                               ideographic-variants
1063                               ;; ideographic-different-form-of
1064                               ))
1065                  (insert (format "(%-18s%s " name line-breaking))
1066                  (setq lbs (concat "\n" (make-string (current-column) ?\ ))
1067                        separator nil)
1068                  (while (consp value)
1069                    (setq cell (car value))
1070                    (if (and (consp cell)
1071                             (consp (car cell)))
1072                        (progn
1073                          (if separator
1074                              (insert lbs))
1075                          (char-db-insert-alist cell readable)
1076                          (setq separator lbs))
1077                      (if separator
1078                          (insert separator))
1079                      (insert (prin1-to-string cell))
1080                      (setq separator " "))
1081                    (setq value (cdr value)))
1082                  (insert ")")
1083                  (insert line-breaking))
1084                 ((consp value)
1085                  (insert (format "(%-18s " name))
1086                  (setq lbs (concat "\n" (make-string (current-column) ?\ ))
1087                        separator nil)
1088                  (while (consp value)
1089                    (setq cell (car value))
1090                    (if (and (consp cell)
1091                             (consp (car cell))
1092                             (setq ret (condition-case nil
1093                                           (find-char cell)
1094                                         (error nil))))
1095                        (progn
1096                          (setq rest cell
1097                                al nil
1098                                cal nil)
1099                          (while rest
1100                            (setq key (car (car rest)))
1101                            (if (find-charset key)
1102                                (setq cal (cons key cal))
1103                              (setq al (cons key al)))
1104                            (setq rest (cdr rest)))
1105                          (if separator
1106                              (insert lbs))
1107                          (insert-char-attributes ret
1108                                                  readable
1109                                                  al cal)
1110                          (setq separator lbs))
1111                      (setq ret (prin1-to-string cell))
1112                      (if separator
1113                          (if (< (+ (current-column)
1114                                    (length ret)
1115                                    (length separator))
1116                                 76)
1117                              (insert separator)
1118                            (insert lbs)))
1119                      (insert ret)
1120                      (setq separator " "))
1121                    (setq value (cdr value)))
1122                  (insert ")")
1123                  (insert line-breaking))
1124                 (t
1125                  (insert (format "(%-18s . %S)%s"
1126                                  name value
1127                                  line-breaking)))
1128                 ))
1129       (setq attributes (cdr attributes)))
1130     (insert ")")))
1131
1132 (defun insert-char-data (char &optional readable
1133                               attributes)
1134   (save-restriction
1135     (narrow-to-region (point)(point))
1136     (insert "(define-char
1137   '")
1138     (insert-char-attributes char readable attributes)
1139     (insert ")\n")
1140     (goto-char (point-min))
1141     (while (re-search-forward "[ \t]+$" nil t)
1142       (replace-match ""))
1143     ;; from tabify.
1144     (goto-char (point-min))
1145     (while (re-search-forward "[ \t][ \t][ \t]*" nil t)
1146       (let ((column (current-column))
1147             (indent-tabs-mode t))
1148         (delete-region (match-beginning 0) (point))
1149         (indent-to column)))
1150     (goto-char (point-max))
1151     ;; (tabify (point-min)(point-max))
1152     ))
1153
1154 (defun insert-char-data-with-variant (char &optional printable
1155                                            no-ucs-unified
1156                                            script excluded-script)
1157   (insert-char-data char printable)
1158   (let ((variants (char-variants char))
1159         rest
1160         variant vs ret)
1161     (setq variants (sort variants #'<))
1162     (setq rest variants)
1163     (setq variants (cons char variants))
1164     (while rest
1165       (setq variant (car rest))
1166       (unless (get-char-attribute variant '<-subsumptive)
1167         (if (and (or (null script)
1168                      (null (setq vs (get-char-attribute variant 'script)))
1169                      (memq script vs))
1170                  (or (null excluded-script)
1171                      (null (setq vs (get-char-attribute variant 'script)))
1172                      (not (memq excluded-script vs))))
1173             (unless (and no-ucs-unified (get-char-attribute variant '=ucs))
1174               (insert-char-data variant printable)
1175               (if (setq ret (char-variants variant))
1176                   (while ret
1177                     (or (memq (car ret) variants)
1178                         ;; (get-char-attribute (car ret) '<-subsumptive)
1179                         (setq rest (nconc rest (list (car ret)))))
1180                     (setq ret (cdr ret)))))))
1181       (setq rest (cdr rest)))))
1182
1183 (defun insert-char-range-data (min max &optional script excluded-script)
1184   (let ((code min)
1185         char)
1186     (while (<= code max)
1187       (setq char (decode-char '=ucs code))
1188       (if (encode-char char '=ucs 'defined-only)
1189           (insert-char-data-with-variant char nil 'no-ucs-unified
1190                                          script excluded-script))
1191       (setq code (1+ code)))))
1192
1193 (defun write-char-range-data-to-file (min max file
1194                                           &optional script excluded-script)
1195   (let ((coding-system-for-write char-db-file-coding-system))
1196     (with-temp-buffer
1197       (insert (format ";; -*- coding: %s -*-\n"
1198                       char-db-file-coding-system))
1199       (insert-char-range-data min max script excluded-script)
1200       (write-region (point-min)(point-max) file))))
1201
1202 (defvar what-character-original-window-configuration)
1203
1204 ;;;###autoload
1205 (defun what-char-definition (char)
1206   (interactive (list (char-after)))
1207   (let ((buf (get-buffer-create "*Character Description*"))
1208         (the-buf (current-buffer))
1209         (win-conf (current-window-configuration)))
1210     (pop-to-buffer buf)
1211     (make-local-variable 'what-character-original-window-configuration)
1212     (setq what-character-original-window-configuration win-conf)
1213     (setq buffer-read-only nil)
1214     (erase-buffer)
1215     (condition-case err
1216         (progn
1217           (insert-char-data-with-variant char 'printable)
1218           (unless (char-attribute-alist char)
1219             (insert (format ";; = %c\n"
1220                             (let* ((rest (split-char char))
1221                                    (ccs (pop rest))
1222                                    (code (pop rest)))
1223                               (while rest
1224                                 (setq code (logior (lsh code 8)
1225                                                    (pop rest))))
1226                               (decode-char ccs code)))))
1227           ;; (char-db-update-comment)
1228           (set-buffer-modified-p nil)
1229           (view-mode the-buf (lambda (buf)
1230                                (set-window-configuration
1231                                 what-character-original-window-configuration)
1232                                ))
1233           (goto-char (point-min)))
1234       (error (progn
1235                (set-window-configuration
1236                 what-character-original-window-configuration)
1237                (signal (car err) (cdr err)))))))
1238
1239 (provide 'char-db-util)
1240
1241 ;;; char-db-util.el ends here