From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <emacs-orgmode-bounces+larch=yhetil.org@gnu.org>
Received: from mp11.migadu.com ([2001:41d0:2:bcc0::])
	(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))
	by ms9.migadu.com with LMTPS
	id IGiCNpOjFWQnNwAASxT56A
	(envelope-from <emacs-orgmode-bounces+larch=yhetil.org@gnu.org>)
	for <larch@yhetil.org>; Sat, 18 Mar 2023 12:42:11 +0100
Received: from aspmx1.migadu.com ([2001:41d0:2:bcc0::])
	(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))
	by mp11.migadu.com with LMTPS
	id IOBuNpOjFWS9PwAA9RJhRA
	(envelope-from <emacs-orgmode-bounces+larch=yhetil.org@gnu.org>)
	for <larch@yhetil.org>; Sat, 18 Mar 2023 12:42:11 +0100
Received: from lists.gnu.org (lists.gnu.org [209.51.188.17])
	(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
	(No client certificate requested)
	by aspmx1.migadu.com (Postfix) with ESMTPS id 8B7D0147E8
	for <larch@yhetil.org>; Sat, 18 Mar 2023 12:42:11 +0100 (CET)
Received: from localhost ([::1] helo=lists1p.gnu.org)
	by lists.gnu.org with esmtp (Exim 4.90_1)
	(envelope-from <emacs-orgmode-bounces@gnu.org>)
	id 1pdUvy-0002PO-Da; Sat, 18 Mar 2023 07:41:14 -0400
Received: from eggs.gnu.org ([2001:470:142:3::10])
 by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <yantar92@posteo.net>)
 id 1pdUvv-0002P1-QM
 for emacs-orgmode@gnu.org; Sat, 18 Mar 2023 07:41:11 -0400
Received: from mout02.posteo.de ([185.67.36.66])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <yantar92@posteo.net>)
 id 1pdUvs-00067s-P5
 for emacs-orgmode@gnu.org; Sat, 18 Mar 2023 07:41:11 -0400
Received: from submission (posteo.de [185.67.36.169]) 
 by mout02.posteo.de (Postfix) with ESMTPS id BF7F42405CC
 for <emacs-orgmode@gnu.org>; Sat, 18 Mar 2023 12:41:06 +0100 (CET)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=posteo.net; s=2017;
 t=1679139666; bh=C5fTkzyOaRYu6tXnrQZozEmuieYxCcb5y9fetcZfhAw=;
 h=From:To:Cc:Subject:Date:From;
 b=iMt99/5DNDzlt50Qaexbk/LV32akhPFOUQ2yNgy551AvHA9gpv9gARDIvWvKiDBAH
 7Mh/8zzU9zGAme7au9I8C0P7J6OCvDwkk2yJ4TGPE9OgcmNq3vSvIg+21svx5lDoFI
 dV1Vti3MXtWrNoyBC3ieHuzPT5t1KOo9qiJVjdgn5wgxF4RkBfJe4VNkVDha27FeZe
 JzEBejnz4eTEAlMaUlNEpBjBPSt8LqVtcqNSP1qzeB1fqUTqRi5YHS6SW5Je4bsSWO
 Zi73reCmleYdiC9LNHzqGdM/ZPR7LXmSrbQ9obCLBEmPfY17AKEz7Sl0gXD+28FC7l
 PYMrq8rbz39tQ==
Received: from customer (localhost [127.0.0.1])
 by submission (posteo.de) with ESMTPSA id 4PdzbP6pLsz9rxD;
 Sat, 18 Mar 2023 12:41:05 +0100 (CET)
From: Ihor Radchenko <yantar92@posteo.net>
To: David Lukes <dafydd.lukes@gmail.com>
Cc: emacs-orgmode@gnu.org
Subject: Re: [PATCH] ox-odt: Prevent auto-formatting in export buffers
In-Reply-To: <20221002035931.12191-1-dafydd.lukes@gmail.com>
References: <20221002035931.12191-1-dafydd.lukes@gmail.com>
Date: Sat, 18 Mar 2023 11:42:59 +0000
Message-ID: <87zg8axp30.fsf@localhost>
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="=-=-="
Received-SPF: pass client-ip=185.67.36.66; envelope-from=yantar92@posteo.net;
 helo=mout02.posteo.de
X-Spam_score_int: -43
X-Spam_score: -4.4
X-Spam_bar: ----
X-Spam_report: (-4.4 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1,
 DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1,
 RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001,
 SPF_PASS=-0.001 autolearn=ham autolearn_force=no
X-Spam_action: no action
X-BeenThere: emacs-orgmode@gnu.org
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: "General discussions about Org-mode." <emacs-orgmode.gnu.org>
List-Unsubscribe: <https://lists.gnu.org/mailman/options/emacs-orgmode>,
 <mailto:emacs-orgmode-request@gnu.org?subject=unsubscribe>
List-Archive: <https://lists.gnu.org/archive/html/emacs-orgmode>
List-Post: <mailto:emacs-orgmode@gnu.org>
List-Help: <mailto:emacs-orgmode-request@gnu.org?subject=help>
List-Subscribe: <https://lists.gnu.org/mailman/listinfo/emacs-orgmode>,
 <mailto:emacs-orgmode-request@gnu.org?subject=subscribe>
Errors-To: emacs-orgmode-bounces+larch=yhetil.org@gnu.org
Sender: emacs-orgmode-bounces+larch=yhetil.org@gnu.org
X-Migadu-Country: US
X-Migadu-Flow: FLOW_IN
ARC-Authentication-Results: i=1;
	aspmx1.migadu.com;
	dkim=pass header.d=posteo.net header.s=2017 header.b="iMt99/5D";
	spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org";
	dmarc=pass (policy=none) header.from=posteo.net
ARC-Seal: i=1; s=key1; d=yhetil.org; t=1679139731; a=rsa-sha256; cv=none;
	b=naTGru7tXfXQZPo0UK9KdjVFU+nk2gzdHMxolH+wPXHTGK/3JK/fjSzcjattcqpSjw4eCq
	E4HEhb/RRew0wd61c+8E++7zD4MLT+HsGVcmeMHdcBeEF3yGd5NFpwOiS/Dwfq7qxaea5D
	Orb9uhkNUi3tw/tPuqh4o/FYseZySk0ph5G9xsBYJIQmobhkk1U2SDbl+IQWYOTpmsvGYC
	5+Wxx8rRtYsw5g6y9hyQqrNQz/6UFK4IqkIPRQ5ct9swqbFsi5ZNQ5aMbFRW7Ar2WMzcSP
	lIf3aLV3dEnLc9529fq/Iq/Qy98V+ahHRmqFkCdN7B/gAM90KTUxV3KSbckJcA==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org;
	s=key1; t=1679139731;
	h=from:from:sender:sender:reply-to:subject:subject:date:date:
	 message-id:message-id:to:to:cc:cc:mime-version:mime-version:
	 content-type:content-type:in-reply-to:in-reply-to:
	 references:references:list-id:list-help:list-unsubscribe:
	 list-subscribe:list-post:dkim-signature;
	bh=D1kc8IhI1644H+Dt+LnJl2uzx/TZ/dzeIQ4XwttVMLo=;
	b=un1fP20olz7Z7joM9atupGjVBdzP9gSSDMa0wFD2Ty/zT4kPQRtJujHPAr26vjoSQPvwq9
	ydIhEKYdu5y4+PhmndVYwk+BRRnTVAdRJSUM1FXzUeBrGjvUC+bTRGIxdbXLrifexuXTTo
	OAE1YapXU129TXtRK9o0DQ0CLzPDarb6b+mh0MfjqOSzuSvaXBgiShGwuK3+m6dJynKwgX
	QzqNGC7IC5oypDYI/U0XjXB6JJ4bTH374grrLx/81E5ixd0iyewtgaORr2q3gTrREupRkM
	4qlD0mS4zg174Ne68gWqirPuIVnvnuCFSFeI6So0EIkAoU595n1BicYJ7YyV/g==
X-Migadu-Spam-Score: -6.27
X-Spam-Score: -6.27
X-Migadu-Queue-Id: 8B7D0147E8
X-Migadu-Scanner: scn1.migadu.com
Authentication-Results: aspmx1.migadu.com;
	dkim=pass header.d=posteo.net header.s=2017 header.b="iMt99/5D";
	spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org";
	dmarc=pass (policy=none) header.from=posteo.net
X-TUID: W79p5QteCZ7L

--=-=-=
Content-Type: text/plain

David Lukes <dafydd.lukes@gmail.com> writes:

> * lisp/ox-odt.el (org-odt-template, org-odt--export-wrap):
> `write-region' instead of `save-buffer'.
>
> `write-file' and `save-buffer' trigger major mode changes, which leads
> to various mode-related hooks being run.  This is undesirable: running
> these on generated files is wasted time and computation, and it can even
> lead to hard to track data corruption when auto-formatting hooks are
> involved.  One such case is the 2006 version of the tidy program which
> ships with stock macOS and can corrupt multi-byte UTF-8 codepoints in
> HTML and ODT (via XML) exports.  And even recent versions of tidy can
> re-arrange whitespace in the exported documents in unwanted ways.

See the attached patch where I applied the idea across Org, where it
made sense. Note that I did not alter `org-odt--export-wrap' because
`save-buffer' there is applied only when XML files are opened as buffers
in Emacs, presumably manually. Interactive `save-buffer' makes sense
then. (Though maybe that code is not needed at all, IDK)

Please let me know if there are any objections.


--=-=-=
Content-Type: text/x-patch
Content-Disposition: inline;
 filename=0001-Prefer-write-region-to-save-file.patch

>From 8d8884bb6be0ba7cb7f9662f067d42b53393e92e Mon Sep 17 00:00:00 2001
Message-Id: <8d8884bb6be0ba7cb7f9662f067d42b53393e92e.1679139580.git.yantar92@posteo.net>
From: Ihor Radchenko <yantar92@posteo.net>
Date: Sat, 18 Mar 2023 12:34:17 +0100
Subject: [PATCH] Prefer `write-region' to `save-file'

* lisp/ob-haskell.el (org-babel-haskell-export-to-lhs): Use
non-interactive `insert-file-contents' + `write-region' to avoid
triggering various interactive hooks.  Ensure that temp files are
always deleted.
* lisp/org-agenda.el (org-agenda-write):
* lisp/org-table.el: Simplify code using `write-region'.
* lisp/ox-odt.el (org-odt-template): Use `insert-file-contents' +
`write-region' instead of `find-file-noselect' that may trigger
various hooks.  The new approach makes `revert-buffer' not
necessary (and do not trigger `revert-buffer' hooks).  Also, the
problem with backups will no longer exists.

Original idea: https://list.orgmode.org/orgmode/20221002035931.12191-1-dafydd.lukes@gmail.com/
---
 lisp/ob-haskell.el | 41 ++++++++++++------------
 lisp/org-agenda.el |  9 ++----
 lisp/org-table.el  | 10 ++----
 lisp/ox-odt.el     | 78 ++++++++++++++++++++--------------------------
 4 files changed, 59 insertions(+), 79 deletions(-)

diff --git a/lisp/ob-haskell.el b/lisp/ob-haskell.el
index 2b1441c2a..3e64c1657 100644
--- a/lisp/ob-haskell.el
+++ b/lisp/ob-haskell.el
@@ -255,26 +255,27 @@ (defun org-babel-haskell-export-to-lhs (&optional arg)
                        t t)
         (indent-code-rigidly (match-beginning 0) (match-end 0) indentation)))
     (save-excursion
-      ;; export to latex w/org and save as .lhs
-      (require 'ox-latex)
-      (find-file tmp-org-file)
-      ;; Ensure we do not clutter kill ring with incomplete results.
-      (let (org-export-copy-to-kill-ring)
-	(org-export-to-file 'latex tmp-tex-file))
-      (kill-buffer nil)
-      (delete-file tmp-org-file)
-      (find-file tmp-tex-file)
-      (goto-char (point-min)) (forward-line 2)
-      (insert "%include polycode.fmt\n")
-      ;; ensure all \begin/end{code} statements start at the first column
-      (while (re-search-forward "^[ \t]+\\\\begin{code}[^\000]+\\\\end{code}" nil t)
-        (replace-match (save-match-data (org-remove-indentation (match-string 0)))
-                       t t))
-      (setq contents (buffer-string))
-      (save-buffer) (kill-buffer nil))
-    (delete-file tmp-tex-file)
-    ;; save org exported latex to a .lhs file
-    (with-temp-file lhs-file (insert contents))
+      (unwind-protect
+          (with-temp-buffer
+            ;; Export to latex w/org and save as .lhs
+            (require 'ox-latex)
+            (insert-file-contents tmp-org-file)
+            ;; Ensure we do not clutter kill ring with incomplete results.
+            (let (org-export-copy-to-kill-ring)
+	      (org-export-to-file 'latex tmp-tex-file)))
+        (delete-file tmp-org-file))
+      (unwind-protect
+          (with-temp-buffer
+            (insert-file-contents tmp-tex-file)
+            (goto-char (point-min)) (forward-line 2)
+            (insert "%include polycode.fmt\n")
+            ;; ensure all \begin/end{code} statements start at the first column
+            (while (re-search-forward "^[ \t]+\\\\begin{code}[^\000]+\\\\end{code}" nil t)
+              (replace-match (save-match-data (org-remove-indentation (match-string 0)))
+                             t t))
+            ;; save org exported latex to a .lhs file
+            (write-region nil nil lhs-file))
+        (delete-file tmp-tex-file)))
     (if (not arg)
         (find-file lhs-file)
       ;; process .lhs file with lhs2tex
diff --git a/lisp/org-agenda.el b/lisp/org-agenda.el
index 3da0967f0..859a80c47 100644
--- a/lisp/org-agenda.el
+++ b/lisp/org-agenda.el
@@ -3667,13 +3667,8 @@ (defun org-agenda-write (file &optional open nosettings agenda-bufname)
 	                        "ox-icalendar" (file))
 	      (org-icalendar-export-current-agenda (expand-file-name file)))
 	     (t
-	      (let ((bs (buffer-string)))
-		(find-file file)
-		(erase-buffer)
-		(insert bs)
-		(save-buffer 0)
-		(kill-buffer (current-buffer))
-		(message "Plain text written to %s" file))))))))
+              (write-region nil nil file)
+              (message "Plain text written to %s" file)))))))
     (set-buffer (or agenda-bufname
 		    ;; FIXME: I'm pretty sure called-interactively-p
                     ;; doesn't do what we want here!
diff --git a/lisp/org-table.el b/lisp/org-table.el
index 5116b1127..97120fffd 100644
--- a/lisp/org-table.el
+++ b/lisp/org-table.el
@@ -4332,14 +4332,8 @@ (defun org-table-export (&optional file format)
 		(table (org-table-to-lisp)))
 	    (unless (fboundp transform)
 	      (user-error "No such transformation function %s" transform))
-	    (let (buf)
-	      (with-current-buffer (find-file-noselect file)
-		(setq buf (current-buffer))
-		(erase-buffer)
-		(fundamental-mode)
-		(insert (funcall transform table params) "\n")
-		(save-buffer))
-	      (kill-buffer buf))
+            (with-temp-file file
+              (insert (funcall transform table params) "\n"))
 	    (message "Export done."))
 	(user-error "TABLE_EXPORT_FORMAT invalid")))))
 
diff --git a/lisp/ox-odt.el b/lisp/ox-odt.el
index cf217c9e7..8fc34a749 100644
--- a/lisp/ox-odt.el
+++ b/lisp/ox-odt.el
@@ -1365,50 +1365,40 @@ (defun org-odt-template (contents info)
     ;; Ensure we have write permissions to this file.
     (set-file-modes (concat org-odt-zip-dir "styles.xml") #o600)
 
-    ;; FIXME: Who is opening an empty styles.xml before this point?
-    (with-current-buffer
-	(find-file-noselect (concat org-odt-zip-dir "styles.xml") t)
-      (revert-buffer t t)
-
-      ;; Write custom styles for source blocks
-      ;; Save STYLES used for colorizing of source blocks.
-      ;; Update styles.xml with styles that were collected as part of
-      ;; `org-odt-hfy-face-to-css' callbacks.
-      (let ((styles (mapconcat (lambda (style) (format " %s\n" (cddr style)))
-			       hfy-user-sheet-assoc "")))
-	(when styles
-	  (goto-char (point-min))
-	  (when (re-search-forward "</office:styles>" nil t)
-	    (goto-char (match-beginning 0))
-	    (insert "\n<!-- Org Htmlfontify Styles -->\n" styles "\n"))))
-
-      ;; Update styles.xml - take care of outline numbering
-
-      ;; Don't make automatic backup of styles.xml file. This setting
-      ;; prevents the backed-up styles.xml file from being zipped in to
-      ;; odt file. This is more of a hackish fix. Better alternative
-      ;; would be to fix the zip command so that the output odt file
-      ;; includes only the needed files and excludes any auto-generated
-      ;; extra files like backups and auto-saves etc etc. Note that
-      ;; currently the zip command zips up the entire temp directory so
-      ;; that any auto-generated files created under the hood ends up in
-      ;; the resulting odt file.
-      (setq-local backup-inhibited t)
-
-      ;; Outline numbering is retained only up to LEVEL.
-      ;; To disable outline numbering pass a LEVEL of 0.
-
-      (goto-char (point-min))
-      (let ((regex
-	     "<text:outline-level-style\\([^>]*\\)text:level=\"\\([^\"]*\\)\"\\([^>]*\\)>")
-	    (replacement
-	     "<text:outline-level-style\\1text:level=\"\\2\" style:num-format=\"\">"))
-	(while (re-search-forward regex nil t)
-	  (unless (let ((sec-num (plist-get info :section-numbers))
-			(level (string-to-number (match-string 2))))
-		    (if (wholenump sec-num) (<= level sec-num) sec-num))
-	    (replace-match replacement t nil))))
-      (save-buffer 0)))
+    (let ((styles-xml (concat org-odt-zip-dir "styles.xml")))
+      (with-temp-buffer
+        (when (file-exists-p styles-xml)
+          (insert-file-contents styles-xml))
+        
+        ;; Write custom styles for source blocks
+        ;; Save STYLES used for colorizing of source blocks.
+        ;; Update styles.xml with styles that were collected as part of
+        ;; `org-odt-hfy-face-to-css' callbacks.
+        (let ((styles (mapconcat (lambda (style) (format " %s\n" (cddr style)))
+			         hfy-user-sheet-assoc "")))
+          (when styles
+	    (goto-char (point-min))
+	    (when (re-search-forward "</office:styles>" nil t)
+	      (goto-char (match-beginning 0))
+	      (insert "\n<!-- Org Htmlfontify Styles -->\n" styles "\n"))))
+
+        ;; Update styles.xml - take care of outline numbering
+        ;; Outline numbering is retained only up to LEVEL.
+        ;; To disable outline numbering pass a LEVEL of 0.
+
+        (let ((regex
+	       "<text:outline-level-style\\([^>]*\\)text:level=\"\\([^\"]*\\)\"\\([^>]*\\)>")
+	      (replacement
+	       "<text:outline-level-style\\1text:level=\"\\2\" style:num-format=\"\">"))
+          (goto-char (point-min))
+          (while (re-search-forward regex nil t)
+	    (unless (let ((sec-num (plist-get info :section-numbers))
+		          (level (string-to-number (match-string 2))))
+		      (if (wholenump sec-num) (<= level sec-num) sec-num))
+	      (replace-match replacement t nil))))
+        
+        ;; Write back the new contents.
+        (write-region nil nil styles-xml))))
   ;; Update content.xml.
 
   (let* ( ;; `org-display-custom-times' should be accessed right
-- 
2.39.1


--=-=-=
Content-Type: text/plain


-- 
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>

--=-=-=--