#include "ckcsym.h"
#include "ckcdeb.h"
#ifdef RENAME
/*
<file>          CK9REN.C
<includes>      system: modes.h, errno.h, direct.h, sgstat.h, sg_codes.h
                user: ckcsym.h ckcdeb.h
<function>      rename
<syntax>        int rename ( char *old_path, char *new_path );
<parameters>    char *old_path      full path name from file to rename
                char *new_path      new path name
<returns>       int                 0 if successful, -1 on error
<description>   rename a file
<history>       1.00 05.01.94 ulli schlueter created
*/

#include <modes.h>
#include <errno.h>
#include <direct.h>
#include <sgstat.h>
#include <sg_codes.h>

#ifdef CK_ANSIC
char *rindex(const char *, int), *strcat(char *, const char *),
    *strcpy(char *, const char *), *strncpy(char *,const char *, int);
char *malloc(unsigned int), *realloc(void *, unsigned int);
int strlen(const char *), strcmp(const char *, const char *),
    _gs_opt(int, struct sgbuf *), toupper(int),
    write(int, const char *, int), read(int, char *, int),
    lseek(int, long, int), _prsnam(const char *), open(const char *, int),
    close(int), free(const char *), _gs_devn(int, char *),
    _ss_lock(int, long);
void *memset(void *, int, unsigned long);
#else
extern char *rindex(), *strcat(), *strcpy(), *strncpy();
extern char *malloc(), *realloc();
extern int strlen(), strcmp(), _gs_opt(), toupper(), write(), lseek(),
_prsnam(), open(), close(), free(), _gs_devn(), _ss_lock();
extern void *memset();
#endif /* !CK_ANSIC */

#ifndef NULL
#define NULL ((void*)0)
#endif /* !NULL */

#define OFFSET_OF(typ, tag) ((long)&((typ)0)->tag)

/***************** strucmp **********************/
static int
#ifdef CK_ANSIC
strucmp(const char *s1, const char *s2)
#else
strucmp(s1, s2, n)
register char *s1, *s2;
register int n;
#endif /* !CK_ANSIC */
{
    int ret;

    while ((ret = toupper(*s1++) - toupper(*s2 & 0x7f)) == 0
        && *s2++ > '\0') {}
    if (*s1 != '\0') return -1;
    return ret;
}

#ifdef MOVEDIR
/***************** findfd ***********************/
int
#ifdef CK_ANSIC
findfd (char *dirname, long fdpsn, struct sgbuf *opts)
#else
findfd (dirname, fdpsn, opts)
register char *dirname;
long fdpsn;
struct sgbuf *opts;
#endif /* !CK_ANSIC */
{
    register char *p, *b, *e;
    register int x, pd, size = strlen(dirname) + 256;

    if ((b = malloc(size)) == NULL) return -1;
    e = b + size;
    strcpy(b, dirname);
    p = b + strlen(b);
    *p++ = '/';
    *p++ = '.';
    while (x = 1, fdpsn != opts->sg_fdpsn) {
        *p++ = '.';
        *p = '\0';
        if (p + 1 >= e) {
            if ((b = realloc(b, size *= 2)) == NULL) return -1;
            e = b + size;
            p = b + strlen(b);
        }
        if ((x = open(b, S_IFDIR)) != -1) {
            pd = x;
            x = _gs_opt(pd, opts);
            close(pd);
        }
        if (x == -1) break;
        if (opts->sg_fdpsn == opts->sg_dipsn) {
            x = 0;
            break;
        }
    }
    free(b);
    return x;
}
#endif /* MOVEDIR */

/***************** rename ***********************/
#ifdef CK_ANSIC
int rename (const char *old, const char *new)
#else
rename(old,new)
register char *old,*new;
#endif /* !CK_ANSIC */
{
#ifdef CK_ANSIC
    const char *newname;
    const char *oldname;
#else
    register char *newname;
    register char *oldname;
#endif /* !CK_ANSIC */
    char *pct = ".";
    register char *olddir;
    char *newdir;
    register int oldpd;
    register int odirpd;
    register int ndirpd;
    int newlen;
    union {
        struct sgbuf opts;
        char devn[4*32];
    } newon, oldon;
    long pd_dcp;
#ifdef MOVEDIR
    long dirlsn;
#endif /* MOVEDIR */
    int amod = 0;
    struct dirent ode;
    char bmovedir;
    char bsamedir;

    olddir = newdir = pct;
    oldpd = odirpd = ndirpd = -1;

    if ((oldname = rindex(old, '/')) == old /* can't rename device */
        || (newname = rindex(new, '/')) == new) /* can't rename to device */
    {
        errno = E_BPNAM;
        return -1;
    }
/* Get and check old name (inhibit renaming of "." and "..") */
    if (oldname != NULL) oldname += 1;
    else oldname = old;
    if ((newlen = _prsnam(oldname)) != strlen(oldname) || newlen > 28) {
        errno = E_BPNAM;
        return -1;
    }
/* Get and check new path name */
    if (newname != NULL) newname += 1;
    else newname = new;
    if ((newlen = _prsnam(newname)) != strlen(newname) || newlen > 28) {
        errno = E_BPNAM;
        return -1; /* wrong path name */
    }
/* Get new directory name */
    if (newname != new) {
        if ((newdir = malloc(newname - new)) == NULL) return -1;
        strncpy(newdir, new, newname - 1 - new);
        newdir[newname - 1 - new] = '\0';
    }
/* Get old directory name */
    if (oldname != old) {
        if ((olddir = malloc(oldname - old)) == NULL) goto rete0;
        strncpy(olddir, old, oldname - 1 - old);
        olddir[oldname - 1 - old] = '\0';
    }
/* Open directories for update */
    if ((odirpd = open(olddir, S_IREAD|S_IWRITE|S_IFDIR)) == -1) {
        goto rete0;
    }
    if ((ndirpd = open(newdir, S_IREAD|S_IWRITE|S_IFDIR)) == -1) {
        goto rete0;
    }
    if (olddir != pct) { free(olddir); olddir = pct; }
/* Check if on the same device */
    if (_gs_devn(odirpd, oldon.devn) == -1
        || _gs_devn(ndirpd, newon.devn) == -1) {
        goto rete0;
    }
    if (strcmp(oldon.devn, newon.devn) != 0) {
        errno = E_BPNAM;
        goto rete0;
    }
/* Check if same directory */
    if (_gs_opt(odirpd, &oldon.opts) == -1
        || _gs_opt(ndirpd, &newon.opts) == -1) {
        goto rete0;
    }
    if (!!(bsamedir = oldon.opts.sg_fdpsn == newon.opts.sg_fdpsn)) {
        close(odirpd);
        odirpd = ndirpd;
    }
#ifdef MOVEDIR
    dirlsn = newon.opts.sg_fdpsn
        / (newon.opts.sg_sctsiz != 0 ? newon.opts.sg_sctsiz : 256);
#endif /* MOVEDIR */
/* Open old path */
    if ((oldpd = open(old, amod)) == -1
        && (errno != E_FNA
        || (oldpd = open(old, amod = bsamedir ? S_IFDIR : S_IFDIR|S_IWRITE)) == -1)) {
        goto rete0;
    }
    bmovedir = !bsamedir && (amod & S_IFDIR) != 0;
#ifndef MOVEDIR
    if (bmovedir) {
        errno = E_FNA;
        goto rete0;
    }
#endif /* !MOVEDIR */
/* Check wether its an rbf device */
    if (_gs_opt(oldpd, &oldon.opts) == -1) {
        goto rete0;
    }
    if (oldon.opts.sg_class != DT_RBF) {
        errno = E_BPNAM;
        goto rete0;
    }
#ifdef MOVEDIR
/* Check if new directory discards the old one */
    if (bmovedir) {
        int x;
        
        if ((x = findfd(newdir, oldon.opts.sg_fdpsn, &newon.opts)) != 0) {
            if (x > 0) errno = E_BPNAM;
            goto rete0;
        }
    }
#endif /* MOVEDIR */
    if (newdir != pct) { free(newdir), newdir = pct; }
/* Lock out the record so that everybody who wants to open the old */
/* path is queued, until the record is released */
    pd_dcp = oldon.opts.sg_dirptr;
    if (lseek(odirpd, pd_dcp, 0) != pd_dcp
        || read(odirpd, (char *)&ode, sizeof ode) != sizeof ode
        || lseek(odirpd, pd_dcp, 0) != pd_dcp) {
        goto rete0;
    }
    if (!bmovedir) { close(oldpd); oldpd = -1; }
/* Check if the new file already exists and wether it is the same file */
    if (bsamedir) {
        int newpd;
        
        if ((newpd = open(new, 0)) != -1 ||
            errno == E_FNA && (newpd = open(new, S_IFDIR)) != -1) {
            if (_gs_opt(newpd, &newon.opts) == -1) {
                close(newpd);
                goto rete0;
            }
            close(newpd);
            if (newon.opts.sg_fdpsn != oldon.opts.sg_fdpsn) {
                errno = E_CEF;
                goto rete0;
            }
        }
    } else {
        int l, f;
        struct dirent de;
        
        pd_dcp = 2 * sizeof de;
        if (_ss_lock(ndirpd, 0xffffffff) == -1
            || lseek(ndirpd, pd_dcp, 0) != pd_dcp) {
            goto rete0;
        }
        f = 0;
        while ((l = read(ndirpd, (char *)&de, sizeof de)) != 0) {
            if (l != sizeof de || strucmp(newname, de.dir_name) == 0
                && (errno = E_CEF, 1)) {
                goto rete0;
            }
            if (!f && de.dir_name[0] != '\0')
                pd_dcp += sizeof de;
            else f = 1;
        }
        if (f) {
            if (lseek(ndirpd, pd_dcp, 0) != pd_dcp) {
                goto rete0;
            }
        }
    }
/* Rename the file, release the record and clean up */
    /*memset(ode.dir_name, 0, sizeof ode.dir_name);*/
    strncpy(ode.dir_name, newname, newlen);
    ode.dir_name[newlen - 1] |= (char)(1<<7);
    ode.dir_name[newlen] = '\0';
    newlen = bsamedir ? sizeof ode.dir_name : sizeof ode;
    if (write(ndirpd, (char *)&ode, newlen) != newlen) {
        goto rete0;
    }
/* Now if in different directories delete the old file */
    if (!bsamedir) {
#ifdef MOVEDIR
        if (bmovedir) {
            /* Change ".." directory entry */
            if (lseek(oldpd, OFFSET_OF(struct dirent *, dir_addr), 0)
                != OFFSET_OF(struct dirent *, dir_addr)) {
                goto rete0;
            }
            if (write(oldpd, (char *)&dirlsn, sizeof dirlsn) != sizeof dirlsn) {
                goto rete0;
            }
            close(oldpd);
            oldpd = -1;
        }
#endif /* MOVEDIR */
        /* Delete old entry */
        ode.dir_name[0] = '\0';
        if (write(odirpd, ode.dir_name, 1) != 1) {
            goto rete0;
        }
    }
    if (!bsamedir) close(odirpd);
    if (close(ndirpd) == -1) return -1;
    return 0;
/* Return resources on error */
    {
        int err;
rete0:  err = errno;
        if (oldpd != -1) close(oldpd);
        if (ndirpd != -1) close(ndirpd);
        if (odirpd != -1 && odirpd != ndirpd) close(odirpd);
        if (olddir != pct && olddir != NULL) free(olddir);
        if (newdir != pct) free(newdir);
        errno = err;
        return -1;
    }
}
#endif /* RENAME */
