From 47073006d2eda82386b19bd21f5c732a46cb8ed9 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Mon, 1 Aug 2011 22:42:23 +0300 Subject: [PATCH] * added the mu mv command for moving, removing messages (WIP) --- man/Makefile.am | 1 + man/mu-mv.1 | 75 +++++++++++++++++++++++++++++++++++++++ src/mu-cmd.c | 38 ++++++++++++++++++++ src/mu-cmd.h | 13 +++++++ src/mu-config.c | 19 ++++++---- src/mu-config.h | 2 ++ src/mu-msg.c | 94 ++++++++++++++++++++++++++++++++++++------------- src/mu-msg.h | 10 ++++-- 8 files changed, 218 insertions(+), 34 deletions(-) create mode 100644 man/mu-mv.1 diff --git a/man/Makefile.am b/man/Makefile.am index 9184ed19..b2189417 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -26,5 +26,6 @@ dist_man_MANS = \ mu-index.1 \ mu-mkdir.1 \ mu-view.1 \ + mu-mv.1 \ mu.1 \ mug.1 diff --git a/man/mu-mv.1 b/man/mu-mv.1 new file mode 100644 index 00000000..619c9f13 --- /dev/null +++ b/man/mu-mv.1 @@ -0,0 +1,75 @@ +.TH MU MV 1 "August 2011" "User Manuals" + +.SH NAME + +mu mv\- move a message file to a Maildir + +.SH SYNOPSIS + +.B mu mv + +.SH DESCRIPTION + +\fBmu mv\fR is the \fBmu\fR sub-command for moving mail files to new +directories. It does \fBnot\fR use the mu database. The command is +deliberately limited and tries hard to maintain the Maildir-integrity and to +minimize chance of accidents + +The \fIsource-path\fR must be a full, absolute path to the message you want to +move, while the \fItarget-maildir\fR is the path to the maildir, but +\fBwithout\fR the 'cur' or 'new' part - that part will be constructed from the +source message, so that message that live in 'new' will also be in 'new' in +the target, and the same for 'cur'. If needed, consult the \fBmaildir(5)\fR +documentation for details about 'cur' and 'new'. + +As a special case, when \fI/dev/null\fR is specified as the target directory, +the mail file will be unlinked (deleted). + +Note, unlike the UNIX \fImv\fR command, \fImu mv\fR takes precisely two +parameters. It's recommended not to use wildcards on the shell, the result may +be unexpected. + +.SH EXAMPLE + +To move a message \fI/home/jimbo/Maildir/scuba/cur/123123123:2,S\fR to +\fI/home/jimbo/Maildir/archive\fR, you can use: + +.nf + mu mv /home/jimbo/Maildir/scuba/cur/123123123:2,S /home/jimbo/Maildir/archive +.fi + +This will move the message to the new path: +\fI/home/jimbo/Maildir/archive/cur/123123123:2,S\fR. + +To remove a message \fI/home/fred/Maildir/trash/cur/123123123:2,S\fR, you +could do: + +.nf + mu mv /home/fred/Maildir/trash/cur/123123123:2,S /dev/null +.fi + +Obviously, you could also simply use \fBrm\fR in this case. + +.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 OPTIONS + +\fBmu mv\fR takes no options. + +.SH BUGS + +Please report bugs if you find them: +.BR http://code.google.com/p/mu0/issues/list + +.SH AUTHOR + +Dirk-Jan C. Binnema + +.SH "SEE ALSO" + +.BR maildir(5) +.BR mu(1) +.BR chmod(1) diff --git a/src/mu-cmd.c b/src/mu-cmd.c index 7870e33a..d0481a10 100644 --- a/src/mu-cmd.c +++ b/src/mu-cmd.c @@ -273,3 +273,41 @@ mu_cmd_mkdir (MuConfig *opts) return MU_EXITCODE_OK; } + + +MuExitCode +mu_cmd_mv (MuConfig *opts) +{ + GError *err; + gchar *fullpath; + + + if (!opts->params[1] || !opts->params[2]) { + g_warning ("usage: mu mv "); + return MU_EXITCODE_ERROR; + } + + err = NULL; + + /* special case: /dev/null */ + if (strcmp (opts->params[2], "/dev/null") == 0) { + if (unlink (opts->params[1]) != 0) { + g_warning ("unlink failed: %s", strerror (errno)); + return MU_EXITCODE_ERROR; + } else + return MU_EXITCODE_OK; + } + + + fullpath = mu_msg_file_move_to_maildir (opts->params[1], opts->params[2], + &err); + if (!fullpath) { + if (err) { + g_warning ("move failed: %s", err->message); + g_error_free (err); + } + return MU_EXITCODE_ERROR; + } + + return MU_EXITCODE_OK; +} diff --git a/src/mu-cmd.h b/src/mu-cmd.h index 891813ab..7b361ac0 100644 --- a/src/mu-cmd.h +++ b/src/mu-cmd.h @@ -94,6 +94,19 @@ MuExitCode mu_cmd_find (MuConfig *opts); MuExitCode mu_cmd_extract (MuConfig *opts); +/** + * execute the 'mv' command + * + * @param opts configuration options + * + * @return MU_EXITCODE_OK (0) if the command succeeds, + * MU_EXITCODE_ERROR otherwise + */ +MuExitCode mu_cmd_mv (MuConfig *opts); + + + + /** * execute the cfind command * diff --git a/src/mu-config.c b/src/mu-config.c index 41f64182..f6d04651 100644 --- a/src/mu-config.c +++ b/src/mu-config.c @@ -355,13 +355,14 @@ parse_cmd (MuConfig *opts, int *argcp, char ***argvp) } Cmd; Cmd cmd_map[] = { - { "index", MU_CONFIG_CMD_INDEX }, - { "find", MU_CONFIG_CMD_FIND }, - { "cleanup", MU_CONFIG_CMD_CLEANUP }, - { "mkdir", MU_CONFIG_CMD_MKDIR }, - { "view", MU_CONFIG_CMD_VIEW }, - { "extract", MU_CONFIG_CMD_EXTRACT }, { "cfind", MU_CONFIG_CMD_CFIND }, + { "cleanup", MU_CONFIG_CMD_CLEANUP }, + { "extract", MU_CONFIG_CMD_EXTRACT }, + { "find", MU_CONFIG_CMD_FIND }, + { "index", MU_CONFIG_CMD_INDEX }, + { "mkdir", MU_CONFIG_CMD_MKDIR }, + { "mv", MU_CONFIG_CMD_MV }, + { "view", MU_CONFIG_CMD_VIEW }, }; opts->cmd = MU_CONFIG_CMD_NONE; @@ -406,6 +407,9 @@ add_context_group (GOptionContext *context, MuConfig *opts) case MU_CONFIG_CMD_EXTRACT: group = config_options_group_extract (opts); break; + case MU_CONFIG_CMD_MV: /* no options for this one yet */ + /* group = config_options_group_mv (opts); */ + break; case MU_CONFIG_CMD_CFIND: group = config_options_group_cfind (opts); break; @@ -527,13 +531,14 @@ mu_config_execute (MuConfig *opts) } switch (opts->cmd) { + case MU_CONFIG_CMD_CFIND: return mu_cmd_cfind (opts); case MU_CONFIG_CMD_CLEANUP: return mu_cmd_cleanup (opts); case MU_CONFIG_CMD_EXTRACT: return mu_cmd_extract (opts); case MU_CONFIG_CMD_FIND: return mu_cmd_find (opts); case MU_CONFIG_CMD_INDEX: return mu_cmd_index (opts); case MU_CONFIG_CMD_MKDIR: return mu_cmd_mkdir (opts); + case MU_CONFIG_CMD_MV: return mu_cmd_mv (opts); case MU_CONFIG_CMD_VIEW: return mu_cmd_view (opts); - case MU_CONFIG_CMD_CFIND: return mu_cmd_cfind (opts); case MU_CONFIG_CMD_UNKNOWN: g_printerr ("mu: unknown command '%s'\n\n", opts->cmdstr); show_usage (FALSE); diff --git a/src/mu-config.h b/src/mu-config.h index 9c72f001..4ce6498b 100644 --- a/src/mu-config.h +++ b/src/mu-config.h @@ -69,6 +69,8 @@ enum _MuConfigCmd { MU_CONFIG_CMD_VIEW, MU_CONFIG_CMD_EXTRACT, MU_CONFIG_CMD_CFIND, + MU_CONFIG_CMD_MV, + MU_CONFIG_CMD_NONE, }; typedef enum _MuConfigCmd MuConfigCmd; diff --git a/src/mu-msg.c b/src/mu-msg.c index cc1e273c..1e3ecdb6 100644 --- a/src/mu-msg.c +++ b/src/mu-msg.c @@ -774,6 +774,67 @@ msg_move (const char* oldpath, const char *newfullpath, GError **err) return TRUE; } + +/* + * move a msg to another maildir, trying to maintain 'integrity', + * ie. msg in 'new/' will go to new/, one in cur/ goes to cur/. be + * super-paranoid here... + */ +gchar* +mu_msg_file_move_to_maildir (const char* oldpath, const char* targetmdir, + GError **err) +{ + MaildirType mtype; + char *newfullpath; + gboolean rv; + + g_return_val_if_fail (oldpath, FALSE); + g_return_val_if_fail (targetmdir, FALSE); + + if (!g_path_is_absolute(oldpath)) { + g_set_error (err, 0, MU_ERROR_FILE, + "source is not an absolute path: '%s'", oldpath); + return FALSE; + } + + mtype = get_maildir_type (oldpath); + if (mtype != MAILDIR_TYPE_CUR && mtype != MAILDIR_TYPE_NEW) { + g_set_error (err, 0, MU_ERROR_FILE, + "source is not in a 'cur' or 'new' maildir: '%s'", + oldpath); + return FALSE; + } + + if (!g_path_is_absolute(targetmdir)) { + g_set_error (err, 0, MU_ERROR_FILE, + "target is not an absolute path: '%s'", targetmdir); + return FALSE; + } + + if (!mu_util_check_dir (targetmdir, TRUE, TRUE)) { + g_set_error (err, 0, MU_ERROR_FILE, + "target is not a read-writable dir: '%s'", targetmdir); + return FALSE; + } + + newfullpath = get_new_fullpath (oldpath, targetmdir, mtype); + if (!newfullpath) { + g_set_error (err, 0, MU_ERROR_FILE, + "failed to determine target full path"); + return FALSE; + } + + rv = msg_move (oldpath, newfullpath, err); + + if (!rv) { + g_free (newfullpath); + return NULL; + } + + return newfullpath; +} + + /* * move a msg to another maildir, trying to maintain 'integrity', * ie. msg in 'new/' will go to new/, one in cur/ goes to cur/. be @@ -782,36 +843,19 @@ msg_move (const char* oldpath, const char *newfullpath, GError **err) gboolean mu_msg_move_to_maildir (MuMsg *self, const char* targetmdir, GError **err) { - const char *oldpath; - MaildirType mtype; char *newfullpath; g_return_val_if_fail (self, FALSE); g_return_val_if_fail (targetmdir, FALSE); - g_return_val_if_fail (g_path_is_absolute(targetmdir), FALSE); - g_return_val_if_fail (mu_util_check_dir (targetmdir, TRUE, TRUE), FALSE); - - oldpath = mu_msg_get_path (self); - - mtype = get_maildir_type (oldpath); - g_return_val_if_fail (mtype==MAILDIR_TYPE_CUR||mtype==MAILDIR_TYPE_NEW, - FALSE); - - newfullpath = get_new_fullpath (oldpath, targetmdir, mtype); - g_return_val_if_fail (newfullpath, FALSE); - - if (!msg_move (oldpath, newfullpath, err)) - goto error; - - /* update our path to new one... */ - mu_msg_cache_set_str (self->_cache, MU_MSG_FIELD_ID_PATH, newfullpath, - TRUE); - return TRUE; - -error: - g_free (newfullpath); - return FALSE; + newfullpath = mu_msg_file_move_to_maildir (mu_msg_get_path (self), + targetmdir, err); + if (newfullpath) /* update our path to new one... */ + mu_msg_cache_set_str (self->_cache, MU_MSG_FIELD_ID_PATH, newfullpath, + TRUE); /* the cache will free the string */ + + return newfullpath ? TRUE : FALSE; } + diff --git a/src/mu-msg.h b/src/mu-msg.h index 2a8ac4f5..bc1b65b7 100644 --- a/src/mu-msg.h +++ b/src/mu-msg.h @@ -376,10 +376,13 @@ char* mu_msg_to_sexp (MuMsg *msg, gboolean dbonly); /** * move a message to another maildir; the function returns the full * path to the new message, and changes the msg to now point to the - * new maildir + * new maildir. mu_msg_file_move_to_maildir, but takes a msgpath as it + * argument * * @param msg a message with an existing file system path in an actual * maildir + * @param msgpath an absolute file system path to an existing message in an + * actual maildir * @param targetmdir the target maildir; note that this the base-level * Maildir, ie. /home/user/Maildir/archive, and must _not_ include the * 'cur' or 'new' part. mu_msg_move_to_maildir will make sure that the @@ -388,10 +391,13 @@ char* mu_msg_to_sexp (MuMsg *msg, gboolean dbonly); * @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 TRUE if it worked, FALSE otherwise + * @return TRUE if it worked, FALSE otherwise (mu_msg_move_to_maildir) or the full path name of the target file (g_free) for mu_msg_file_move_to_maildir */ gboolean mu_msg_move_to_maildir (MuMsg *msg, const char* targetmdir, GError **err); +char* mu_msg_file_move_to_maildir (const char *msgpath, const char* targetmdir, + GError **err); + enum _MuMsgContactType { /* Reply-To:? */