diff --git a/man/mu-mv.1 b/man/mu-mv.1 index b6000209..a64161d7 100644 --- a/man/mu-mv.1 +++ b/man/mu-mv.1 @@ -46,10 +46,20 @@ The flags is a sequence of characters from the set D (draft), F (flagged), N flags-parameter is case-sensitive. Any other characters will be silently ignored. +The \fB\-\-flags\fR also has a second, 'delta', syntax. In this syntax, each +of the flag characters is prefixed with either '+' or '-', which means that +the corresponding flag will be added or removed. Using this syntax, you can +change individual flags, without changing all of them. + .TP -\fB\-\-printtarget\fR +\fB\-\-print-target\fR return the target path on standard output upon succesful completion of the -move (with or without a succesful database update) +move (with or without a succesful database update). + +.TP +\fB\-\-ignore-dups\fR +silently ignore the case where the source file is the same as the target. + .SH EXAMPLE @@ -76,23 +86,30 @@ To mark a message as no longer new and 'Seen', and update the database afterwards, you could do: .nf - mu --flags=S mv /home/roger/Maildir/inbox/new/123123123 /home/roger/Maildir/inbox/ + mu mv /home/roger/Maildir/inbox/new/123123123 /home/roger/Maildir/inbox/ --flags=S .fi In this case, as we are not moving the message to a diffent maildir, we can leave off the maildir-argument; so the following is equivalent: .nf - mu --flags=S mv /home/roger/Maildir/inbox/new/123123123 + mu mv /home/roger/Maildir/inbox/new/123123123 --flags=S .fi +Finally, using the 'delta'-syntax, you can set the 'seen'-flag +and 'replied'-flag while removing the 'new' flag with: + +.nf + mu mv /home/billy/Maildir/inbox/new/12aa34343 --flags=+S+R-N +.fi +which would give us a new file: +\fI/home/billy/Maildir/inbox/cur/12aa34343:2,SR\fR .SH LIMITATIONS Both source-path and target-directory must be on the same disk partition, except when the target-directory is \fI/dev/null\fR. - .SH RETURN VALUE \fBmu mv\fR returns 0 upon success, and some other value when an error diff --git a/src/mu-cmd.c b/src/mu-cmd.c index dd38cab2..12e295fb 100644 --- a/src/mu-cmd.c +++ b/src/mu-cmd.c @@ -285,19 +285,31 @@ mu_cmd_mkdir (MuConfig *opts) static gboolean mv_check_params (MuConfig *opts, MuFlags *flags) { - if (!opts->params[1] || !opts->params[2]) { + if (!opts->params[1]) { g_warning ("usage: mu mv [--flags=] " - ""); + "[]"); return FALSE; } /* FIXME: check for invalid flags */ if (!opts->flagstr) *flags = MU_FLAG_INVALID; /* ie., ignore flags */ - else - *flags = mu_flags_from_str (opts->flagstr, - MU_FLAG_TYPE_MAILDIR | - MU_FLAG_TYPE_MAILFILE); + else { + /* if there's a '+' or '-' sign in the string, it must + * be a flag-delta */ + if (strstr (opts->flagstr, "+") || strstr (opts->flagstr, "-")) { + MuFlags oldflags; + oldflags = mu_maildir_get_flags_from_path (opts->params[1]); + *flags = mu_flags_from_str_delta (opts->flagstr, + oldflags, + MU_FLAG_TYPE_MAILDIR| + MU_FLAG_TYPE_MAILFILE); + } else + *flags = mu_flags_from_str (opts->flagstr, + MU_FLAG_TYPE_MAILDIR | + MU_FLAG_TYPE_MAILFILE); + } + return TRUE; } @@ -310,7 +322,7 @@ cmd_mv_dev_null (MuConfig *opts) return MU_ERROR_FILE; } - if (opts->printtarget) + if (opts->print_target) g_print ("/dev/null\n"); /* /dev/null */ return MU_OK; @@ -334,7 +346,8 @@ mu_cmd_mv (MuConfig *opts) err = NULL; fullpath = mu_maildir_move_message (opts->params[1], opts->params[2], - flags, &err); + flags, opts->ignore_dups, + &err); if (!fullpath) { if (err) { MuError code; @@ -346,7 +359,7 @@ mu_cmd_mv (MuConfig *opts) return MU_ERROR_FILE; } else { - if (opts->printtarget) + if (opts->print_target) g_print ("%s\n", fullpath); return MU_OK; } diff --git a/src/mu-config.c b/src/mu-config.c index 0436c3c6..d62d9f95 100644 --- a/src/mu-config.c +++ b/src/mu-config.c @@ -322,7 +322,10 @@ config_options_group_mv (MuConfig *opts) GOptionEntry entries[] = { {"flags", 0, 0, G_OPTION_ARG_STRING, &opts->flagstr, "flags to set for the target (DFNPRST)", NULL}, - {"printtarget", 0, 0, G_OPTION_ARG_NONE, &opts->printtarget, + {"ignore-dups", 0, 0, G_OPTION_ARG_NONE, &opts->ignore_dups, + "whether to silently ignore the source = target case", + NULL}, + {"print-target", 0, 0, G_OPTION_ARG_NONE, &opts->print_target, "whether to print the target path upon succesful completion", NULL}, {NULL, 0, 0, 0, NULL, NULL, NULL} diff --git a/src/mu-config.h b/src/mu-config.h index 0cebf13a..0837ab02 100644 --- a/src/mu-config.h +++ b/src/mu-config.h @@ -128,10 +128,11 @@ struct _MuConfig { /* options for mv */ char *flagstr; /* message flags to set for * the target */ - gboolean printtarget; /* should be print the - * target file path on - * stdout */ - + gboolean print_target; /* should be print the + * target file path on + * stdout */ + gboolean ignore_dups; /* silently ignore the + * src=target case */ /* options for view */ gboolean terminator; /* add separator \f between * multiple messages in mu diff --git a/src/mu-flags.c b/src/mu-flags.c index 2ea79a17..d8286346 100644 --- a/src/mu-flags.c +++ b/src/mu-flags.c @@ -60,7 +60,6 @@ mu_flag_type (MuFlags flag) if (flag >= MU_FLAG_SIGNED && flag <= MU_FLAG_HAS_ATTACH) return MU_FLAG_TYPE_CONTENT; - g_return_val_if_reached (MU_FLAG_TYPE_INVALID); return MU_FLAG_TYPE_INVALID; } @@ -87,7 +86,6 @@ mu_flag_char (MuFlags flag) case MU_FLAG_UNREAD: return 'u'; default: - g_return_val_if_reached (0); return 0; } } @@ -115,13 +113,11 @@ mu_flag_from_char (char kar) case 'u': return MU_FLAG_UNREAD; default: - g_return_val_if_reached (MU_FLAG_INVALID); return MU_FLAG_INVALID; } } - /* does not use FLAG_INFO, optimized */ const char* mu_flag_name (MuFlags flag) @@ -143,7 +139,6 @@ mu_flag_name (MuFlags flag) case MU_FLAG_UNREAD: return "unread"; default: - g_return_val_if_reached (NULL); return NULL; } } @@ -160,7 +155,7 @@ mu_flags_to_str_s (MuFlags flags, MuFlagType types) types & FLAG_INFO[u].flag_type) str[v++] = FLAG_INFO[u].kar; str[v] = '\0'; - + return str; } @@ -199,3 +194,39 @@ mu_flags_foreach (MuFlagsForeachFunc func, gpointer user_data) func (FLAG_INFO[u].flag, user_data); } + +MuFlags +mu_flags_from_str_delta (const char *str, MuFlags oldflags, + MuFlagType types) +{ + const char *cur; + MuFlags newflags; + + g_return_val_if_fail (str, MU_FLAG_INVALID); + + for (cur = str, newflags = oldflags; *cur; ++cur) { + + MuFlags f; + if (*cur == '+' || *cur == '-') { + f = mu_flag_from_char (cur[1]); + if (f == 0) + goto error; + if (*cur == '+') + newflags |= f; + else + newflags &= ~f; + ++cur; + continue; + } + + goto error; + } + + return newflags; +error: + g_warning ("invalid flag string"); + return MU_FLAG_INVALID; + +} + + diff --git a/src/mu-flags.h b/src/mu-flags.h index 1f5681b7..002285b7 100644 --- a/src/mu-flags.h +++ b/src/mu-flags.h @@ -122,6 +122,26 @@ const char* mu_flags_to_str_s (MuFlags flags, MuFlagType types); MuFlags mu_flags_from_str (const char *str, MuFlagType types); + +/** + * Update #oldflags with the flags in #str, where #str consists of the + * the normal flag characters, but prefixed with either '+' or '-', + * which means resp. "add this flag" or "remove this flag" from + * oldflags. So, e.g. "-N+S" would unset the NEW flag and set the + * SEEN flag, without affecting other flags. + * + * @param str the string representation + * @param old flags to update + * @param types the flag types to accept (other will be ignored) + * + * @return + */ +MuFlags mu_flags_from_str_delta (const char *str, MuFlags oldflags, + MuFlagType types); + + + + typedef void (*MuFlagsForeachFunc) (MuFlags flag, gpointer user_data); /** diff --git a/src/mu-maildir.c b/src/mu-maildir.c index cce2e21e..7374494b 100644 --- a/src/mu-maildir.c +++ b/src/mu-maildir.c @@ -812,10 +812,12 @@ msg_move (const char* src, const char *dst, GError **err) gchar* mu_maildir_move_message (const char* oldpath, const char* targetmdir, - MuFlags newflags, GError **err) + MuFlags newflags, gboolean ignore_dups, + GError **err) { char *newfullpath; gboolean rv; + gboolean src_is_target; g_return_val_if_fail (oldpath, FALSE); @@ -827,16 +829,21 @@ mu_maildir_move_message (const char* oldpath, const char* targetmdir, return FALSE; } - if (g_strcmp0 (oldpath, newfullpath) == 0) { + + src_is_target = (g_strcmp0 (oldpath, newfullpath) == 0); + + if (!ignore_dups && src_is_target) { g_set_error (err, 0, MU_ERROR_FILE_TARGET_EQUALS_SOURCE, "target equals source"); return FALSE; } - rv = msg_move (oldpath, newfullpath, err); - if (!rv) { - g_free (newfullpath); - return NULL; + if (!src_is_target) { + rv = msg_move (oldpath, newfullpath, err); + if (!rv) { + g_free (newfullpath); + return NULL; + } } return newfullpath; diff --git a/src/mu-maildir.h b/src/mu-maildir.h index abf2f90a..1a7c9e19 100644 --- a/src/mu-maildir.h +++ b/src/mu-maildir.h @@ -171,14 +171,17 @@ char* mu_maildir_get_new_path (const char *oldpath, const char *new_mdir, * of the message are affected; note that this may still involve a * moved to another directory (say, from new/ to cur/) * @param flags to set for the target (influences the filename, path) + * @param ignore_dups whether to silent ignore the src=target case (and return TRUE) * @param err (may be NULL) may contain error information; note if the * function return FALSE, err is not set for all error condition * (ie. not for parameter errors) + * * @return return the full path name of the target file (g_free) if * the move succeeded, NULL otherwise */ gchar* mu_maildir_move_message (const char* oldpath, const char* targetmdir, - MuFlags newflags, GError **err); + MuFlags newflags, gboolean ignore_dups, + GError **err); G_END_DECLS