
/*
 *  LOCKFILE.C
 *
 *  (C) Copyright 1989-1990 by Matthew Dillon,  All Rights Reserved.
 *
 *  Lock and unlock a file.  Under AmigaDOS, openning a file mode 1006
 *  (accomplished with fopen(,"w"), locks the file exclusively.  That
 *  is, further fopen()s will fail.  Thus, we need only keep a live
 *  file descriptor to 'lock' the file.
 *
 *  This is advantagious because if the program exits without removing
 *  the lock file we are still ok... the file is unlocked by virtue of
 *  the file descriptor getting closed.
 *
 *  WARNING:	cannot call MakeConfigPath() since static buffer will
 *		be overwritten and caller might expect it not to be
 */

#include <exec/types.h>
#include <exec/lists.h>
#include <libraries/dos.h>
#include <stdio.h>
#include <stdlib.h>
#include "config.h"

typedef struct List LIST;
typedef struct Node NODE;

typedef struct {
    NODE    Node;
    FILE    *Fi;
    short   Refs;
} LNode;

Prototype void LockFile(const char *);
Prototype void UnLockFile(const char *);
Prototype void UnLockFiles(void);
Prototype int FileIsLocked(const char *);
Local void FreeLockNode(LNode *);

LIST LockList = { (NODE *)&LockList.lh_Tail, NULL, (NODE *)&LockList.lh_Head };
static char Buf[512];

void
LockFile(file)
const char *file;
{
    const char *ptr;
    char *lockDir = GetConfigDir(LOCKDIR);
    short lockLen = strlen(lockDir);

    LNode *node;
    LNode *n;

    for (ptr = file + strlen(file); ptr >= file && *ptr != '/' && *ptr != ':'; --ptr);
    ++ptr;

    if (node = malloc(sizeof(LNode) + lockLen + 16 + strlen(ptr))) {
	node->Node.ln_Name = (char *)(node + 1);

	strcpy(node->Node.ln_Name, MakeConfigPathBuf(Buf, LOCKDIR, ptr));
	strcat(node->Node.ln_Name, ".LOCK");

	for (n = (LNode *)LockList.lh_Head; n != (LNode *)&LockList.lh_Tail; n = (LNode *)n->Node.ln_Succ) {
	    if (strcmp(node->Node.ln_Name, n->Node.ln_Name) == 0) {
		++n->Refs;
		free(node);
		return;
	    }
	}

	while ((node->Fi = fopen(node->Node.ln_Name, "w")) == NULL) {
	    sleep(2);
	    chkabort();
	}
	node->Refs = 1;
	AddTail(&LockList, &node->Node);
    }
}

/*
 *  Check to see whether a file is locked.  We could try to fopen the
 *  file for 'w', but this causes unnecesary filesystem activity
 */

int
FileIsLocked(file)
const char *file;
{
    const char *ptr;
    FILE *fi;
    char buf[128];
    long lock;

    for (ptr = file + strlen(file); ptr >= file && *ptr != '/' && *ptr != ':'; --ptr);
    ++ptr;
    sprintf(buf, "%s.LOCK", MakeConfigPathBuf(Buf, LOCKDIR, ptr));

    if (lock = Lock(buf, EXCLUSIVE_LOCK)) {
	UnLock(lock);
	return(0);
    }
    if (IoErr() == ERROR_OBJECT_IN_USE)
	return(1);
    return(0);
}

void
UnLockFile(file)
const char *file;
{
    LNode *node;
    short len;
    const char *ptr;

    for (ptr = file + strlen(file); ptr >= file && *ptr != '/' && *ptr != ':'; --ptr);
    ++ptr;

    MakeConfigPathBuf(Buf, LOCKDIR, ptr);
    strcat(Buf, ".LOCK");

    for (node = (LNode *)LockList.lh_Head; node != (LNode *)&LockList.lh_Tail; node = (LNode *)node->Node.ln_Succ) {
	if (stricmp(Buf, node->Node.ln_Name) == 0) {
	    if (--node->Refs == 0)
		FreeLockNode(node);
	    break;
	}
    }
}

void
UnLockFiles()
{
    LNode *node;

    while ((node = (LNode *)LockList.lh_Head) != (LNode *)&LockList.lh_Tail)
	FreeLockNode(node);
}

static void
FreeLockNode(node)
LNode *node;
{
    Remove(node);
    fclose(node->Fi);
    unlink(node->Node.ln_Name);
    free(node);
}

