/* $Id: mt-misc.c,v 4.4.3.1 1991/11/22 04:12:15 davison Trn $
**
** $Log: mt-misc.c,v $
** Revision 4.4.3.1  1991/11/22  04:12:15  davison
** Trn Release 2.0
** 
*/

#include "EXTERN.h"
#include "common.h"
#include "threads.h"
#include "mthreads.h"

char *lib, *rnlib, *mtlib, *spool, *threaddir, *homedir;
int locked = 0, cron_locking = 0;

void
mt_init()
{
    /* Set up a nice friendly umask. */
    umask(002);

    /* Init the directory strings, possibly translating them into real paths */
    homedir = getenv("HOME");
    if (homedir == Nullch) {
	homedir = getenv("LOGDIR");
    }
    spool = savestr(file_exp(SPOOL));
    lib = savestr(file_exp(LIB));
    rnlib = savestr(file_exp(RNLIB));
    mtlib = savestr(file_exp(MTLIB));
    threaddir = savestr(file_exp(THREAD_DIR));
}

/* Make sure we're not already running by creating a lock file. */
long
mt_lock(which_lock, sig)
int which_lock;
int sig;
{
    char buff[LBUFLEN], *filename;
    FILE *fp;

    sprintf(buff, "%s.%ld", file_exp(MTPRELOCK), (long)getpid());
    if ((fp = fopen(buff, "w")) == Nullfp) {
	log_entry("Unable to create lock temporary `%s'.\n", buff);
	wrap_it_up(1);
    }
    fprintf(fp, "%s%ld\n", which_lock == DAEMON_LOCK ? "pid " : nullstr,
	(long)getpid());
    fclose(fp);

    /* Try to link to lock file. */
    if (which_lock == DAEMON_LOCK) {
	filename = file_exp(MTDLOCK);
    } else {
	filename = file_exp(MTLOCK);
    }
  dolink:
    while (link(buff, filename) < 0) {
      long otherpid;
	if ((fp = fopen(filename, "r")) == Nullfp) {
	    log_entry("unable to open %s\n", filename);
	    if (cron_locking) {
		goto Sleep;
	    }
	    unlink(buff);
	    wrap_it_up(1);
	}
	if (fscanf(fp, "%ld", &otherpid) != 1) { 
	    log_entry("unable to read pid from %s\n", filename);
	    fclose(fp);
	    if (cron_locking) {
		goto Sleep;
	    }
	    unlink(buff);
	    wrap_it_up(1);
	}
	fclose(fp);
	if (kill(otherpid, sig) == -1 && errno == ESRCH) {
	    if (unlink(filename) == -1) {
		log_entry("unable to unlink lockfile %s\n", filename);
		unlink(buff);
		wrap_it_up(1);
	    }
	    goto dolink;
	}
	if (cron_locking) {
	  Sleep:
	    sleep(60);
	    continue;
	}
	unlink(buff);
	return otherpid;
    }
    unlink(buff);			/* remove temporary LOCK.<pid> file */
    locked |= which_lock;
    return 0;				/* return success */
}

void
mt_unlock(which_lock)
int which_lock;
{
    which_lock &= locked;
    if (which_lock & PASS_LOCK) {
	unlink(file_exp(MTLOCK));		/* remove single-pass lock */
    }
    if (which_lock & DAEMON_LOCK) {
	unlink(file_exp(MTDLOCK));		/* remove daemon lock */
    }
    locked &= ~which_lock;
}

/* Interpret rn's %x prefixes and ~name expansions without including tons
** of useless source.  NOTE:  names that don't start with '/', '%' or '~'
** are prefixed with the SPOOL directory.  (Note that ~'s don't work yet.)
*/
char *
file_exp(name)
char *name;
{
    static char namebuf[MAXFILENAME];

    if (*name == '/') {	/* fully qualified names are left alone */
	return name;
    }
    switch (name[0]) {
    case '%':			/* interpret certain %x values */
	switch (name[1]) {
	case 'P':
	    strcpy(namebuf, spool);
	    break;
	case 'w':
	    strcpy(namebuf, mtlib);
	    break;
	case 'W':
	    strcpy(namebuf, threaddir);
	    break;
	case 'x':
	    strcpy(namebuf, lib);
	    break;
	case 'X':
	    strcpy(namebuf, rnlib);
	    break;
	default:
	    log_entry("Unknown expansion: %s\n", name);
	    wrap_it_up(1);
	}
	strcat(namebuf, name+2);
	break;
    case '~':
    {
	char *s = name + 1;

	if (!*s || *s == '/') {
	    sprintf(namebuf, "%s%s", homedir, s);
	} else {
	    log_entry("~name expansions not implemented.");
	    wrap_it_up(1);
	}
    }
    default:			/* all "normal" names are relative to SPOOL */
	sprintf(namebuf, "%s/%s", spool, name);
	break;
    }
    return namebuf;
}

#ifndef lint
/* A malloc that bombs-out when memory is exhausted. */
char *
safemalloc(amount)
MEM_SIZE amount;
{
    register char *cp;
    char *malloc();

    if ((cp = malloc(amount)) == Nullch) {
	log_error("malloc(%ld) failed.\n", (long)amount);
	wrap_it_up(1);
    }
    return cp;
}

/* paranoid version of realloc */
char *
saferealloc(where,size)
char *where;
MEM_SIZE size;
{
    char *ptr;
    char *realloc();

    ptr = realloc(where,size?size:1);	/* realloc(0) is NASTY on our system */
    if (ptr == Nullch) {
	log_error("realloc(..., %ld) failed.\n", (long)size);
	wrap_it_up(1);
    }
    return ptr;
}
#endif

/* Create a malloc'ed copy of a string. */
char *
savestr(str)
char *str;
{
    register MEM_SIZE len = strlen(str) + 1;
    register char *newaddr = safemalloc(len);

    bcopy(str, newaddr, (int)len);

    return newaddr;
}

#ifndef lint
/* Free some memory if it hasn't already been freed. */
void
safefree(pp)
char **pp;
{
    if (*pp) {
	free(*pp);
	*pp = Nullch;
    }
}
#endif
