/* $Id: mt-misc.c,v 2.3 1992/12/14 00:14:02 davison Trn $
*/

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

char *newslib, *privlib, *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(NEWSSPOOL));
    newslib = savestr(file_exp(NEWSLIB));
    privlib = savestr(file_exp(PRIVLIB));
    threaddir = file_exp(THREAD_DIR);
    if (strEQ(threaddir,spool))
	threaddir = spool;
    else
	threaddir = savestr(threaddir);
}

/* 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, threaddir);
	    break;
	case 'x':
	    strcpy(namebuf, newslib);
	    break;
	case 'X':
	    strcpy(namebuf, privlib);
	    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);
	}
	break;
    }
    default:			/* all "normal" names are relative to SPOOL */
	sprintf(namebuf, "%s/%s", spool, name);
	break;
    }
    return namebuf;
}

/* Change a newsgroup name into the name of the thread data file.  We
** subsitute any '.'s in the group name into '/'s (unless LONG_THREAD_NAMES
** is defined), prepend the path, and append the '/.thread' (or '.th') on to
** the end.
*/
char *
thread_name(group)
char *group;
{
    static char name_buf[MAXFILENAME];
#ifdef LONG_THREAD_NAMES
    sprintf(name_buf, "%s/%s", threaddir, group);
#else
    register char *cp;

    cp = strcpy(name_buf, threaddir) + strlen(threaddir);
    *cp++ = '/';
    strcpy(cp, group);
    while ((cp = index(cp, '.')))
	*cp = '/';
#endif
    if (threaddir == spool)
	strcat(name_buf, "/.thread");
    else
	strcat(name_buf, ".th");
    return name_buf;
}

#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

/* Determine this machine's byte map for WORDs and LONGs.  A byte map is an
** array of BYTEs (sizeof (WORD) or sizeof (LONG) of them) with the 0th BYTE
** being the byte number of the high-order byte in my <type>, and so forth.
*/
void
mybytemap(map)
BMAP *map;
{
    union {
	BYTE b[sizeof (LONG)];
	WORD w;
	LONG l;
    } u;
    register BYTE *mp;
    register int i, j;

    mp = &map->w[sizeof (WORD)];
    u.w = 1;
    for (i = sizeof (WORD); i > 0; i--) {
	for (j = 0; j < sizeof (WORD); j++) {
	    if (u.b[j] != 0) {
		break;
	    }
	}
	if (j == sizeof (WORD)) {
	    goto bad_news;
	}
	*--mp = j;
	while (u.b[j] != 0 && u.w) {
	    u.w <<= 1;
	}
    }

    mp = &map->l[sizeof (LONG)];
    u.l = 1;
    for (i = sizeof (LONG); i > 0; i--) {
	for (j = 0; j < sizeof (LONG); j++) {
	    if (u.b[j] != 0) {
		break;
	    }
	}
	if (j == sizeof (LONG)) {
	  bad_news:
	    /* trouble -- set both to *something* consistent */
	    for (j = 0; j < sizeof (WORD); j++) {
		map->w[j] = j;
	    }
	    for (j = 0; j < sizeof (LONG); j++) {
		map->l[j] = j;
	    }
	    return;
	}
	*--mp = j;
	while (u.b[j] != 0 && u.l) {
	    u.l <<= 1;
	}
    }
}

/* Transform each WORD's byte-ordering in a buffer of the designated length.
*/
void
wp_bmap(buf, len)
WORD *buf;
int len;
{
    union {
	BYTE b[sizeof (WORD)];
	WORD w;
    } in, out;
    register int i;

    if (word_same) {
	return;
    }
    while (len--) {
	in.w = *buf;
	for (i = 0; i < sizeof (WORD); i++) {
	    out.b[my_bmap.w[i]] = in.b[mt_bmap.w[i]];
	}
	*buf++ = out.w;
    }
}

/* Transform each LONG's byte-ordering in a buffer of the designated length.
*/
void
lp_bmap(buf, len)
LONG *buf;
int len;
{
    union {
	BYTE b[sizeof (LONG)];
	LONG l;
    } in, out;
    register int i;

    if (long_same) {
	return;
    }
    while (len--) {
	in.l = *buf;
	for (i = 0; i < sizeof (LONG); i++) {
	    out.b[my_bmap.l[i]] = in.b[mt_bmap.l[i]];
	}
	*buf++ = out.l;
    }
}
