[PATCH v2 2/2] new: Centralize file type stat-ing logic
This moves our logic to get a file's type into one function. This has several benefits: we can support OSes and file systems that do not provide dirent.d_type or always return DT_UNKNOWN, complex symlink-handling logic has been replaced by a simple stat fall-through in one place, and the error message for un-stat-able file is more accurate (previously, the error always mentioned directories, even though a broken symlink is not a directory). --- notmuch-new.c | 103 +++-- test/new |2 +- 2 files changed, 64 insertions(+), 41 deletions(-) diff --git a/notmuch-new.c b/notmuch-new.c index cb720cc..8955677 100644 --- a/notmuch-new.c +++ b/notmuch-new.c @@ -154,6 +154,48 @@ dirent_sort_strcmp_name (const struct dirent **a, const struct dirent **b) return strcmp ((*a)->d_name, (*b)->d_name); } +/* Return the type of a directory entry relative to path as a stat(2) + * mode. Like stat, this follows symlinks. Returns -1 and sets errno + * if the file's type cannot be determined (which includes dangling + * symlinks). + */ +static int +dirent_type (const char *path, const struct dirent *entry) +{ +struct stat statbuf; +char *abspath; +int err, saved_errno; + +#ifdef _DIRENT_HAVE_D_TYPE +/* Mapping from d_type to stat mode_t. We omit DT_LNK so that + * we'll fall through to stat and get the real file type. */ +static const mode_t modes[] = { + [DT_BLK] = S_IFBLK, + [DT_CHR] = S_IFCHR, + [DT_DIR] = S_IFDIR, + [DT_FIFO] = S_IFIFO, + [DT_REG] = S_IFREG, + [DT_SOCK] = S_IFSOCK +}; +if (entry->d_type < ARRAY_SIZE(modes) && modes[entry->d_type]) + return modes[entry->d_type]; +#endif + +abspath = talloc_asprintf (NULL, "%s/%s", path, entry->d_name); +if (!abspath) { + errno = ENOMEM; + return -1; +} +err = stat(abspath, ); +saved_errno = errno; +talloc_free (abspath); +if (err < 0) { + errno = saved_errno; + return -1; +} +return statbuf.st_mode & S_IFMT; +} + /* Test if the directory looks like a Maildir directory. * * Search through the array of directory entries to see if we can find all @@ -162,12 +204,12 @@ dirent_sort_strcmp_name (const struct dirent **a, const struct dirent **b) * Return 1 if the directory looks like a Maildir and 0 otherwise. */ static int -_entries_resemble_maildir (struct dirent **entries, int count) +_entries_resemble_maildir (const char *path, struct dirent **entries, int count) { int i, found = 0; for (i = 0; i < count; i++) { - if (entries[i]->d_type != DT_DIR && entries[i]->d_type != DT_UNKNOWN) + if (dirent_type (path, entries[i]) != S_IFDIR) continue; if (strcmp(entries[i]->d_name, "new") == 0 || @@ -250,7 +292,7 @@ add_files_recursive (notmuch_database_t *notmuch, notmuch_status_t status, ret = NOTMUCH_STATUS_SUCCESS; notmuch_message_t *message = NULL; struct dirent **fs_entries = NULL; -int i, num_fs_entries; +int i, num_fs_entries, entry_type; notmuch_directory_t *directory; notmuch_filenames_t *db_files = NULL; notmuch_filenames_t *db_subdirs = NULL; @@ -317,7 +359,7 @@ add_files_recursive (notmuch_database_t *notmuch, } /* Pass 1: Recurse into all sub-directories. */ -is_maildir = _entries_resemble_maildir (fs_entries, num_fs_entries); +is_maildir = _entries_resemble_maildir (path, fs_entries, num_fs_entries); for (i = 0; i < num_fs_entries; i++) { if (interrupted) @@ -325,17 +367,16 @@ add_files_recursive (notmuch_database_t *notmuch, entry = fs_entries[i]; - /* We only want to descend into directories. -* But symlinks can be to directories too, of course. -* -* And if the filesystem doesn't tell us the file type in the -* scandir results, then it might be a directory (and if not, -* then we'll stat and return immediately in the next level of -* recursion). */ - if (entry->d_type != DT_DIR && - entry->d_type != DT_LNK && - entry->d_type != DT_UNKNOWN) - { + /* We only want to descend into directories (and symlinks to +* directories). */ + entry_type = dirent_type (path, entry); + if (entry_type == -1) { + /* Be pessimistic, e.g. so we don't lose lots of mail just +* because a user broke a symlink. */ + fprintf (stderr, "Error reading file %s/%s: %s\n", +path, entry->d_name, strerror (errno)); + return NOTMUCH_STATUS_FILE_ERROR; + } else if (entry_type != S_IFDIR) { continue; } @@ -425,31 +466,13 @@ add_files_recursive (notmuch_database_t *notmuch, notmuch_filenames_move_to_next (db_subdirs); } - /* If we're looking at a symlink, we only want to add it if it -* links to a regular file, (and not to a
[PATCH v2 1/2] test: Test notmuch new with a broken symlink
--- test/new | 10 ++ 1 file changed, 10 insertions(+) diff --git a/test/new b/test/new index 99f9913..26253db 100755 --- a/test/new +++ b/test/new @@ -136,6 +136,16 @@ output=$(NOTMUCH_NEW) test_expect_equal "$output" "Added 1 new message to the database." +test_begin_subtest "Broken symlink aborts" +ln -s does-not-exist "${MAIL_DIR}/broken" +output=$(NOTMUCH_NEW 2>&1) +test_expect_equal "$output" \ +"Error reading directory /run/shm/nm/tmp.new/mail/broken: No such file or directory +Note: A fatal error was encountered: Something went wrong trying to read or write a file +No new mail." +rm "${MAIL_DIR}/broken" + + test_begin_subtest "New two-level directory" generate_message [dir]=two/levels -- 1.7.10
[PATCH 2/2] new: Centralize file type stat-ing logic
Quoth Jani Nikula on May 08 at 12:08 am: >On May 7, 2012 9:10 PM, "Austin Clements" <[1]amdragon at mit.edu> wrote: >> >> This moves our logic to get a file's type into one function. ?This has >> several benefits: we can support OSes and file systems that do not >> provide dirent.d_type or always return DT_UNKNOWN, complex >> symlink-handling logic has been replaced by a simple stat fall-through >> in one place, and the error message for un-stat-able file is more >> accurate (previously, the error always mentioned directories, even >> though a broken symlink is not a directory). > >Please find some quick drive-by-review below. Thanks for the review! New version on its way... >J. > >> --- >> ?notmuch-new.c | ? 99 >++--- >> ?test/new ? ? ?| ? ?2 +- >> ?2 files changed, 60 insertions(+), 41 deletions(-) >> >> diff --git a/notmuch-new.c b/notmuch-new.c >> index cb720cc..cf2580e 100644 >> --- a/notmuch-new.c >> +++ b/notmuch-new.c >> @@ -154,6 +154,44 @@ dirent_sort_strcmp_name (const struct dirent **a, >const struct dirent **b) >> ? ? return strcmp ((*a)->d_name, (*b)->d_name); >> ?} >> >> +/* Return the type of a directory entry relative to path as a stat(2) >> + * mode. ?Like stat, this follows symlinks. ?Returns -1 and sets errno >> + * if the file's type cannot be determined (which includes dangling >> + * symlinks). >> + */ >> +static int >> +dirent_type (const char *path, const struct dirent *entry) >> +{ >> + ? ?struct stat statbuf; >> + ? ?char *abspath; >> + ? ?int err; >> + >> +#ifdef _DIRENT_HAVE_D_TYPE >> + ? ?/* Mapping from d_type to stat mode_t. ?We omit DT_LNK so that >> + ? ? * we'll fall through to stat and get the real file type. */ >> + ? ?static const mode_t modes[] = { >> + ? ? ? [DT_BLK] ?= S_IFBLK, >> + ? ? ? [DT_CHR] ?= S_IFCHR, >> + ? ? ? [DT_DIR] ?= S_IFDIR, >> + ? ? ? [DT_FIFO] = S_IFIFO, >> + ? ? ? [DT_REG] ?= S_IFREG, >> + ? ? ? [DT_SOCK] = S_IFSOCK >> + ? ?}; >> + ? ?if (entry->d_type < sizeof(modes)/sizeof(modes[0]) && > >ARRAY_SIZE() Huh. I went looking for that. I wonder how I missed it. >> + ? ? ? modes[entry->d_type]) >> + ? ? ? return modes[entry->d_type]; >> +#endif >> + >> + ? ?abspath = talloc_asprintf (NULL, "%s/%s", path, entry->d_name); >> + ? ?if (!abspath) >> + ? ? ? return -1; > >Does talloc set errno in this case? I suspect not. > >> + ? ?err = stat(abspath, ); >> + ? ?talloc_free (abspath); > >This likely breaks your promise about errno. You can't trust talloc_free() >not calling some function that sets errno. Setting errno was a late and apparently not well thought-out addition. The new patch should set it on all of the error paths and save it around things that may modify it. >> + ? ?if (err < 0) >> + ? ? ? return -1; >> + ? ?return statbuf.st_mode & S_IFMT; >> +} >> + >> ?/* Test if the directory looks like a Maildir directory. >> ?* >> ?* Search through the array of directory entries to see if we can find >all >> @@ -162,12 +200,12 @@ dirent_sort_strcmp_name (const struct dirent **a, >const struct dirent **b) >> ?* Return 1 if the directory looks like a Maildir and 0 otherwise. >> ?*/ >> ?static int >> -_entries_resemble_maildir (struct dirent **entries, int count) >> +_entries_resemble_maildir (const char *path, struct dirent **entries, >int count) >> ?{ >> ? ? int i, found = 0; >> >> ? ? for (i = 0; i < count; i++) { >> - ? ? ? if (entries[i]->d_type != DT_DIR && entries[i]->d_type != >DT_UNKNOWN) >> + ? ? ? if (dirent_type (path, entries[i]) != S_IFDIR) >> ? ? ? ? ? ?continue; >> >> ? ? ? ?if (strcmp(entries[i]->d_name, "new") == 0 || >> @@ -250,7 +288,7 @@ add_files_recursive (notmuch_database_t *notmuch, >> ? ? notmuch_status_t status, ret = NOTMUCH_STATUS_SUCCESS; >> ? ? notmuch_message_t *message = NULL; >> ? ? struct dirent **fs_entries = NULL; >> - ? ?int i, num_fs_entries; >> + ? ?int i, num_fs_entries, entry_type; >> ? ? notmuch_directory_t *directory; >> ? ? notmuch_filenames_t *db_files = NULL; >> ? ? notmuch_filenames_t *db_subdirs = NULL; >> @@ -317,7 +355,7 @@ add_files_recursive (notmuch_database_t *notmuch, >> ? ? } >> >> ? ? /* Pass 1: Recurse into all sub-directories. */ >> - ? ?is_maildir = _entries_resemble_maildir (fs_entries, >num_fs_entries); >> + ? ?is_maildir = _entries_resemble_maildir (path, fs_entries, >num_fs_entries); >> >> ? ? for (i = 0; i < num_fs_entries; i++) { >> ? ? ? ?if (interrupted) >> @@ -325,17 +363,16 @@ add_files_recursive (notmuch_database_t *notmuch, >> >> ? ? ? ?entry = fs_entries[i]; >> >> -
[PATCH 4/4] ruby: Add wrapper for notmuch_query_set_omit_excluded()
--- bindings/ruby/defs.h |3 +++ bindings/ruby/init.c |7 +++ bindings/ruby/query.c | 18 ++ 3 files changed, 28 insertions(+) diff --git a/bindings/ruby/defs.h b/bindings/ruby/defs.h index 6fe5787..85d8205 100644 --- a/bindings/ruby/defs.h +++ b/bindings/ruby/defs.h @@ -220,6 +220,9 @@ VALUE notmuch_rb_query_add_tag_exclude (VALUE self, VALUE tagv); VALUE +notmuch_rb_query_set_omit_excluded (VALUE self, VALUE omitv); + +VALUE notmuch_rb_query_search_threads (VALUE self); VALUE diff --git a/bindings/ruby/init.c b/bindings/ruby/init.c index edcf101..3fe60fb 100644 --- a/bindings/ruby/init.c +++ b/bindings/ruby/init.c @@ -96,6 +96,12 @@ Init_notmuch (void) */ rb_define_const (mod, "MESSAGE_FLAG_MATCH", INT2FIX (NOTMUCH_MESSAGE_FLAG_MATCH)); /* + * Document-const: Notmuch::MESSAGE_FLAG_EXCLUDED + * + * Message flag "excluded" + */ +rb_define_const (mod, "MESSAGE_FLAG_EXCLUDED", INT2FIX (NOTMUCH_MESSAGE_FLAG_EXCLUDED)); +/* * Document-const: Notmuch::TAG_MAX * * Maximum allowed length of a tag @@ -235,6 +241,7 @@ Init_notmuch (void) rb_define_method (notmuch_rb_cQuery, "sort=", notmuch_rb_query_set_sort, 1); /* in query.c */ rb_define_method (notmuch_rb_cQuery, "to_s", notmuch_rb_query_get_string, 0); /* in query.c */ rb_define_method (notmuch_rb_cQuery, "add_tag_exclude", notmuch_rb_query_add_tag_exclude, 1); /* in query.c */ +rb_define_method (notmuch_rb_cQuery, "omit_excluded=", notmuch_rb_query_set_omit_excluded, 1); /* in query.c */ rb_define_method (notmuch_rb_cQuery, "search_threads", notmuch_rb_query_search_threads, 0); /* in query.c */ rb_define_method (notmuch_rb_cQuery, "search_messages", notmuch_rb_query_search_messages, 0); /* in query.c */ rb_define_method (notmuch_rb_cQuery, "count_messages", notmuch_rb_query_count_messages, 0); /* in query.c */ diff --git a/bindings/ruby/query.c b/bindings/ruby/query.c index 2a80008..e5ba1b7 100644 --- a/bindings/ruby/query.c +++ b/bindings/ruby/query.c @@ -107,6 +107,24 @@ notmuch_rb_query_add_tag_exclude (VALUE self, VALUE tagv) } /* + * call-seq: QUERY.omit_excluded=(boolean) => nil + * + * Specify whether to omit excluded results or simply flag them. + * By default, this is set to +true+. + */ +VALUE +notmuch_rb_query_set_omit_excluded (VALUE self, VALUE omitv) +{ +notmuch_query_t *query; + +Data_Get_Notmuch_Query (self, query); + +notmuch_query_set_omit_excluded (query, RTEST (omitv)); + +return Qnil; +} + +/* * call-seq: QUERY.search_threads => THREADS * * Search for threads -- 1.7.10.1
[PATCH 3/4] ruby: Add workarounds to use in-tree build not the installed one
- Make mkmf use the notmuch.h under ../../lib - Use libnotmuch.a instead of linking to the installed libnotmuch.so --- bindings/ruby/defs.h |4 ++-- bindings/ruby/extconf.rb | 26 ++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/bindings/ruby/defs.h b/bindings/ruby/defs.h index a41cf10..6fe5787 100644 --- a/bindings/ruby/defs.h +++ b/bindings/ruby/defs.h @@ -1,6 +1,6 @@ /* The Ruby interface to the notmuch mail library * - * Copyright ? 2010, 2011 Ali Polatel + * Copyright ? 2010, 2011, 2012 Ali Polatel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,8 +21,8 @@ #ifndef DEFS_H #define DEFS_H -#include #include +#include "notmuch.h" VALUE notmuch_rb_cDatabase; VALUE notmuch_rb_cDirectory; diff --git a/bindings/ruby/extconf.rb b/bindings/ruby/extconf.rb index ccac609..933f34a 100644 --- a/bindings/ruby/extconf.rb +++ b/bindings/ruby/extconf.rb @@ -1,13 +1,31 @@ #!/usr/bin/env ruby # coding: utf-8 -# Copyright 2010, 2011 Ali Polatel +# Copyright 2010, 2011, 2012 Ali Polatel # Distributed under the terms of the GNU General Public License v3 require 'mkmf' -# Notmuch Library -find_header('notmuch.h', '../../lib') -find_library('notmuch', 'notmuch_database_create', '../../lib') +NOTDIR = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'lib')) +NOTHDR = File.join(NOTDIR, 'notmuch.h') +NOTLIB = File.join(NOTDIR, 'libnotmuch.a') + +unless File.exists? NOTHDR + $stderr.puts "notmuch.h is missing under #{NOTDIR}" + exit 1 +end + +unless File.exists? NOTLIB + $stderr.puts "libnotmuch.a is missing under #{NOTDIR}" + exit 1 +end + +# Small hack to build with in-tree version not the installed one. +# find_header() and friends use standard include/library paths first. +$stderr.puts "Added -I#{NOTDIR} to $INCFLAGS" +$INCFLAGS = "-I#{NOTDIR}".quote + " " + $INCFLAGS +find_header('notmuch.h', NOTDIR) + +$LOCAL_LIBS += NOTLIB # Create Makefile dir_config('notmuch') -- 1.7.10.1
[PATCH 2/4] ruby: Add wrapper for notmuch_query_add_tag_exclude
--- bindings/ruby/defs.h |3 +++ bindings/ruby/init.c |1 + bindings/ruby/query.c | 18 ++ 3 files changed, 22 insertions(+) diff --git a/bindings/ruby/defs.h b/bindings/ruby/defs.h index 25222a6..a41cf10 100644 --- a/bindings/ruby/defs.h +++ b/bindings/ruby/defs.h @@ -217,6 +217,9 @@ VALUE notmuch_rb_query_get_string (VALUE self); VALUE +notmuch_rb_query_add_tag_exclude (VALUE self, VALUE tagv); + +VALUE notmuch_rb_query_search_threads (VALUE self); VALUE diff --git a/bindings/ruby/init.c b/bindings/ruby/init.c index 7ad0ecf..edcf101 100644 --- a/bindings/ruby/init.c +++ b/bindings/ruby/init.c @@ -234,6 +234,7 @@ Init_notmuch (void) rb_define_method (notmuch_rb_cQuery, "sort", notmuch_rb_query_get_sort, 0); /* in query.c */ rb_define_method (notmuch_rb_cQuery, "sort=", notmuch_rb_query_set_sort, 1); /* in query.c */ rb_define_method (notmuch_rb_cQuery, "to_s", notmuch_rb_query_get_string, 0); /* in query.c */ +rb_define_method (notmuch_rb_cQuery, "add_tag_exclude", notmuch_rb_query_add_tag_exclude, 1); /* in query.c */ rb_define_method (notmuch_rb_cQuery, "search_threads", notmuch_rb_query_search_threads, 0); /* in query.c */ rb_define_method (notmuch_rb_cQuery, "search_messages", notmuch_rb_query_search_messages, 0); /* in query.c */ rb_define_method (notmuch_rb_cQuery, "count_messages", notmuch_rb_query_count_messages, 0); /* in query.c */ diff --git a/bindings/ruby/query.c b/bindings/ruby/query.c index 02b7819..2a80008 100644 --- a/bindings/ruby/query.c +++ b/bindings/ruby/query.c @@ -89,6 +89,24 @@ notmuch_rb_query_get_string (VALUE self) } /* + * call-seq: QUERY.add_tag_exclude(tag) => nil + * + * Add a tag that will be excluded from the query results by default. + */ +VALUE +notmuch_rb_query_add_tag_exclude (VALUE self, VALUE tagv) +{ +notmuch_query_t *query; +const char *tag; + +Data_Get_Notmuch_Query (self, query); +tag = RSTRING_PTR(tagv); + +notmuch_query_add_tag_exclude(query, tag); +return Qnil; +} + +/* * call-seq: QUERY.search_threads => THREADS * * Search for threads -- 1.7.10.1
[PATCH 1/4] ruby: Add wrapper for notmuch_query_count_messages
--- bindings/ruby/defs.h |3 +++ bindings/ruby/init.c |3 ++- bindings/ruby/query.c | 21 - 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/bindings/ruby/defs.h b/bindings/ruby/defs.h index 81f652f..25222a6 100644 --- a/bindings/ruby/defs.h +++ b/bindings/ruby/defs.h @@ -222,6 +222,9 @@ notmuch_rb_query_search_threads (VALUE self); VALUE notmuch_rb_query_search_messages (VALUE self); +VALUE +notmuch_rb_query_count_messages (VALUE self); + /* threads.c */ VALUE notmuch_rb_threads_destroy (VALUE self); diff --git a/bindings/ruby/init.c b/bindings/ruby/init.c index 4405f19..7ad0ecf 100644 --- a/bindings/ruby/init.c +++ b/bindings/ruby/init.c @@ -1,6 +1,6 @@ /* The Ruby interface to the notmuch mail library * - * Copyright ? 2010, 2011 Ali Polatel + * Copyright ? 2010, 2011, 2012 Ali Polatel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -236,6 +236,7 @@ Init_notmuch (void) rb_define_method (notmuch_rb_cQuery, "to_s", notmuch_rb_query_get_string, 0); /* in query.c */ rb_define_method (notmuch_rb_cQuery, "search_threads", notmuch_rb_query_search_threads, 0); /* in query.c */ rb_define_method (notmuch_rb_cQuery, "search_messages", notmuch_rb_query_search_messages, 0); /* in query.c */ +rb_define_method (notmuch_rb_cQuery, "count_messages", notmuch_rb_query_count_messages, 0); /* in query.c */ /* * Document-class: Notmuch::Threads diff --git a/bindings/ruby/query.c b/bindings/ruby/query.c index 74fd585..02b7819 100644 --- a/bindings/ruby/query.c +++ b/bindings/ruby/query.c @@ -1,6 +1,6 @@ /* The Ruby interface to the notmuch mail library * - * Copyright ? 2010, 2011 Ali Polatel + * Copyright ? 2010, 2011, 2012 Ali Polatel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -127,3 +127,22 @@ notmuch_rb_query_search_messages (VALUE self) return Data_Wrap_Struct (notmuch_rb_cMessages, NULL, NULL, messages); } + +/* + * call-seq: QUERY.count_messages => Fixnum + * + * Return an estimate of the number of messages matching a search + */ +VALUE +notmuch_rb_query_count_messages (VALUE self) +{ +notmuch_query_t *query; + +Data_Get_Notmuch_Query (self, query); + +/* Xapian exceptions are not handled properly. + * (function may return 0 after printing a message) + * Thus there is nothing we can do here... + */ +return UINT2FIX(notmuch_query_count_messages(query)); +} -- 1.7.10.1
[PATCH 0/4] ruby: quick update before the freeze!
Hey David, I've polished the small patch series I had lying around. I believe I've covered all the lacking API changes. In addition, the third patch fixes a long-standing bug about the build. I've decided to send these to you and the list instead of committing right away in case someone notices any quick errors. Otherwise, please commit these before the freeze and make rubyists happy. Ali Polatel (4): ruby: Add wrapper for notmuch_query_count_messages ruby: Add wrapper for notmuch_query_add_tag_exclude ruby: Add workarounds to use in-tree build not the installed one ruby: Add wrapper for notmuch_query_set_omit_excluded() bindings/ruby/defs.h | 13 +-- bindings/ruby/extconf.rb | 26 + bindings/ruby/init.c | 11 - bindings/ruby/query.c| 57 +- 4 files changed, 99 insertions(+), 8 deletions(-) -- 1.7.10.1
[Patch v2] emacs: add pipe attachment command
On Mon, 07 May 2012, Jameson Graef Rollins wrote: > On Mon, May 07 2012, Mark Walters wrote: >> Here is a rebased to current master version of the previous patch: no >> other changes. > > Actually, what I sent was a rebase. Sorry somehow I missed that you had actually sent the rebase too: and then I messed up my rebase. How embarrassing! Anyway many thanks for the correct rebase, and for spotting my stupid error. Best wishes Mark
[PATCH 2/2] new: Centralize file type stat-ing logic
This moves our logic to get a file's type into one function. This has several benefits: we can support OSes and file systems that do not provide dirent.d_type or always return DT_UNKNOWN, complex symlink-handling logic has been replaced by a simple stat fall-through in one place, and the error message for un-stat-able file is more accurate (previously, the error always mentioned directories, even though a broken symlink is not a directory). --- notmuch-new.c | 99 ++--- test/new |2 +- 2 files changed, 60 insertions(+), 41 deletions(-) diff --git a/notmuch-new.c b/notmuch-new.c index cb720cc..cf2580e 100644 --- a/notmuch-new.c +++ b/notmuch-new.c @@ -154,6 +154,44 @@ dirent_sort_strcmp_name (const struct dirent **a, const struct dirent **b) return strcmp ((*a)->d_name, (*b)->d_name); } +/* Return the type of a directory entry relative to path as a stat(2) + * mode. Like stat, this follows symlinks. Returns -1 and sets errno + * if the file's type cannot be determined (which includes dangling + * symlinks). + */ +static int +dirent_type (const char *path, const struct dirent *entry) +{ +struct stat statbuf; +char *abspath; +int err; + +#ifdef _DIRENT_HAVE_D_TYPE +/* Mapping from d_type to stat mode_t. We omit DT_LNK so that + * we'll fall through to stat and get the real file type. */ +static const mode_t modes[] = { + [DT_BLK] = S_IFBLK, + [DT_CHR] = S_IFCHR, + [DT_DIR] = S_IFDIR, + [DT_FIFO] = S_IFIFO, + [DT_REG] = S_IFREG, + [DT_SOCK] = S_IFSOCK +}; +if (entry->d_type < sizeof(modes)/sizeof(modes[0]) && + modes[entry->d_type]) + return modes[entry->d_type]; +#endif + +abspath = talloc_asprintf (NULL, "%s/%s", path, entry->d_name); +if (!abspath) + return -1; +err = stat(abspath, ); +talloc_free (abspath); +if (err < 0) + return -1; +return statbuf.st_mode & S_IFMT; +} + /* Test if the directory looks like a Maildir directory. * * Search through the array of directory entries to see if we can find all @@ -162,12 +200,12 @@ dirent_sort_strcmp_name (const struct dirent **a, const struct dirent **b) * Return 1 if the directory looks like a Maildir and 0 otherwise. */ static int -_entries_resemble_maildir (struct dirent **entries, int count) +_entries_resemble_maildir (const char *path, struct dirent **entries, int count) { int i, found = 0; for (i = 0; i < count; i++) { - if (entries[i]->d_type != DT_DIR && entries[i]->d_type != DT_UNKNOWN) + if (dirent_type (path, entries[i]) != S_IFDIR) continue; if (strcmp(entries[i]->d_name, "new") == 0 || @@ -250,7 +288,7 @@ add_files_recursive (notmuch_database_t *notmuch, notmuch_status_t status, ret = NOTMUCH_STATUS_SUCCESS; notmuch_message_t *message = NULL; struct dirent **fs_entries = NULL; -int i, num_fs_entries; +int i, num_fs_entries, entry_type; notmuch_directory_t *directory; notmuch_filenames_t *db_files = NULL; notmuch_filenames_t *db_subdirs = NULL; @@ -317,7 +355,7 @@ add_files_recursive (notmuch_database_t *notmuch, } /* Pass 1: Recurse into all sub-directories. */ -is_maildir = _entries_resemble_maildir (fs_entries, num_fs_entries); +is_maildir = _entries_resemble_maildir (path, fs_entries, num_fs_entries); for (i = 0; i < num_fs_entries; i++) { if (interrupted) @@ -325,17 +363,16 @@ add_files_recursive (notmuch_database_t *notmuch, entry = fs_entries[i]; - /* We only want to descend into directories. -* But symlinks can be to directories too, of course. -* -* And if the filesystem doesn't tell us the file type in the -* scandir results, then it might be a directory (and if not, -* then we'll stat and return immediately in the next level of -* recursion). */ - if (entry->d_type != DT_DIR && - entry->d_type != DT_LNK && - entry->d_type != DT_UNKNOWN) - { + /* We only want to descend into directories (and symlinks to +* directories). */ + entry_type = dirent_type (path, entry); + if (entry_type == -1) { + /* Be pessimistic, e.g. so we don't loose lots of mail +* just because a user broke a symlink. */ + fprintf (stderr, "Error reading file %s/%s: %s\n", +path, entry->d_name, strerror (errno)); + return NOTMUCH_STATUS_FILE_ERROR; + } else if (entry_type != S_IFDIR) { continue; } @@ -425,31 +462,13 @@ add_files_recursive (notmuch_database_t *notmuch, notmuch_filenames_move_to_next (db_subdirs); } - /* If we're looking at a symlink, we only want to add it if it -* links to a regular file, (and not to a directory, say). -* -* Similarly, if the file is of unknown type (due to
[PATCH 1/2] test: Test notmuch new with a broken symlink
--- test/new | 10 ++ 1 file changed, 10 insertions(+) diff --git a/test/new b/test/new index 99f9913..26253db 100755 --- a/test/new +++ b/test/new @@ -136,6 +136,16 @@ output=$(NOTMUCH_NEW) test_expect_equal "$output" "Added 1 new message to the database." +test_begin_subtest "Broken symlink aborts" +ln -s does-not-exist "${MAIL_DIR}/broken" +output=$(NOTMUCH_NEW 2>&1) +test_expect_equal "$output" \ +"Error reading directory /run/shm/nm/tmp.new/mail/broken: No such file or directory +Note: A fatal error was encountered: Something went wrong trying to read or write a file +No new mail." +rm "${MAIL_DIR}/broken" + + test_begin_subtest "New two-level directory" generate_message [dir]=two/levels -- 1.7.10
[PATCH 0/2] Better, more portable stat-ing logic for notmuch new
This fully implements my suggestion from id:"20120411193609.GC13549 at mit.edu". Originally this was just meant to make the code more portable (since d_type is not portable), but it wound up also simplifying some of the notmuch new logic.
android 3.0 tablet and notmuch?
David Belohrad writes: > Dear All, > > i experience very weird problem: have samsung galaxy tab 10.1 without > any firmware tweaks. now, when I take a picture in the tablet and send > it via email to me, then I open this email in emacs, the picture is not > there. Only text part of the email is present. > > It seems like the emacs does not recognize an image. When I open the > email in the roundcube via web interface, the image is there. As well > when I open the message in the tablet itself, the image is there as > well. > > Any idea what this might be? Anyone experiences the same problem? Yes, it happens to me with some emails sent from MS Outlook. JSON output contains the image part, but emacs doesn't show anything. I have no clue why. -Michal
[PATCH] test: Force reply to use html2text for consistency
On Mon, May 07 2012, David Bremner wrote: > Adam Wolfe Gordonwrites: > >> The output of the HTML reply test in the emacs suite can vary >> depending on which HTML renderers are installed on the machine running >> the tests. The renderer that is always available is emacs's builtin >> html2text function. In order to get consistency, force the test to use >> html2text even if other renderers are available. > > pushed, and it seems to replicate the test results on the buildbot. If > html2text ever gets less crappy I guess we'll have to redo the test > data. If html2text ever gets less crappy then we face the same problem -- users with different emacs get different rendering output making tests fail for some users. Then we can do "(let ... (mm-text-html-renderer 'mm-insert-part)) to get html inserted unmodified; excerpt from test output using above: -> Hi,This is an HTML test message.OK? +> Hi,This is an HTML test message.OK? > > d Tomi
[Patch v2] emacs: add pipe attachment command
On Mon, May 07 2012, Mark Walters wrote: > Sorry somehow I missed that you had actually sent the rebase too: and > then I messed up my rebase. How embarrassing! > > Anyway many thanks for the correct rebase, and for spotting my stupid error. np ;) I've done plenty of things much more embarrassing than that! jamie. -- next part -- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 835 bytes Desc: not available URL: <http://notmuchmail.org/pipermail/notmuch/attachments/20120507/cf43f036/attachment.pgp>
[Patch v2] emacs: add pipe attachment command
Allow the user to pipe the attachment somewhere. Bound to '|' on the attachment button. --- Thanks for the review! Here is a rebased to current master version of the previous patch: no other changes. Best wishes Mark emacs/notmuch-show.el | 27 +++ 1 files changed, 27 insertions(+), 0 deletions(-) diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el index d318430..b6a1980 100644 --- a/emacs/notmuch-show.el +++ b/emacs/notmuch-show.el @@ -453,6 +453,7 @@ message at DEPTH in the current thread." (define-key map "s" 'notmuch-show-part-button-save) (define-key map "v" 'notmuch-show-part-button-view) (define-key map "o" 'notmuch-show-part-button-interactively-view) +(define-key map "|" 'notmuch-show-part-button-pipe) map) "Submap for button commands") (fset 'notmuch-show-part-button-map notmuch-show-part-button-map) @@ -524,6 +525,28 @@ message at DEPTH in the current thread." (let ((handle (mm-make-handle (current-buffer) (list content-type (mm-interactively-view-part handle +(defun notmuch-show-pipe-part (message-id nth filename content-type) + (notmuch-with-temp-part-buffer message-id nth +(let ((handle (mm-make-handle (current-buffer) (list content-type + (mm-pipe-part handle + +(defun notmuch-show-mm-display-part-inline (msg part nth content-type) + "Use the mm-decode/mm-view functions to display a part in the +current buffer, if possible." + (let ((display-buffer (current-buffer))) +(with-temp-buffer + (let* ((charset (plist-get part :content-charset)) +(handle (mm-make-handle (current-buffer) `(,content-type (charset . ,charset) + ;; If the user wants the part inlined, insert the content and + ;; test whether we are able to inline it (which includes both + ;; capability and suitability tests). + (when (mm-inlined-p handle) + (insert (notmuch-get-bodypart-content msg part nth notmuch-show-process-crypto)) + (when (mm-inlinable-p handle) + (set-buffer display-buffer) + (mm-display-part handle) + t)) + (defun notmuch-show-multipart/*-to-list (part) (mapcar (lambda (inner-part) (plist-get inner-part :content-type)) (plist-get part :content))) @@ -1874,6 +1897,10 @@ the user (see `notmuch-show-stash-mlarchive-link-alist')." (interactive) (notmuch-show-part-button-internal button #'notmuch-show-interactively-view-part)) +(defun notmuch-show-part-button-pipe ( button) + (interactive) + (notmuch-show-part-button-internal button #'notmuch-show-pipe-part)) + (defun notmuch-show-part-button-internal (button handler) (let ((button (or button (button-at (point) (if button -- 1.7.9.1
[Patch v2] emacs: add pipe attachment command
On Mon, May 07 2012, Mark Walters wrote: > Here is a rebased to current master version of the previous patch: no > other changes. Actually, what I sent was a rebase. And this rebase doesn't look correct. The notmuch-show-mm-display-part-inline function moved to notmuch-lib.el. It certainly has nothing to do with this patch, so I don't think adding it here is correct. jamie. -- next part -- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 835 bytes Desc: not available URL: <http://notmuchmail.org/pipermail/notmuch/attachments/20120507/d0040d05/attachment.pgp>
[PATCH] emacs: add pipe attachment command
From: Mark WaltersAllow the user to pipe the attachment somewhere. Bound to '|' on the attachment button. Signed-off-by: Jameson Graef Rollins --- Nice missing feature. Tested and reviewed. It just needed to be rebased against the current master. emacs/notmuch-show.el | 10 ++ 1 file changed, 10 insertions(+) diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el index d318430..26bff8f 100644 --- a/emacs/notmuch-show.el +++ b/emacs/notmuch-show.el @@ -453,6 +453,7 @@ message at DEPTH in the current thread." (define-key map "s" 'notmuch-show-part-button-save) (define-key map "v" 'notmuch-show-part-button-view) (define-key map "o" 'notmuch-show-part-button-interactively-view) +(define-key map "|" 'notmuch-show-part-button-pipe) map) "Submap for button commands") (fset 'notmuch-show-part-button-map notmuch-show-part-button-map) @@ -524,6 +525,11 @@ message at DEPTH in the current thread." (let ((handle (mm-make-handle (current-buffer) (list content-type (mm-interactively-view-part handle +(defun notmuch-show-pipe-part (message-id nth filename content-type) + (notmuch-with-temp-part-buffer message-id nth +(let ((handle (mm-make-handle (current-buffer) (list content-type + (mm-pipe-part handle + (defun notmuch-show-multipart/*-to-list (part) (mapcar (lambda (inner-part) (plist-get inner-part :content-type)) (plist-get part :content))) @@ -1874,6 +1880,10 @@ the user (see `notmuch-show-stash-mlarchive-link-alist')." (interactive) (notmuch-show-part-button-internal button #'notmuch-show-interactively-view-part)) +(defun notmuch-show-part-button-pipe ( button) + (interactive) + (notmuch-show-part-button-internal button #'notmuch-show-pipe-part)) + (defun notmuch-show-part-button-internal (button handler) (let ((button (or button (button-at (point) (if button -- 1.7.10
Re: [PATCH] test: Force reply to use html2text for consistency
On Mon, May 07 2012, David Bremner da...@tethera.net wrote: Adam Wolfe Gordon awg+notm...@xvx.ca writes: The output of the HTML reply test in the emacs suite can vary depending on which HTML renderers are installed on the machine running the tests. The renderer that is always available is emacs's builtin html2text function. In order to get consistency, force the test to use html2text even if other renderers are available. pushed, and it seems to replicate the test results on the buildbot. If html2text ever gets less crappy I guess we'll have to redo the test data. If html2text ever gets less crappy then we face the same problem -- users with different emacs get different rendering output making tests fail for some users. Then we can do (let ... (mm-text-html-renderer 'mm-insert-part)) to get html inserted unmodified; excerpt from test output using above: - Hi,This is an HTML test message.OK? + Hi,br /This is an bHTML/b test message.br /br /OK? d Tomi ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH] emacs: add pipe attachment command
From: Mark Walters markwalters1...@gmail.com Allow the user to pipe the attachment somewhere. Bound to '|' on the attachment button. Signed-off-by: Jameson Graef Rollins jroll...@finestructure.net --- Nice missing feature. Tested and reviewed. It just needed to be rebased against the current master. emacs/notmuch-show.el | 10 ++ 1 file changed, 10 insertions(+) diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el index d318430..26bff8f 100644 --- a/emacs/notmuch-show.el +++ b/emacs/notmuch-show.el @@ -453,6 +453,7 @@ message at DEPTH in the current thread. (define-key map s 'notmuch-show-part-button-save) (define-key map v 'notmuch-show-part-button-view) (define-key map o 'notmuch-show-part-button-interactively-view) +(define-key map | 'notmuch-show-part-button-pipe) map) Submap for button commands) (fset 'notmuch-show-part-button-map notmuch-show-part-button-map) @@ -524,6 +525,11 @@ message at DEPTH in the current thread. (let ((handle (mm-make-handle (current-buffer) (list content-type (mm-interactively-view-part handle +(defun notmuch-show-pipe-part (message-id nth optional filename content-type) + (notmuch-with-temp-part-buffer message-id nth +(let ((handle (mm-make-handle (current-buffer) (list content-type + (mm-pipe-part handle + (defun notmuch-show-multipart/*-to-list (part) (mapcar (lambda (inner-part) (plist-get inner-part :content-type)) (plist-get part :content))) @@ -1874,6 +1880,10 @@ the user (see `notmuch-show-stash-mlarchive-link-alist'). (interactive) (notmuch-show-part-button-internal button #'notmuch-show-interactively-view-part)) +(defun notmuch-show-part-button-pipe (optional button) + (interactive) + (notmuch-show-part-button-internal button #'notmuch-show-pipe-part)) + (defun notmuch-show-part-button-internal (button handler) (let ((button (or button (button-at (point) (if button -- 1.7.10 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[Patch v2] emacs: add pipe attachment command
Allow the user to pipe the attachment somewhere. Bound to '|' on the attachment button. --- Thanks for the review! Here is a rebased to current master version of the previous patch: no other changes. Best wishes Mark emacs/notmuch-show.el | 27 +++ 1 files changed, 27 insertions(+), 0 deletions(-) diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el index d318430..b6a1980 100644 --- a/emacs/notmuch-show.el +++ b/emacs/notmuch-show.el @@ -453,6 +453,7 @@ message at DEPTH in the current thread. (define-key map s 'notmuch-show-part-button-save) (define-key map v 'notmuch-show-part-button-view) (define-key map o 'notmuch-show-part-button-interactively-view) +(define-key map | 'notmuch-show-part-button-pipe) map) Submap for button commands) (fset 'notmuch-show-part-button-map notmuch-show-part-button-map) @@ -524,6 +525,28 @@ message at DEPTH in the current thread. (let ((handle (mm-make-handle (current-buffer) (list content-type (mm-interactively-view-part handle +(defun notmuch-show-pipe-part (message-id nth optional filename content-type) + (notmuch-with-temp-part-buffer message-id nth +(let ((handle (mm-make-handle (current-buffer) (list content-type + (mm-pipe-part handle + +(defun notmuch-show-mm-display-part-inline (msg part nth content-type) + Use the mm-decode/mm-view functions to display a part in the +current buffer, if possible. + (let ((display-buffer (current-buffer))) +(with-temp-buffer + (let* ((charset (plist-get part :content-charset)) +(handle (mm-make-handle (current-buffer) `(,content-type (charset . ,charset) + ;; If the user wants the part inlined, insert the content and + ;; test whether we are able to inline it (which includes both + ;; capability and suitability tests). + (when (mm-inlined-p handle) + (insert (notmuch-get-bodypart-content msg part nth notmuch-show-process-crypto)) + (when (mm-inlinable-p handle) + (set-buffer display-buffer) + (mm-display-part handle) + t)) + (defun notmuch-show-multipart/*-to-list (part) (mapcar (lambda (inner-part) (plist-get inner-part :content-type)) (plist-get part :content))) @@ -1874,6 +1897,10 @@ the user (see `notmuch-show-stash-mlarchive-link-alist'). (interactive) (notmuch-show-part-button-internal button #'notmuch-show-interactively-view-part)) +(defun notmuch-show-part-button-pipe (optional button) + (interactive) + (notmuch-show-part-button-internal button #'notmuch-show-pipe-part)) + (defun notmuch-show-part-button-internal (button handler) (let ((button (or button (button-at (point) (if button -- 1.7.9.1 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Re: android 3.0 tablet and notmuch?
David Belohrad da...@belohrad.ch writes: Dear All, i experience very weird problem: have samsung galaxy tab 10.1 without any firmware tweaks. now, when I take a picture in the tablet and send it via email to me, then I open this email in emacs, the picture is not there. Only text part of the email is present. It seems like the emacs does not recognize an image. When I open the email in the roundcube via web interface, the image is there. As well when I open the message in the tablet itself, the image is there as well. Any idea what this might be? Anyone experiences the same problem? Yes, it happens to me with some emails sent from MS Outlook. JSON output contains the image part, but emacs doesn't show anything. I have no clue why. -Michal ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH 0/4] ruby: quick update before the freeze!
Hey David, I've polished the small patch series I had lying around. I believe I've covered all the lacking API changes. In addition, the third patch fixes a long-standing bug about the build. I've decided to send these to you and the list instead of committing right away in case someone notices any quick errors. Otherwise, please commit these before the freeze and make rubyists happy. Ali Polatel (4): ruby: Add wrapper for notmuch_query_count_messages ruby: Add wrapper for notmuch_query_add_tag_exclude ruby: Add workarounds to use in-tree build not the installed one ruby: Add wrapper for notmuch_query_set_omit_excluded() bindings/ruby/defs.h | 13 +-- bindings/ruby/extconf.rb | 26 + bindings/ruby/init.c | 11 - bindings/ruby/query.c| 57 +- 4 files changed, 99 insertions(+), 8 deletions(-) -- 1.7.10.1 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH 1/4] ruby: Add wrapper for notmuch_query_count_messages
--- bindings/ruby/defs.h |3 +++ bindings/ruby/init.c |3 ++- bindings/ruby/query.c | 21 - 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/bindings/ruby/defs.h b/bindings/ruby/defs.h index 81f652f..25222a6 100644 --- a/bindings/ruby/defs.h +++ b/bindings/ruby/defs.h @@ -222,6 +222,9 @@ notmuch_rb_query_search_threads (VALUE self); VALUE notmuch_rb_query_search_messages (VALUE self); +VALUE +notmuch_rb_query_count_messages (VALUE self); + /* threads.c */ VALUE notmuch_rb_threads_destroy (VALUE self); diff --git a/bindings/ruby/init.c b/bindings/ruby/init.c index 4405f19..7ad0ecf 100644 --- a/bindings/ruby/init.c +++ b/bindings/ruby/init.c @@ -1,6 +1,6 @@ /* The Ruby interface to the notmuch mail library * - * Copyright © 2010, 2011 Ali Polatel + * Copyright © 2010, 2011, 2012 Ali Polatel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -236,6 +236,7 @@ Init_notmuch (void) rb_define_method (notmuch_rb_cQuery, to_s, notmuch_rb_query_get_string, 0); /* in query.c */ rb_define_method (notmuch_rb_cQuery, search_threads, notmuch_rb_query_search_threads, 0); /* in query.c */ rb_define_method (notmuch_rb_cQuery, search_messages, notmuch_rb_query_search_messages, 0); /* in query.c */ +rb_define_method (notmuch_rb_cQuery, count_messages, notmuch_rb_query_count_messages, 0); /* in query.c */ /* * Document-class: Notmuch::Threads diff --git a/bindings/ruby/query.c b/bindings/ruby/query.c index 74fd585..02b7819 100644 --- a/bindings/ruby/query.c +++ b/bindings/ruby/query.c @@ -1,6 +1,6 @@ /* The Ruby interface to the notmuch mail library * - * Copyright © 2010, 2011 Ali Polatel + * Copyright © 2010, 2011, 2012 Ali Polatel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -127,3 +127,22 @@ notmuch_rb_query_search_messages (VALUE self) return Data_Wrap_Struct (notmuch_rb_cMessages, NULL, NULL, messages); } + +/* + * call-seq: QUERY.count_messages = Fixnum + * + * Return an estimate of the number of messages matching a search + */ +VALUE +notmuch_rb_query_count_messages (VALUE self) +{ +notmuch_query_t *query; + +Data_Get_Notmuch_Query (self, query); + +/* Xapian exceptions are not handled properly. + * (function may return 0 after printing a message) + * Thus there is nothing we can do here... + */ +return UINT2FIX(notmuch_query_count_messages(query)); +} -- 1.7.10.1 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH 2/4] ruby: Add wrapper for notmuch_query_add_tag_exclude
--- bindings/ruby/defs.h |3 +++ bindings/ruby/init.c |1 + bindings/ruby/query.c | 18 ++ 3 files changed, 22 insertions(+) diff --git a/bindings/ruby/defs.h b/bindings/ruby/defs.h index 25222a6..a41cf10 100644 --- a/bindings/ruby/defs.h +++ b/bindings/ruby/defs.h @@ -217,6 +217,9 @@ VALUE notmuch_rb_query_get_string (VALUE self); VALUE +notmuch_rb_query_add_tag_exclude (VALUE self, VALUE tagv); + +VALUE notmuch_rb_query_search_threads (VALUE self); VALUE diff --git a/bindings/ruby/init.c b/bindings/ruby/init.c index 7ad0ecf..edcf101 100644 --- a/bindings/ruby/init.c +++ b/bindings/ruby/init.c @@ -234,6 +234,7 @@ Init_notmuch (void) rb_define_method (notmuch_rb_cQuery, sort, notmuch_rb_query_get_sort, 0); /* in query.c */ rb_define_method (notmuch_rb_cQuery, sort=, notmuch_rb_query_set_sort, 1); /* in query.c */ rb_define_method (notmuch_rb_cQuery, to_s, notmuch_rb_query_get_string, 0); /* in query.c */ +rb_define_method (notmuch_rb_cQuery, add_tag_exclude, notmuch_rb_query_add_tag_exclude, 1); /* in query.c */ rb_define_method (notmuch_rb_cQuery, search_threads, notmuch_rb_query_search_threads, 0); /* in query.c */ rb_define_method (notmuch_rb_cQuery, search_messages, notmuch_rb_query_search_messages, 0); /* in query.c */ rb_define_method (notmuch_rb_cQuery, count_messages, notmuch_rb_query_count_messages, 0); /* in query.c */ diff --git a/bindings/ruby/query.c b/bindings/ruby/query.c index 02b7819..2a80008 100644 --- a/bindings/ruby/query.c +++ b/bindings/ruby/query.c @@ -89,6 +89,24 @@ notmuch_rb_query_get_string (VALUE self) } /* + * call-seq: QUERY.add_tag_exclude(tag) = nil + * + * Add a tag that will be excluded from the query results by default. + */ +VALUE +notmuch_rb_query_add_tag_exclude (VALUE self, VALUE tagv) +{ +notmuch_query_t *query; +const char *tag; + +Data_Get_Notmuch_Query (self, query); +tag = RSTRING_PTR(tagv); + +notmuch_query_add_tag_exclude(query, tag); +return Qnil; +} + +/* * call-seq: QUERY.search_threads = THREADS * * Search for threads -- 1.7.10.1 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH 3/4] ruby: Add workarounds to use in-tree build not the installed one
- Make mkmf use the notmuch.h under ../../lib - Use libnotmuch.a instead of linking to the installed libnotmuch.so --- bindings/ruby/defs.h |4 ++-- bindings/ruby/extconf.rb | 26 ++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/bindings/ruby/defs.h b/bindings/ruby/defs.h index a41cf10..6fe5787 100644 --- a/bindings/ruby/defs.h +++ b/bindings/ruby/defs.h @@ -1,6 +1,6 @@ /* The Ruby interface to the notmuch mail library * - * Copyright © 2010, 2011 Ali Polatel + * Copyright © 2010, 2011, 2012 Ali Polatel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,8 +21,8 @@ #ifndef DEFS_H #define DEFS_H -#include notmuch.h #include ruby.h +#include notmuch.h VALUE notmuch_rb_cDatabase; VALUE notmuch_rb_cDirectory; diff --git a/bindings/ruby/extconf.rb b/bindings/ruby/extconf.rb index ccac609..933f34a 100644 --- a/bindings/ruby/extconf.rb +++ b/bindings/ruby/extconf.rb @@ -1,13 +1,31 @@ #!/usr/bin/env ruby # coding: utf-8 -# Copyright 2010, 2011 Ali Polatel a...@exherbo.org +# Copyright 2010, 2011, 2012 Ali Polatel a...@exherbo.org # Distributed under the terms of the GNU General Public License v3 require 'mkmf' -# Notmuch Library -find_header('notmuch.h', '../../lib') -find_library('notmuch', 'notmuch_database_create', '../../lib') +NOTDIR = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'lib')) +NOTHDR = File.join(NOTDIR, 'notmuch.h') +NOTLIB = File.join(NOTDIR, 'libnotmuch.a') + +unless File.exists? NOTHDR + $stderr.puts notmuch.h is missing under #{NOTDIR} + exit 1 +end + +unless File.exists? NOTLIB + $stderr.puts libnotmuch.a is missing under #{NOTDIR} + exit 1 +end + +# Small hack to build with in-tree version not the installed one. +# find_header() and friends use standard include/library paths first. +$stderr.puts Added -I#{NOTDIR} to $INCFLAGS +$INCFLAGS = -I#{NOTDIR}.quote + + $INCFLAGS +find_header('notmuch.h', NOTDIR) + +$LOCAL_LIBS += NOTLIB # Create Makefile dir_config('notmuch') -- 1.7.10.1 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH 4/4] ruby: Add wrapper for notmuch_query_set_omit_excluded()
--- bindings/ruby/defs.h |3 +++ bindings/ruby/init.c |7 +++ bindings/ruby/query.c | 18 ++ 3 files changed, 28 insertions(+) diff --git a/bindings/ruby/defs.h b/bindings/ruby/defs.h index 6fe5787..85d8205 100644 --- a/bindings/ruby/defs.h +++ b/bindings/ruby/defs.h @@ -220,6 +220,9 @@ VALUE notmuch_rb_query_add_tag_exclude (VALUE self, VALUE tagv); VALUE +notmuch_rb_query_set_omit_excluded (VALUE self, VALUE omitv); + +VALUE notmuch_rb_query_search_threads (VALUE self); VALUE diff --git a/bindings/ruby/init.c b/bindings/ruby/init.c index edcf101..3fe60fb 100644 --- a/bindings/ruby/init.c +++ b/bindings/ruby/init.c @@ -96,6 +96,12 @@ Init_notmuch (void) */ rb_define_const (mod, MESSAGE_FLAG_MATCH, INT2FIX (NOTMUCH_MESSAGE_FLAG_MATCH)); /* + * Document-const: Notmuch::MESSAGE_FLAG_EXCLUDED + * + * Message flag excluded + */ +rb_define_const (mod, MESSAGE_FLAG_EXCLUDED, INT2FIX (NOTMUCH_MESSAGE_FLAG_EXCLUDED)); +/* * Document-const: Notmuch::TAG_MAX * * Maximum allowed length of a tag @@ -235,6 +241,7 @@ Init_notmuch (void) rb_define_method (notmuch_rb_cQuery, sort=, notmuch_rb_query_set_sort, 1); /* in query.c */ rb_define_method (notmuch_rb_cQuery, to_s, notmuch_rb_query_get_string, 0); /* in query.c */ rb_define_method (notmuch_rb_cQuery, add_tag_exclude, notmuch_rb_query_add_tag_exclude, 1); /* in query.c */ +rb_define_method (notmuch_rb_cQuery, omit_excluded=, notmuch_rb_query_set_omit_excluded, 1); /* in query.c */ rb_define_method (notmuch_rb_cQuery, search_threads, notmuch_rb_query_search_threads, 0); /* in query.c */ rb_define_method (notmuch_rb_cQuery, search_messages, notmuch_rb_query_search_messages, 0); /* in query.c */ rb_define_method (notmuch_rb_cQuery, count_messages, notmuch_rb_query_count_messages, 0); /* in query.c */ diff --git a/bindings/ruby/query.c b/bindings/ruby/query.c index 2a80008..e5ba1b7 100644 --- a/bindings/ruby/query.c +++ b/bindings/ruby/query.c @@ -107,6 +107,24 @@ notmuch_rb_query_add_tag_exclude (VALUE self, VALUE tagv) } /* + * call-seq: QUERY.omit_excluded=(boolean) = nil + * + * Specify whether to omit excluded results or simply flag them. + * By default, this is set to +true+. + */ +VALUE +notmuch_rb_query_set_omit_excluded (VALUE self, VALUE omitv) +{ +notmuch_query_t *query; + +Data_Get_Notmuch_Query (self, query); + +notmuch_query_set_omit_excluded (query, RTEST (omitv)); + +return Qnil; +} + +/* * call-seq: QUERY.search_threads = THREADS * * Search for threads -- 1.7.10.1 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Re: [Patch v2] emacs: add pipe attachment command
On Mon, May 07 2012, Mark Walters markwalters1...@gmail.com wrote: Here is a rebased to current master version of the previous patch: no other changes. Actually, what I sent was a rebase. And this rebase doesn't look correct. The notmuch-show-mm-display-part-inline function moved to notmuch-lib.el. It certainly has nothing to do with this patch, so I don't think adding it here is correct. jamie. pgpRKaRxTYjat.pgp Description: PGP signature ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Re: [Patch v2] emacs: add pipe attachment command
On Mon, 07 May 2012, Jameson Graef Rollins jroll...@finestructure.net wrote: On Mon, May 07 2012, Mark Walters markwalters1...@gmail.com wrote: Here is a rebased to current master version of the previous patch: no other changes. Actually, what I sent was a rebase. Sorry somehow I missed that you had actually sent the rebase too: and then I messed up my rebase. How embarrassing! Anyway many thanks for the correct rebase, and for spotting my stupid error. Best wishes Mark ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
Re: [Patch v2] emacs: add pipe attachment command
On Mon, May 07 2012, Mark Walters markwalters1...@gmail.com wrote: Sorry somehow I missed that you had actually sent the rebase too: and then I messed up my rebase. How embarrassing! Anyway many thanks for the correct rebase, and for spotting my stupid error. np ;) I've done plenty of things much more embarrassing than that! jamie. pgpaxK3rmUbFa.pgp Description: PGP signature ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH 0/2] Better, more portable stat-ing logic for notmuch new
This fully implements my suggestion from id:20120411193609.gc13...@mit.edu. Originally this was just meant to make the code more portable (since d_type is not portable), but it wound up also simplifying some of the notmuch new logic. ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH 1/2] test: Test notmuch new with a broken symlink
--- test/new | 10 ++ 1 file changed, 10 insertions(+) diff --git a/test/new b/test/new index 99f9913..26253db 100755 --- a/test/new +++ b/test/new @@ -136,6 +136,16 @@ output=$(NOTMUCH_NEW) test_expect_equal $output Added 1 new message to the database. +test_begin_subtest Broken symlink aborts +ln -s does-not-exist ${MAIL_DIR}/broken +output=$(NOTMUCH_NEW 21) +test_expect_equal $output \ +Error reading directory /run/shm/nm/tmp.new/mail/broken: No such file or directory +Note: A fatal error was encountered: Something went wrong trying to read or write a file +No new mail. +rm ${MAIL_DIR}/broken + + test_begin_subtest New two-level directory generate_message [dir]=two/levels -- 1.7.10 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH 2/2] new: Centralize file type stat-ing logic
This moves our logic to get a file's type into one function. This has several benefits: we can support OSes and file systems that do not provide dirent.d_type or always return DT_UNKNOWN, complex symlink-handling logic has been replaced by a simple stat fall-through in one place, and the error message for un-stat-able file is more accurate (previously, the error always mentioned directories, even though a broken symlink is not a directory). --- notmuch-new.c | 99 ++--- test/new |2 +- 2 files changed, 60 insertions(+), 41 deletions(-) diff --git a/notmuch-new.c b/notmuch-new.c index cb720cc..cf2580e 100644 --- a/notmuch-new.c +++ b/notmuch-new.c @@ -154,6 +154,44 @@ dirent_sort_strcmp_name (const struct dirent **a, const struct dirent **b) return strcmp ((*a)-d_name, (*b)-d_name); } +/* Return the type of a directory entry relative to path as a stat(2) + * mode. Like stat, this follows symlinks. Returns -1 and sets errno + * if the file's type cannot be determined (which includes dangling + * symlinks). + */ +static int +dirent_type (const char *path, const struct dirent *entry) +{ +struct stat statbuf; +char *abspath; +int err; + +#ifdef _DIRENT_HAVE_D_TYPE +/* Mapping from d_type to stat mode_t. We omit DT_LNK so that + * we'll fall through to stat and get the real file type. */ +static const mode_t modes[] = { + [DT_BLK] = S_IFBLK, + [DT_CHR] = S_IFCHR, + [DT_DIR] = S_IFDIR, + [DT_FIFO] = S_IFIFO, + [DT_REG] = S_IFREG, + [DT_SOCK] = S_IFSOCK +}; +if (entry-d_type sizeof(modes)/sizeof(modes[0]) + modes[entry-d_type]) + return modes[entry-d_type]; +#endif + +abspath = talloc_asprintf (NULL, %s/%s, path, entry-d_name); +if (!abspath) + return -1; +err = stat(abspath, statbuf); +talloc_free (abspath); +if (err 0) + return -1; +return statbuf.st_mode S_IFMT; +} + /* Test if the directory looks like a Maildir directory. * * Search through the array of directory entries to see if we can find all @@ -162,12 +200,12 @@ dirent_sort_strcmp_name (const struct dirent **a, const struct dirent **b) * Return 1 if the directory looks like a Maildir and 0 otherwise. */ static int -_entries_resemble_maildir (struct dirent **entries, int count) +_entries_resemble_maildir (const char *path, struct dirent **entries, int count) { int i, found = 0; for (i = 0; i count; i++) { - if (entries[i]-d_type != DT_DIR entries[i]-d_type != DT_UNKNOWN) + if (dirent_type (path, entries[i]) != S_IFDIR) continue; if (strcmp(entries[i]-d_name, new) == 0 || @@ -250,7 +288,7 @@ add_files_recursive (notmuch_database_t *notmuch, notmuch_status_t status, ret = NOTMUCH_STATUS_SUCCESS; notmuch_message_t *message = NULL; struct dirent **fs_entries = NULL; -int i, num_fs_entries; +int i, num_fs_entries, entry_type; notmuch_directory_t *directory; notmuch_filenames_t *db_files = NULL; notmuch_filenames_t *db_subdirs = NULL; @@ -317,7 +355,7 @@ add_files_recursive (notmuch_database_t *notmuch, } /* Pass 1: Recurse into all sub-directories. */ -is_maildir = _entries_resemble_maildir (fs_entries, num_fs_entries); +is_maildir = _entries_resemble_maildir (path, fs_entries, num_fs_entries); for (i = 0; i num_fs_entries; i++) { if (interrupted) @@ -325,17 +363,16 @@ add_files_recursive (notmuch_database_t *notmuch, entry = fs_entries[i]; - /* We only want to descend into directories. -* But symlinks can be to directories too, of course. -* -* And if the filesystem doesn't tell us the file type in the -* scandir results, then it might be a directory (and if not, -* then we'll stat and return immediately in the next level of -* recursion). */ - if (entry-d_type != DT_DIR - entry-d_type != DT_LNK - entry-d_type != DT_UNKNOWN) - { + /* We only want to descend into directories (and symlinks to +* directories). */ + entry_type = dirent_type (path, entry); + if (entry_type == -1) { + /* Be pessimistic, e.g. so we don't loose lots of mail +* just because a user broke a symlink. */ + fprintf (stderr, Error reading file %s/%s: %s\n, +path, entry-d_name, strerror (errno)); + return NOTMUCH_STATUS_FILE_ERROR; + } else if (entry_type != S_IFDIR) { continue; } @@ -425,31 +462,13 @@ add_files_recursive (notmuch_database_t *notmuch, notmuch_filenames_move_to_next (db_subdirs); } - /* If we're looking at a symlink, we only want to add it if it -* links to a regular file, (and not to a directory, say). -* -* Similarly, if the file is of unknown type (due to filesystem -*
Re: [PATCH 2/2] new: Centralize file type stat-ing logic
On May 7, 2012 9:10 PM, Austin Clements amdra...@mit.edu wrote: This moves our logic to get a file's type into one function. This has several benefits: we can support OSes and file systems that do not provide dirent.d_type or always return DT_UNKNOWN, complex symlink-handling logic has been replaced by a simple stat fall-through in one place, and the error message for un-stat-able file is more accurate (previously, the error always mentioned directories, even though a broken symlink is not a directory). Please find some quick drive-by-review below. J. --- notmuch-new.c | 99 ++--- test/new |2 +- 2 files changed, 60 insertions(+), 41 deletions(-) diff --git a/notmuch-new.c b/notmuch-new.c index cb720cc..cf2580e 100644 --- a/notmuch-new.c +++ b/notmuch-new.c @@ -154,6 +154,44 @@ dirent_sort_strcmp_name (const struct dirent **a, const struct dirent **b) return strcmp ((*a)-d_name, (*b)-d_name); } +/* Return the type of a directory entry relative to path as a stat(2) + * mode. Like stat, this follows symlinks. Returns -1 and sets errno + * if the file's type cannot be determined (which includes dangling + * symlinks). + */ +static int +dirent_type (const char *path, const struct dirent *entry) +{ +struct stat statbuf; +char *abspath; +int err; + +#ifdef _DIRENT_HAVE_D_TYPE +/* Mapping from d_type to stat mode_t. We omit DT_LNK so that + * we'll fall through to stat and get the real file type. */ +static const mode_t modes[] = { + [DT_BLK] = S_IFBLK, + [DT_CHR] = S_IFCHR, + [DT_DIR] = S_IFDIR, + [DT_FIFO] = S_IFIFO, + [DT_REG] = S_IFREG, + [DT_SOCK] = S_IFSOCK +}; +if (entry-d_type sizeof(modes)/sizeof(modes[0]) ARRAY_SIZE() + modes[entry-d_type]) + return modes[entry-d_type]; +#endif + +abspath = talloc_asprintf (NULL, %s/%s, path, entry-d_name); +if (!abspath) + return -1; Does talloc set errno in this case? I suspect not. +err = stat(abspath, statbuf); +talloc_free (abspath); This likely breaks your promise about errno. You can't trust talloc_free() not calling some function that sets errno. +if (err 0) + return -1; +return statbuf.st_mode S_IFMT; +} + /* Test if the directory looks like a Maildir directory. * * Search through the array of directory entries to see if we can find all @@ -162,12 +200,12 @@ dirent_sort_strcmp_name (const struct dirent **a, const struct dirent **b) * Return 1 if the directory looks like a Maildir and 0 otherwise. */ static int -_entries_resemble_maildir (struct dirent **entries, int count) +_entries_resemble_maildir (const char *path, struct dirent **entries, int count) { int i, found = 0; for (i = 0; i count; i++) { - if (entries[i]-d_type != DT_DIR entries[i]-d_type != DT_UNKNOWN) + if (dirent_type (path, entries[i]) != S_IFDIR) continue; if (strcmp(entries[i]-d_name, new) == 0 || @@ -250,7 +288,7 @@ add_files_recursive (notmuch_database_t *notmuch, notmuch_status_t status, ret = NOTMUCH_STATUS_SUCCESS; notmuch_message_t *message = NULL; struct dirent **fs_entries = NULL; -int i, num_fs_entries; +int i, num_fs_entries, entry_type; notmuch_directory_t *directory; notmuch_filenames_t *db_files = NULL; notmuch_filenames_t *db_subdirs = NULL; @@ -317,7 +355,7 @@ add_files_recursive (notmuch_database_t *notmuch, } /* Pass 1: Recurse into all sub-directories. */ -is_maildir = _entries_resemble_maildir (fs_entries, num_fs_entries); +is_maildir = _entries_resemble_maildir (path, fs_entries, num_fs_entries); for (i = 0; i num_fs_entries; i++) { if (interrupted) @@ -325,17 +363,16 @@ add_files_recursive (notmuch_database_t *notmuch, entry = fs_entries[i]; - /* We only want to descend into directories. -* But symlinks can be to directories too, of course. -* -* And if the filesystem doesn't tell us the file type in the -* scandir results, then it might be a directory (and if not, -* then we'll stat and return immediately in the next level of -* recursion). */ - if (entry-d_type != DT_DIR - entry-d_type != DT_LNK - entry-d_type != DT_UNKNOWN) - { + /* We only want to descend into directories (and symlinks to +* directories). */ + entry_type = dirent_type (path, entry); + if (entry_type == -1) { + /* Be pessimistic, e.g. so we don't loose lots of mail s/loose/lose/ ? +* just because a user broke a symlink. */ + fprintf (stderr, Error reading file %s/%s: %s\n, +path, entry-d_name, strerror (errno)); You can't trust errno here, as explained above. + return NOTMUCH_STATUS_FILE_ERROR;
Re: [PATCH 2/2] new: Centralize file type stat-ing logic
On May 8, 2012 12:08 AM, Jani Nikula j...@nikula.org wrote: On May 7, 2012 9:10 PM, Austin Clements amdra...@mit.edu wrote: This moves our logic to get a file's type into one function. This has several benefits: we can support OSes and file systems that do not provide dirent.d_type or always return DT_UNKNOWN, complex symlink-handling logic has been replaced by a simple stat fall-through in one place, and the error message for un-stat-able file is more accurate (previously, the error always mentioned directories, even though a broken symlink is not a directory). Please find some quick drive-by-review below. Forgot to add that the general approach seems sensible to me. J. --- notmuch-new.c | 99 ++--- test/new |2 +- 2 files changed, 60 insertions(+), 41 deletions(-) diff --git a/notmuch-new.c b/notmuch-new.c index cb720cc..cf2580e 100644 --- a/notmuch-new.c +++ b/notmuch-new.c @@ -154,6 +154,44 @@ dirent_sort_strcmp_name (const struct dirent **a, const struct dirent **b) return strcmp ((*a)-d_name, (*b)-d_name); } +/* Return the type of a directory entry relative to path as a stat(2) + * mode. Like stat, this follows symlinks. Returns -1 and sets errno + * if the file's type cannot be determined (which includes dangling + * symlinks). + */ +static int +dirent_type (const char *path, const struct dirent *entry) +{ +struct stat statbuf; +char *abspath; +int err; + +#ifdef _DIRENT_HAVE_D_TYPE +/* Mapping from d_type to stat mode_t. We omit DT_LNK so that + * we'll fall through to stat and get the real file type. */ +static const mode_t modes[] = { + [DT_BLK] = S_IFBLK, + [DT_CHR] = S_IFCHR, + [DT_DIR] = S_IFDIR, + [DT_FIFO] = S_IFIFO, + [DT_REG] = S_IFREG, + [DT_SOCK] = S_IFSOCK +}; +if (entry-d_type sizeof(modes)/sizeof(modes[0]) ARRAY_SIZE() + modes[entry-d_type]) + return modes[entry-d_type]; +#endif + +abspath = talloc_asprintf (NULL, %s/%s, path, entry-d_name); +if (!abspath) + return -1; Does talloc set errno in this case? I suspect not. +err = stat(abspath, statbuf); +talloc_free (abspath); This likely breaks your promise about errno. You can't trust talloc_free() not calling some function that sets errno. +if (err 0) + return -1; +return statbuf.st_mode S_IFMT; +} + /* Test if the directory looks like a Maildir directory. * * Search through the array of directory entries to see if we can find all @@ -162,12 +200,12 @@ dirent_sort_strcmp_name (const struct dirent **a, const struct dirent **b) * Return 1 if the directory looks like a Maildir and 0 otherwise. */ static int -_entries_resemble_maildir (struct dirent **entries, int count) +_entries_resemble_maildir (const char *path, struct dirent **entries, int count) { int i, found = 0; for (i = 0; i count; i++) { - if (entries[i]-d_type != DT_DIR entries[i]-d_type != DT_UNKNOWN) + if (dirent_type (path, entries[i]) != S_IFDIR) continue; if (strcmp(entries[i]-d_name, new) == 0 || @@ -250,7 +288,7 @@ add_files_recursive (notmuch_database_t *notmuch, notmuch_status_t status, ret = NOTMUCH_STATUS_SUCCESS; notmuch_message_t *message = NULL; struct dirent **fs_entries = NULL; -int i, num_fs_entries; +int i, num_fs_entries, entry_type; notmuch_directory_t *directory; notmuch_filenames_t *db_files = NULL; notmuch_filenames_t *db_subdirs = NULL; @@ -317,7 +355,7 @@ add_files_recursive (notmuch_database_t *notmuch, } /* Pass 1: Recurse into all sub-directories. */ -is_maildir = _entries_resemble_maildir (fs_entries, num_fs_entries); +is_maildir = _entries_resemble_maildir (path, fs_entries, num_fs_entries); for (i = 0; i num_fs_entries; i++) { if (interrupted) @@ -325,17 +363,16 @@ add_files_recursive (notmuch_database_t *notmuch, entry = fs_entries[i]; - /* We only want to descend into directories. -* But symlinks can be to directories too, of course. -* -* And if the filesystem doesn't tell us the file type in the -* scandir results, then it might be a directory (and if not, -* then we'll stat and return immediately in the next level of -* recursion). */ - if (entry-d_type != DT_DIR - entry-d_type != DT_LNK - entry-d_type != DT_UNKNOWN) - { + /* We only want to descend into directories (and symlinks to +* directories). */ + entry_type = dirent_type (path, entry); + if (entry_type == -1) { + /* Be pessimistic, e.g. so we don't loose lots of mail s/loose/lose/ ? +* just
Re: [PATCH 2/2] new: Centralize file type stat-ing logic
Quoth Jani Nikula on May 08 at 12:08 am: On May 7, 2012 9:10 PM, Austin Clements [1]amdra...@mit.edu wrote: This moves our logic to get a file's type into one function. This has several benefits: we can support OSes and file systems that do not provide dirent.d_type or always return DT_UNKNOWN, complex symlink-handling logic has been replaced by a simple stat fall-through in one place, and the error message for un-stat-able file is more accurate (previously, the error always mentioned directories, even though a broken symlink is not a directory). Please find some quick drive-by-review below. Thanks for the review! New version on its way... J. --- notmuch-new.c | 99 ++--- test/new | 2 +- 2 files changed, 60 insertions(+), 41 deletions(-) diff --git a/notmuch-new.c b/notmuch-new.c index cb720cc..cf2580e 100644 --- a/notmuch-new.c +++ b/notmuch-new.c @@ -154,6 +154,44 @@ dirent_sort_strcmp_name (const struct dirent **a, const struct dirent **b) return strcmp ((*a)-d_name, (*b)-d_name); } +/* Return the type of a directory entry relative to path as a stat(2) + * mode. Like stat, this follows symlinks. Returns -1 and sets errno + * if the file's type cannot be determined (which includes dangling + * symlinks). + */ +static int +dirent_type (const char *path, const struct dirent *entry) +{ + struct stat statbuf; + char *abspath; + int err; + +#ifdef _DIRENT_HAVE_D_TYPE + /* Mapping from d_type to stat mode_t. We omit DT_LNK so that + * we'll fall through to stat and get the real file type. */ + static const mode_t modes[] = { + [DT_BLK] = S_IFBLK, + [DT_CHR] = S_IFCHR, + [DT_DIR] = S_IFDIR, + [DT_FIFO] = S_IFIFO, + [DT_REG] = S_IFREG, + [DT_SOCK] = S_IFSOCK + }; + if (entry-d_type sizeof(modes)/sizeof(modes[0]) ARRAY_SIZE() Huh. I went looking for that. I wonder how I missed it. + modes[entry-d_type]) + return modes[entry-d_type]; +#endif + + abspath = talloc_asprintf (NULL, %s/%s, path, entry-d_name); + if (!abspath) + return -1; Does talloc set errno in this case? I suspect not. + err = stat(abspath, statbuf); + talloc_free (abspath); This likely breaks your promise about errno. You can't trust talloc_free() not calling some function that sets errno. Setting errno was a late and apparently not well thought-out addition. The new patch should set it on all of the error paths and save it around things that may modify it. + if (err 0) + return -1; + return statbuf.st_mode S_IFMT; +} + /* Test if the directory looks like a Maildir directory. * * Search through the array of directory entries to see if we can find all @@ -162,12 +200,12 @@ dirent_sort_strcmp_name (const struct dirent **a, const struct dirent **b) * Return 1 if the directory looks like a Maildir and 0 otherwise. */ static int -_entries_resemble_maildir (struct dirent **entries, int count) +_entries_resemble_maildir (const char *path, struct dirent **entries, int count) { int i, found = 0; for (i = 0; i count; i++) { - if (entries[i]-d_type != DT_DIR entries[i]-d_type != DT_UNKNOWN) + if (dirent_type (path, entries[i]) != S_IFDIR) continue; if (strcmp(entries[i]-d_name, new) == 0 || @@ -250,7 +288,7 @@ add_files_recursive (notmuch_database_t *notmuch, notmuch_status_t status, ret = NOTMUCH_STATUS_SUCCESS; notmuch_message_t *message = NULL; struct dirent **fs_entries = NULL; - int i, num_fs_entries; + int i, num_fs_entries, entry_type; notmuch_directory_t *directory; notmuch_filenames_t *db_files = NULL; notmuch_filenames_t *db_subdirs = NULL; @@ -317,7 +355,7 @@ add_files_recursive (notmuch_database_t *notmuch, } /* Pass 1: Recurse into all sub-directories. */ - is_maildir = _entries_resemble_maildir (fs_entries, num_fs_entries); + is_maildir = _entries_resemble_maildir (path, fs_entries, num_fs_entries); for (i = 0; i num_fs_entries; i++) { if (interrupted) @@ -325,17 +363,16 @@ add_files_recursive (notmuch_database_t *notmuch, entry = fs_entries[i]; - /* We only want to descend into directories. - * But symlinks can be to directories too, of course. - * - * And if the filesystem doesn't tell us the file type in the - * scandir results,
[PATCH v2 2/2] new: Centralize file type stat-ing logic
This moves our logic to get a file's type into one function. This has several benefits: we can support OSes and file systems that do not provide dirent.d_type or always return DT_UNKNOWN, complex symlink-handling logic has been replaced by a simple stat fall-through in one place, and the error message for un-stat-able file is more accurate (previously, the error always mentioned directories, even though a broken symlink is not a directory). --- notmuch-new.c | 103 +++-- test/new |2 +- 2 files changed, 64 insertions(+), 41 deletions(-) diff --git a/notmuch-new.c b/notmuch-new.c index cb720cc..8955677 100644 --- a/notmuch-new.c +++ b/notmuch-new.c @@ -154,6 +154,48 @@ dirent_sort_strcmp_name (const struct dirent **a, const struct dirent **b) return strcmp ((*a)-d_name, (*b)-d_name); } +/* Return the type of a directory entry relative to path as a stat(2) + * mode. Like stat, this follows symlinks. Returns -1 and sets errno + * if the file's type cannot be determined (which includes dangling + * symlinks). + */ +static int +dirent_type (const char *path, const struct dirent *entry) +{ +struct stat statbuf; +char *abspath; +int err, saved_errno; + +#ifdef _DIRENT_HAVE_D_TYPE +/* Mapping from d_type to stat mode_t. We omit DT_LNK so that + * we'll fall through to stat and get the real file type. */ +static const mode_t modes[] = { + [DT_BLK] = S_IFBLK, + [DT_CHR] = S_IFCHR, + [DT_DIR] = S_IFDIR, + [DT_FIFO] = S_IFIFO, + [DT_REG] = S_IFREG, + [DT_SOCK] = S_IFSOCK +}; +if (entry-d_type ARRAY_SIZE(modes) modes[entry-d_type]) + return modes[entry-d_type]; +#endif + +abspath = talloc_asprintf (NULL, %s/%s, path, entry-d_name); +if (!abspath) { + errno = ENOMEM; + return -1; +} +err = stat(abspath, statbuf); +saved_errno = errno; +talloc_free (abspath); +if (err 0) { + errno = saved_errno; + return -1; +} +return statbuf.st_mode S_IFMT; +} + /* Test if the directory looks like a Maildir directory. * * Search through the array of directory entries to see if we can find all @@ -162,12 +204,12 @@ dirent_sort_strcmp_name (const struct dirent **a, const struct dirent **b) * Return 1 if the directory looks like a Maildir and 0 otherwise. */ static int -_entries_resemble_maildir (struct dirent **entries, int count) +_entries_resemble_maildir (const char *path, struct dirent **entries, int count) { int i, found = 0; for (i = 0; i count; i++) { - if (entries[i]-d_type != DT_DIR entries[i]-d_type != DT_UNKNOWN) + if (dirent_type (path, entries[i]) != S_IFDIR) continue; if (strcmp(entries[i]-d_name, new) == 0 || @@ -250,7 +292,7 @@ add_files_recursive (notmuch_database_t *notmuch, notmuch_status_t status, ret = NOTMUCH_STATUS_SUCCESS; notmuch_message_t *message = NULL; struct dirent **fs_entries = NULL; -int i, num_fs_entries; +int i, num_fs_entries, entry_type; notmuch_directory_t *directory; notmuch_filenames_t *db_files = NULL; notmuch_filenames_t *db_subdirs = NULL; @@ -317,7 +359,7 @@ add_files_recursive (notmuch_database_t *notmuch, } /* Pass 1: Recurse into all sub-directories. */ -is_maildir = _entries_resemble_maildir (fs_entries, num_fs_entries); +is_maildir = _entries_resemble_maildir (path, fs_entries, num_fs_entries); for (i = 0; i num_fs_entries; i++) { if (interrupted) @@ -325,17 +367,16 @@ add_files_recursive (notmuch_database_t *notmuch, entry = fs_entries[i]; - /* We only want to descend into directories. -* But symlinks can be to directories too, of course. -* -* And if the filesystem doesn't tell us the file type in the -* scandir results, then it might be a directory (and if not, -* then we'll stat and return immediately in the next level of -* recursion). */ - if (entry-d_type != DT_DIR - entry-d_type != DT_LNK - entry-d_type != DT_UNKNOWN) - { + /* We only want to descend into directories (and symlinks to +* directories). */ + entry_type = dirent_type (path, entry); + if (entry_type == -1) { + /* Be pessimistic, e.g. so we don't lose lots of mail just +* because a user broke a symlink. */ + fprintf (stderr, Error reading file %s/%s: %s\n, +path, entry-d_name, strerror (errno)); + return NOTMUCH_STATUS_FILE_ERROR; + } else if (entry_type != S_IFDIR) { continue; } @@ -425,31 +466,13 @@ add_files_recursive (notmuch_database_t *notmuch, notmuch_filenames_move_to_next (db_subdirs); } - /* If we're looking at a symlink, we only want to add it if it -* links to a regular file, (and not to a directory, say). -
[PATCH v2 1/2] test: Test notmuch new with a broken symlink
--- test/new | 10 ++ 1 file changed, 10 insertions(+) diff --git a/test/new b/test/new index 99f9913..26253db 100755 --- a/test/new +++ b/test/new @@ -136,6 +136,16 @@ output=$(NOTMUCH_NEW) test_expect_equal $output Added 1 new message to the database. +test_begin_subtest Broken symlink aborts +ln -s does-not-exist ${MAIL_DIR}/broken +output=$(NOTMUCH_NEW 21) +test_expect_equal $output \ +Error reading directory /run/shm/nm/tmp.new/mail/broken: No such file or directory +Note: A fatal error was encountered: Something went wrong trying to read or write a file +No new mail. +rm ${MAIL_DIR}/broken + + test_begin_subtest New two-level directory generate_message [dir]=two/levels -- 1.7.10 ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch
[PATCH v2 0/2] Better, more portable stat-ing logic for notmuch new
This version uses ARRAY_SIZE, fixes errno handling bugs, and corrects a spelling error. ___ notmuch mailing list notmuch@notmuchmail.org http://notmuchmail.org/mailman/listinfo/notmuch