Remove arch-tags from all files, since these are no longer needed.
[gnus] / texi / xml2texi.scm
1 ;;; xml2texi.scm --- Convert gnus-faq.xml to gnus-faq.texi
2 ;; Copyright (C) 2005  Free Software Foundation, Inc.
3
4 ;; Author:  Karl Pflästerer <sigurd@12move.de>
5 ;; Keywords: tools
6
7 ;; This file is not part of GNU Emacs.
8
9 ;; GNU Emacs is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 3, or (at your option)
12 ;; any later version.
13
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 ;; GNU General Public License for more details.
18
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
21 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 ;; Boston, MA 02110-1301, USA.
23
24 ;;; Commentary:
25 ;; 
26
27 ;;; Code:
28
29 (require (lib "ssax.ss" "ssax")
30          (lib "sxpath.ss" "ssax")
31          (lib "sxml-tree-trans.ss" "ssax")
32          (lib "pregexp.ss")
33          (lib "list.ss")
34          (lib "etc.ss")
35          (rename (lib "1.ss" "srfi") list-index list-index)
36          (rename (lib "13.ss" "srfi") string-join string-join))
37
38
39 ;;; Constants
40 ;; In and out; for convenience if we work from the REPL
41 (define +infile+ "gnus-faq.xml")
42 (define +outfile+ "gnus-faq.texi")
43
44 ;; These are the names of the sections.  These variables hold the names
45 ;; of the sections where numbering starts in the main menu.
46 ;; Where we start numbering in menu
47 (define +first-numbered-section+ "Installation FAQ")
48 ;; Where we end numbering in menu
49 (define +last-numbered-section+ "Tuning Gnus")
50
51 ;; Which sections not to include; i.e. not to name a node.
52 (define +ignored-sections+ '("Frequently Asked Questions with Answers"))
53
54 ;; Names of menu entries and the corresponding descriptions (used in the
55 ;; main menu).
56 (define +section-comments-alist+
57     '(("Introduction" . "About Gnus and this FAQ.")
58       ("Installation FAQ" . "Installation of Gnus.")
59       ("Startup / Group buffer" . "Start up questions and the first buffer Gnus shows you.")
60       ("Getting Messages" . "Making Gnus read your mail and news.")
61       ("Reading messages" . "How to efficiently read messages.")
62       ("Composing messages" . "Composing mails or Usenet postings.")
63       ("Old messages" . "Importing, archiving, searching and deleting messages.")
64       ("Gnus in a dial-up environment" . "Reading mail and news while offline.")
65       ("Getting help" . "When this FAQ isn't enough.")
66       ("Tuning Gnus" .  "How to make Gnus faster.")
67       ("Glossary" . "Terms used in the FAQ explained.")))
68
69 ;; Where to break descriptions in menus
70 (define +width+ 72)
71
72 ;; The boilerplate text we include before the document
73 (define boilerplate
74     (lambda (titel)
75       (format
76        "\
77 @c \\input texinfo @c -*-texinfo-*-~%\
78 @c Uncomment 1st line before texing this file alone.~%\
79 @c %**start of header~%\
80 @c Copyright (C) 1995, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
81 @c   2008 Free Software Foundation, Inc.~%\
82 @c~%\
83 @c Do not modify this file, it was generated from gnus-faq.xml, available from~%\
84 @c <URL:http://my.gnus.org/FAQ/>.~%\
85 @c~%\
86 @setfilename gnus-faq.info~%\
87 @settitle ~A~%\
88 @c %**end of header~%\
89 @c~%\
90 " titel)))
91
92 ;; Inserted right before the end of the file
93 (define +tag-for-gnus-faq-texi+
94     (format "\
95 ~%\
96 @ignore~%\
97 arch-\
98 tag: 64dc5692-edb4-4848-a965-7aa0181acbb8~%\
99 @end ignore~%\
100 "))
101
102 ;;; Little Helpers
103 ;; (a b c) -> (1 2 3)
104 (define (number-list start inc lst)
105     (let loop ((lst lst) (lvl start) (acc '()))
106          (if (null? lst)
107            (reverse acc)
108            (loop (cdr lst) (+ inc lvl) (cons lvl acc)))))
109
110 ;; Given an alist made of regexps and their replacements (key and value
111 ;; are in a proper list) returns a function which given a string
112 ;; replaces all occurences of the regexps (from left to right).
113 ;; ((re1 repl1) (re2 repl2)) -> str -> str
114 (define make-reg-replacer
115     (lambda (defalist)
116       (let ((allreg (string-join (map car defalist) "|")))
117         (lambda (str)
118           (if (and (string? str) (pregexp-match allreg str))
119             (let loop ((lst defalist) (str str))
120                  (if (null? lst)
121                    str
122                    (loop (cdr lst) (pregexp-replace* (caar lst) str (cadar lst)))))
123             str)))))
124
125 (define escape-texi
126     (make-reg-replacer '(("@"  "@@") ("{"  "@{") ("}"  "@}"))))
127
128 (define normalize
129     (compose escape-texi (make-reg-replacer `((,(format "~%\\s+") ,(format "~%"))))))
130
131 (define normalize-example
132     (compose escape-texi (make-reg-replacer '(("^\\s+|\\s+$" "")))))
133
134 (define trim-ws (make-reg-replacer '(("^\\s+|\\s+$" ""))))
135
136 (define filter-sect
137     (lambda (lst)
138       (filter (lambda (e) (not (member e +ignored-sections+))) lst)))
139
140 ;;;; Para
141 (define format-para
142     (lambda (list-of-entries)
143       (format "~%~A~%" (trim-ws (apply string-append list-of-entries)))))
144
145 ;;;; Questions
146 (define format-q-level
147     (lambda (level)
148       ;; (apply format "[~A.~A]" (reverse level))))
149       (apply format "FAQ ~A-~A" (reverse level))))
150
151 (define format-q-description
152     (compose trim-ws (make-reg-replacer `((,(format "~%") " ")))))
153
154 ;;;; Building nodes
155 ;; curr-node up-node (list of nodes) (list of node names) ->
156 ;;   ((curr-node curr-name) (next next-name) (prev prev-name) up)
157 (define (find-prev-next-up curr up search-list name-list)
158     (do ((lst   search-list (cdr lst))
159          (rlst  name-list   (cdr rlst))
160          (prev  up   (car lst))
161          (prevn up   (car rlst)))
162         ((or (null? lst) (equal? (car lst) curr))
163          (values (cons curr (if (pair? rlst) (car rlst) curr))
164                  (if (and (pair? lst) (pair? (cdr lst))) ;next
165                    (cons (cadr lst) (cadr rlst))
166                    (cons "" ""))
167                  (cons prev prevn)
168                  up))))
169
170
171 (define (format-node section title up lst-of-nodes lst-of-names)
172     (if (member title +ignored-sections+)
173       ()
174       (call-with-values
175        (lambda () (find-prev-next-up title up lst-of-nodes lst-of-names))
176        (lambda (currn prevn nextn up)
177          (format "~%@node ~A~%~A ~A~%"
178                  (cdr currn) ;; (cdr prevn) (cdr nextn) up
179                  section ;; @subsection etc.
180                  (if (pair? title)
181                    (apply format "~A.~A" (reverse title))
182                    title))))))
183
184 ;;;; Building menus
185
186 (define format-menu
187     (lambda (alist-of-entries)
188       (let ((len (apply max (map (lambda (s) (string-length (car s))) alist-of-entries))))
189         (format "~%@menu~%~A@end menu~%"
190                 (apply string-append
191                        (map (lambda (e)
192                               (format "* ~A::~A~A~%"
193                                       (car e) ;the entry
194                                       (make-string (- len (string-length (car e)) -3) #\ )
195                                       (format-menu-description (cdr e) +width+ (+ len 7))))
196                             alist-of-entries))))))
197
198
199 (define format-menu-description
200     (lambda (entry width offset)
201       (let loop ((lst (pregexp-split "\\s" entry)) (len 0) (acc '()))
202            (if (null? lst)
203              (apply string-append (reverse! acc))
204              (let ((slen (+ 1 (string-length (car lst))))) ; +1 because of whitespace added later
205                (if (> (+ slen len) (- width offset))
206                  (loop (cdr lst) 0 (cons
207                                     (format "~%~A ~A"                 ; start a new line
208                                             (make-string offset #\ ) ; the whitespace
209                                             (car lst))
210                                     acc))
211                  (loop (cdr lst) (+ slen len) (cons (format " ~A"(car lst)) acc))))))))
212
213
214 (define format-sub-titles
215     (lambda (list-of-entries first-number-entry last-number-entry)
216       (let ((offset (or (list-index (lambda (e) (equal? e first-number-entry)) list-of-entries) 0))
217             (end (or (list-index (lambda (e) (equal? e last-number-entry)) list-of-entries)
218                      (length list-of-entries))))
219       (map (lambda (entry ind)
220              (format "FAQ ~A ~A"
221                      (if (<= offset ind end)
222                        (format "~A -" (- ind offset -1)) ;numbered entry
223                        "-")
224                      entry))
225            list-of-entries (number-list 0 1 list-of-entries)))))
226
227 ;;;; We number some sections first
228
229 ;; ntags is an alist => ((tag startcounter increment)
230 (define (number-nodes tree level ntags)
231     (if (null? ntags)
232       tree
233       (let* ((vals  (car ntags))
234              (ntag  (car vals))
235              (start (second vals))
236              (inc   (third vals))
237              (ntags (cdr ntags)))
238
239         (map
240          (lambda (node sublevel)
241            (pre-post-order
242             node
243             `((,ntag *preorder*
244                      . ,(lambda (tag . entry)
245                           `(,tag ,(cons sublevel level)
246                                  ,@(number-nodes entry (cons sublevel level) ntags))))
247               (*default* . ,(lambda x x))
248               (*text* . ,(lambda (tag s) s)))))
249          tree (number-list start inc tree)))))
250
251
252 ;;(transform->numbered faqsxml '(section article qandaset ((qandadiv 1 1) (qandaentry 0 1))))
253 (define transform->numbered
254     (lambda (sxml rules)
255       (let* ((rules (reverse rules))
256              (rule (car rules))
257              (ntag (cadr rules))
258              (styles (map (lambda (tag) (cons tag (lambda x x))) (list-tail rules 2))))
259   (pre-post-order
260    sxml
261      `((*default* *preorder* . ,(lambda x x))
262        (*TOP* . ,(lambda x x))
263        ,@styles
264        (,ntag *preorder*
265         . ,(lambda (tag . nodes)
266              (cons tag (number-nodes nodes '() rule)))))))))
267
268
269 ;;;; The main transform function
270
271 (define (transform sxml)
272     (let* ((sxml (transform->numbered
273                   sxml '(section article qandaset ((qandadiv 1 1) (qandaentry 0 1)))))
274            (qandadivtitles (filter-sect (map second ((sxpath '(// qandadiv title)) sxml))))
275            (fqandadivtitles (format-sub-titles qandadivtitles "" ""))
276            (subtitles (filter-sect (append (map second ((sxpath '(// section title)) sxml))
277                                            qandadivtitles
278                                            (map second ((sxpath '(// glossary title)) sxml)))))
279            (fsubtitles (format-sub-titles subtitles +first-numbered-section+
280                                           +last-numbered-section+))
281            (questlevel (map second ((sxpath '(article section qandaset qandadiv qandaentry)) sxml)))
282            (up1 (cadar ((sxpath '(article articleinfo title)) sxml)))
283
284 ;;; ************************************************************
285 ;;; The Style Sheet
286 ;;; ************************************************************
287            (style-sheet
288              `(
289 ;;; ************************************************************
290 ;;; First the SXML special markers
291 ;;; ************************************************************
292                ;; *TOP* *PI* @ are markers from SXML
293                (*TOP* . ,(lambda (tag . x) x))
294                (*PI* . ,(lambda _ '()))
295                (@ . ,(lambda _ ""))
296
297                ;; Look for the example rule where we overwrite the *text* rule
298                ;; so code doesn't get mangled.
299                (*text*
300                 . ,(lambda (tag string)
301                      (normalize string)))
302                ;; If nothing else matches
303                (*default* . ,(lambda x x))
304 ;;; ************************************************************
305 ;;; Now to the tags of our FAQ
306 ;;; ************************************************************
307                (article . ,(lambda (tag . sects)
308                              (list (boilerplate up1) sects 
309                                    +tag-for-gnus-faq-texi+)))
310
311                (articleinfo
312                 ((*default* . ,(lambda _ '()))
313                  (title
314                   . ,(lambda (tag titel)
315                        (let ((menucom (map (lambda (entry)
316                                              (let ((e (assoc entry +section-comments-alist+)))
317                                                (if e (cdr e) "")))
318                                            subtitles)))
319                          (list (format-node '@section titel "" '() '())
320                                (format-menu (map cons fsubtitles menucom)))))))
321                 . ,(lambda (tag . info) info))
322
323                ;; Sections
324                (abstract
325                 . ,(lambda (tag . text)
326                      (cons (format "~%@subheading Abstract~%") text)))
327                (section
328                 ((title
329                   . ,(lambda (tag titel)
330                        (format-node '@subheading titel up1 subtitles fsubtitles))))
331                 . ,(lambda (tag . entry) entry))
332
333                ;; Q&A well it's called FAQ isn't it?
334                (qandaset . ,(lambda (tag . x) x))
335                (qandadiv
336                 ((title
337                   . ,(lambda (tag titel) titel)))
338                 . ,(lambda (tag level titel . entries)
339                      (let ((questions (map cadr entries))
340                            (nlevel (filter (lambda (lvl) (eq? (car level) (cadr lvl))) questlevel)))
341                        (list*
342                         (format-node '@subsection titel up1 subtitles fsubtitles)
343                         (format-menu (map (lambda (lvl quest)
344                                             (cons (format-q-level lvl)
345                                                   (format-q-description quest)))
346                                           nlevel questions))
347                         entries))))
348                (qandaentry
349                 . ,(lambda (tag level question answer)
350                      (let ((nodes
351                              (filter (lambda (lvl) (eq? (cadr lvl) (cadr level))) questlevel))
352                            (up (list-ref fqandadivtitles (- (cadr level) 1))))
353                        (list*
354                         (format-node "@subsubheading Question" level up nodes (map format-q-level nodes))
355                         question answer))))
356                (question . ,(lambda (tag quest) quest))
357                (answer
358                 . ,(lambda (tag  . answ) (list* (format "~%@subsubheading Answer~%") answ)))
359
360                ;; Para
361                (para . ,(lambda (tag . x) (format-para x)))
362                (simpara . ,(lambda (tag . x) (cons (format "~%")  x)))
363
364                ;; Itemized lists.
365                ;; We rewrite para here because it plays here the role of an
366                ;; item marker
367                (itemizedlist
368                 . ,(lambda (tag lstitem)
369                      (format "~%@itemize @bullet~%~A@end itemize~%" lstitem)))
370                (listitem
371                 ((para
372                   . ,(lambda (tag item)
373                        (format "~%@item~%~A~%" (trim-ws item)))))
374                 . ,(lambda (tag . x) (string-join x "")))
375
376                ;; The glossary.
377                (glossary
378                 ((title . ,(lambda _'())))
379                 . ,(lambda (tag . terms)
380                      (let ((titel (cadar ((sxpath '(article glossary title)) sxml))))
381                        (cons (format-node '@subsection titel up1 subtitles fsubtitles)
382                              (list (format "~%@table @dfn~%")
383                                    terms
384                                    (format "~%@end table~%"))))))
385                (glossentry . ,(lambda (tag . entry) entry))
386                (glossterm
387                 . ,(lambda (tag term)
388                      (format "~%@item ~A" term)))
389                (glossdef
390                 . ,(lambda (tag def) def))
391
392                ;; Lisp examples
393                ;; We rewrite the *text* rule so code stays the way it's writen.
394                (programlisting
395                 ((*text*
396                   . ,(lambda (tag exampl)
397                        (normalize-example exampl))))
398                 . ,(lambda (tag . exampl)
399                      (format "~%@example~%~A~%@end example~%@noindent~%" (string-join exampl ""))))
400
401                ;; The link handling
402                ;; Here we are interested in the attributes, so we rewrite the @
403                ;; rule.  If we find a value we look if it's an email or http
404                ;; uri.
405                (ulink
406                 ((@
407                   . ,(lambda (at val) val)))
408                 . ,(lambda (tag uri name)
409                      (if (pregexp-match "^http:|^ftp:" uri)
410                          (if (equal? uri name)
411                              (format "@uref{~A}"  uri)
412                              (format "@uref{~A, ~A}"  uri name))
413                          (format "@email{~A, ~A}" (substring uri 7) name))))
414                (url
415                 . ,(lambda (tag val) val))
416
417                ;; userinput
418                (userinput
419                 . ,(lambda (tag val)
420                      (format "@samp{~A}" val)))
421                )))
422       (pre-post-order sxml style-sheet)))
423
424 ;;;; We call main with infile and outfile as arguments
425 (define main
426     (lambda (in out)
427       (with-output-to-file out
428         (lambda ()
429           (call-with-input-file in
430             (lambda (port)
431               (SRV:send-reply (transform (ssax:xml->sxml port '()))))))
432         'replace)))
433
434 ;; Local Variables:
435 ;; coding: iso-8859-1
436 ;; End:
437
438 ;;; xml2texi.scm ends here