#include "EXTERN.h"
#include "common.h"
#include "INTERN.h"
#include "util.h"

/* Rename a file, copying it if necessary. */


#ifdef AMIGA

#include <libraries/dos.h>
#include <libraries/dosextens.h>


/* backup & copy the file */
  static void copy_file();

int
move_file(from,to)
 char *from, *to;
{
  register long success;
  register int fromfd;
  register int i;
  register struct FileLock *destlock;
  int curnum='0';

  static char bakname[256];

/* declare us some AmigaDOS functions: */
  extern long DeleteFile();
  extern struct FileLock *Lock();
  extern void UnLock();
  extern long Rename();


    if (strEQ(to, "-")) {   /* cat it to stdout. */
#ifdef DEBUGGING
        if (debug & 4)
            say2("Moving %s to stdout.\n", from);
#endif
        fromfd = open(from, 0);
        if (fromfd < 0)
            fatal2("patch: internal error, can't reopen %s\n", from);
        while ((i=read(fromfd, buf, sizeof buf)) > 0)
            if (write(1, buf, i) != 1)
                fatal1("patch: write failed\n");
        Close(fromfd);
        return 0;
    }

/* add the extension to the name */
    Strcpy(bakname, to);
/* see if the "to" exists. If not, don't add the extension. */
    if (destlock=Lock(bakname)) {
        UnLock(destlock);
        Strcat(bakname, ORIGEXT);

        i = strlen(bakname);   /* find the end of string */
        bakname[i+1]=0;        /* so that the following will work: */

/* now to see if the destination exists: */
        while (destlock=Lock(bakname)) {
            UnLock(destlock);   /* clean up after ourselves. */
            bakname[i]=curnum++;
        }
/* ok, now we can do the relinking! */
        success = Rename(to,bakname);
        if (success==0) {
            say3("patch: can't backup %s, output is in %s\n",
                to, from);
            return -1;
        }
    }
/* ok, now copy the from to the to file. */
/* try renaming it first, & if that don't work, then copy it. */
    success = Rename(from,to);
    if (success==0) {                  /* if don't work, then copy! */
       copy_file(from,to);
       success=DeleteFile(from);
    }
    return 0;
}
#endif

#ifndef AMIGA
int
move_file(from,to)
char *from, *to;
{
    char bakname[512];
    Reg1 char *s;
    Reg2 int i;
    Reg3 int fromfd;

    /* to stdout? */

    if (strEQ(to, "-")) {
#ifdef DEBUGGING
        if (debug & 4)
            say2("Moving %s to stdout.\n", from);
#endif
        fromfd = open(from, 0);
        if (fromfd < 0)
            fatal2("patch: internal error, can't reopen %s\n", from);
        while ((i=read(fromfd, buf, sizeof buf)) > 0)
            if (write(1, buf, i) != 1)
                fatal1("patch: write failed\n");
        Close(fromfd);
        return 0;
    }

        if (origprae) {
                Strcpy (bakname, origprae);
        Strcat(bakname, to);
        } else {
                Strcpy(bakname, to);
        Strcat(bakname, origext?origext:ORIGEXT);
        }
    if (stat(to, &filestat) >= 0) {     /* output file exists */
        dev_t to_device = filestat.st_dev;
        ino_t to_inode  = filestat.st_ino;
        char *simplename = bakname;

        for (s=bakname; *s; s++) {
            if (*s == '/')
                simplename = s+1;
        }
        /* find a backup name that is not the same file */
        while (stat(bakname, &filestat) >= 0 &&
                to_device == filestat.st_dev && to_inode == filestat.st_ino) {
            for (s=simplename; *s && !islower(*s); s++) ;
            if (*s)
                *s = toupper(*s);
            else
                Strcpy(simplename, simplename+1);
        }
        while (unlink(bakname) >= 0) ;  /* while() is for benefit of Eunice */
#ifdef DEBUGGING
        if (debug & 4)
            say3("Moving %s to %s.\n", to, bakname);
#endif
        if (link(to, bakname) < 0) {
            say3("patch: can't backup %s, output is in %s\n",
                to, from);
            return -1;
        }
        while (unlink(to) >= 0) ;
    }
#ifdef DEBUGGING
    if (debug & 4)
        say3("Moving %s to %s.\n", from, to);
#endif
    if (link(from, to) < 0) {           /* different file system? */
        Reg4 int tofd;

        tofd = creat(to, 0666);
        if (tofd < 0) {
            say3("patch: can't create %s, output is in %s.\n",
              to, from);
            return -1;
        }
        fromfd = open(from, 0);
        if (fromfd < 0)
            fatal2("patch: internal error, can't reopen %s\n", from);
        while ((i=read(fromfd, buf, sizeof buf)) > 0)
            if (write(tofd, buf, i) != i)
                fatal1("patch: write failed\n");
        Close(fromfd);
        Close(tofd);
    }
    Unlink(from);
    return 0;
}
#endif

/* Copy a file. */

void
copy_file(from,to)
char *from, *to;
{
    Reg3 int tofd;
    Reg2 int fromfd;
    Reg1 int i;

    tofd = creat(to, 0666);

    if (tofd < 0)
        fatal2("patch: can't create %s.\n", to);
    fromfd = open(from, 0);
    if (fromfd < 0)
        fatal2("patch: internal error, can't reopen %s\n", from);
    while ((i=read(fromfd, buf, sizeof buf)) > 0)
        if (write(tofd, buf, i) != i)
            fatal2("patch: write (%s) failed\n", to);
    Close(fromfd);
    Close(tofd);
}

/* Allocate a unique area for a string. */

char *
savestr(s)
Reg1 char *s;
{
    Reg3 char *rv;
    Reg2 char *t;

    if (!s)
        s = "Oops";
    t = s;
    while (*t++);
    rv = malloc((MEM) (t - s));
    if (rv == Nullch) {
        if (using_plan_a)
            out_of_mem = TRUE;
        else
            fatal1("patch: out of memory (savestr)\n");
    }
    else {
        t = rv;
        while (*t++ = *s++);
    }
    return rv;
}

#ifdef lint

#ifdef CANVARARG

/*VARARGS ARGSUSED*/
say(pat) char *pat; { ; }
/*VARARGS ARGSUSED*/
fatal(pat) char *pat; { ; }
/*VARARGS ARGSUSED*/
ask(pat) char *pat; { ; }
#endif

#else

/* Vanilla terminal output (buffered). */

void
say(pat,arg1,arg2,arg3)
char *pat;
long arg1,arg2,arg3;
{
    fprintf(stderr, pat, arg1, arg2, arg3);
    Fflush(stderr);
}

/* Terminal output, pun intended. */

void                            /* very void */
fatal(pat,arg1,arg2,arg3)
char *pat;
long arg1,arg2,arg3;
{
    void my_exit();

    say(pat, arg1, arg2, arg3);
    my_exit(1);
}


#ifndef AMIGA

/* Get a response from the user, somehow or other. */

void
ask(pat,arg1,arg2,arg3)
char *pat;
long arg1,arg2,arg3;
{
    int ttyfd;
    int r;
    bool tty2 = isatty(2);

    Sprintf(buf, pat, arg1, arg2, arg3);
    Fflush(stderr);
    write(2, buf, strlen(buf));
    if (tty2) {                         /* might be redirected to a file */
        r = read(2, buf, sizeof buf);
    }
    else if (isatty(1)) {               /* this may be new file output */
        Fflush(stdout);
        write(1, buf, strlen(buf));
        r = read(1, buf, sizeof buf);
    }
    else if ((ttyfd = open("/dev/tty", 2)) >= 0 && isatty(ttyfd)) {
                                        /* might be deleted or unwriteable */
        write(ttyfd, buf, strlen(buf));
        r = read(ttyfd, buf, sizeof buf);
        Close(ttyfd);
    }
    else if (isatty(0)) {               /* this is probably patch input */
        Fflush(stdin);
        write(0, buf, strlen(buf));
        r = read(0, buf, sizeof buf);
    }
    else {                              /* no terminal at all--default it */
        buf[0] = '\n';
        r = 1;
    }
    if (r <= 0)
        buf[0] = 0;
    else
        buf[r] = '\0';
    if (!tty2)
        say1(buf);
}
#else  /* Amiga */

/* for the Amiga we're going to have to do some heavy-duty magic.
   We can't open /dev/tty! So open a whole new window!
*/

void
ask(pat,arg1,arg2,arg3)
char *pat;
long arg1,arg2,arg3;
{
    int rwin;
    long actual_read;


    Sprintf(buf, pat, arg1, arg2, arg3);
    rwin=open("CON:0/10/400/100/Patch Response Window",O_RDWR+O_CREAT);
    if (rwin==0) {
        printf("Error:No window dressing.");
    }
    write(rwin,buf,strlen(buf));
    actual_read=read(rwin,buf,sizeof buf);
    if (actual_read==0)
       buf[0]=0;
    buf[actual_read]=0;
/*    printf("actual_read=%d buf=%s\n",actual_read,buf); */
    Close(rwin);
}
#endif  /* amiga */
#endif  /* lint */


/* How to handle certain events when not in a critical region. */

void
set_signals(reset)
 int reset;
{
    void my_exit();

#ifdef AMIGA
    extern short Enable_Abort;

    Enable_Abort=1;

#else

#ifndef lint
#ifdef VOIDSIG
    static void (*hupval)(),(*intval)();
#else
    static int (*hupval)(),(*intval)();
#endif
    if (!reset) {
      hupval = signal(SIGHUP, SIG_IGN);
      if (hupval != SIG_IGN)
#ifdef VOIDSIG
          hupval = my_exit;
#else
          hupval = (int(*)())my_exit;
#endif
      intval = signal(SIGINT, SIG_IGN);
      if (intval != SIG_IGN)
#ifdef VOIDSIG
          intval = my_exit;
#else
          intval = (int(*)())my_exit;
#endif
    }
    Signal(SIGHUP, hupval);
    Signal(SIGINT, intval);
#endif

#endif
}

/* How to handle certain events when in a critical region. */

void
ignore_signals()
{
#ifdef AMIGA
  extern short Enable_Abort;

  Enable_Abort=0;
#else

#ifndef lint
    Signal(SIGHUP, SIG_IGN);
    Signal(SIGINT, SIG_IGN);
#endif
#endif

}

#ifdef AMIGA

extern int stat();

/*
  This is more space-efficient than Larry's, but has the
  disadvantage of groking with the other person's copy of the
  string. Restores it before the end, but that's still nasty.
*/

void
makedirs(filename,striplast)
  char *filename;
  bool striplast;
{
    register char *walkptr=filename;
    register char temp;
    extern char *index();
    register struct FileLock *tmplock;
    register int result;

    extern struct FileLock *CreateDir();

    struct stat *buf;  /* for the stat() command. */

/* first the easy part: */
    buf = (struct stat *) malloc(sizeof(struct stat));
    if (walkptr[0]=='.' && walkptr[1]=='/')
        walkptr+=2;
    if (walkptr[0]=='/')  /* starting at root: */
       walkptr[0]=':';    /* AmigaDOS root. */

/* loop: */

    for(;;) {
        walkptr=index(walkptr,'/'); /* see if there's subdirectories. */
        if (walkptr) {
            temp=*walkptr;
            *walkptr++=0;               /* filename now points to dirname. */
        } else if (striplast)  /* top-of-loop exit, and... */
            break;
        result = stat(filename,buf);

/* If we can't create the directory, we'll sit here spinning till
  the end of the filename. On the other hand, it don't really matter
  what we do here. They won't be able to open the final file name upstairs,
  so they'll say "can't create file" up there. Phew.
*/
        if (result==-1) {   /* make a directory... */
            tmplock=CreateDir(filename);
            UnLock(tmplock);
        }
        if (walkptr)
           *(walkptr-1)=temp; /* restore what we did to his filename. */
        else
            break;   /* bottom of loop exit. */
    }
    free(buf);
    return;
}

#else

/* Make sure we'll have the directories to create a file. */

void
makedirs(filename,striplast)
Reg1 char *filename;
bool striplast;
{
    char tmpbuf[256];
    Reg2 char *s = tmpbuf;
    char *dirv[20];
    Reg3 int i;
    Reg4 int dirvp = 0;

    while (*filename) {
        if (*filename == '/') {
            filename++;
            dirv[dirvp++] = s;
            *s++ = '\0';
        }
        else {
            *s++ = *filename++;
        }
    }
    *s = '\0';
    dirv[dirvp] = s;
    if (striplast)
        dirvp--;
    if (dirvp < 0)
        return;
    strcpy(buf, "mkdir");
    s = buf;
    for (i=0; i<=dirvp; i++) {
        while (*s) s++;
        *s++ = ' ';
        strcpy(s, tmpbuf);
        *dirv[i] = '/';
    }
    system(buf);

}

#endif


#ifdef AMIGA

/* chmod to satisfy us: */

void chmod() {}

/* abort to satisfy us: */

void abort() { exit(1); }

#endif

/* Make filenames more reasonable. */

char *
fetchname(at,strip_leading,assume_exists)
char *at;
int strip_leading;
int assume_exists;
{
    char *s;
    char *name;
    Reg1 char *t;
    char tmpbuf[200];

    if (!at)
        return Nullch;
    s = savestr(at);
    for (t=s; isspace(*t); t++) ;
    name = t;
#ifdef DEBUGGING
    if (debug & 128)
        say4("fetchname %s %d %d\n",name,strip_leading,assume_exists);
#endif
    if (strnEQ(name, "/dev/null", 9))   /* so files can be created by diffing */
        return Nullch;                  /*   against /dev/null. */
    for (; *t && !isspace(*t); t++)
        if (*t == '/')
            if (--strip_leading >= 0)
                name = t+1;
    *t = '\0';
    if (name != s && *s != '/') {
        name[-1] = '\0';
        if (stat(s, &filestat) && filestat.st_mode & S_IFDIR) {
            name[-1] = '/';
            name=s;
        }
    }
    name = savestr(name);
    Sprintf(tmpbuf, "RCS/%s", name);
    free(s);
    if (stat(name, &filestat) < 0 && !assume_exists) {
#ifdef AMIGA
        name = Nullch;   /* Amiga doesn't have RCS. */
#else
        Strcat(tmpbuf, RCSSUFFIX);
        if (stat(tmpbuf, &filestat) < 0 && stat(tmpbuf+4, &filestat) < 0) {
            Sprintf(tmpbuf, "SCCS/%s%s", SCCSPREFIX, name);
            if (stat(tmpbuf, &filestat) < 0 && stat(tmpbuf+5, &filestat) < 0) {
                free(name);
                name = Nullch;
            }
        }
#endif
    }
    return name;
}
