#ifndef NO_IDENT
static char *Id = "$Id: flrnam.c,v 1.10 1995/10/21 18:42:11 tom Exp $";
#endif

/*
 * Title:	flrnam.c
 * Author:	T.E.Dickey
 * Created:	15 May 1984
 * Last update:
 *		18 Mar 1995, dircmd prototypes
 *		19 Feb 1995, sys utils prototypes
 *		09 Feb 1989, for vms5.0, may have to alter protection to rename
 *		24 Aug 1985, use 'dds_add2' instead of 'dirdata_one'
 *		31 Jul 1985, added log-file tracking
 *		27 Jul 1985, provide wildcard-rename via 'dirseek_spec2' loop.
 *		19 Jul 1985, use 'dirdata_ren'.
 *		16 Jul 1985, corrected use of 'filelist' (=> filelink).
 *		03 Jul 1985, cleanup 'filelist' definition.
 *		09 Mar 1985, add code for special case of renamed-directory.
 *		21 Dec 1984, use 'flist_sysmsg'.
 *		10 Dec 1984, use 'dirent_chk' instead of 'dirent_one'.
 *		06 Sep 1984, use "sysrename"
 *		03 Sep 1984, use "import"
 *		25 Aug 1984, cleanup buffer sizes
 *		15 Jul 1984, use PATHOF()
 *		14 Jul 1984, use $GETMSG
 *		10 Jul 1984, re-coded to DCLARG-list
 *		22 May 1984
 *
 * Function:	This module performs the RENAME function for "FLIST".  The
 *		first argument may be a name outside the file-list.
 *
 * Notes:	If the "versions" option is selected, a rename may uncover
 *		an older version of the same name.  When this is found, must
 *		create a new 'filelist[]' entry at the end of the list.
 *		Even more subtly, a rename may put the target file below
 *		the version-level of an existing file, causing it to vanish.
 *
 *		If the old-file is a directory (e.g., having ".DIR" filetype),
 *		we require that the new-file be also.  FLIST will not permit
 *		the user to accidentally zap himself by renaming a directory
 *		out of existence (or by making a file into a directory).
 *
 *		On the other hand, if the user renames a directory to
 *		another directory, then we must update the display-list
 *		to reflect the altered paths in dependent files.
 *
 * Patch:	Does VMS properly test for a user accidentally creating
 *		a maximum tree higher than 8 levels, e.g., by renaming a
 *		level-2 directory which has dependents down to level-8
 *		into a level-8 directory?
 */

#include	<rms.h>
#include	<string.h>

#include	"flist.h"
#include	"dirent.h"
#include	"dircmd.h"
#include	"dirdata.h"
#include	"dirpath.h"
#include	"dirseek.h"
#include	"dds.h"
#include	"getprot.h"
#include	"chprot.h"

#include	"sysutils.h"

import(filelist);
import(V_opt);
import(conv_list);

static	void	flrnam_all (char *spec, int len, unsigned status);

static	int	curfile;
static	char	*newspec;

/*
 * Perform the actual rename.  For ease of use under vms5.0, check to see if we
 * have delete-privilege for the file.  If not, try to set it.  Returns 0 iff
 * no error occurred; otherwise the corresponding RMS status code.
 */
static
doit(char *newspec, char *oldspec, GETPROT *prot_)
{
	unsigned status;
	int	modified = FALSE;
#define	DELETE_MASK	 ((XAB$M_NODEL << XAB$V_SYS)\
			| (XAB$M_NODEL << XAB$V_OWN)\
			| (XAB$M_NODEL << XAB$V_GRP)\
			| (XAB$M_NODEL << XAB$V_WLD))

#define	UNMASK(name,mask) if ((status = chprot(name, mask, 0)) == RMS$_NORMAL)\
				status = 0

	if (!cmpprot(prot_, "d")) {
		UNMASK(oldspec, prot_->p_mask & ~DELETE_MASK);
		if (status != 0)
			return (status);
		modified = TRUE;
	}
	if (!(status = sysrename(newspec, oldspec))) {
		if (modified)
			UNMASK(newspec, prot_->p_mask);
	}
	return (status);
}

/*
 * Initiate a search for files matching the old-specification.  Whenever we
 * find one, call 'flrnam_all' to perform the actual RENAME-operation.
 */
tDIRCMD(flrnam)
{
	char	*oldspec = dclinx (xdcl_, 1, 0);

	curfile	= *curfile_;		/* save, in case we do refresh	*/
	newspec = dclinx (xdcl_, 2, 0);	/* save, for use in 'flrnam_all'*/
	dirseek_spec2 (oldspec, flrnam_all);
}

/*
 * This procedure is called from 'dirseek_spec' after each file-spec is found.
 */
static
void	flrnam_all (char *spec, int len, unsigned status)
{
	FILENT	ztmp,	*z = &ztmp, zold;
	char	oldspec	[MAX_PATH],
		tmpspec	[MAX_PATH],
		fixspec	[MAX_PATH],
		msg	[CRT_COLS];
	int	inx,
		wasdir;

	if (status != RMS$_NORMAL)	/* (file not found)	*/
		return;

	strncpy (oldspec, spec, len);
	oldspec[len] = EOS;

	inx = dirent_old_any (z, oldspec, 1);

	if (!diropen (zPATHOF(z)))
		return;

	wasdir	= dirent_isdir (z);
	zold	= *z;			/* Save, in case this was a directory */

	strcpy (tmpspec, newspec);	/* "sysrename" alters output arg */

	flist_log ("! rename %s to %s", oldspec, tmpspec);
	if ((status = doit(tmpspec, oldspec, &z->f_getprot)) == 0)
	{
	    if (status = dirent_chk (z, tmpspec))
	    {
		if (wasdir ^ dirent_isdir(z))
		{
		    warn ("Illegal directory-rename");
		    (void)doit(strcpy(fixspec, oldspec), tmpspec, &z->f_getprot);
		    return;
		}
		if (wasdir)	dirpath_rename (z, &zold);
	    }
	    else
		return;		/* ? (Probably a bug)		*/

	    dirdata_ren (z, &zold); /* rename the data-object	*/

	    if (inx < 0)	/* renamed new (?) entry into list	*/
		inx = dds_add (z);
	    else if (! (V_opt || wasdir))
	    {
		if (dirhigh (oldspec)) /* Check for uncovering	*/
		{
		    /*
		     * There was a lower version of the 'oldspec' around.
		     * Check the new name against the resulting list, using
		     * DIRHIGH to allocate a new entry, if needed.
		     */
		    dirhigh (tmpspec);
		    inx = -1;
		}
	    }

	    if (inx >= 0)
	    {
		dds_add2 (z, inx);
		if (wasdir && strchr(conv_list, 'p'))
		    dds_all (crt_top(), curfile);
		if (! V_opt)	dirhigh (tmpspec);
	    }
	}
	else
	{
	    flist_sysmsg (status);
	}
}
