#include "args.h"

#include "config.h"
/*
 * Copyright (c) 1986, 2014 by The Trustees of Columbia University in
 * the City of New York.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  + Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *  + Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 *  + Neither the name of Columbia University nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 */

#ifndef lint
static const char *rcsid = "$Header: /usr/local/src/mm/mm-0.94/mm/RCS/move.c,v 1.2 2005/05/28 22:39:23 beebe Exp $";
#endif

/*
 * move.c - support for the move and copy commands
 */

#include "mm.h"				/* for msgvec */
#include "cmds.h"			/* for CMD_MOVE */
#include "parse.h"			/* for atmbuf */
#include "rd.h"				/* for PR_xxx's */

extern char cmswbeg;
extern int auto_create_files;		/* do we need to ask? */
extern int num_msg_ops;			/* how many mail formats we know */
extern keytab formattab;

/* global guys just for move and copy */
static msgvec *dest;			/* file to copy to */
static int delete_msg;			/* delete the message after copy? */
static int copy_err;			/* did we have a problem copying? */

static int do_copy ARGS((int n));
static void parse_copyfile ARGS((char name[], int *ptype));

/*
 * cmd_move:
 * copy a message from the current file to another file
 * if n = CMD_MOVE, delete it as well
 */
int
#if HAVE_STDC
cmd_copy (int n)
#else /* K&R style */
cmd_copy (n)
int n;
#endif /* HAVE_STDC */
{
    string fname;			/* new file to copy to */
    int format;				/* format to use for new file */

    if (!check_cf (O_RDONLY))		/* make sure we have current file */
	return false;
    delete_msg = (n == CMD_MOVE);	/* delete if it's "move" not "copy" */

    noise ("into file");		/* hint hint */
    parse_copyfile (fname, &format);	/* parse filename and maybe switches */

    parse_sequence ("current",NULL,NULL); /* parse a message sequence */

    if (delete_msg && (cf->flags & MF_RDONLY)) { /* trying to delete? */
	if (modify_read_only == SET_NEVER) {
	   fprintf(stderr,
		   "Warning: cannot modify a file visited with \"examine\"\n");
	   delete_msg = FALSE;		/* don't bother trying */
        }
	else if (modify_read_only == SET_ASK)
	   delete_msg = yesno("File is read-only, delete messages anyway? ", "no");
    }
    dest = getdestfile (fname, format,
                       (cmcsb._cmflg & CM_ITTY) ? TRUE : FALSE);
        if (dest == NULL) {
	return false;
    }
    sequence_loop ((void (*)ARGS((int)))do_copy); /* copy each message */
    (*msg_ops[dest->type].close)(dest->filep); /* close it */
    free (dest);			/* maybe free other guys too */
    if ((mode == MM_TOP_LEVEL) && !copy_err)
	seq_print (true);
    return true;
}

#define used used_               /* avoid warnings about shadowed global */

/*
 * parse_copyfile:
 * parse the switches for the move/copy command and the filename
 * switches are optional, filename is not
 */
static void
#if HAVE_STDC
parse_copyfile (char *name, int *ptype)
#else /* K&R style */
parse_copyfile (name,ptype)
char name[];
int *ptype;
#endif /* HAVE_STDC */
{
    pval parseval;
    fdb *used;
    static char *def = NULL;
    static fdb formatfdb = {
	_CMSWI, 0, NULL, (pdat)&formattab, NULL, NULL, NULL };
    static fdb filefdb = {
	_CMFIL, FIL_PO|FIL_VAL, NULL, NULL, "file to copy to",
	NULL, NULL};
    filblk fb;
    const char *dirs[3];
    char *cp;
    char wd[MAXPATHLEN];

    /* don't list files twice, also if mail_directory is unset, it means "." */
    if (mail_directory[0] == '\0' || same_file(mail_directory, ".")) {
	dirs[0] = ".";
	dirs[1] = nil;
    }
    else {
	dirs[0] = mail_directory;
	dirs[1] = ".";
    }
    dirs[2] = nil;

    fb.pathv = dirs;
    fb.exceptionspec = nil;
    fb.def_extension = nil;
    filefdb._cmdat = (pdat) &fb;

    /* set up defaults for parse */
    if (def != nil)			/* in case of reparse */
	free(def);
    if (default_mail_type.current != NULL) {
	def = (char *) malloc (strlen (default_mail_type.current) +2);
	def[0] = cmswbeg;
	strcpy (&def[1], default_mail_type.current); /* get our default */
    }
    else
	def = NULL;
    formatfdb._cmdef = def;		/* insert the default */
    *ptype = -1;			/* mark that it hasn't been set */

    if (directory_folders)
	filefdb._cmffl &= ~FIL_NODIR;
    else
	filefdb._cmffl |= FIL_NODIR;
    parse(fdbchn (&formatfdb,&filefdb,NULL), &parseval, &used);
    if (used == &formatfdb) {
	*ptype = parseval._pvint;	/* what they want */
	parse (fdbchn (&filefdb,NULL), &parseval, &used); /* NEED a file */
    }

    free (def);				/* free us some space */
    def = NULL;

    /* handle the parsed file now */
#ifdef UNDEF
    if (rindex(parseval._pvfil[0], '/') == NULL) { /* XXX assumes name */
	if (access (parseval._pvfil[0], F_OK) != 0 && /* XXX is big enough */
	    mail_directory[0] != '\0')
	    sprintf (name, "%s/%s", mail_directory, parseval._pvfil[0]);
	else
	    sprintf (name, "./%s", parseval._pvfil[0]);
    }
    else				/* directory specified by user */
	strncpy(name, parseval._pvfil[0], STRLEN);
#endif /* UNDEF */

    /* ****  SEE ALSO, similar code in parse.c: parse_in_out_file **** */
    if (*parseval._pvfil[0] == '/') {		/* absolute path specified */
      strcpy (name, parseval._pvfil[0]);
    }
    else {
	/*
	 * file does not exist in mail-directory (mail-directory is an
	 * absolute path), so see if it is in .
	 * in which case use it, otherwise, try to default to new file
	 * in mail-directory
	 */
	if ((index(parseval._pvfil[0], '/') != NULL) || /* a dir specified */
	    access (parseval._pvfil[0], F_OK) == 0 || /* file exists in . */
	    mail_directory[0] == '\0') { /* or no mail_directory, use wd */
	    if (getwd (wd) == NULL) {	/* got some kind of error */
		fprintf (stderr, "%s\n", wd); /* print the error message */
		strcpy (name, parseval._pvfil[0]);
	    }
	    else {
		cp = parseval._pvfil[0];
		if (cp[0] == '.' && cp[1] == '/')
		    cp += 2;
		sprintf (name, "%s/%s", wd, cp);
	    }
	}
	else				/* use mail_directory */
	    sprintf (name, "%s/%s", mail_directory, parseval._pvfil[0]);
    }

    if (access (name, F_OK) != 0)	{ /* does it exist? */
	int ok;
	char c = 0;
	char *p = rindex(name, '/');	/* no, check directory */
					/* (NOTE: there WILL be a slash) */
	c = *p; *p = 0;			/* consider only the directory */
	ok = access(name, F_OK|W_OK);	/* can we create a file in there? */
	*p = c;				/* fix the path again */
	if (ok < 0)
	    cmerr(errstr(-1));		/* nope, complain */
    }
}

/*
 * getdestfile:
 * open up this file for appending to, figure out its type
 */
msgvec *
#if HAVE_STDC
getdestfile(const char *name, int format, int inter)
           				/* default format */
          				/* interactive errors */
#else /* K&R style */
getdestfile(name,format,inter)
const char *name;
int format;				/* default format */
int inter;				/* interactive errors */
#endif /* HAVE_STDC */
{
    msgvec *df;
    int type;
    int flags;				/* flags for msg_ops.open */
    int fd;

    if (same_file (cf->filename, name)) {
	if (inter)
	    cmerr ("Cannot append to current mail file");
	else
	    return (NULL);
    }
    switch (mail_probe (name, &type))	/* can we read this? */
    {
    case PR_NAME:
	if (inter)
	    cmerr ("Badly formed filename: %s", name);
	else
	    return (NULL);
	break;
    case PR_PERM:
	if (inter)
	    cmerr ("Cannot determine mail format: %s", name); /* can't read */
	else
	    return (NULL);
	break;
    case PR_NOEX:			/* must make new file */
	if (inter) {
	    fprintf (stderr, "File does not exist: %s\n", name);
	    if ((auto_create_files == SET_NO) ||
		(auto_create_files == SET_ASK &&
		 !yesno("Do you want to create it? ", "yes")))
		return(NULL);
	}
	fd = creat(name, new_file_mode); /* XXX assuming local file */
	if (fd >= 0)			/* error msg on open later */
	    close(fd);
	/* ** fall through ** */	/*FALLTHROUGH*/
    case PR_EMPTY:
	if (format != -1)		/* did we parse something? */
	    type = format;		/* whatever we parsed in switch */
	else {
	    for (type = 0; type < num_msg_ops; type++) /* try default */
		if (strcmp (default_mail_type.current, msg_ops[type].typename)
		    == 0)
		    break;		/* use their default */
	    if (type == num_msg_ops)
		type = TYPE_MTXT;	/* no match, pick some type */
	}
	break;
    case PR_NOTOK:
	if (inter)
	    cmerr ("File is damaged or in unknown format: %s", name);
	else
	    return (NULL);
    default:
	if ((format != type) && (format != -1)
	    && inter)			/* they guessed wrong */
	    fprintf (stderr, "File is in %s format, ignoring switch\n",
		     msg_ops[type].typename);
    }
    df = (msgvec *) malloc (sizeof (msgvec)); /* room to put all this stuff */
    if (df == NULL) {
	if (inter)
	    cmerr ("No room to handle file: %s", name);
	else
	    return (NULL);
    }

    bzero (df, sizeof (msgvec));

    df->type = type;

    strcpy (df->filename, name);

    flags = inter ? (OP_INT|OP_APP) : OP_APP;
    if ((*msg_ops[df->type].open)(df,flags) != 0) /* open */
	return (NULL);
    return (df);			/* don't need data filled in */
}

/*
 * do_copy:
 * copy the nth message from cf (current file) into dest
 * delete it if in CMD_MOVE.
 */
static int
#if HAVE_STDC
do_copy (int n)
      					/* which message */
#else /* K&R style */
do_copy (n)
int n;					/* which message */
#endif /* HAVE_STDC */
{
    int oldflags;

    if (n == 0) {
	copy_err = FALSE;		/* no errors yet */
	return (true);
    }
    if (n < 0)
	return (true);			/* no special ending */

    if (!copy_err && !ignore (n)) {
	oldflags = cf->msgs[n].flags;
	cf->msgs[n].flags &= ~M_DELETED; /* copy we move not deleted */

	/* copy this message to the end of dest (0 for this unused field) */
	if ((copy_err = (*msg_ops[dest->type].wr_msg)
	     (dest,&cf->msgs[n],0,WR_COPY), copy_err)) {
	    cf->msgs[n].flags = oldflags;
	    fprintf (stderr, "Trouble writing message %d to %s, aborting %s\n",
		     n, dest->filename, delete_msg ? "move" : "copy");
	    return (false);
	}
	cf->msgs[n].flags = oldflags;
	if (delete_msg && !(cf->msgs[n].flags & M_DELETED)) { /* changing it? */
	    cf->msgs[n].flags |= (M_DELETED|M_MODIFIED); /* delete it */
	    cf->flags |= MF_DIRTY;	/* file (msg flags) must be saved */
	}
    }
    return (copy_err);
}
