Browse Source

org-export: Fix bug with recursive file inclusion and relative paths

* contrib/lisp/org-export.el (org-export-expand-include-keyword): Use
  another optional argument to specify the current working directory.
(org-export-as): Apply changes.
* testing/contrib/lisp/test-org-export.el: Add tests.
* testing/examples/include.org: New test file.
* testing/examples/include2.org: New test file.
Nicolas Goaziou 7 years ago
parent
commit
94185eac92

+ 14 - 11
contrib/lisp/org-export.el

@@ -2101,7 +2101,7 @@ Return code as a string."
 		;; visible part of buffer.
 		(if noexpand (org-element-parse-buffer nil visible-only)
 		  (org-export-with-current-buffer-copy
-		   (org-export-expand-include-keyword nil)
+		   (org-export-expand-include-keyword)
 		   (let ((org-current-export-file (current-buffer)))
 		     (org-export-blocks-preprocess))
 		   (org-element-parse-buffer nil visible-only)))
@@ -2250,14 +2250,16 @@ Point is at buffer's beginning when BODY is applied."
 	   (progn ,@body))))))
 (def-edebug-spec org-export-with-current-buffer-copy (body))
 
-(defun org-export-expand-include-keyword (included)
+(defun org-export-expand-include-keyword (&optional included dir)
   "Expand every include keyword in buffer.
-INCLUDED is a list of included file names along with their line
-restriction, when appropriate.  It is used to avoid infinite
-recursion."
-  (let ((case-fold-search nil))
+Optional argument INCLUDED is a list of included file names along
+with their line restriction, when appropriate.  It is used to
+avoid infinite recursion.  Optional argument DIR is the current
+working directory.  It is used to properly resolve relative
+paths."
+  (let ((case-fold-search t))
     (goto-char (point-min))
-    (while (re-search-forward "^[ \t]*#\\+include: \\(.*\\)" nil t)
+    (while (re-search-forward "^[ \t]*#\\+INCLUDE: \\(.*\\)" nil t)
       (when (eq (org-element-type (save-match-data (org-element-at-point)))
 		'keyword)
 	(beginning-of-line)
@@ -2265,7 +2267,7 @@ recursion."
 	(let* ((value (match-string 1))
 	       (ind (org-get-indentation))
 	       (file (and (string-match "^\"\\(\\S-+\\)\"" value)
-			  (prog1 (expand-file-name (match-string 1 value))
+			  (prog1 (expand-file-name (match-string 1 value) dir)
 			    (setq value (replace-match "" nil nil value)))))
 	       (lines
 		(and (string-match
@@ -2306,7 +2308,7 @@ recursion."
 		       "\\(^\\)\\([*]\\|[ \t]*#\\+\\)" ","
 		       (org-export-prepare-file-contents file lines)
 		       nil nil 1)))
-		 (format "%s#+begin_example\n%s%s#+end_example\n"
+		 (format "%s#+BEGIN_EXAMPLE\n%s%s#+END_EXAMPLE\n"
 			 ind-str contents ind-str))))
 	     ((stringp env)
 	      (insert
@@ -2318,7 +2320,7 @@ recursion."
 			 "\\(^\\)\\([*]\\|[ \t]*#\\+\\)") ","
 			 (org-export-prepare-file-contents file lines)
 			 nil nil 1)))
-		 (format "%s#+begin_src %s\n%s%s#+end_src\n"
+		 (format "%s#+BEGIN_SRC %s\n%s%s#+END_SRC\n"
 			 ind-str env contents ind-str))))
 	     (t
 	      (insert
@@ -2327,7 +2329,8 @@ recursion."
 		 (insert
 		  (org-export-prepare-file-contents file lines ind minlevel))
 		 (org-export-expand-include-keyword
-		  (cons (list file lines) included))
+		  (cons (list file lines) included)
+		  (file-name-directory file))
 		 (buffer-string))))))))))))
 
 (defun org-export-prepare-file-contents (file &optional lines ind minlevel)

+ 49 - 0
testing/contrib/lisp/test-org-export.el

@@ -247,3 +247,52 @@ text
 	  (should (equal (org-export-as 'test) "A\n")))
 	(let ((org-export-snippet-translation-alist '(("t" . "test"))))
 	  (should (equal (org-export-as 'test) "AB\n")))))))
+
+(ert-deftest test-org-export/expand-include ()
+  "Test file inclusion in an Org buffer."
+  ;; Full insertion with recursive inclusion.
+  (org-test-with-temp-text
+      (format "#+INCLUDE: \"%s/examples/include.org\"" org-test-dir)
+    (org-export-expand-include-keyword)
+    (should (equal (buffer-string)
+		   "Small Org file with an include keyword.
+
+#+BEGIN_SRC emacs-lisp :exports results
+(+ 2 1)
+#+END_SRC
+
+Success!
+
+* Heading
+body\n")))
+  ;; Localized insertion.
+  (org-test-with-temp-text
+      (format "#+INCLUDE: \"%s/examples/include.org\" :lines \"1-2\""
+	      org-test-dir)
+    (org-export-expand-include-keyword)
+    (should (equal (buffer-string)
+		   "Small Org file with an include keyword.\n")))
+  ;; Insertion with constraints on headlines level.
+  (org-test-with-temp-text
+      (format
+       "* Top heading\n#+INCLUDE: \"%s/examples/include.org\" :lines \"9-\""
+       org-test-dir)
+    (org-export-expand-include-keyword)
+    (should (equal (buffer-string) "* Top heading\n** Heading\nbody\n")))
+  ;; Inclusion within an example block.
+  (org-test-with-temp-text
+      (format "#+INCLUDE: \"%s/examples/include.org\" :lines \"1-2\" example"
+	      org-test-dir)
+    (org-export-expand-include-keyword)
+    (should
+     (equal
+      (buffer-string)
+      "#+BEGIN_EXAMPLE\nSmall Org file with an include keyword.\n#+END_EXAMPLE\n")))
+  ;; Inclusion within a src-block.
+  (org-test-with-temp-text
+      (format
+       "#+INCLUDE: \"%s/examples/include.org\" :lines \"4-5\" src emacs-lisp"
+       org-test-dir)
+    (org-export-expand-include-keyword)
+    (should (equal (buffer-string)
+		   "#+BEGIN_SRC emacs-lisp\n(+ 2 1)\n#+END_SRC\n"))))

+ 10 - 0
testing/examples/include.org

@@ -0,0 +1,10 @@
+Small Org file with an include keyword.
+
+#+BEGIN_SRC emacs-lisp :exports results
+(+ 2 1)
+#+END_SRC
+
+#+INCLUDE: "include2.org"
+
+* Heading
+body

+ 1 - 0
testing/examples/include2.org

@@ -0,0 +1 @@
+Success!