config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

ox-man.el (38958B)


      1 ;;; ox-man.el --- Man Backend for Org Export Engine -*- lexical-binding: t; -*-
      2 
      3 ;; Copyright (C) 2011-2024 Free Software Foundation, Inc.
      4 
      5 ;; Author: Nicolas Goaziou <n.goaziou at gmail dot com>
      6 ;;      Luis R Anaya <papoanaya aroba hot mail punto com>
      7 ;; Keywords: outlines, hypermedia, calendar, text
      8 
      9 ;; This file is part of GNU Emacs.
     10 
     11 ;; GNU Emacs is free software: you can redistribute it and/or modify
     12 ;; it under the terms of the GNU General Public License as published by
     13 ;; the Free Software Foundation, either version 3 of the License, or
     14 ;; (at your option) any later version.
     15 
     16 ;; GNU Emacs is distributed in the hope that it will be useful,
     17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
     18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     19 ;; GNU General Public License for more details.
     20 
     21 ;; You should have received a copy of the GNU General Public License
     22 ;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
     23 
     24 ;;; Commentary:
     25 ;;
     26 ;; This library implements a Man backend for Org generic exporter.
     27 ;;
     28 ;; To test it, run
     29 ;;
     30 ;;   M-: (org-export-to-buffer 'man "*Test Man*") RET
     31 ;;
     32 ;; in an Org buffer then switch to the buffer to see the Man export.
     33 ;; See ox.el for more details on how this exporter works.
     34 ;;
     35 ;; It introduces one new buffer keywords:
     36 ;; "MAN_CLASS_OPTIONS".
     37 
     38 ;;; Code:
     39 
     40 (require 'org-macs)
     41 (org-assert-version)
     42 
     43 (require 'cl-lib)
     44 (require 'ox)
     45 
     46 ;;; Function Declarations
     47 
     48 (defvar org-export-man-default-packages-alist)
     49 (defvar org-export-man-packages-alist)
     50 (defvar orgtbl-exp-regexp)
     51 
     52 
     53 
     54 ;;; Define Backend
     55 
     56 (org-export-define-backend 'man
     57   '((babel-call . org-man-babel-call)
     58     (bold . org-man-bold)
     59     (center-block . org-man-center-block)
     60     (code . org-man-code)
     61     (drawer . org-man-drawer)
     62     (dynamic-block . org-man-dynamic-block)
     63     (entity . org-man-entity)
     64     (example-block . org-man-example-block)
     65     (export-block . org-man-export-block)
     66     (export-snippet . org-man-export-snippet)
     67     (fixed-width . org-man-fixed-width)
     68     (footnote-definition . org-man-footnote-definition)
     69     (footnote-reference . org-man-footnote-reference)
     70     (headline . org-man-headline)
     71     (horizontal-rule . org-man-horizontal-rule)
     72     (inline-babel-call . org-man-inline-babel-call)
     73     (inline-src-block . org-man-inline-src-block)
     74     (inlinetask . org-man-inlinetask)
     75     (italic . org-man-italic)
     76     (item . org-man-item)
     77     (keyword . org-man-keyword)
     78     (line-break . org-man-line-break)
     79     (link . org-man-link)
     80     (node-property . org-man-node-property)
     81     (paragraph . org-man-paragraph)
     82     (plain-list . org-man-plain-list)
     83     (plain-text . org-man-plain-text)
     84     (planning . org-man-planning)
     85     (property-drawer . org-man-property-drawer)
     86     (quote-block . org-man-quote-block)
     87     (radio-target . org-man-radio-target)
     88     (section . org-man-section)
     89     (special-block . org-man-special-block)
     90     (src-block . org-man-src-block)
     91     (statistics-cookie . org-man-statistics-cookie)
     92     (strike-through . org-man-strike-through)
     93     (subscript . org-man-subscript)
     94     (superscript . org-man-superscript)
     95     (table . org-man-table)
     96     (table-cell . org-man-table-cell)
     97     (table-row . org-man-table-row)
     98     (target . org-man-target)
     99     (template . org-man-template)
    100     (timestamp . org-man-timestamp)
    101     (underline . org-man-underline)
    102     (verbatim . org-man-verbatim)
    103     (verse-block . org-man-verse-block))
    104   :menu-entry
    105   '(?M "Export to MAN"
    106        ((?m "As MAN file" org-man-export-to-man)
    107 	(?p "As PDF file" org-man-export-to-pdf)
    108 	(?o "As PDF file and open"
    109 	    (lambda (a s v b)
    110 	      (if a (org-man-export-to-pdf t s v b)
    111 		(org-open-file (org-man-export-to-pdf nil s v b)))))))
    112   :options-alist
    113   '((:man-class "MAN_CLASS" nil nil t)
    114     (:man-class-options "MAN_CLASS_OPTIONS" nil nil t)
    115     (:man-header-extra "MAN_HEADER" nil nil newline)
    116     ;; Other variables.
    117     (:man-tables-centered nil nil org-man-tables-centered)
    118     (:man-tables-verbatim nil nil org-man-tables-verbatim)
    119     (:man-table-scientific-notation nil nil org-man-table-scientific-notation)
    120     (:man-source-highlight nil nil org-man-source-highlight)
    121     (:man-source-highlight-langs nil nil org-man-source-highlight-langs)))
    122 
    123 
    124 
    125 ;;; User Configurable Variables
    126 
    127 (defgroup org-export-man nil
    128   "Options for exporting Org mode files to Man."
    129   :tag "Org Export Man"
    130   :group 'org-export)
    131 
    132 ;;; Tables
    133 
    134 (defcustom org-man-tables-centered t
    135   "When non-nil, tables are exported in a center environment."
    136   :group 'org-export-man
    137   :version "24.4"
    138   :package-version '(Org . "8.0")
    139   :type 'boolean)
    140 
    141 (defcustom org-man-tables-verbatim nil
    142   "When non-nil, tables are exported verbatim."
    143   :group 'org-export-man
    144   :version "24.4"
    145   :package-version '(Org . "8.0")
    146   :type 'boolean)
    147 
    148 
    149 (defcustom org-man-table-scientific-notation "%sE%s"
    150   "Format string to display numbers in scientific notation.
    151 The format should have \"%s\" twice, for mantissa and exponent
    152 \(i.e. \"%s\\\\times10^{%s}\").
    153 
    154 When nil, no transformation is made."
    155   :group 'org-export-man
    156   :version "24.4"
    157   :package-version '(Org . "8.0")
    158   :type '(choice
    159           (string :tag "Format string")
    160           (const :tag "No formatting")))
    161 
    162 
    163 ;;; Inlinetasks
    164 ;; Src blocks
    165 
    166 (defcustom org-man-source-highlight nil
    167   "Use GNU source highlight to embellish source blocks."
    168   :group 'org-export-man
    169   :version "24.4"
    170   :package-version '(Org . "8.0")
    171   :type 'boolean)
    172 
    173 
    174 (defcustom org-man-source-highlight-langs
    175   '((emacs-lisp "lisp") (lisp "lisp") (clojure "lisp")
    176     (scheme "scheme")
    177     (c "c") (cc "cpp") (csharp "csharp") (d "d")
    178     (fortran "fortran") (cobol "cobol") (pascal "pascal")
    179     (ada "ada") (asm "asm")
    180     (perl "perl") (cperl "perl")
    181     (python "python") (ruby "ruby") (tcl "tcl") (lua "lua")
    182     (java "java") (javascript "javascript")
    183     (tex "latex")
    184     (shell-script "sh") (awk "awk") (diff "diff") (m4 "m4")
    185     (ocaml "caml") (caml "caml")
    186     (sql "sql") (sqlite "sql")
    187     (html "html") (css "css") (xml "xml")
    188     (bat "bat") (bison "bison") (clipper "clipper")
    189     (ldap "ldap") (opa "opa")
    190     (php "php") (postscript "postscript") (prolog "prolog")
    191     (properties "properties") (makefile "makefile")
    192     (tml "tml") (vbscript "vbscript") (xorg "xorg"))
    193   "Alist mapping languages to their listing language counterpart.
    194 The key is a symbol, the major mode symbol without the \"-mode\".
    195 The value is the string that should be inserted as the language
    196 parameter for the listings package.  If the mode name and the
    197 listings name are the same, the language does not need an entry
    198 in this list - but it does not hurt if it is present."
    199   :group 'org-export-man
    200   :version "24.4"
    201   :package-version '(Org . "8.0")
    202   :type '(repeat
    203           (list
    204            (symbol :tag "Major mode       ")
    205            (string :tag "Listings language"))))
    206 
    207 
    208 ;;; Compilation
    209 
    210 (defcustom org-man-pdf-process
    211   '("tbl %f | eqn | groff -man | ps2pdf - > %b.pdf"
    212     "tbl %f | eqn | groff -man | ps2pdf - > %b.pdf"
    213     "tbl %f | eqn | groff -man | ps2pdf - > %b.pdf")
    214 
    215   "Commands to process a Man file to a PDF file.
    216 
    217 This is a list of strings, each of them will be given to the
    218 shell as a command.  %f in the command will be replaced by the
    219 relative file name, %F by the absolute file name, %b by the file
    220 base name (i.e. without directory and extension parts), %o by the
    221 base directory of the file and %O by the absolute file name of
    222 the output file.
    223 
    224 By default, Org uses 3 runs of to do the processing.
    225 
    226 Alternatively, this may be a Lisp function that does the
    227 processing.  This function should accept the file name as
    228 its single argument."
    229   :group 'org-export-man
    230   :version "24.4"
    231   :package-version '(Org . "8.0")
    232   :type '(choice
    233           (repeat :tag "Shell command sequence"
    234                   (string :tag "Shell command"))
    235           (const :tag "2 runs of pdfgroff"
    236                  ("tbl %f | eqn | groff -mm | ps2pdf - > %b.pdf"
    237                   "tbl %f | eqn | groff -mm | ps2pdf - > %b.pdf" ))
    238           (const :tag "3 runs of pdfgroff"
    239                  ("tbl %f | eqn | groff -mm | ps2pdf - > %b.pdf"
    240                   "tbl %f | eqn | groff -mm | ps2pdf - > %b.pdf"
    241                   "tbl %f | eqn | groff -mm | ps2pdf - > %b.pdf"))
    242           (function)))
    243 
    244 (defcustom org-man-logfiles-extensions
    245   '("log" "out" "toc")
    246   "The list of file extensions to consider as Man logfiles."
    247   :group 'org-export-man
    248   :version "24.4"
    249   :package-version '(Org . "8.0")
    250   :type '(repeat (string :tag "Extension")))
    251 
    252 (defcustom org-man-remove-logfiles t
    253   "Non-nil means remove the logfiles produced by PDF production.
    254 These are the .aux, .log, .out, and .toc files."
    255   :group 'org-export-man
    256   :version "24.4"
    257   :package-version '(Org . "8.0")
    258   :type 'boolean)
    259 
    260 
    261 
    262 ;;; Internal Functions
    263 
    264 (defun org-man--caption/label-string (element info)
    265   "Return caption and label Man string for ELEMENT.
    266 
    267 INFO is a plist holding contextual information.  If there's no
    268 caption nor label, return the empty string.
    269 
    270 For non-floats, see `org-man--wrap-label'."
    271   (let ((label (org-element-property :label element))
    272 	(main (org-export-get-caption element))
    273 	(short (org-export-get-caption element t)))
    274     (cond ((and (not main) (not label)) "")
    275 	  ((not main) (format "\\fI%s\\fP" label))
    276 	  ;; Option caption format with short name.
    277 	  (short (format "\\fR%s\\fP - \\fI\\P - %s\n"
    278 			 (org-export-data short info)
    279 			 (org-export-data main info)))
    280 	  ;; Standard caption format.
    281 	  (t (format "\\fR%s\\fP" (org-export-data main info))))))
    282 
    283 (defun org-man--wrap-label (element output)
    284   "Wrap label associated to ELEMENT around OUTPUT, if appropriate.
    285 This function shouldn't be used for floats.  See
    286 `org-man--caption/label-string'."
    287   (let ((label (org-element-property :name element)))
    288     (if (or (not output) (not label) (string= output "") (string= label ""))
    289         output
    290       (concat (format "%s\n.br\n" label) output))))
    291 
    292 (defun org-man--protect-text (text)
    293   "Protect minus and backslash characters in string TEXT."
    294   (replace-regexp-in-string "-" "\\-" text nil t))
    295 
    296 (defun org-man--protect-example (text)
    297   "Escape necessary characters for verbatim TEXT."
    298   ;; See man groff_man_style; \e must be used to render backslash.
    299   ;; Note that groff's .eo (disable backslash) and .ec (re-enable
    300   ;; backslash) cannot be used as per the same man page.
    301   (replace-regexp-in-string "\\\\" "\\e" text nil t))
    302 
    303 
    304 
    305 ;;; Template
    306 
    307 (defun org-man-template (contents info)
    308   "Return complete document string after Man conversion.
    309 CONTENTS is the transcoded contents string.  INFO is a plist
    310 holding export options."
    311   (let* ((title (when (plist-get info :with-title)
    312 		  (org-export-data (plist-get info :title) info)))
    313          (attr (read (format "(%s)"
    314                              (mapconcat
    315                               #'identity
    316                               (list (plist-get info :man-class-options))
    317                               " "))))
    318          (section-item (plist-get attr :section-id)))
    319 
    320     (concat
    321 
    322      (cond
    323       ((and title (stringp section-item))
    324        (format ".TH \"%s\" \"%s\" \n" title section-item))
    325       ((and (string= "" title) (stringp section-item))
    326        (format ".TH \"%s\" \"%s\" \n" " " section-item))
    327       (title
    328        (format ".TH \"%s\" \"1\" \n" title))
    329       (t
    330        ".TH \" \" \"1\" "))
    331      contents)))
    332 
    333 
    334 
    335 
    336 ;;; Transcode Functions
    337 
    338 ;;; Babel Call
    339 ;;
    340 ;; Babel Calls are ignored.
    341 
    342 
    343 ;;; Bold
    344 
    345 (defun org-man-bold (_bold contents _info)
    346   "Transcode BOLD from Org to Man.
    347 CONTENTS is the text with bold markup.  INFO is a plist holding
    348 contextual information."
    349   (format "\\fB%s\\fP" contents))
    350 
    351 
    352 ;;; Center Block
    353 
    354 (defun org-man-center-block (center-block contents _info)
    355   "Transcode a CENTER-BLOCK element from Org to Man.
    356 CONTENTS holds the contents of the center block.  INFO is a plist
    357 holding contextual information."
    358   (org-man--wrap-label
    359    center-block
    360    (format ".ce %d\n.nf\n%s\n.fi"
    361            (- (length (split-string contents "\n")) 1 )
    362            contents)))
    363 
    364 
    365 ;;; Code
    366 
    367 (defun org-man-code (code _contents _info)
    368   "Transcode a CODE object from Org to Man."
    369   (format "\\fC%s\\fP"
    370 	  (org-man--protect-text (org-element-property :value code))))
    371 
    372 
    373 ;;; Drawer
    374 
    375 (defun org-man-drawer (_drawer contents _info)
    376   "Transcode a DRAWER element from Org to Man.
    377 DRAWER holds the drawer information
    378 CONTENTS holds the contents of the block.
    379 INFO is a plist holding contextual information."
    380   contents)
    381 
    382 
    383 ;;; Dynamic Block
    384 
    385 (defun org-man-dynamic-block (dynamic-block contents _info)
    386   "Transcode a DYNAMIC-BLOCK element from Org to Man.
    387 CONTENTS holds the contents of the block.  INFO is a plist
    388 holding contextual information.  See `org-export-data'."
    389   (org-man--wrap-label dynamic-block contents))
    390 
    391 
    392 ;;; Entity
    393 
    394 (defun org-man-entity (entity _contents _info)
    395   "Transcode an ENTITY object from Org to Man.
    396 CONTENTS are the definition itself.  INFO is a plist holding
    397 contextual information."
    398   (org-element-property :utf-8 entity))
    399 
    400 
    401 ;;; Example Block
    402 
    403 (defun org-man-example-block (example-block _contents info)
    404   "Transcode an EXAMPLE-BLOCK element from Org to Man.
    405 CONTENTS is nil.  INFO is a plist holding contextual
    406 information."
    407   (org-man--wrap-label
    408    example-block
    409    (format ".RS\n.nf\n%s\n.fi\n.RE"
    410            (org-man--protect-example (org-export-format-code-default example-block info)))))
    411 
    412 
    413 ;;; Export Block
    414 
    415 (defun org-man-export-block (export-block _contents _info)
    416   "Transcode a EXPORT-BLOCK element from Org to Man.
    417 CONTENTS is nil.  INFO is a plist holding contextual information."
    418   (when (string= (org-element-property :type export-block) "MAN")
    419     (org-remove-indentation (org-element-property :value export-block))))
    420 
    421 
    422 ;;; Export Snippet
    423 
    424 (defun org-man-export-snippet (export-snippet _contents _info)
    425   "Transcode a EXPORT-SNIPPET object from Org to Man.
    426 CONTENTS is nil.  INFO is a plist holding contextual information."
    427   (when (eq (org-export-snippet-backend export-snippet) 'man)
    428     (org-element-property :value export-snippet)))
    429 
    430 
    431 ;;; Fixed Width
    432 
    433 (defun org-man-fixed-width (fixed-width _contents _info)
    434   "Transcode a FIXED-WIDTH element from Org to Man.
    435 CONTENTS is nil.  INFO is a plist holding contextual information."
    436   (org-man--wrap-label
    437    fixed-width
    438    (format "\\fC\n%s\n\\fP"
    439            (org-remove-indentation
    440             (org-element-property :value fixed-width)))))
    441 
    442 
    443 ;;; Footnote Definition
    444 ;;
    445 ;; Footnote Definitions are ignored.
    446 
    447 ;;; Footnote References
    448 ;;
    449 ;; Footnote References are Ignored
    450 
    451 
    452 ;;; Headline
    453 
    454 (defun org-man-headline (headline contents info)
    455   "Transcode a HEADLINE element from Org to Man.
    456 CONTENTS holds the contents of the headline.  INFO is a plist
    457 holding contextual information."
    458   (let* ((level (org-export-get-relative-level headline info))
    459 	 ;; Section formatting will set two placeholders: one for the
    460 	 ;; title and the other for the contents.
    461 	 (section-fmt
    462 	  (pcase level
    463 	    (1 ".SH \"%s\"\n%s")
    464 	    (2 ".SS \"%s\"\n%s")
    465 	    (3 ".SS \"%s\"\n%s")
    466 	    (_ nil)))
    467 	 (text (org-export-data (org-element-property :title headline) info)))
    468 
    469     (cond
    470      ;; Case 1: This is a footnote section: ignore it.
    471      ((org-element-property :footnote-section-p headline) nil)
    472 
    473      ;; Case 2. This is a deep sub-tree: export it as a list item.
    474      ;;         Also export as items headlines for which no section
    475      ;;         format has been found.
    476      ((or (not section-fmt) (org-export-low-level-p headline info))
    477       ;; Build the real contents of the sub-tree.
    478       (let ((low-level-body
    479 	     (concat
    480 	      ;; If the headline is the first sibling, start a list.
    481 	      (when (org-export-first-sibling-p headline info)
    482 		(format "%s\n" ".RS"))
    483 	      ;; Itemize headline
    484 	      ".TP\n.ft I\n" text "\n.ft\n"
    485 	      contents ".RE")))
    486 	;; If headline is not the last sibling simply return
    487 	;; LOW-LEVEL-BODY.  Otherwise, also close the list, before any
    488 	;; blank line.
    489 	(if (not (org-export-last-sibling-p headline info)) low-level-body
    490 	  (replace-regexp-in-string
    491 	   "[ \t\n]*\\'" ""
    492 	   low-level-body))))
    493 
    494      ;; Case 3. Standard headline.  Export it as a section.
    495      (t (format section-fmt text contents )))))
    496 
    497 ;;; Horizontal Rule
    498 ;; Not supported
    499 
    500 ;;; Inline Babel Call
    501 ;;
    502 ;; Inline Babel Calls are ignored.
    503 
    504 ;;; Inline Src Block
    505 
    506 (defun org-man-inline-src-block (inline-src-block _contents info)
    507   "Transcode an INLINE-SRC-BLOCK element from Org to Man.
    508 CONTENTS holds the contents of the item.  INFO is a plist holding
    509 contextual information."
    510   (let* ((code (org-element-property :value inline-src-block)))
    511     (cond
    512      ((plist-get info :man-source-highlight)
    513       (let* ((tmpdir temporary-file-directory)
    514              (in-file  (make-temp-name
    515                         (expand-file-name "srchilite" tmpdir)))
    516              (out-file (make-temp-name
    517                         (expand-file-name "reshilite" tmpdir)))
    518              (org-lang (org-element-property :language inline-src-block))
    519              (lst-lang
    520               (and org-lang
    521 	           (cadr (assq (intern org-lang)
    522 			       (plist-get info :man-source-highlight-langs)))))
    523 
    524              (cmd (concat (expand-file-name "source-highlight")
    525                           " -s " lst-lang
    526                           " -f groff_man"
    527                           " -i " in-file
    528                           " -o " out-file )))
    529 
    530         (if lst-lang
    531             (let ((code-block "" ))
    532               (with-temp-file in-file (insert code))
    533               (shell-command cmd)
    534               (setq code-block  (org-file-contents out-file))
    535               (delete-file in-file)
    536               (delete-file out-file)
    537               code-block)
    538           (format ".RS\n.nf\n\\fC\\m[black]%s\\m[]\\fP\n.fi\n.RE\n"
    539                   (org-man--protect-example code)))))
    540 
    541      ;; Do not use a special package: transcode it verbatim.
    542      (t
    543       (concat ".RS\n.nf\n" "\\fC" "\n" (org-man--protect-example code) "\n"
    544               "\\fP\n.fi\n.RE\n")))))
    545 
    546 
    547 ;;; Inlinetask
    548 ;;; Italic
    549 
    550 (defun org-man-italic (_italic contents _info)
    551   "Transcode ITALIC from Org to Man.
    552 CONTENTS is the text with italic markup.  INFO is a plist holding
    553 contextual information."
    554   (format "\\fI%s\\fP" contents))
    555 
    556 
    557 ;;; Item
    558 
    559 
    560 (defun org-man-item (item contents info)
    561   "Transcode an ITEM element from Org to Man.
    562 CONTENTS holds the contents of the item.  INFO is a plist holding
    563 contextual information."
    564   (let* ((bullet (org-element-property :bullet item))
    565          (type (org-element-property :type (org-element-parent item)))
    566          (checkbox (pcase (org-element-property :checkbox item)
    567                      (`on "\\o'\\(sq\\(mu'")
    568                      (`off "\\(sq ")
    569                      (`trans "\\o'\\(sq\\(mi'")))
    570 
    571          (tag (let ((tag (org-element-property :tag item)))
    572                 ;; Check-boxes must belong to the tag.
    573                 (and tag (format "\\fB%s\\fP"
    574                                  (concat checkbox
    575                                          (org-export-data tag info)))))))
    576 
    577     (if (and (null tag) (null checkbox))
    578 	(let* ((bullet (org-trim bullet))
    579 	       (marker (cond  ((string= "-" bullet) "\\(em")
    580 			      ((string= "*" bullet) "\\(bu")
    581 			      ((eq type 'ordered)
    582 			       (format "%s " (org-trim bullet)))
    583 			      (t "\\(dg"))))
    584 	  (concat ".IP " marker " 4\n"
    585 		  (org-trim (or contents " " ))))
    586       (concat ".TP\n" (or tag (concat " " checkbox)) "\n"
    587               (org-trim (or contents " " ))))))
    588 
    589 ;;; Keyword
    590 
    591 
    592 (defun org-man-keyword (keyword _contents _info)
    593   "Transcode a KEYWORD element from Org to Man.
    594 CONTENTS is nil.  INFO is a plist holding contextual information."
    595   (let ((key (org-element-property :key keyword))
    596         (value (org-element-property :value keyword)))
    597     (cond
    598      ((string= key "MAN") value)
    599      ((string= key "INDEX") nil)
    600      ((string= key "TOC"   ) nil))))
    601 
    602 
    603 ;;; Line Break
    604 
    605 (defun org-man-line-break (_line-break _contents _info)
    606   "Transcode a LINE-BREAK object from Org to Man.
    607 CONTENTS is nil.  INFO is a plist holding contextual information."
    608   "\n.br\n")
    609 
    610 
    611 ;;; Link
    612 
    613 
    614 (defun org-man-link (link desc info)
    615   "Transcode a LINK object from Org to Man.
    616 
    617 DESC is the description part of the link, or the empty string.
    618 INFO is a plist holding contextual information.  See
    619 `org-export-data'."
    620   (let* ((type (org-element-property :type link))
    621 	 (raw-path (org-element-property :path link))
    622          ;; Ensure DESC really exists, or set it to nil.
    623          (desc (and (not (string= desc "")) desc))
    624          (path (pcase type
    625                  ("file" (org-export-file-uri raw-path))
    626                  (_ (concat type ":" raw-path)))))
    627     (cond
    628      ;; Link type is handled by a special function.
    629      ((org-export-custom-protocol-maybe link desc 'man info))
    630      ;; External link with a description part.
    631      ((and path desc) (format "%s \\fBat\\fP \\fI%s\\fP" path desc))
    632      ;; External link without a description part.
    633      (path (format "\\fI%s\\fP" path))
    634      ;; No path, only description.  Try to do something useful.
    635      (t (format "\\fI%s\\fP" desc)))))
    636 
    637 ;;;; Node Property
    638 
    639 (defun org-man-node-property (node-property _contents _info)
    640   "Transcode a NODE-PROPERTY element from Org to Man.
    641 CONTENTS is nil.  INFO is a plist holding contextual
    642 information."
    643   (format "%s:%s"
    644           (org-element-property :key node-property)
    645           (let ((value (org-element-property :value node-property)))
    646             (if value (concat " " value) ""))))
    647 
    648 ;;; Paragraph
    649 
    650 (defun org-man-paragraph (paragraph contents _info)
    651   "Transcode a PARAGRAPH element from Org to Man.
    652 CONTENTS is the contents of the paragraph, as a string.  INFO is
    653 the plist used as a communication channel."
    654   (let ((parent (org-element-parent paragraph)))
    655     (when parent
    656       (let ((parent-type (org-element-type parent))
    657             (fixed-paragraph ""))
    658         (cond ((and (eq parent-type 'item)
    659                     (org-element-property :bullet parent))
    660                (setq fixed-paragraph (concat "" contents)))
    661               ((eq parent-type 'section)
    662                (setq fixed-paragraph (concat ".PP\n" contents)))
    663               ((eq parent-type 'footnote-definition)
    664                (setq fixed-paragraph contents))
    665               (t (setq fixed-paragraph (concat "" contents))))
    666         fixed-paragraph))))
    667 
    668 
    669 ;;; Plain List
    670 
    671 (defun org-man-plain-list (_plain-list contents _info)
    672   "Transcode a PLAIN-LIST element from Org to Man.
    673 CONTENTS is the contents of the list.  INFO is a plist holding
    674 contextual information."
    675   contents)
    676 
    677 ;;; Plain Text
    678 
    679 (defun org-man-plain-text (text info)
    680   "Transcode a TEXT string from Org to Man.
    681 TEXT is the string to transcode.  INFO is a plist holding
    682 contextual information."
    683   (let ((output text))
    684     ;; Protect various chars.
    685     (setq output (replace-regexp-in-string
    686 		  "\\(?:[^\\]\\|^\\)\\(\\\\\\)\\(?:[^%$#&{}~^_\\]\\|$\\)"
    687 		  "$\\" output nil t 1))
    688     ;; Activate smart quotes.  Be sure to provide original TEXT string
    689     ;; since OUTPUT may have been modified.
    690     (when (plist-get info :with-smart-quotes)
    691       (setq output (org-export-activate-smart-quotes output :utf-8 info text)))
    692     ;; Handle break preservation if required.
    693     (when (plist-get info :preserve-breaks)
    694       (setq output (replace-regexp-in-string "\\(\\\\\\\\\\)?[ \t]*\n" ".br\n"
    695 					     output)))
    696     ;; Return value.
    697     output))
    698 
    699 
    700 
    701 ;;; Planning
    702 
    703 
    704 ;;; Property Drawer
    705 
    706 (defun org-man-property-drawer (_property-drawer contents _info)
    707   "Transcode a PROPERTY-DRAWER element from Org to Man.
    708 CONTENTS holds the contents of the drawer.  INFO is a plist
    709 holding contextual information."
    710   (and (org-string-nw-p contents)
    711        (format ".RS\n.nf\n%s\n.fi\n.RE" contents)))
    712 
    713 ;;; Quote Block
    714 
    715 (defun org-man-quote-block (quote-block contents _info)
    716   "Transcode a QUOTE-BLOCK element from Org to Man.
    717 CONTENTS holds the contents of the block.  INFO is a plist
    718 holding contextual information."
    719   (org-man--wrap-label
    720    quote-block
    721    (format ".RS\n%s\n.RE" contents)))
    722 
    723 
    724 ;;; Radio Target
    725 
    726 (defun org-man-radio-target (_radio-target text _info)
    727   "Transcode a RADIO-TARGET object from Org to Man.
    728 TEXT is the text of the target.  INFO is a plist holding
    729 contextual information."
    730   text)
    731 
    732 
    733 ;;; Section
    734 
    735 (defun org-man-section (_section contents _info)
    736   "Transcode a SECTION element from Org to Man.
    737 CONTENTS holds the contents of the section.  INFO is a plist
    738 holding contextual information."
    739   contents)
    740 
    741 
    742 ;;; Special Block
    743 
    744 (defun org-man-special-block (special-block contents _info)
    745   "Transcode a SPECIAL-BLOCK element from Org to Man.
    746 CONTENTS holds the contents of the block.  INFO is a plist
    747 holding contextual information."
    748   (org-man--wrap-label special-block (format "%s\n" contents)))
    749 
    750 
    751 ;;; Src Block
    752 
    753 (defun org-man-src-block (src-block _contents info)
    754   "Transcode a SRC-BLOCK element from Org to Man.
    755 CONTENTS holds the contents of the item.  INFO is a plist holding
    756 contextual information."
    757   (if (not (plist-get info :man-source-highlight))
    758       (format ".RS\n.nf\n\\fC%s\\fP\n.fi\n.RE\n\n"
    759 	      (org-man--protect-example (org-export-format-code-default src-block info)))
    760     (let* ((tmpdir temporary-file-directory)
    761 	   (in-file  (make-temp-name (expand-file-name "srchilite" tmpdir)))
    762 	   (out-file (make-temp-name (expand-file-name "reshilite" tmpdir)))
    763 	   (code (org-element-property :value src-block))
    764 	   (org-lang (org-element-property :language src-block))
    765 	   (lst-lang
    766             (and org-lang
    767 	         (cadr (assq (intern org-lang)
    768 			     (plist-get info :man-source-highlight-langs)))))
    769 	   (cmd (concat "source-highlight"
    770 			" -s " lst-lang
    771 			" -f groff_man "
    772 			" -i " in-file
    773 			" -o " out-file)))
    774       (if lst-lang
    775 	  (let ((code-block ""))
    776 	    (with-temp-file in-file (insert code))
    777 	    (shell-command cmd)
    778 	    (setq code-block  (org-file-contents out-file))
    779 	    (delete-file in-file)
    780 	    (delete-file out-file)
    781 	    code-block)
    782 	(format ".RS\n.nf\n\\fC\\m[black]%s\\m[]\\fP\n.fi\n.RE" (org-man--protect-example code))))))
    783 
    784 
    785 ;;; Statistics Cookie
    786 
    787 (defun org-man-statistics-cookie (statistics-cookie _contents _info)
    788   "Transcode a STATISTICS-COOKIE object from Org to Man.
    789 CONTENTS is nil.  INFO is a plist holding contextual information."
    790   (org-element-property :value statistics-cookie))
    791 
    792 
    793 ;;; Strike-Through
    794 
    795 (defun org-man-strike-through (_strike-through contents _info)
    796   "Transcode STRIKE-THROUGH from Org to Man.
    797 CONTENTS is the text with strike-through markup.  INFO is a plist
    798 holding contextual information."
    799   (format "\\fI%s\\fP" contents))
    800 
    801 ;;; Subscript
    802 
    803 (defun org-man-subscript (_subscript contents _info)
    804   "Transcode a SUBSCRIPT object from Org to Man.
    805 CONTENTS is the contents of the object.  INFO is a plist holding
    806 contextual information."
    807   (format  "\\d\\s-2%s\\s+2\\u" contents))
    808 
    809 ;;; Superscript "^_%s$
    810 
    811 (defun org-man-superscript (_superscript contents _info)
    812   "Transcode a SUPERSCRIPT object from Org to Man.
    813 CONTENTS is the contents of the object.  INFO is a plist holding
    814 contextual information."
    815   (format  "\\u\\s-2%s\\s+2\\d" contents))
    816 
    817 
    818 ;;; Table
    819 ;;
    820 ;; `org-man-table' is the entry point for table transcoding.  It
    821 ;; takes care of tables with a "verbatim" attribute.  Otherwise, it
    822 ;; delegates the job to either `org-man-table--table.el-table' or
    823 ;; `org-man-table--org-table' functions, depending of the type of
    824 ;; the table.
    825 ;;
    826 ;; `org-man-table--align-string' is a subroutine used to build
    827 ;; alignment string for Org tables.
    828 
    829 (defun org-man-table (table contents info)
    830   "Transcode a TABLE element from Org to Man.
    831 CONTENTS is the contents of the table.  INFO is a plist holding
    832 contextual information."
    833   (cond
    834    ;; Case 1: verbatim table.
    835    ((or (plist-get info :man-tables-verbatim)
    836         (let ((attr (read (format "(%s)"
    837                                   (mapconcat
    838                                    #'identity
    839                                    (org-element-property :attr_man table)
    840                                    " ")))))
    841 
    842           (and attr (plist-get attr :verbatim))))
    843 
    844     (format ".nf\n\\fC%s\\fP\n.fi"
    845             ;; Re-create table, without affiliated keywords.
    846             (org-man--protect-example
    847              (org-trim
    848               (org-element-interpret-data
    849                `(table nil ,@(org-element-contents table)))))))
    850    ;; Case 2: Standard table.
    851    (t (org-man-table--org-table table contents info))))
    852 
    853 (defun org-man-table--align-string (divider table info)
    854   "Return an appropriate Man alignment string.
    855 TABLE is the considered table.  INFO is a plist used as
    856 a communication channel."
    857   (let (alignment)
    858     ;; Extract column groups and alignment from first (non-rule) row.
    859     (org-element-map
    860 	(org-element-map table 'table-row
    861 	  (lambda (row)
    862 	    (and (eq (org-element-property :type row) 'standard) row))
    863 	  info 'first-match)
    864 	'table-cell
    865       (lambda (cell)
    866 	(let* ((borders (org-export-table-cell-borders cell info))
    867 	       (raw-width (org-export-table-cell-width cell info))
    868 	       (width-cm (when raw-width (/ raw-width 5)))
    869 	       (width (if raw-width (format "w(%dc)"
    870 					    (if (< width-cm 1) 1 width-cm)) "")))
    871 	  ;; Check left border for the first cell only.
    872 	  (when (and (memq 'left borders) (not alignment))
    873 	    (push "|" alignment))
    874 	  (push
    875 	   (concat (pcase (org-export-table-cell-alignment cell info)
    876 		     (`left "l") (`right "r") (`center "c"))
    877 		   width
    878 		   divider)
    879 	   alignment)
    880 	  (when (memq 'right borders) (push "|" alignment))))
    881       info)
    882     (apply #'concat (reverse alignment))))
    883 
    884 (defun org-man-table--org-table (table contents info)
    885   "Return appropriate Man code for an Org table.
    886 
    887 TABLE is the table type element to transcode.  CONTENTS is its
    888 contents, as a string.  INFO is a plist used as a communication
    889 channel.
    890 
    891 This function assumes TABLE has `org' as its `:type' attribute."
    892   (let* ((attr (org-export-read-attribute :attr_man table))
    893          (caption (and (not (plist-get attr :disable-caption))
    894 		       (org-man--caption/label-string table info)))
    895          (divider (if (plist-get attr :divider) "|" " "))
    896 
    897          ;; Determine alignment string.
    898          (alignment (org-man-table--align-string divider table info))
    899          ;; Extract others display options.
    900 
    901          (lines (org-split-string contents "\n"))
    902 
    903          (attr-list
    904 	  (delq nil
    905 		(list
    906 		 (and (plist-get attr :expand) "expand")
    907 		 (let ((placement (plist-get attr :placement)))
    908 		   (cond ((string= placement 'center) "center")
    909 			 ((string= placement 'left) nil)
    910 			 ((plist-get info :man-tables-centered) "center")
    911 			 (t "")))
    912 		 (or (plist-get attr :boxtype) "box"))))
    913 
    914          (title-line  (plist-get attr :title-line))
    915          (long-cells (plist-get attr :long-cells))
    916 
    917          (table-format (concat
    918                         (format "%s" (or (car attr-list) "" ))
    919                         (or
    920                          (let ((output-list '()))
    921                            (when (cdr attr-list)
    922                              (dolist (attr-item (cdr attr-list))
    923 			       (setq output-list (concat output-list  (format ",%s" attr-item)))))
    924                            output-list)
    925                          "")))
    926 
    927 	 (first-line (when lines (org-split-string (car lines) "\t"))))
    928     ;; Prepare the final format string for the table.
    929 
    930 
    931     (cond
    932      ;; Others.
    933      (lines (concat ".TS\n " table-format ";\n"
    934 
    935                     (format "%s.\n"
    936                             (let ((final-line ""))
    937                               (when title-line
    938                                 (dotimes (_ (length first-line))
    939                                   (setq final-line (concat final-line "cb" divider))))
    940 
    941                               (setq final-line (concat final-line "\n"))
    942 
    943                               (if alignment
    944                                   (setq final-line (concat final-line alignment))
    945                                 (dotimes (_ (length first-line))
    946                                   (setq final-line (concat final-line "c" divider))))
    947                               final-line ))
    948 
    949                     (format "%s.TE\n"
    950                             (let ((final-line "")
    951                                   (long-line "")
    952                                   (lines (org-split-string contents "\n")))
    953 
    954                               (dolist (line-item lines)
    955                                 (setq long-line "")
    956 
    957                                 (if long-cells
    958                                     (progn
    959                                       (if (string= line-item "_")
    960                                           (setq long-line (format "%s\n" line-item))
    961                                         ;; else string =
    962                                         (let ((cell-item-list (org-split-string line-item "\t")))
    963                                           (dolist (cell-item cell-item-list)
    964 
    965                                             (cond  ((eq cell-item (car (last cell-item-list)))
    966                                                     (setq long-line (concat long-line
    967                                                                             (format "T{\n%s\nT}\t\n"  cell-item ))))
    968                                                    (t
    969                                                     (setq long-line (concat long-line
    970                                                                             (format "T{\n%s\nT}\t"  cell-item ))))))
    971 					  long-line))
    972 				      ;; else long cells
    973 				      (setq final-line (concat final-line long-line )))
    974 
    975                                   (setq final-line (concat final-line line-item "\n"))))
    976                               final-line))
    977 
    978                     (and caption (format ".TB \"%s\"" caption)))))))
    979 
    980 ;;; Table Cell
    981 
    982 (defun org-man-table-cell (table-cell contents info)
    983   "Transcode a TABLE-CELL element from Org to Man.
    984 CONTENTS is the cell contents.  INFO is a plist used as
    985 a communication channel."
    986   (concat
    987    (let ((scientific-format (plist-get info :man-table-scientific-notation)))
    988      (if (and contents
    989 	      scientific-format
    990 	      (string-match orgtbl-exp-regexp contents))
    991 	 ;; Use appropriate format string for scientific notation.
    992 	 (format scientific-format
    993 		 (match-string 1 contents)
    994 		 (match-string 2 contents))
    995        contents))
    996    (when (org-export-get-next-element table-cell info) "\t")))
    997 
    998 
    999 ;;; Table Row
   1000 
   1001 (defun org-man-table-row (table-row contents info)
   1002   "Transcode a TABLE-ROW element from Org to Man.
   1003 CONTENTS is the contents of the row.  INFO is a plist used as
   1004 a communication channel."
   1005   ;; Rules are ignored since table separators are deduced from borders
   1006   ;; of the current row.
   1007   (when (eq (org-element-property :type table-row) 'standard)
   1008     (let ((borders
   1009 	   ;; TABLE-ROW's borders are extracted from its first cell.
   1010 	   (org-export-table-cell-borders
   1011 	    (car (org-element-contents table-row)) info)))
   1012       (concat
   1013        (cond ((and (memq 'top borders) (memq 'above borders)) "_\n"))
   1014        contents
   1015        (cond ((and (memq 'bottom borders) (memq 'below borders)) "\n_")
   1016 	     ((memq 'below borders) "\n_"))))))
   1017 
   1018 
   1019 ;;; Target
   1020 
   1021 (defun org-man-target (target _contents info)
   1022   "Transcode a TARGET object from Org to Man.
   1023 CONTENTS is nil.  INFO is a plist holding contextual
   1024 information."
   1025   (format "\\fI%s\\fP" (org-export-get-reference target info)))
   1026 
   1027 
   1028 ;;; Timestamp
   1029 
   1030 (defun org-man-timestamp (_timestamp _contents _info)
   1031   "Transcode a TIMESTAMP object from Org to Man.
   1032 CONTENTS is nil.  INFO is a plist holding contextual information."
   1033   "")
   1034 
   1035 
   1036 ;;; Underline
   1037 
   1038 (defun org-man-underline (_underline contents _info)
   1039   "Transcode UNDERLINE from Org to Man.
   1040 CONTENTS is the text with underline markup.  INFO is a plist
   1041 holding contextual information."
   1042   (format "\\fI%s\\fP" contents))
   1043 
   1044 
   1045 ;;; Verbatim
   1046 
   1047 (defun org-man-verbatim (verbatim _contents _info)
   1048   "Transcode a VERBATIM object from Org to Man."
   1049   (format "\\fI%s\\fP"
   1050 	  (org-man--protect-text (org-element-property :value verbatim))))
   1051 
   1052 
   1053 ;;; Verse Block
   1054 
   1055 (defun org-man-verse-block (_verse-block contents _info)
   1056   "Transcode a VERSE-BLOCK element from Org to Man.
   1057 CONTENTS is verse block contents.  INFO is a plist holding
   1058 contextual information."
   1059   (format ".RS\n.ft I\n%s\n.ft\n.RE" contents))
   1060 
   1061 
   1062 
   1063 ;;; Interactive functions
   1064 
   1065 (defun org-man-export-to-man
   1066     (&optional async subtreep visible-only body-only ext-plist)
   1067   "Export current buffer to a Man file.
   1068 
   1069 If narrowing is active in the current buffer, only export its
   1070 narrowed part.
   1071 
   1072 If a region is active, export that region.
   1073 
   1074 A non-nil optional argument ASYNC means the process should happen
   1075 asynchronously.  The resulting file should be accessible through
   1076 the `org-export-stack' interface.
   1077 
   1078 When optional argument SUBTREEP is non-nil, export the sub-tree
   1079 at point, extracting information from the headline properties
   1080 first.
   1081 
   1082 When optional argument VISIBLE-ONLY is non-nil, don't export
   1083 contents of hidden elements.
   1084 
   1085 When optional argument BODY-ONLY is non-nil, only the body
   1086 without any markers.
   1087 
   1088 EXT-PLIST, when provided, is a property list with external
   1089 parameters overriding Org default settings, but still inferior to
   1090 file-local settings.
   1091 
   1092 Return output file's name."
   1093   (interactive)
   1094   (let ((outfile (org-export-output-file-name ".man" subtreep)))
   1095     (org-export-to-file 'man outfile
   1096       async subtreep visible-only body-only ext-plist)))
   1097 
   1098 (defun org-man-export-to-pdf
   1099     (&optional async subtreep visible-only body-only ext-plist)
   1100   "Export current buffer to Groff then process through to PDF.
   1101 
   1102 If narrowing is active in the current buffer, only export its
   1103 narrowed part.
   1104 
   1105 If a region is active, export that region.
   1106 
   1107 A non-nil optional argument ASYNC means the process should happen
   1108 asynchronously.  The resulting file should be accessible through
   1109 the `org-export-stack' interface.
   1110 
   1111 When optional argument SUBTREEP is non-nil, export the sub-tree
   1112 at point, extracting information from the headline properties
   1113 first.
   1114 
   1115 When optional argument VISIBLE-ONLY is non-nil, don't export
   1116 contents of hidden elements.
   1117 
   1118 When optional argument BODY-ONLY is non-nil, only write between
   1119 markers.
   1120 
   1121 EXT-PLIST, when provided, is a property list with external
   1122 parameters overriding Org default settings, but still inferior to
   1123 file-local settings.
   1124 
   1125 Return PDF file's name."
   1126   (interactive)
   1127   (let ((outfile (org-export-output-file-name ".man" subtreep)))
   1128     (org-export-to-file 'man outfile
   1129       async subtreep visible-only body-only ext-plist
   1130       #'org-latex-compile)))
   1131 
   1132 (defun org-man-compile (file)
   1133   "Compile a Groff file.
   1134 
   1135 FILE is the name of the file being compiled.  Processing is done
   1136 through the command specified in `org-man-pdf-process'.
   1137 
   1138 Return PDF file name or an error if it couldn't be produced."
   1139   (message "Processing Groff file %s..." file)
   1140   (let ((output (org-compile-file file org-man-pdf-process "pdf")))
   1141     (when org-man-remove-logfiles
   1142       (let ((base (file-name-sans-extension output)))
   1143 	(dolist (ext org-man-logfiles-extensions)
   1144 	  (let ((file (concat base "." ext)))
   1145 	    (when (file-exists-p file) (delete-file file))))))
   1146     (message "Process completed.")
   1147     output))
   1148 
   1149 (provide 'ox-man)
   1150 
   1151 ;;; ox-man.el ends here