Re: [PATCH] emacs: wash: make word-wrap bound message width
[notmuch-archives.git] / 1d / 2a919499d1dd137831ace6236a7bc6905f1049
1 Return-Path: <bremner@tesseract.cs.unb.ca>\r
2 X-Original-To: notmuch@notmuchmail.org\r
3 Delivered-To: notmuch@notmuchmail.org\r
4 Received: from localhost (localhost [127.0.0.1])\r
5         by olra.theworths.org (Postfix) with ESMTP id 2A08B431FCF\r
6         for <notmuch@notmuchmail.org>; Sat,  5 Apr 2014 08:46:41 -0700 (PDT)\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
8 X-Spam-Flag: NO\r
9 X-Spam-Score: 0\r
10 X-Spam-Level: \r
11 X-Spam-Status: No, score=0 tagged_above=-999 required=5 tests=[none]\r
12         autolearn=disabled\r
13 Received: from olra.theworths.org ([127.0.0.1])\r
14         by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
15         with ESMTP id aNyATK6fzQ4O for <notmuch@notmuchmail.org>;\r
16         Sat,  5 Apr 2014 08:46:32 -0700 (PDT)\r
17 Received: from mx.xen14.node3324.gplhost.com (gitolite.debian.net\r
18         [87.98.215.224]) (using TLSv1 with cipher AES256-SHA (256/256 bits))\r
19         (No client certificate requested)\r
20         by olra.theworths.org (Postfix) with ESMTPS id 50436431FB6\r
21         for <notmuch@notmuchmail.org>; Sat,  5 Apr 2014 08:46:27 -0700 (PDT)\r
22 Received: from remotemail by mx.xen14.node3324.gplhost.com with local (Exim\r
23         4.72) (envelope-from <bremner@tesseract.cs.unb.ca>)\r
24         id 1WWSmv-000833-VR; Sat, 05 Apr 2014 15:45:45 +0000\r
25 Received: (nullmailer pid 10912 invoked by uid 1000); Sat, 05 Apr 2014\r
26         15:43:58 -0000\r
27 From: David Bremner <david@tethera.net>\r
28 To: notmuch@notmuchmail.org\r
29 Subject: [Patch v7 1/6] dump: support gzipped and atomic output\r
30 Date: Sat,  5 Apr 2014 12:43:51 -0300\r
31 Message-Id: <1396712636-10640-2-git-send-email-david@tethera.net>\r
32 X-Mailer: git-send-email 1.9.0\r
33 In-Reply-To: <1396712636-10640-1-git-send-email-david@tethera.net>\r
34 References: <1396712636-10640-1-git-send-email-david@tethera.net>\r
35 X-BeenThere: notmuch@notmuchmail.org\r
36 X-Mailman-Version: 2.1.13\r
37 Precedence: list\r
38 List-Id: "Use and development of the notmuch mail system."\r
39         <notmuch.notmuchmail.org>\r
40 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
41         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
42 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
43 List-Post: <mailto:notmuch@notmuchmail.org>\r
44 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
45 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
46         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
47 X-List-Received-Date: Sat, 05 Apr 2014 15:46:41 -0000\r
48 \r
49 The main goal is to support gzipped output for future internal\r
50 calls (e.g. from notmuch-new) to notmuch_database_dump.\r
51 \r
52 The additional dependency is not very heavy since xapian already pulls\r
53 in zlib.\r
54 \r
55 We want the dump to be "atomic", in the sense that after running the\r
56 dump file is either present and complete, or not present.  This avoids\r
57 certain classes of mishaps involving overwriting a good backup with a\r
58 bad or partial one.\r
59 ---\r
60  INSTALL                   |  20 +++++++--\r
61  Makefile.local            |   2 +-\r
62  configure                 |  28 +++++++++++--\r
63  doc/man1/notmuch-dump.rst |   3 ++\r
64  notmuch-client.h          |   4 +-\r
65  notmuch-dump.c            | 101 +++++++++++++++++++++++++++++++++++++---------\r
66  test/T240-dump-restore.sh |  12 ++++++\r
67  7 files changed, 142 insertions(+), 28 deletions(-)\r
68 \r
69 diff --git a/INSTALL b/INSTALL\r
70 index 690b0ef..b543c50 100644\r
71 --- a/INSTALL\r
72 +++ b/INSTALL\r
73 @@ -20,8 +20,8 @@ configure stage.\r
74  \r
75  Dependencies\r
76  ------------\r
77 -Notmuch depends on three libraries: Xapian, GMime 2.4 or 2.6, and\r
78 -Talloc which are each described below:\r
79 +Notmuch depends on four libraries: Xapian, GMime 2.4 or 2.6,\r
80 +Talloc, and zlib which are each described below:\r
81  \r
82         Xapian\r
83         ------\r
84 @@ -60,6 +60,18 @@ Talloc which are each described below:\r
85  \r
86         Talloc is available from http://talloc.samba.org/\r
87  \r
88 +       zlib\r
89 +       ----\r
90 +\r
91 +       zlib is an extremely popular compression library. It is used\r
92 +       by Xapian, so if you installed that you will already have\r
93 +       zlib. You may need to install the zlib headers separately.\r
94 +\r
95 +       Notmuch needs the transparent write feature of zlib introduced\r
96 +       in version 1.2.5.2 (Dec. 2011).\r
97 +\r
98 +       zlib is available from http://zlib.net\r
99 +\r
100  Building Documentation\r
101  ----------------------\r
102  \r
103 @@ -79,11 +91,11 @@ dependencies with a simple simple command line. For example:\r
104  \r
105    For Debian and similar:\r
106  \r
107 -        sudo apt-get install libxapian-dev libgmime-2.6-dev libtalloc-dev python-sphinx\r
108 +        sudo apt-get install libxapian-dev libgmime-2.6-dev libtalloc-dev zlib1g-dev python-sphinx\r
109  \r
110    For Fedora and similar:\r
111  \r
112 -       sudo yum install xapian-core-devel gmime-devel libtalloc-devel python-sphinx\r
113 +       sudo yum install xapian-core-devel gmime-devel libtalloc-devel zlib-devel python-sphinx\r
114  \r
115  On other systems, a similar command can be used, but the details of\r
116  the package names may be different.\r
117 diff --git a/Makefile.local b/Makefile.local\r
118 index cb7b106..e5a20a7 100644\r
119 --- a/Makefile.local\r
120 +++ b/Makefile.local\r
121 @@ -41,7 +41,7 @@ PV_FILE=bindings/python/notmuch/version.py\r
122  # Smash together user's values with our extra values\r
123  FINAL_CFLAGS = -DNOTMUCH_VERSION=$(VERSION) $(CPPFLAGS) $(CFLAGS) $(WARN_CFLAGS) $(extra_cflags) $(CONFIGURE_CFLAGS)\r
124  FINAL_CXXFLAGS = $(CPPFLAGS) $(CXXFLAGS) $(WARN_CXXFLAGS) $(extra_cflags) $(extra_cxxflags) $(CONFIGURE_CXXFLAGS)\r
125 -FINAL_NOTMUCH_LDFLAGS = $(LDFLAGS) -Lutil -lutil -Llib -lnotmuch $(AS_NEEDED_LDFLAGS) $(GMIME_LDFLAGS) $(TALLOC_LDFLAGS)\r
126 +FINAL_NOTMUCH_LDFLAGS = $(LDFLAGS) -Lutil -lutil -Llib -lnotmuch $(AS_NEEDED_LDFLAGS) $(GMIME_LDFLAGS) $(TALLOC_LDFLAGS) $(ZLIB_LDFLAGS)\r
127  FINAL_NOTMUCH_LINKER = CC\r
128  ifneq ($(LINKER_RESOLVES_LIBRARY_DEPENDENCIES),1)\r
129  FINAL_NOTMUCH_LDFLAGS += $(CONFIGURE_LDFLAGS)\r
130 diff --git a/configure b/configure\r
131 index 1d430b9..83b4af7 100755\r
132 --- a/configure\r
133 +++ b/configure\r
134 @@ -340,6 +340,18 @@ else\r
135      errors=$((errors + 1))\r
136  fi\r
137  \r
138 +printf "Checking for zlib (>= 1.2.5.2)... "\r
139 +have_zlib=0\r
140 +if pkg-config --atleast-version=1.2.5.2 zlib; then\r
141 +    printf "Yes.\n"\r
142 +    have_zlib=1\r
143 +    zlib_cflags=$(pkg-config --cflags zlib)\r
144 +    zlib_ldflags=$(pkg-config --libs zlib)\r
145 +else\r
146 +    printf "No.\n"\r
147 +    errors=$((errors + 1))\r
148 +fi\r
149 +\r
150  printf "Checking for talloc development files... "\r
151  if pkg-config --exists talloc; then\r
152      printf "Yes.\n"\r
153 @@ -496,6 +508,11 @@ EOF\r
154         echo "  Xapian library (including development files such as headers)"\r
155         echo "  http://xapian.org/"\r
156      fi\r
157 +    if [ $have_zlib -eq 0 ]; then\r
158 +       echo "  zlib library (>= version 1.2.5.2, including development files such as headers)"\r
159 +       echo "  http://zlib.net/"\r
160 +       echo\r
161 +    fi\r
162      if [ $have_gmime -eq 0 ]; then\r
163         echo "  Either GMime 2.4 library" $GMIME_24_VERSION_CTR "or GMime 2.6 library" $GMIME_26_VERSION_CTR\r
164         echo "  (including development files such as headers)"\r
165 @@ -519,11 +536,11 @@ case a simple command will install everything you need. For example:\r
166  \r
167  On Debian and similar systems:\r
168  \r
169 -       sudo apt-get install libxapian-dev libgmime-2.6-dev libtalloc-dev\r
170 +       sudo apt-get install libxapian-dev libgmime-2.6-dev libtalloc-dev zlib1g-dev\r
171  \r
172  Or on Fedora and similar systems:\r
173  \r
174 -       sudo yum install xapian-core-devel gmime-devel libtalloc-devel\r
175 +       sudo yum install xapian-core-devel gmime-devel libtalloc-devel zlib-devel\r
176  \r
177  On other systems, similar commands can be used, but the details of the\r
178  package names may be different.\r
179 @@ -844,6 +861,10 @@ XAPIAN_LDFLAGS = ${xapian_ldflags}\r
180  GMIME_CFLAGS = ${gmime_cflags}\r
181  GMIME_LDFLAGS = ${gmime_ldflags}\r
182  \r
183 +# Flags needed to compile and link against zlib\r
184 +ZLIB_CFLAGS = ${zlib_cflags}\r
185 +ZLIB_LDFLAGS = ${zlib_ldflags}\r
186 +\r
187  # Flags needed to compile and link against talloc\r
188  TALLOC_CFLAGS = ${talloc_cflags}\r
189  TALLOC_LDFLAGS = ${talloc_ldflags}\r
190 @@ -882,6 +903,7 @@ CONFIGURE_CFLAGS = -DHAVE_GETLINE=\$(HAVE_GETLINE) \$(GMIME_CFLAGS)      \\\r
191                    -DUTIL_BYTE_ORDER=\$(UTIL_BYTE_ORDER)\r
192  \r
193  CONFIGURE_CXXFLAGS = -DHAVE_GETLINE=\$(HAVE_GETLINE) \$(GMIME_CFLAGS)    \\\r
194 +                    \$(ZLIB_CFLAGS)                                     \\\r
195                      \$(TALLOC_CFLAGS) -DHAVE_VALGRIND=\$(HAVE_VALGRIND) \\\r
196                      \$(VALGRIND_CFLAGS) \$(XAPIAN_CXXFLAGS)             \\\r
197                      -DHAVE_STRCASESTR=\$(HAVE_STRCASESTR)               \\\r
198 @@ -892,5 +914,5 @@ CONFIGURE_CXXFLAGS = -DHAVE_GETLINE=\$(HAVE_GETLINE) \$(GMIME_CFLAGS)    \\\r
199                      -DHAVE_XAPIAN_COMPACT=\$(HAVE_XAPIAN_COMPACT)       \\\r
200                      -DUTIL_BYTE_ORDER=\$(UTIL_BYTE_ORDER)\r
201  \r
202 -CONFIGURE_LDFLAGS =  \$(GMIME_LDFLAGS) \$(TALLOC_LDFLAGS) \$(XAPIAN_LDFLAGS)\r
203 +CONFIGURE_LDFLAGS =  \$(GMIME_LDFLAGS) \$(TALLOC_LDFLAGS) \$(ZLIB_LDFLAGS) \$(XAPIAN_LDFLAGS)\r
204  EOF\r
205 diff --git a/doc/man1/notmuch-dump.rst b/doc/man1/notmuch-dump.rst\r
206 index 17d1da5..d94cb4f 100644\r
207 --- a/doc/man1/notmuch-dump.rst\r
208 +++ b/doc/man1/notmuch-dump.rst\r
209 @@ -19,6 +19,9 @@ recreated from the messages themselves. The output of notmuch dump is\r
210  therefore the only critical thing to backup (and much more friendly to\r
211  incremental backup than the native database files.)\r
212  \r
213 +``--gzip``\r
214 +    Compress the output in a format compatible with **gzip(1)**.\r
215 +\r
216  ``--format=(sup|batch-tag)``\r
217      Notmuch restore supports two plain text dump formats, both with one\r
218      message-id per line, followed by a list of tags.\r
219 diff --git a/notmuch-client.h b/notmuch-client.h\r
220 index d110648..e1efbe0 100644\r
221 --- a/notmuch-client.h\r
222 +++ b/notmuch-client.h\r
223 @@ -450,7 +450,9 @@ typedef enum dump_formats {\r
224  int\r
225  notmuch_database_dump (notmuch_database_t *notmuch,\r
226                        const char *output_file_name,\r
227 -                      const char *query_str, dump_format_t output_format);\r
228 +                      const char *query_str,\r
229 +                      dump_format_t output_format,\r
230 +                      notmuch_bool_t gzip_output);\r
231  \r
232  #include "command-line-arguments.h"\r
233  #endif\r
234 diff --git a/notmuch-dump.c b/notmuch-dump.c\r
235 index 21702d7..2849eab 100644\r
236 --- a/notmuch-dump.c\r
237 +++ b/notmuch-dump.c\r
238 @@ -21,9 +21,11 @@\r
239  #include "notmuch-client.h"\r
240  #include "hex-escape.h"\r
241  #include "string-util.h"\r
242 +#include <zlib.h>\r
243 +\r
244  \r
245  static int\r
246 -database_dump_file (notmuch_database_t *notmuch, FILE *output,\r
247 +database_dump_file (notmuch_database_t *notmuch, gzFile output,\r
248                     const char *query_str, int output_format)\r
249  {\r
250      notmuch_query_t *query;\r
251 @@ -69,7 +71,7 @@ database_dump_file (notmuch_database_t *notmuch, FILE *output,\r
252         }\r
253  \r
254         if (output_format == DUMP_FORMAT_SUP) {\r
255 -           fprintf (output, "%s (", message_id);\r
256 +           gzprintf (output, "%s (", message_id);\r
257         }\r
258  \r
259         for (tags = notmuch_message_get_tags (message);\r
260 @@ -78,12 +80,12 @@ database_dump_file (notmuch_database_t *notmuch, FILE *output,\r
261             const char *tag_str = notmuch_tags_get (tags);\r
262  \r
263             if (! first)\r
264 -               fputs (" ", output);\r
265 +               gzputs (output, " ");\r
266  \r
267             first = 0;\r
268  \r
269             if (output_format == DUMP_FORMAT_SUP) {\r
270 -               fputs (tag_str, output);\r
271 +               gzputs (output, tag_str);\r
272             } else {\r
273                 if (hex_encode (notmuch, tag_str,\r
274                                 &buffer, &buffer_size) != HEX_SUCCESS) {\r
275 @@ -91,12 +93,12 @@ database_dump_file (notmuch_database_t *notmuch, FILE *output,\r
276                              tag_str);\r
277                     return EXIT_FAILURE;\r
278                 }\r
279 -               fprintf (output, "+%s", buffer);\r
280 +               gzprintf (output, "+%s", buffer);\r
281             }\r
282         }\r
283  \r
284         if (output_format == DUMP_FORMAT_SUP) {\r
285 -           fputs (")\n", output);\r
286 +           gzputs (output, ")\n");\r
287         } else {\r
288             if (make_boolean_term (notmuch, "id", message_id,\r
289                                    &buffer, &buffer_size)) {\r
290 @@ -104,7 +106,7 @@ database_dump_file (notmuch_database_t *notmuch, FILE *output,\r
291                              message_id, strerror (errno));\r
292                     return EXIT_FAILURE;\r
293             }\r
294 -           fprintf (output, " -- %s\n", buffer);\r
295 +           gzprintf (output, " -- %s\n", buffer);\r
296         }\r
297  \r
298         notmuch_message_destroy (message);\r
299 @@ -121,24 +123,83 @@ database_dump_file (notmuch_database_t *notmuch, FILE *output,\r
300  int\r
301  notmuch_database_dump (notmuch_database_t *notmuch,\r
302                        const char *output_file_name,\r
303 -                      const char *query_str, dump_format_t output_format)\r
304 +                      const char *query_str,\r
305 +                      dump_format_t output_format,\r
306 +                      notmuch_bool_t gzip_output)\r
307  {\r
308 -    FILE *output = stdout;\r
309 -    int ret;\r
310 +    gzFile output = NULL;\r
311 +    const char *mode = gzip_output ? "w9" : "wT";\r
312 +    const char *name_for_error = output_file_name ? output_file_name : "stdout";\r
313 +\r
314 +    char *tempname = NULL;\r
315 +    int outfd = -1;\r
316 +\r
317 +    int ret = -1;\r
318  \r
319      if (output_file_name) {\r
320 -       output = fopen (output_file_name, "w");\r
321 -       if (output == NULL) {\r
322 -           fprintf (stderr, "Error opening %s for writing: %s\n",\r
323 -                    output_file_name, strerror (errno));\r
324 -           return EXIT_FAILURE;\r
325 -       }\r
326 +       tempname = talloc_asprintf (notmuch, "%s.XXXXXX", output_file_name);\r
327 +       outfd = mkstemp (tempname);\r
328 +    } else {\r
329 +       outfd = dup (STDOUT_FILENO);\r
330 +    }\r
331 +\r
332 +    if (outfd < 0) {\r
333 +       fprintf (stderr, "Bad output file %s\n", name_for_error);\r
334 +       goto DONE;\r
335 +    }\r
336 +\r
337 +    output = gzdopen (outfd, mode);\r
338 +\r
339 +    if (output == NULL) {\r
340 +       fprintf (stderr, "Error opening %s for (gzip) writing: %s\n",\r
341 +                name_for_error, strerror (errno));\r
342 +       if (close (outfd))\r
343 +           fprintf (stderr, "Error closing %s during shutdown: %s\n",\r
344 +                name_for_error, strerror (errno));\r
345 +       goto DONE;\r
346      }\r
347  \r
348      ret = database_dump_file (notmuch, output, query_str, output_format);\r
349 +    if (ret) goto DONE;\r
350 +\r
351 +    ret = gzflush (output, Z_FINISH);\r
352 +    if (ret) {\r
353 +       fprintf (stderr, "Error flushing output: %s\n", gzerror (output, NULL));\r
354 +       goto DONE;\r
355 +    }\r
356 +\r
357 +    if (output_file_name) {\r
358 +       ret = fdatasync (outfd);\r
359 +       if (ret) {\r
360 +           fprintf (stderr, "Error syncing %s to disk: %s\n",\r
361 +                    name_for_error, strerror (errno));\r
362 +           goto DONE;\r
363 +       }\r
364 +    }\r
365 +\r
366 +    if (gzclose_w (output) != Z_OK) {\r
367 +       fprintf (stderr, "Error closing %s: %s\n", name_for_error,\r
368 +                gzerror (output, NULL));\r
369 +       ret = EXIT_FAILURE;\r
370 +       output = NULL;\r
371 +       goto DONE;\r
372 +    }\r
373 +\r
374 +    if (output_file_name) {\r
375 +       ret = rename (tempname, output_file_name);\r
376 +       if (ret) {\r
377 +           fprintf (stderr, "Error renaming %s to %s: %s\n",\r
378 +                    tempname, output_file_name, strerror (errno));\r
379 +           goto DONE;\r
380 +       }\r
381 +\r
382 +    }\r
383 + DONE:\r
384 +    if (ret != EXIT_SUCCESS && output)\r
385 +       (void) gzclose_w (output);\r
386  \r
387 -    if (output != stdout)\r
388 -       fclose (output);\r
389 +    if (ret != EXIT_SUCCESS && output_file_name)\r
390 +       (void) unlink (tempname);\r
391  \r
392      return ret;\r
393  }\r
394 @@ -158,6 +219,7 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[])\r
395      int opt_index;\r
396  \r
397      int output_format = DUMP_FORMAT_BATCH_TAG;\r
398 +    notmuch_bool_t gzip_output = 0;\r
399  \r
400      notmuch_opt_desc_t options[] = {\r
401         { NOTMUCH_OPT_KEYWORD, &output_format, "format", 'f',\r
402 @@ -165,6 +227,7 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[])\r
403                                   { "batch-tag", DUMP_FORMAT_BATCH_TAG },\r
404                                   { 0, 0 } } },\r
405         { NOTMUCH_OPT_STRING, &output_file_name, "output", 'o', 0  },\r
406 +       { NOTMUCH_OPT_BOOLEAN, &gzip_output, "gzip", 'z', 0 },\r
407         { 0, 0, 0, 0, 0 }\r
408      };\r
409  \r
410 @@ -181,7 +244,7 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[])\r
411      }\r
412  \r
413      ret = notmuch_database_dump (notmuch, output_file_name, query_str,\r
414 -                                output_format);\r
415 +                                output_format, gzip_output);\r
416  \r
417      notmuch_database_destroy (notmuch);\r
418  \r
419 diff --git a/test/T240-dump-restore.sh b/test/T240-dump-restore.sh\r
420 index 0004438..d79aca8 100755\r
421 --- a/test/T240-dump-restore.sh\r
422 +++ b/test/T240-dump-restore.sh\r
423 @@ -68,6 +68,18 @@ test_begin_subtest "dump --output=outfile --"\r
424  notmuch dump --output=dump-1-arg-dash.actual --\r
425  test_expect_equal_file dump.expected dump-1-arg-dash.actual\r
426  \r
427 +# gzipped output\r
428 +\r
429 +test_begin_subtest "dump --gzip"\r
430 +notmuch dump --gzip > dump-gzip.gz\r
431 +gunzip dump-gzip.gz\r
432 +test_expect_equal_file dump.expected dump-gzip\r
433 +\r
434 +test_begin_subtest "dump --gzip --output=outfile"\r
435 +notmuch dump --gzip --output=dump-gzip-outfile.gz\r
436 +gunzip dump-gzip-outfile.gz\r
437 +test_expect_equal_file dump.expected dump-gzip-outfile\r
438 +\r
439  # Note, we assume all messages from cworth have a message-id\r
440  # containing cworth.org\r
441  \r
442 -- \r
443 1.9.0\r
444 \r