Refill some long/short copyright headers.
[bpt/emacs.git] / lisp / org / org-publish.el
CommitLineData
9542795a 1;;; org-publish.el --- publish related org-mode files as a website
95df8112 2;; Copyright (C) 2006-2011 Free Software Foundation, Inc.
9542795a
CD
3
4;; Author: David O'Toole <dto@gnu.org>
c8d0cf5c 5;; Maintainer: Carsten Dominik <carsten DOT dominik AT gmail DOT com>
699b9291 6;; Keywords: hypermedia, outlines, wp
acedf35c 7;; Version: 7.4
9542795a 8
514a6ce6
CD
9;; This file is part of GNU Emacs.
10;;
b1fc2b50 11;; GNU Emacs is free software: you can redistribute it and/or modify
9542795a 12;; it under the terms of the GNU General Public License as published by
b1fc2b50
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
9542795a 15
514a6ce6 16;; GNU Emacs is distributed in the hope that it will be useful,
9542795a
CD
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
b1fc2b50 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
9542795a 23
9542795a
CD
24;;; Commentary:
25
699b9291
CD
26;; This program allow configurable publishing of related sets of
27;; Org-mode files as a complete website.
9542795a
CD
28;;
29;; org-publish.el can do the following:
30;;
c8d0cf5c 31;; + Publish all one's org-files to HTML or PDF
699b9291 32;; + Upload HTML, images, attachments and other files to a web server
9542795a 33;; + Exclude selected private pages from publishing
ed21c5c8 34;; + Publish a clickable sitemap of pages
699b9291 35;; + Manage local timestamps for publishing only changed files
9542795a
CD
36;; + Accept plugin functions to extend range of publishable content
37;;
c8d0cf5c 38;; Documentation for publishing is in the manual.
9542795a
CD
39
40;;; Code:
41
ed21c5c8
CD
42
43(defun org-publish-sanitize-plist (plist)
44 (mapcar (lambda (x)
45 (or (cdr (assq x '((:index-filename . :sitemap-filename)
46 (:index-title . :sitemap-title)
47 (:index-function . :sitemap-function)
48 (:index-style . :sitemap-style)
49 (:auto-index . :auto-sitemap))))
50 x))
51 plist))
52
9542795a
CD
53(eval-when-compile
54 (require 'cl))
b349f79f
CD
55(require 'org)
56(require 'org-exp)
9542795a 57
1e63a7fb
GM
58(eval-and-compile
59 (unless (fboundp 'declare-function)
60 (defmacro declare-function (fn file &optional arglist fileonly))))
61
9542795a 62(defgroup org-publish nil
b349f79f
CD
63 "Options for publishing a set of Org-mode and related files."
64 :tag "Org Publishing"
65 :group 'org)
9542795a 66
9542795a
CD
67(defcustom org-publish-project-alist nil
68 "Association list to control publishing behavior.
69Each element of the alist is a publishing 'project.' The CAR of
50243adf 70each element is a string, uniquely identifying the project. The
9542795a
CD
71CDR of each element is in one of the following forms:
72
afe98dfa
CD
731. A well-formed property list with an even number of elements, alternating
74 keys and values, specifying parameters for the publishing process.
9542795a 75
afe98dfa 76 (:property value :property value ... )
9542795a 77
afe98dfa
CD
782. A meta-project definition, specifying of a list of sub-projects:
79
80 (:components (\"project-1\" \"project-2\" ...))
9542795a
CD
81
82When the CDR of an element of org-publish-project-alist is in
83this second form, the elements of the list after :components are
84taken to be components of the project, which group together files
50243adf
JB
85requiring different publishing options. When you publish such a
86project with \\[org-publish], the components all publish.
9542795a
CD
87
88When a property is given a value in org-publish-project-alist, its
89setting overrides the value of the corresponding user variable
50243adf 90\(if any) during publishing. However, options set within a file
9542795a
CD
91override everything.
92
93Most properties are optional, but some should always be set:
94
699b9291
CD
95 :base-directory Directory containing publishing source files
96 :base-extension Extension (without the dot!) of source files.
afe98dfa
CD
97 This can be a regular expression. If not given,
98 \"org\" will be used as default extension.
699b9291
CD
99 :publishing-directory Directory (possibly remote) where output
100 files will be published
9542795a
CD
101
102The :exclude property may be used to prevent certain files from
50243adf 103being published. Its value may be a string or regexp matching
9542795a
CD
104file names you don't want to be published.
105
50243adf 106The :include property may be used to include extra files. Its
b349f79f
CD
107value may be a list of filenames to include. The filenames are
108considered relative to the base directory.
9542795a
CD
109
110When both :include and :exclude properties are given values, the
111exclusion step happens first.
112
113One special property controls which back-end function to use for
50243adf 114publishing files in the project. This can be used to extend the
9542795a
CD
115set of file types publishable by org-publish, as well as the set
116of output formats.
117
50243adf
JB
118 :publishing-function Function to publish file. The default is
119 `org-publish-org-to-html', but other
120 values are possible. May also be a
699b9291
CD
121 list of functions, in which case
122 each function in the list is invoked
123 in turn.
9542795a
CD
124
125Another property allows you to insert code that prepares a
50243adf 126project for publishing. For example, you could call GNU Make on a
699b9291 127certain makefile, to ensure published files are built up to date.
9542795a 128
699b9291 129 :preparation-function Function to be called before publishing
ed21c5c8
CD
130 this project. This may also be a list
131 of functions.
b349f79f 132 :completion-function Function to be called after publishing
ed21c5c8
CD
133 this project. This may also be a list
134 of functions.
9542795a
CD
135
136Some properties control details of the Org publishing process,
137and are equivalent to the corresponding user variables listed in
50243adf 138the right column. See the documentation for those variables to
9542795a
CD
139learn more about their use and default values.
140
50243adf
JB
141 :language `org-export-default-language'
142 :headline-levels `org-export-headline-levels'
143 :section-numbers `org-export-with-section-numbers'
144 :table-of-contents `org-export-with-toc'
145 :emphasize `org-export-with-emphasize'
146 :sub-superscript `org-export-with-sub-superscripts'
147 :TeX-macros `org-export-with-TeX-macros'
148 :fixed-width `org-export-with-fixed-width'
149 :tables `org-export-with-tables'
150 :table-auto-headline `org-export-highlight-first-table-line'
151 :style `org-export-html-style'
152 :convert-org-links `org-export-html-link-org-files-as-html'
153 :inline-images `org-export-html-inline-images'
154 :expand-quoted-html `org-export-html-expand'
155 :timestamp `org-export-html-with-timestamp'
156 :publishing-directory `org-export-publishing-directory'
157 :preamble `org-export-html-preamble'
158 :postamble `org-export-html-postamble'
159 :auto-preamble `org-export-html-auto-preamble'
160 :auto-postamble `org-export-html-auto-postamble'
161 :author `user-full-name'
162 :email `user-mail-address'
9542795a 163
ed21c5c8
CD
164The following properties may be used to control publishing of a
165sitemap of files or summary page for a given project.
9542795a 166
ed21c5c8 167 :auto-sitemap Whether to publish a sitemap during
50243adf 168 `org-publish-current-project' or `org-publish-all'.
ed21c5c8 169 :sitemap-filename Filename for output of sitemap. Defaults
c8d0cf5c 170 to 'sitemap.org' (which becomes 'sitemap.html').
ed21c5c8
CD
171 :sitemap-title Title of sitemap page. Defaults to name of file.
172 :sitemap-function Plugin function to use for generation of sitemap.
173 Defaults to `org-publish-org-sitemap', which
699b9291 174 generates a plain list of links to all files
2c3ad40d 175 in the project.
ed21c5c8 176 :sitemap-style Can be `list' (sitemap is just an itemized list
ff4be292 177 of the titles of the files involved) or
2c3ad40d 178 `tree' (the directory structure of the source
ed21c5c8 179 files is reflected in the sitemap). Defaults to
86fbb8ca
CD
180 `tree'.
181
182 If you create a sitemap file, adjust the sorting like this:
183
184 :sitemap-sort-folders Where folders should appear in the sitemap.
185 Set this to `first' (default) or `last' to
186 display folders first or last, respectively.
187 Any other value will mix files and folders.
188 :sitemap-alphabetically The site map is normally sorted alphabetically.
189 Set this explicitly to nil to turn off sorting.
190 :sitemap-ignore-case Should sorting be case-sensitive? Default nil.
191
192The following properties control the creation of a concept index.
193
afe98dfa
CD
194 :makeindex Create a concept index.
195
196Other properties affecting publication.
197
198 :body-only Set this to 't' to publish only the body of the
199 documents, excluding everything outside and
200 including the <body> tags in HTML, or
201 \begin{document}..\end{document} in LaTeX."
9542795a
CD
202 :group 'org-publish
203 :type 'alist)
204
9542795a 205(defcustom org-publish-use-timestamps-flag t
86fbb8ca 206 "Non-nil means use timestamp checking to publish only changed files.
50243adf 207When nil, do no timestamp checking and always publish all files."
9542795a
CD
208 :group 'org-publish
209 :type 'boolean)
210
ff4be292 211(defcustom org-publish-timestamp-directory (convert-standard-filename
71d35b24 212 "~/.org-timestamps/")
9542795a
CD
213 "Name of directory in which to store publishing timestamps."
214 :group 'org-publish
699b9291 215 :type 'directory)
9542795a 216
c8d0cf5c 217(defcustom org-publish-list-skipped-files t
ed21c5c8 218 "Non-nil means show message about files *not* published."
c8d0cf5c
CD
219 :group 'org-publish
220 :type 'boolean)
221
699b9291
CD
222(defcustom org-publish-before-export-hook nil
223 "Hook run before export on the Org file.
c8d0cf5c 224The hook may modify the file in arbitrary ways before publishing happens.
8bfe682a 225The original version of the buffer will be restored after publishing."
699b9291
CD
226 :group 'org-publish
227 :type 'hook)
9542795a 228
699b9291
CD
229(defcustom org-publish-after-export-hook nil
230 "Hook run after export on the exported buffer.
c8d0cf5c 231Any changes made by this hook will be saved."
699b9291
CD
232 :group 'org-publish
233 :type 'hook)
9542795a 234
86fbb8ca
CD
235(defcustom org-publish-sitemap-sort-alphabetically t
236 "Should sitemaps be sorted alphabetically by default?
237
238You can overwrite this default per project in your
239`org-publish-project-alist', using `:sitemap-alphabetically'."
240 :group 'org-publish
241 :type 'boolean)
242
243(defcustom org-publish-sitemap-sort-folders 'first
244 "A symbol, denoting if folders are sorted first in sitemaps.
245Possible values are `first', `last', and nil.
246If `first', folders will be sorted before files.
247If `last', folders are sorted to the end after the files.
248Any other value will not mix files and folders.
249
250You can overwrite this default per project in your
251`org-publish-project-alist', using `:sitemap-sort-folders'."
252 :group 'org-publish
253 :type 'symbol)
254
255(defcustom org-publish-sitemap-sort-ignore-case nil
256 "Sort sitemaps case insensitively by default?
257
258You can overwrite this default per project in your
259`org-publish-project-alist', using `:sitemap-ignore-case'."
260 :group 'org-publish
261 :type 'boolean)
262
699b9291
CD
263;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
264;;; Timestamp-related functions
9542795a 265
c8d0cf5c 266(defun org-publish-timestamp-filename (filename &optional pub-dir pub-func)
9542795a 267 "Return path to timestamp file for filename FILENAME."
c8d0cf5c
CD
268 (setq filename (concat filename "::" (or pub-dir "") "::"
269 (format "%s" (or pub-func ""))))
86fbb8ca 270 (concat "X" (if (fboundp 'sha1) (sha1 filename) (md5 filename))))
9542795a 271
c8d0cf5c 272(defun org-publish-needed-p (filename &optional pub-dir pub-func true-pub-dir)
86fbb8ca
CD
273 "Return t if FILENAME should be published in PUB-DIR using PUB-FUNC.
274TRUE-PUB-DIR is where the file will truly end up. Currently we are not using
c8d0cf5c
CD
275this - maybe it can eventually be used to check if the file is present at
276the target location, and how old it is. Right ow we cannot do this, because
277we do not know under what file name the file will be stored - the publishing
278function can still decide about that independently."
2c3ad40d
CD
279 (let ((rtn
280 (if org-publish-use-timestamps-flag
86fbb8ca
CD
281 (org-publish-cache-file-needs-publishing
282 filename pub-dir pub-func)
2c3ad40d
CD
283 ;; don't use timestamps, always return t
284 t)))
285 (if rtn
c8d0cf5c
CD
286 (message "Publishing file %s using `%s'" filename pub-func)
287 (when org-publish-list-skipped-files
288 (message "Skipping unmodified file %s" filename)))
2c3ad40d 289 rtn))
9542795a 290
c8d0cf5c 291(defun org-publish-update-timestamp (filename &optional pub-dir pub-func)
699b9291
CD
292 "Update publishing timestamp for file FILENAME.
293If there is no timestamp, create one."
86fbb8ca
CD
294 (let ((key (org-publish-timestamp-filename filename pub-dir pub-func))
295 (stamp (org-publish-cache-ctime-of-src filename)))
296 (org-publish-cache-set key stamp)))
c8d0cf5c
CD
297
298(defun org-publish-remove-all-timestamps ()
86fbb8ca 299 "Remove all files in the timestamp directory."
c8d0cf5c
CD
300 (let ((dir org-publish-timestamp-directory)
301 files)
302 (when (and (file-exists-p dir)
303 (file-directory-p dir))
86fbb8ca
CD
304 (mapc 'delete-file (directory-files dir 'full "[^.]\\'"))
305 (org-publish-reset-cache))))
c8d0cf5c 306
699b9291
CD
307
308;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
86fbb8ca 309;;;
699b9291 310
20908596
CD
311(defvar org-publish-initial-buffer nil
312 "The buffer `org-publish' has been called from.")
313(defvar org-publish-temp-files nil
314 "Temporary list of files to be published.")
315
86fbb8ca
CD
316;; Here, so you find the variable right before it's used the first time:
317(defvar org-publish-cache nil
318 "This will cache timestamps and titles for files in publishing projects.
319Blocks could hash sha1 values here.")
699b9291 320
93b62de8 321
699b9291
CD
322;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
323;;; Compatibility aliases
324
325;; Delete-dups is not in Emacs <22
326(if (fboundp 'delete-dups)
327 (defalias 'org-publish-delete-dups 'delete-dups)
328 (defun org-publish-delete-dups (list)
329 "Destructively remove `equal' duplicates from LIST.
330Store the result in LIST and return it. LIST must be a proper list.
331Of several `equal' occurrences of an element in LIST, the first
332one is kept.
333
334This is a compatibility function for Emacsen without `delete-dups'."
335 ;; Code from `subr.el' in Emacs 22:
336 (let ((tail list))
337 (while tail
338 (setcdr tail (delete (car tail) (cdr tail)))
339 (setq tail (cdr tail))))
340 list))
341
1e63a7fb 342(declare-function org-publish-delete-dups "org-publish" (list))
ed21c5c8 343(declare-function find-lisp-find-files "find-lisp" (directory regexp))
1e63a7fb 344
699b9291
CD
345;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
346;;; Getting project information out of org-publish-project-alist
347
699b9291 348(defun org-publish-expand-projects (projects-alist)
621f83e4
CD
349 "Expand projects in PROJECTS-ALIST.
350This splices all the components into the list."
351 (let ((rest projects-alist) rtn p components)
352 (while (setq p (pop rest))
353 (if (setq components (plist-get (cdr p) :components))
354 (setq rest (append
355 (mapcar (lambda (x) (assoc x org-publish-project-alist))
356 components)
357 rest))
358 (push p rtn)))
359 (nreverse (org-publish-delete-dups (delq nil rtn)))))
ff4be292 360
86fbb8ca
CD
361
362(defvar sitemap-alphabetically)
363(defvar sitemap-sort-folders)
364(defvar sitemap-ignore-case)
365(defvar sitemap-requested)
366(defun org-publish-compare-directory-files (a b)
367 "Predicate for `sort', that sorts folders-first/last and alphabetically."
368 (let ((retval t))
369 (when (or sitemap-alphabetically sitemap-sort-folders)
370 ;; First we sort alphabetically:
371 (when sitemap-alphabetically
372 (let* ((adir (file-directory-p a))
373 (aorg (and (string-match "\\.org$" a) (not adir)))
374 (bdir (file-directory-p b))
375 (borg (and (string-match "\\.org$" b) (not bdir)))
376 (A (if aorg
377 (concat (file-name-directory a)
378 (org-publish-find-title a)) a))
379 (B (if borg
380 (concat (file-name-directory b)
381 (org-publish-find-title b)) b)))
382 (setq retval (if sitemap-ignore-case
383 (not (string-lessp (upcase B) (upcase A)))
384 (not (string-lessp B A))))))
385
386 ;; Directory-wise wins:
387 (when sitemap-sort-folders
388 ;; a is directory, b not:
389 (cond
390 ((and (file-directory-p a) (not (file-directory-p b)))
391 (setq retval (equal sitemap-sort-folders 'first)))
392 ;; a is not a directory, but b is:
393 ((and (not (file-directory-p a)) (file-directory-p b))
394 (setq retval (equal sitemap-sort-folders 'last))))))
395 retval))
396
20908596
CD
397(defun org-publish-get-base-files-1 (base-dir &optional recurse match skip-file skip-dir)
398 "Set `org-publish-temp-files' with files from BASE-DIR directory.
399If RECURSE is non-nil, check BASE-DIR recursively. If MATCH is
400non-nil, restrict this list to the files matching the regexp
401MATCH. If SKIP-FILE is non-nil, skip file matching the regexp
402SKIP-FILE. If SKIP-DIR is non-nil, don't check directories
33306645 403matching the regexp SKIP-DIR when recursing through BASE-DIR."
20908596 404 (mapc (lambda (f)
93b62de8 405 (let ((fd-p (file-directory-p f))
20908596
CD
406 (fnd (file-name-nondirectory f)))
407 (if (and fd-p recurse
408 (not (string-match "^\\.+$" fnd))
409 (if skip-dir (not (string-match skip-dir fnd)) t))
410 (org-publish-get-base-files-1 f recurse match skip-file skip-dir)
411 (unless (or fd-p ;; this is a directory
412 (and skip-file (string-match skip-file fnd))
93b62de8 413 (not (file-exists-p (file-truename f)))
20908596 414 (not (string-match match fnd)))
86fbb8ca 415
20908596 416 (pushnew f org-publish-temp-files)))))
86fbb8ca
CD
417 (if sitemap-requested
418 (sort (directory-files base-dir t (unless recurse match))
419 'org-publish-compare-directory-files)
420 (directory-files base-dir t (unless recurse match)))))
20908596 421
699b9291
CD
422(defun org-publish-get-base-files (project &optional exclude-regexp)
423 "Return a list of all files in PROJECT.
9542795a
CD
424If EXCLUDE-REGEXP is set, this will be used to filter out
425matching filenames."
699b9291
CD
426 (let* ((project-plist (cdr project))
427 (base-dir (file-name-as-directory
428 (plist-get project-plist :base-directory)))
33306645
CD
429 (include-list (plist-get project-plist :include))
430 (recurse (plist-get project-plist :recursive))
431 (extension (or (plist-get project-plist :base-extension) "org"))
86fbb8ca
CD
432 ;; sitemap-... variables are dynamically scoped for
433 ;; org-publish-compare-directory-files:
434 (sitemap-requested
435 (plist-get project-plist :auto-sitemap))
436 (sitemap-sort-folders
437 (if (plist-member project-plist :sitemap-sort-folders)
438 (plist-get project-plist :sitemap-sort-folders)
439 org-publish-sitemap-sort-folders))
440 (sitemap-alphabetically
441 (if (plist-member project-plist :sitemap-alphabetically)
442 (plist-get project-plist :sitemap-alphabetically)
443 org-publish-sitemap-sort-alphabetically))
444 (sitemap-ignore-case
445 (if (plist-member project-plist :sitemap-ignore-case)
446 (plist-get project-plist :sitemap-ignore-case)
447 org-publish-sitemap-sort-ignore-case))
c8d0cf5c
CD
448 (match (if (eq extension 'any)
449 "^[^\\.]"
450 (concat "^[^\\.].*\\.\\(" extension "\\)$"))))
86fbb8ca
CD
451 ;; Make sure sitemap-sort-folders' has an accepted value
452 (unless (memq sitemap-sort-folders '(first last))
453 (setq sitemap-sort-folders nil))
454
20908596
CD
455 (setq org-publish-temp-files nil)
456 (org-publish-get-base-files-1 base-dir recurse match
457 ;; FIXME distinguish exclude regexp
458 ;; for skip-file and skip-dir?
459 exclude-regexp exclude-regexp)
b349f79f 460 (mapc (lambda (f)
ff4be292 461 (pushnew
b349f79f
CD
462 (expand-file-name (concat base-dir f))
463 org-publish-temp-files))
464 include-list)
20908596 465 org-publish-temp-files))
9542795a 466
c8d0cf5c 467(defun org-publish-get-project-from-filename (filename &optional up)
86fbb8ca
CD
468 "Return the project that FILENAME belongs to."
469 (let* ((filename (expand-file-name filename))
470 project-name)
471
472 (catch 'p-found
473 (dolist (prj org-publish-project-alist)
474 (unless (plist-get (cdr prj) :components)
475 ;; [[info:org:Selecting%20files]] shows how this is supposed to work:
476 (let* ((r (plist-get (cdr prj) :recursive))
afe98dfa
CD
477 (b (expand-file-name (file-name-as-directory
478 (plist-get (cdr prj) :base-directory))))
86fbb8ca
CD
479 (x (or (plist-get (cdr prj) :base-extension) "org"))
480 (e (plist-get (cdr prj) :exclude))
481 (i (plist-get (cdr prj) :include))
482 (xm (concat "^" b (if r ".+" "[^/]+") "\\.\\(" x "\\)$")))
483 (when (or
afe98dfa
CD
484 (and
485 i
486 (member filename
487 (mapcar
488 (lambda (file) (expand-file-name file b))
489 i)))
86fbb8ca
CD
490 (and
491 (not (and e (string-match e filename)))
492 (string-match xm filename)))
493 (setq project-name (car prj))
494 (throw 'p-found project-name))))))
c8d0cf5c
CD
495 (when up
496 (dolist (prj org-publish-project-alist)
497 (if (member project-name (plist-get (cdr prj) :components))
498 (setq project-name (car prj)))))
699b9291 499 (assoc project-name org-publish-project-alist)))
daa89d0f 500
699b9291
CD
501;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
502;;; Pluggable publishing back-end functions
daa89d0f 503
699b9291 504(defun org-publish-org-to (format plist filename pub-dir)
daa89d0f
CD
505 "Publish an org file to FORMAT.
506PLIST is the property list for the given project.
699b9291
CD
507FILENAME is the filename of the org file to be published.
508PUB-DIR is the publishing directory."
03f3cf35 509 (require 'org)
699b9291
CD
510 (unless (file-exists-p pub-dir)
511 (make-directory pub-dir t))
71d35b24
CD
512 (let ((visiting (find-buffer-visiting filename)))
513 (save-excursion
514 (switch-to-buffer (or visiting (find-file filename)))
515 (let* ((plist (cons :buffer-will-be-killed (cons t plist)))
516 (init-buf (current-buffer))
517 (init-point (point))
518 (init-buf-string (buffer-string))
519 export-buf-or-file)
520 ;; run hooks before exporting
521 (run-hooks 'org-publish-before-export-hook)
522 ;; export the possibly modified buffer
523 (setq export-buf-or-file
524 (funcall (intern (concat "org-export-as-" format))
525 (plist-get plist :headline-levels)
afe98dfa
CD
526 nil plist nil
527 (plist-get plist :body-only)
528 pub-dir))
71d35b24
CD
529 (when (and (bufferp export-buf-or-file)
530 (buffer-live-p export-buf-or-file))
531 (set-buffer export-buf-or-file)
532 ;; run hooks after export and save export
ed21c5c8
CD
533 (progn (run-hooks 'org-publish-after-export-hook)
534 (if (buffer-modified-p) (save-buffer)))
71d35b24
CD
535 (kill-buffer export-buf-or-file))
536 ;; maybe restore buffer's content
537 (set-buffer init-buf)
538 (when (buffer-modified-p init-buf)
539 (erase-buffer)
540 (insert init-buf-string)
541 (save-buffer)
542 (goto-char init-point))
543 (unless visiting
544 (kill-buffer init-buf))))))
699b9291 545
ed21c5c8
CD
546(defmacro org-publish-with-aux-preprocess-maybe (&rest body)
547 "Execute BODY with a modified hook to preprocess for index."
548 `(let ((org-export-preprocess-after-headline-targets-hook
549 (if (plist-get project-plist :makeindex)
550 (cons 'org-publish-aux-preprocess
551 org-export-preprocess-after-headline-targets-hook)
552 org-export-preprocess-after-headline-targets-hook)))
553 ,@body))
554
555(defvar project-plist)
699b9291
CD
556(defun org-publish-org-to-latex (plist filename pub-dir)
557 "Publish an org file to LaTeX.
558See `org-publish-org-to' to the list of arguments."
ed21c5c8
CD
559 (org-publish-with-aux-preprocess-maybe
560 (org-publish-org-to "latex" plist filename pub-dir)))
699b9291 561
71d35b24
CD
562(defun org-publish-org-to-pdf (plist filename pub-dir)
563 "Publish an org file to PDF (via LaTeX).
564See `org-publish-org-to' to the list of arguments."
ed21c5c8
CD
565 (org-publish-with-aux-preprocess-maybe
566 (org-publish-org-to "pdf" plist filename pub-dir)))
71d35b24 567
699b9291
CD
568(defun org-publish-org-to-html (plist filename pub-dir)
569 "Publish an org file to HTML.
570See `org-publish-org-to' to the list of arguments."
ed21c5c8
CD
571 (org-publish-with-aux-preprocess-maybe
572 (org-publish-org-to "html" plist filename pub-dir)))
699b9291 573
c8d0cf5c
CD
574(defun org-publish-org-to-org (plist filename pub-dir)
575 "Publish an org file to HTML.
576See `org-publish-org-to' to the list of arguments."
577 (org-publish-org-to "org" plist filename pub-dir))
578
afe98dfa
CD
579(defun org-publish-org-to-ascii (plist filename pub-dir)
580 "Publish an org file to ASCII.
581See `org-publish-org-to' to the list of arguments."
582 (org-publish-with-aux-preprocess-maybe
583 (org-publish-org-to "ascii" plist filename pub-dir)))
584
585(defun org-publish-org-to-latin1 (plist filename pub-dir)
586 "Publish an org file to Latin-1.
587See `org-publish-org-to' to the list of arguments."
588 (org-publish-with-aux-preprocess-maybe
589 (org-publish-org-to "latin1" plist filename pub-dir)))
590
591(defun org-publish-org-to-utf8 (plist filename pub-dir)
592 "Publish an org file to UTF-8.
593See `org-publish-org-to' to the list of arguments."
594 (org-publish-with-aux-preprocess-maybe
595 (org-publish-org-to "utf8" plist filename pub-dir)))
596
699b9291 597(defun org-publish-attachment (plist filename pub-dir)
9542795a 598 "Publish a file with no transformation of any kind.
699b9291 599See `org-publish-org-to' to the list of arguments."
93b62de8 600 ;; make sure eshell/cp code is loaded
afe98dfa
CD
601 (unless (file-directory-p pub-dir)
602 (make-directory pub-dir t))
603 (or (equal (expand-file-name (file-name-directory filename))
604 (file-name-as-directory (expand-file-name pub-dir)))
c8d0cf5c
CD
605 (copy-file filename
606 (expand-file-name (file-name-nondirectory filename) pub-dir)
607 t)))
699b9291
CD
608
609;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
610;;; Publishing files, sets of files, and indices
611
86fbb8ca
CD
612(defun org-publish-file (filename &optional project no-cache)
613 "Publish file FILENAME from PROJECT.
614If NO-CACHE is not nil, do not initialize org-publish-cache and
615write it to disk. This is needed, since this function is used to
616publish single files, when entire projects are published.
617See `org-publish-projects'."
c8d0cf5c
CD
618 (let* ((project
619 (or project
620 (or (org-publish-get-project-from-filename filename)
86fbb8ca
CD
621 (error "File %s not part of any known project"
622 (abbreviate-file-name filename)))))
c8d0cf5c 623 (project-plist (cdr project))
afe98dfa 624 (ftname (expand-file-name filename))
c8d0cf5c
CD
625 (publishing-function
626 (or (plist-get project-plist :publishing-function)
627 'org-publish-org-to-html))
afe98dfa
CD
628 (base-dir
629 (file-name-as-directory
630 (expand-file-name
631 (or (plist-get project-plist :base-directory)
632 (error "Project %s does not have :base-directory defined"
633 (car project))))))
634 (pub-dir
635 (file-name-as-directory
636 (file-truename
637 (or (plist-get project-plist :publishing-directory)
638 (error "Project %s does not have :publishing-directory defined"
639 (car project))))))
c8d0cf5c 640 tmp-pub-dir)
86fbb8ca
CD
641
642 (unless no-cache
643 (org-publish-initialize-cache (car project)))
644
c8d0cf5c
CD
645 (setq tmp-pub-dir
646 (file-name-directory
647 (concat pub-dir
648 (and (string-match (regexp-quote base-dir) ftname)
649 (substring ftname (match-end 0))))))
650 (if (listp publishing-function)
651 ;; allow chain of publishing functions
652 (mapc (lambda (f)
653 (when (org-publish-needed-p filename pub-dir f tmp-pub-dir)
654 (funcall f project-plist filename tmp-pub-dir)
655 (org-publish-update-timestamp filename pub-dir f)))
656 publishing-function)
657 (when (org-publish-needed-p filename pub-dir publishing-function
658 tmp-pub-dir)
659 (funcall publishing-function project-plist filename tmp-pub-dir)
660 (org-publish-update-timestamp
86fbb8ca
CD
661 filename pub-dir publishing-function)))
662 (unless no-cache (org-publish-write-cache-file))))
699b9291
CD
663
664(defun org-publish-projects (projects)
665 "Publish all files belonging to the PROJECTS alist.
ed21c5c8
CD
666If :auto-sitemap is set, publish the sitemap too.
667If :makeindex is set, also produce a file theindex.org."
699b9291
CD
668 (mapc
669 (lambda (project)
86fbb8ca
CD
670 ;; Each project uses it's own cache file:
671 (org-publish-initialize-cache (car project))
b349f79f
CD
672 (let*
673 ((project-plist (cdr project))
674 (exclude-regexp (plist-get project-plist :exclude))
ed21c5c8
CD
675 (sitemap-p (plist-get project-plist :auto-sitemap))
676 (sitemap-filename (or (plist-get project-plist :sitemap-filename)
677 "sitemap.org"))
678 (sitemap-function (or (plist-get project-plist :sitemap-function)
679 'org-publish-org-sitemap))
b349f79f
CD
680 (preparation-function (plist-get project-plist :preparation-function))
681 (completion-function (plist-get project-plist :completion-function))
682 (files (org-publish-get-base-files project exclude-regexp)) file)
ed21c5c8
CD
683 (when preparation-function (run-hooks 'preparation-function))
684 (if sitemap-p (funcall sitemap-function project sitemap-filename))
699b9291 685 (while (setq file (pop files))
86fbb8ca 686 (org-publish-file file project t))
ed21c5c8
CD
687 (when (plist-get project-plist :makeindex)
688 (org-publish-index-generate-theindex.inc
689 (plist-get project-plist :base-directory))
690 (org-publish-file (expand-file-name
691 "theindex.org"
692 (plist-get project-plist :base-directory))
86fbb8ca
CD
693 project t))
694 (when completion-function (run-hooks 'completion-function))
695 (org-publish-write-cache-file)))
699b9291
CD
696 (org-publish-expand-projects projects)))
697
ed21c5c8 698(defun org-publish-org-sitemap (project &optional sitemap-filename)
86fbb8ca 699 "Create a sitemap of pages in set defined by PROJECT.
ed21c5c8
CD
700Optionally set the filename of the sitemap with SITEMAP-FILENAME.
701Default for SITEMAP-FILENAME is 'sitemap.org'."
699b9291
CD
702 (let* ((project-plist (cdr project))
703 (dir (file-name-as-directory
704 (plist-get project-plist :base-directory)))
b349f79f
CD
705 (localdir (file-name-directory dir))
706 (indent-str (make-string 2 ?\ ))
699b9291 707 (exclude-regexp (plist-get project-plist :exclude))
b349f79f 708 (files (nreverse (org-publish-get-base-files project exclude-regexp)))
ed21c5c8
CD
709 (sitemap-filename (concat dir (or sitemap-filename "sitemap.org")))
710 (sitemap-title (or (plist-get project-plist :sitemap-title)
711 (concat "Sitemap for project " (car project))))
712 (sitemap-style (or (plist-get project-plist :sitemap-style)
2c3ad40d 713 'tree))
ed21c5c8
CD
714 (visiting (find-buffer-visiting sitemap-filename))
715 (ifn (file-name-nondirectory sitemap-filename))
716 file sitemap-buffer)
717 (with-current-buffer (setq sitemap-buffer
718 (or visiting (find-file sitemap-filename)))
ff4be292 719 (erase-buffer)
ed21c5c8 720 (insert (concat "#+TITLE: " sitemap-title "\n\n"))
699b9291 721 (while (setq file (pop files))
b349f79f
CD
722 (let ((fn (file-name-nondirectory file))
723 (link (file-relative-name file dir))
724 (oldlocal localdir))
ed21c5c8
CD
725 ;; sitemap shouldn't list itself
726 (unless (equal (file-truename sitemap-filename)
ff4be292 727 (file-truename file))
ed21c5c8
CD
728 (if (eq sitemap-style 'list)
729 (message "Generating list-style sitemap for %s" sitemap-title)
730 (message "Generating tree-style sitemap for %s" sitemap-title)
2c3ad40d
CD
731 (setq localdir (concat (file-name-as-directory dir)
732 (file-name-directory link)))
733 (unless (string= localdir oldlocal)
734 (if (string= localdir dir)
735 (setq indent-str (make-string 2 ?\ ))
736 (let ((subdirs
737 (split-string
738 (directory-file-name
739 (file-name-directory
740 (file-relative-name localdir dir))) "/"))
93b62de8
CD
741 (subdir "")
742 (old-subdirs (split-string
743 (file-relative-name oldlocal dir) "/")))
2c3ad40d 744 (setq indent-str (make-string 2 ?\ ))
93b62de8
CD
745 (while (string= (car old-subdirs) (car subdirs))
746 (setq indent-str (concat indent-str (make-string 2 ?\ )))
747 (pop old-subdirs)
748 (pop subdirs))
2c3ad40d
CD
749 (dolist (d subdirs)
750 (setq subdir (concat subdir d "/"))
93b62de8
CD
751 (insert (concat indent-str " + " d "\n"))
752 (setq indent-str (make-string
2c3ad40d
CD
753 (+ (length indent-str) 2) ?\ )))))))
754 ;; This is common to 'flat and 'tree
b349f79f 755 (insert (concat indent-str " + [[file:" link "]["
2c3ad40d 756 (org-publish-find-title file)
ff4be292
CD
757 "]]\n")))))
758 (save-buffer))
ed21c5c8 759 (or visiting (kill-buffer sitemap-buffer))))
9542795a 760
b349f79f 761(defun org-publish-find-title (file)
86fbb8ca
CD
762 "Find the title of FILE in project."
763 (or
764 (org-publish-cache-get-file-property file :title nil t)
765 (let* ((visiting (find-buffer-visiting file))
93b62de8
CD
766 (buffer (or visiting (find-file-noselect file)))
767 title)
81ad75af 768 (with-current-buffer buffer
93b62de8
CD
769 (let* ((opt-plist (org-combine-plists (org-default-export-plist)
770 (org-infile-export-plist))))
771 (setq title
772 (or (plist-get opt-plist :title)
773 (and (not
774 (plist-get opt-plist :skip-before-1st-heading))
775 (org-export-grab-title-from-buffer))
776 (file-name-nondirectory (file-name-sans-extension file))))))
777 (unless visiting
778 (kill-buffer buffer))
86fbb8ca
CD
779 (org-publish-cache-set-file-property file :title title)
780 title)))
b349f79f 781
699b9291
CD
782;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
783;;; Interactive publishing functions
9542795a 784
71d35b24 785;;;###autoload
20908596 786(defalias 'org-publish-project 'org-publish)
9542795a
CD
787
788;;;###autoload
699b9291
CD
789(defun org-publish (project &optional force)
790 "Publish PROJECT."
c8d0cf5c
CD
791 (interactive
792 (list
54a0dee5 793 (assoc (org-icompleting-read
c8d0cf5c
CD
794 "Publish project: "
795 org-publish-project-alist nil t)
796 org-publish-project-alist)
797 current-prefix-arg))
20908596 798 (setq org-publish-initial-buffer (current-buffer))
9542795a 799 (save-window-excursion
c8d0cf5c 800 (let* ((org-publish-use-timestamps-flag
699b9291 801 (if force nil org-publish-use-timestamps-flag)))
86fbb8ca
CD
802 (org-publish-projects
803 (if (stringp project)
804 ;; If this function is called in batch mode,
805 ;; project is still a string here.
806 (list (assoc project org-publish-project-alist))
807 (list project))))))
9542795a
CD
808
809;;;###autoload
699b9291
CD
810(defun org-publish-all (&optional force)
811 "Publish all projects.
c8d0cf5c
CD
812With prefix argument, remove all files in the timestamp
813directory and force publishing all files."
9542795a 814 (interactive "P")
c8d0cf5c
CD
815 (when force
816 (org-publish-remove-all-timestamps))
9542795a 817 (save-window-excursion
699b9291
CD
818 (let ((org-publish-use-timestamps-flag
819 (if force nil org-publish-use-timestamps-flag)))
820 (org-publish-projects org-publish-project-alist))))
9542795a 821
ed21c5c8 822
9542795a
CD
823;;;###autoload
824(defun org-publish-current-file (&optional force)
825 "Publish the current file.
826With prefix argument, force publish the file."
827 (interactive "P")
828 (save-window-excursion
829 (let ((org-publish-use-timestamps-flag
699b9291 830 (if force nil org-publish-use-timestamps-flag)))
9542795a
CD
831 (org-publish-file (buffer-file-name)))))
832
9542795a 833;;;###autoload
699b9291
CD
834(defun org-publish-current-project (&optional force)
835 "Publish the project associated with the current file.
836With a prefix argument, force publishing of all files in
837the project."
9542795a
CD
838 (interactive "P")
839 (save-window-excursion
c8d0cf5c 840 (let ((project (org-publish-get-project-from-filename (buffer-file-name) 'up))
699b9291
CD
841 (org-publish-use-timestamps-flag
842 (if force nil org-publish-use-timestamps-flag)))
843 (if (not project)
844 (error "File %s is not part of any known project" (buffer-file-name)))
86fbb8ca 845 ;; FIXME: force is not used here?
699b9291 846 (org-publish project))))
d5098885 847
ed21c5c8
CD
848
849;;; Index generation
850
851(defvar backend) ; dynamically scoped
852(defun org-publish-aux-preprocess ()
853 "Find index entries and write them to an .orgx file."
854 (let ((case-fold-search t)
855 entry index target)
856 (goto-char (point-min))
857 (while
858 (and
859 (re-search-forward "^[ \t]*#\\+index:[ \t]*\\(.*?\\)[ \t]*$" nil t)
860 (> (match-end 1) (match-beginning 1)))
861 (setq entry (match-string 1))
862 (when (eq backend 'latex)
863 (replace-match (format "\\index{%s}" entry) t t))
864 (save-excursion
86fbb8ca 865 (ignore-errors (org-back-to-heading t))
ed21c5c8
CD
866 (setq target (get-text-property (point) 'target))
867 (setq target (or (cdr (assoc target org-export-preferred-target-alist))
868 (cdr (assoc target org-export-id-target-alist))
86fbb8ca 869 target ""))
ed21c5c8
CD
870 (push (cons entry target) index)))
871 (with-temp-file
872 (concat (file-name-sans-extension org-current-export-file) ".orgx")
873 (dolist (entry (nreverse index))
874 (insert (format "INDEX: (%s) %s\n" (cdr entry) (car entry)))))))
875
876(defun org-publish-index-generate-theindex.inc (directory)
877 "Generate the index from all .orgx files in the current directory and below."
878 (require 'find-lisp)
879 (let* ((fulldir (file-name-as-directory
880 (expand-file-name directory)))
881 (full-files (find-lisp-find-files directory "\\.orgx\\'"))
882 (re (concat "\\`" fulldir))
883 (files (mapcar (lambda (f) (if (string-match re f)
884 (substring f (match-end 0))
885 f))
886 full-files))
887 (default-directory directory)
888 index origfile buf target entry ibuffer
86fbb8ca 889 main last-main letter last-letter file sub link tgext)
ed21c5c8
CD
890 ;; `files' contains the list of relative file names
891 (dolist (file files)
892 (setq origfile (substring file 0 -1))
893 (setq buf (find-file-noselect file))
894 (with-current-buffer buf
895 (goto-char (point-min))
896 (while (re-search-forward "^INDEX: (\\(.*?\\)) \\(.*\\)" nil t)
897 (setq target (match-string 1)
898 entry (match-string 2))
899 (push (list entry origfile target) index)))
900 (kill-buffer buf))
901 (setq index (sort index (lambda (a b) (string< (downcase (car a))
902 (downcase (car b))))))
903 (setq ibuffer (find-file-noselect (expand-file-name "theindex.inc" directory)))
904 (with-current-buffer ibuffer
905 (erase-buffer)
906 (insert "* Index\n")
907 (setq last-letter nil)
908 (dolist (idx index)
909 (setq entry (car idx) file (nth 1 idx) target (nth 2 idx))
86fbb8ca
CD
910 (if (and (stringp target) (string-match "\\S-" target))
911 (setq tgext (concat "::#" target))
912 (setq tgext ""))
ed21c5c8
CD
913 (setq letter (upcase (substring entry 0 1)))
914 (when (not (equal letter last-letter))
915 (insert "** " letter "\n")
916 (setq last-letter letter))
917 (if (string-match "!" entry)
918 (setq main (substring entry 0 (match-beginning 0))
919 sub (substring entry (match-end 0)))
920 (setq main nil sub nil last-main nil))
921 (when (and main (not (equal main last-main)))
922 (insert " - " main "\n")
923 (setq last-main main))
86fbb8ca 924 (setq link (concat "[[file:" file tgext "]"
ed21c5c8
CD
925 "[" (or sub entry) "]]"))
926 (if (and main sub)
927 (insert " - " link "\n")
928 (insert " - " link "\n")))
929 (save-buffer))
930 (kill-buffer ibuffer)
931
932 (let ((index-file (expand-file-name "theindex.org" directory)))
933 (unless (file-exists-p index-file)
934 (setq ibuffer (find-file-noselect index-file))
935 (with-current-buffer ibuffer
936 (erase-buffer)
937 (insert "\n\n#+include: \"theindex.inc\"\n\n")
938 (save-buffer))
939 (kill-buffer ibuffer)))))
940
4fbd4750 941
86fbb8ca
CD
942;; Caching functions:
943
944(defun org-publish-write-cache-file (&optional free-cache)
945 "Write `org-publish-cache' to file.
946If FREE-CACHE, empty the cache."
947 (unless org-publish-cache
948 (error "%s" "`org-publish-write-cache-file' called, but no cache present"))
949
950 (let ((cache-file (org-publish-cache-get ":cache-file:")))
951 (unless cache-file
952 (error
953 "%s" "Cannot find cache-file name in `org-publish-write-cache-file'"))
954 (with-temp-file cache-file
955 (let ((print-level nil)
956 (print-length nil))
957 (insert "(setq org-publish-cache (make-hash-table :test 'equal :weakness nil :size 100))\n")
958 (maphash (lambda (k v)
959 (insert
960 (format (concat "(puthash %S "
961 (if (or (listp v) (symbolp v))
962 "'" "")
963 "%S org-publish-cache)\n") k v)))
964 org-publish-cache)))
965 (when free-cache (org-publish-reset-cache))))
966
967(defun org-publish-initialize-cache (project-name)
968 "Initialize the projects cache if not initialized yet and return it."
969
970 (unless project-name
971 (error "%s%s" "Cannot initialize `org-publish-cache' without projects name"
972 " in `org-publish-initialize-cache'"))
973
974 (unless (file-exists-p org-publish-timestamp-directory)
975 (make-directory org-publish-timestamp-directory t))
976 (if (not (file-directory-p org-publish-timestamp-directory))
977 (error "Org publish timestamp: %s is not a directory"
978 org-publish-timestamp-directory))
979
980 (unless (and org-publish-cache
981 (string= (org-publish-cache-get ":project:") project-name))
982 (let* ((cache-file (concat
983 (expand-file-name org-publish-timestamp-directory)
984 project-name
985 ".cache"))
986 (cexists (file-exists-p cache-file)))
987
988 (when org-publish-cache
989 (org-publish-reset-cache))
990
991 (if cexists
992 (load-file cache-file)
993 (setq org-publish-cache
994 (make-hash-table :test 'equal :weakness nil :size 100))
995 (org-publish-cache-set ":project:" project-name)
996 (org-publish-cache-set ":cache-file:" cache-file))
997 (unless cexists (org-publish-write-cache-file nil))))
998 org-publish-cache)
999
1000(defun org-publish-reset-cache ()
1001 "Empty org-publish-cache and reset it nil."
1002 (message "%s" "Resetting org-publish-cache")
1003 (if (hash-table-p org-publish-cache)
1004 (clrhash org-publish-cache))
1005 (setq org-publish-cache nil))
1006
1007(defun org-publish-cache-file-needs-publishing (filename &optional pub-dir pub-func)
1008 "Check the timestamp of the last publishing of FILENAME.
1009Return `t', if the file needs publishing"
1010 (unless org-publish-cache
1011 (error "%s" "`org-publish-cache-file-needs-publishing' called, but no cache present"))
1012 (let* ((key (org-publish-timestamp-filename filename pub-dir pub-func))
1013 (pstamp (org-publish-cache-get key)))
1014 (if (null pstamp)
1015 t
1016 (let ((ctime (org-publish-cache-ctime-of-src filename)))
1017 (< pstamp ctime)))))
1018
1019(defun org-publish-cache-set-file-property (filename property value &optional project-name)
1020 "Set the VALUE for a PROPERTY of file FILENAME in publishing cache to VALUE.
1021Use cache file of PROJECT-NAME. If the entry does not exist, it will be
1022created. Return VALUE."
1023 ;; Evtl. load the requested cache file:
1024 (if project-name (org-publish-initialize-cache project-name))
1025 (let ((pl (org-publish-cache-get filename)))
1026 (if pl
1027 (progn
1028 (plist-put pl property value)
1029 value)
1030 (org-publish-cache-get-file-property
1031 filename property value nil project-name))))
1032
1033(defun org-publish-cache-get-file-property
1034 (filename property &optional default no-create project-name)
1035 "Return the value for a PROPERTY of file FILENAME in publishing cache.
1036Use cache file of PROJECT-NAME. Return the value of that PROPERTY or
1037DEFAULT, if the value does not yet exist.
1038If the entry will be created, unless NO-CREATE is not nil."
1039 ;; Evtl. load the requested cache file:
1040 (if project-name (org-publish-initialize-cache project-name))
1041 (let ((pl (org-publish-cache-get filename))
1042 (retval nil))
1043 (if pl
1044 (if (plist-member pl property)
1045 (setq retval (plist-get pl property))
1046 (setq retval default))
1047 ;; no pl yet:
1048 (unless no-create
1049 (org-publish-cache-set filename (list property default)))
1050 (setq retval default))
1051 retval))
1052
1053(defun org-publish-cache-get (key)
1054 "Return the value stored in `org-publish-cache' for key KEY.
1055Returns nil, if no value or nil is found, or the cache does not
1056exist."
1057 (unless org-publish-cache
1058 (error "%s" "`org-publish-cache-get' called, but no cache present"))
1059 (gethash key org-publish-cache))
1060
1061(defun org-publish-cache-set (key value)
1062 "Store KEY VALUE pair in `org-publish-cache'.
1063Returns value on success, else nil."
1064 (unless org-publish-cache
1065 (error "%s" "`org-publish-cache-set' called, but no cache present"))
1066 (puthash key value org-publish-cache))
1067
1068(defun org-publish-cache-ctime-of-src (filename)
1069 "Get the files ctime as integer."
1070 (let ((src-attr (file-attributes filename)))
1071 (+
1072 (lsh (car (nth 5 src-attr)) 16)
1073 (cadr (nth 5 src-attr)))))
1074
1075
1076
1077(provide 'org-publish)
699b9291 1078
b349f79f 1079
9542795a 1080;;; org-publish.el ends here