/* a public domain rename, by ERS */

#include <stdio.h>
#include <errno.h>
#include <osbind.h>
#include <unistd.h>
#include <support.h>
#include <string.h>
#include <stdlib.h>
#include "symdir.h"

int rename(_oldname, _newname)
	const char *_oldname, *_newname;
{
	char oldname[FILENAME_MAX], newname[FILENAME_MAX];
	char oldpath[FILENAME_MAX];
	int rval, name_munged;
	SYMDIR *olddir = 0, *newdir = 0;
	SYMENTRY *ent = 0, *old;
	char *s;
	int save_errno = errno;

	if (!access(_newname, 0)) {	/* new name already exists */
#ifdef DEBUG
printf("rename: unlinking %s\n", _newname);
#endif
		if (unlink(_newname))
			return -1;
	}
	errno = save_errno;

	rval = _unx2dos(_oldname, oldname);
	/* _unx2dos returns _NM_LINK and sets __link_path and __link_name if
         * given a symbolic link
	 */
	if (rval == _NM_LINK) {			/* a symbolic link */
		strcpy(oldpath, __link_path);	/* save path to symlink */
		olddir = _read_symdir(oldpath);
		if (!olddir) return -1;
		old = 0; ent = olddir->s_dir;
		while (ent) {
			if (!strcmp(ent->linkname, __link_name)) {
#ifdef DEBUG
printf("rename: found symbolic link %s\n", __link_name);
#endif
				break;
			}
			old = ent; ent = ent->next;
		}
		if (!ent) { 
			errno = EERROR; _free_symdir(olddir); return -1;
		}
		if (old)
			old->next = ent->next;
		else
			olddir->s_dir = ent->next;
		ent->next = 0;
		_write_symdir(oldpath, olddir);
		_free_symdir(olddir);
	}

/*
 * save info about whether or not the name got changed by _unx2dos
 */
	name_munged = (_unx2dos(_newname, newname) == _NM_CHANGE);
/*
 * for a symbolic link, we now have an entry "ent" which points at
 * the old symbolic link. Make a new symbolic link, with the new name,
 * in the new directory. EXCEPTION: automatic symbolic links get handled
 * later.
 */
	if (ent && !(ent->flags & SD_AUTO)) {
		if ((s = strrchr(newname, '\\')))
			*s = 0;
		if (!s || !(newdir = _read_symdir(newname))) {
link_failed:
#ifdef DEBUG
printf("rename: attempt to link to directory %s failed\n", newname);
#endif
	/* if we can't put the symlink into the new directory, put it
         * back in the old one again
         */
			olddir = _read_symdir(oldpath);
			if (olddir) {
				ent->next = olddir->s_dir;
				olddir->s_dir = ent;
				_write_symdir(oldpath, olddir);
				_free_symdir(olddir);
			}
			return -1;
		}
	/* OK, now allocate a new symentry to hold the new name and link
         * info. Note that "old" is definitely a misnomer here!!!
         */
		old = (SYMENTRY *)malloc(3+sizeof(SYMENTRY)+
		    strlen(__link_name)+strlen(ent->linkto)+strlen(ent->cflags)
		   );
		if (!old) {
			errno = ENOMEM;
			goto link_failed;
		}
	/*
	 * Now copy all the fields of "ent" into the new symentry
	 * (which is, contrary to reason, called "old"; actually, this
         * is so that on an error we can "goto link_failed" and restore
         * the status quo).
	 */ 
		s = old->linkname;
		strcpy(old->linkname, __link_name);
		s += strlen(__link_name)+1;
		old->linkto = s;
		strcpy(s, ent->linkto);
		s += strlen(ent->linkto)+1;
		old->cflags = s;
		strcpy(s, ent->cflags);
		free(ent);
		old->next = newdir->s_dir;
		newdir->s_dir = old;
	/*
	 * update the copy on disk, and return
	 */
#ifdef DEBUG
printf("rename: updating symbolic link info\n");
#endif
		rval = _write_symdir(newname, newdir);
		_free_symdir(newdir);
		if (rval) {
			errno = -rval;
			return -1;
		}
		return 0;
	}

/*
 * normal files, and automatic symbolic links, come here
 */
#ifdef DEBUG
printf("rename: calling Frename(%s, %s)\n", oldname, newname);
#endif
	if (!strcmp(oldname, newname)) {
		rval = 0;
	}
	else if ( (rval = Frename(0, oldname, newname)) < 0 ) {
		errno = -rval;	/* set errno now for link_failed */
		rval = -1;
	}
	if (ent) {
		if (rval < 0)
			goto link_failed;
		else {
			free(ent);
			ent = 0;
		}
	}
/*
 * finally, check to see if the new name should have an automatic symbolic
 * link attached
 */
	if (rval == 0 && name_munged)
		_make_autolink(newname, __link_name);

	return rval;
}
