/*ident	"@(#)cls4:tools/pt/ptutil.c	1.4" */
#include <stdio.h>
#include <ctype.h>
#include "pt.h"

/************************* UTILITIES *************************/

extern char* tdir;
extern int verbose;
extern long start_time;
extern char* PROGNAME;

/* fatal errors */
void fatal(msg, arg1, arg2)
char* msg;
char* arg1;
char* arg2;
{
	char buf[MAXLINE];

	sprintf(buf, msg, arg1, arg2);
	fprintf(stderr, "%s fatal error: %s\n", PROGNAME, buf);

	exit(1);
}

static long scnt = 0;
/* get space */
char* gs(s)
int s;
{
	char* p;

	if (s < 1)
		fatal("bad argument to gs()", 0, 0);

	if ((p = malloc(s)) == NULL)
		fatal("malloc() out of space", 0, 0);

	scnt++;

	return p;
}

/* free space */
void fs(p)
char* p;
{
	if (p == NULL)
		fatal("bad argument to fs()", 0, 0);
	if (scnt < 1)
		fatal("too many calls of fs()", 0, 0);

	free(p);

	scnt--;
}

/* copy a string */
char* copy(s)
char* s;
{
	char* p;

	if (s == NULL || !*s)
		fatal("bad argument to copy()", 0, 0);

	p = gs(strlen(s) + 1);

	strcpy(p, s);

	return p;
}

/* basename of a file */
char* basename(f, first)
char* f;
int first;
{
	char* s;

	if (f == NULL || !*f || first < 0 || first > 1)
		fatal("bad argument to basename()", 0, 0);

	s = f + strlen(f) - 1;
	while (s > f && *s != '/') {

		/* maybe clip extension */

		if (*s == '.' && first) {
			*s = 0;
			if (s > f && s[-1] == '.') {
				s[-1] = 0;
				s--;
			}
			first = 0;
		}
		s--;
	}
	if (s[0] == '/' && s[1])
		return s + 1;
	else
		return f;
}

/* replace one file extension with another */
void replace_ext(f, ext)
char* f;
char* ext;
{
	char* s;
	char nwext[25];
	char* t;
	int i;

	if (f == NULL || !*f || ext == NULL || !*ext)
		fatal("bad argument to replace_ext()", 0, 0);

	s = f + strlen(f) - 1;
	while (s > f && *s != '.')
		s--;
	if (s == f)
		return;

	/* preserve case when replacing extension */

	strcpy(nwext, ext);
	t = s;
	i = 0;
	while (*t && nwext[i]) {
		if (isupper(*t) && islower(nwext[i]))
			nwext[i] = toupper(nwext[i]);
		else if (islower(*t) && isupper(nwext[i]))
			nwext[i] = tolower(nwext[i]);
		i++;
		t++;
	}
	strcpy(s, nwext);
}

/************************* FILE MANIPULATION *************************/

#include <sys/types.h>
#include <sys/stat.h>

/* copy a file */
int cp(old, new)
char* old;
char* new;
{
	FILE* fpin;
	FILE* fpout;
	int c;

	if (old == NULL || !*old || new == NULL || !*new)
		fatal("bad argument to cp()", 0, 0);

	if ((fpin = fopen(old, "r")) == NULL)
		return -1;
	if ((fpout = fopen(new, "w")) == NULL) {
		fclose(fpin);
		return -1;
	}

	/* actual copy - needs to work on binary files */

	for (;;) {
		c = getc(fpin);
		if (feof(fpin))
			break;
		putc(c, fpout);
	}
	fclose(fpin);
	fclose(fpout);

	return 0;
}

/* move a file */
int mv(old, new, d1, d2)
char* old;
char* new;
char* d1;
char* d2;
{
	struct stat sb1;
	struct stat sb2;
	int flag;

	if (old == NULL || !*old || new == NULL || !*new ||
	    d1 == NULL || !*d1 || d2 == NULL || !*d2)
		fatal("bad argument to mv()", 0, 0);

	unlink(new);

	/* can we use links? */

	flag = 0;
	if (!strcmp(d1, d2))
		flag = 1;
	if (!flag) {
		if (stat(d1, &sb1) < 0 || stat(d2, &sb2) < 0)
			return -1;
		flag = (sb1.st_dev == sb2.st_dev);
	}

	/* links */

	if (flag) {
		if (link(old, new) < 0 || unlink(old) < 0)
			return -1;
	}

	/* actual copy */

	else {
		if (cp(old, new) < 0)
			return -1;
		unlink(old);
	}

	return 0;
}

/* compare two files to see if they are the same physical file */
int icmp(f1, f2)
char* f1;
char* f2;
{
	struct stat sb1;
	struct stat sb2;

	if (f1 == NULL || !*f1 || f2 == NULL || !*f2)
		fatal("bad argument to icmp()", 0, 0);

	if (stat(f1, &sb1) < 0 || stat(f2, &sb2) < 0)
		return 0;
	return sb1.st_dev == sb2.st_dev && sb1.st_ino == sb2.st_ino;
}

/* compare two files for equality */
int fcmp(f1, f2)
char* f1;
char* f2;
{
	FILE* fp1;
	FILE* fp2;
	int c1;
	int c2;

	if (f1 == NULL || !*f1 || f2 == NULL || !*f2)
		fatal("bad argument to fcmp()", 0, 0);

	if ((fp1 = fopen(f1, "r")) == NULL)
		return 0;
	if ((fp2 = fopen(f2, "r")) == NULL) {
		fclose(fp1);
		return 0;
	}
	for (;;) {
		c1 = getc(fp1);
		c2 = getc(fp2);
		if (c1 != c2 || c1 == EOF)
			break;
	}

	fclose(fp1);
	fclose(fp2);

	return c1 == c2;
}

/* delete a file */
void del_file(f)
char* f;
{
	if (f == NULL || !*f)
		fatal("bad argument to del_file()", 0, 0);

	unlink(f);
}

/* get a modification timestamp for a file;  0 if file does not exist */
unsigned long timestamp(s)
char* s;
{
	struct stat sb;

	if (s == NULL || !*s)
		fatal("bad argument to timestamp()", 0, 0);

	if (stat(s, &sb) < 0)
		return 0;
	return sb.st_mtime;	
}

/* set the umask for file creation */
void set_umask(r)
char* r;
{
	struct stat sb;

	if (r == NULL || !*r)
		fatal("bad argument to set_umask()", 0, 0);
	if (stat(r, &sb) < 0)
		fatal("could not stat %s", r, 0);

	umask(~sb.st_mode & 0777);
}

/* set repository permissions to those of the parent directory */
void set_rep_perms(r)
char* r;
{
	struct stat sb;
#if sparc || i386 || hpux
	uid_t getuid();
#endif
	if (r == NULL || !*r)
		fatal("bad argument to set_rep_perms()", 0, 0);

	/* change the mode */

	umask(0);
	if (stat(".", &sb) < 0)
		fatal("could not stat %s", ".", 0);
	if (chmod(r, sb.st_mode & 0777) < 0)
		fatal("could not chmod %s", r, 0);

	/* change the group - no error if doesn't work */

	chown(r, getuid(), sb.st_gid);
}

/* check whether a file exists */
int facc(f)
char* f;
{
	struct stat sb;

	if (f == NULL || !*f)
		fatal("bad argument to facc()", 0, 0);

	return stat(f, &sb) == 0;
}

/* hash a string */
unsigned long hash(s, n)
char* s;
long n;
{
	unsigned long h;

	if (s == NULL || !*s || n < 1)
		fatal("bad argument to hash()", 0, 0);

	h = 0;
	while (*s)
		h = h * 271 + *s++;
	return h % n;
}

/* give progress messages */
void progress(s, a1, a2)
char* s;
char* a1;
char* a2;
{
	char buf[MAXLINE];

	if (s == NULL || !*s)
		fatal("bad argument to progress()", 0, 0);
	if (!verbose)
		return;

	sprintf(buf, s, a1, a2);

	fprintf(stderr, "%s %s [%d] ...\n", PROGNAME, buf, time(0) - start_time);
	start_time = time(0);
}
void progress2()
{
	if (verbose)
		fprintf(stderr, "\n");
}

/************************* SIGNALS *************************/

#ifdef NO_SIGNAL
void signal_block()
{
}
void signal_unblock()
{
}
#else
typedef void (*T)();
static T old_int;
static T old_quit;
static int sig_flag = 0;
void signal_block(t)
T t;
{
	if (t == NULL)
		fatal("bad argument to signal_block()", 0, 0);

	if (sig_flag)
		fatal("nested call of signal_block()", 0, 0);
	old_int = (T)signal(SIGINT, t);
	old_quit = (T)signal(SIGQUIT, t);
	sig_flag = 1;
}
void signal_unblock()
{
	if (!sig_flag)
		fatal("signal_unblock() called with no prior block", 0, 0);
	signal(SIGINT, old_int);
	signal(SIGQUIT, old_quit);
	sig_flag = 0;
}
#endif

/************************* LOCKING *************************/

#ifdef LOCK_FCNTL
#undef LOCK_FLOCK
#undef LOCK_LOCKF
#endif

#ifdef LOCK_FLOCK
#undef LOCK_FCNTL
#undef LOCK_LOCKF
#endif

#ifdef LOCK_LOCKF
#undef LOCK_FCNTL
#undef LOCK_FLOCK
#endif

static int lock_cnt = 0;
static int lock_fd = -1;
static char* lock_str = "_lock";
#ifdef NO_LOCK
/* lock a file */
int lock_file(f)
char* f;
{
	return 0;
}
/* unlock a file */
int unlock_file()
{
	return 0;
}
/* lock a repository */
void lock_rep(r)
char* r;
{
}
/* unlock a repository */
void unlock_rep(r)
char* r;
{
}
#else
#ifdef LOCK_FCNTL
#include <unistd.h>
#include <fcntl.h>
#endif
#ifdef LOCK_FLOCK
#include <sys/file.h>
#endif
#ifdef LOCK_LOCKF
#include <unistd.h>
#include <fcntl.h>
#endif
/* lock a file */
int lock_file(f)
char* f;
{
#ifdef LOCK_FCNTL
	struct flock fl;
#endif

	if (lock_fd < 0)
		lock_fd = open(f, O_WRONLY | O_CREAT, 0666);
	if (lock_fd < 0)
		fatal("could not get file descriptor to lock %s - possible permissions problem", f, 0);
#ifdef LOCK_FCNTL
	fl.l_type = F_WRLCK;
	fl.l_whence = SEEK_SET;
	fl.l_start = 0;
	fl.l_len = 0;

	return fcntl(lock_fd, F_SETLK, &fl);
#endif
#ifdef LOCK_FLOCK
	return flock(lock_fd, LOCK_EX | LOCK_NB);
#endif
#ifdef LOCK_LOCKF
	return lockf(lock_fd, F_TLOCK, 0);
#endif
}
/* unlock a file */
int unlock_file()
{
	int ret;

#ifdef LOCK_FCNTL
	struct flock fl;

	fl.l_type = F_UNLCK;
	fl.l_whence = SEEK_SET;
	fl.l_start = 0;
	fl.l_len = 0;

	ret = fcntl(lock_fd, F_SETLK, &fl);
#endif
#ifdef LOCK_FLOCK
	ret= flock(lock_fd, LOCK_UN);
#endif
#ifdef LOCK_LOCKF
	ret = lockf(lock_fd, F_ULOCK, 0);
#endif
	if (ret < 0 || close(lock_fd) < 0) {
		lock_fd = -1;
		return -1;
	}

	lock_fd = -1;
	return 0;
}
/* lock a repository */
void lock_rep(r)
char* r;
{
	char fbuf[MAXPATH];
	int i;

	if (r == NULL || !*r)
		fatal("bad argument to lock_rep()", 0, 0);

	if (lock_cnt++ > 0)
		fatal("repository %s already locked", r, 0);

	sprintf(fbuf, "%s/%s", r, lock_str);

	i = LOCK_MAX;
	while (i-- > 0) {
		if (lock_file(fbuf) >= 0)
			return;
		fprintf(stderr, "%s: waiting to lock repository %s, sleeping ...\n", PROGNAME, r);
		sleep(LOCK_SLEEP);
	}

	fatal("could not get lock on repository %s", r, 0);
}
/* unlock a repository */
void unlock_rep(r)
char* r;
{
	if (r == NULL || !*r)
		fatal("bad argument to unlock_rep()", 0, 0);

	if (lock_cnt-- != 1)
		fatal("unlock_rep() called when not locked", 0, 0);

	if (unlock_file() < 0)
		fatal("cannot unlock repository %s", r, 0);
}
#endif

/************************* SYSTEM AND POPEN *************************/

#ifndef SLOW_SYSTEM
/* fast system() function */
int fast_system(s)
char* s;
{
	int status;
	int pid;
	int w;
	char cbuf[MAXCBUF];
	char* cbufp[MAXCBUF/4];
	int n;
	char* t;
	int fdcnt;
	int fdlist[3][2];
	int fd;
	int i;
	int j;

	if (s == NULL || !*s)
		fatal("bad argument to fast_system()", 0, 0);

	/* split apart input */

	strcpy(cbuf, s);
	t = cbuf;
	n = 0;
	for (;;) {
		while (*t && *t <= ' ')
			t++;
		if (!*t)
			break;
		cbufp[n++] = t;
		while (*t > ' ')
			t++;
		if (*t)
			*t++ = 0;
	}
	cbufp[n] = 0;
	if (n < 2)
		fatal("missing argument to fast_system()", 0, 0);

	/* start up child */

	if ((pid = fork()) == 0) {

		/* handle I/O redirection */

		fdcnt = 0;
		for (i = 0; i < n; i++) {
			if (isdigit(cbufp[i][0]) && cbufp[i][1] == '>') {
				fd = creat(&cbufp[i][2], 0666);
				if (fd < 0)
					return -1;
				fdlist[fdcnt][0] = cbufp[i][0] - '0';
				fdlist[fdcnt][1] = fd;
				fdcnt++;
				cbufp[i] = 0;
			}
		}
		for (i = 0; i <= 2; i++) {
			for (j = 0; j < fdcnt; j++) {
				if (i == fdlist[j][0])
					break;
			}
			if (j < fdcnt) {
				close(i);
				dup(fdlist[j][1]);
				close(fdlist[j][1]);
			}
		}

		/* execute */

		execvp(cbufp[0], cbufp);
		_exit(127);
	}

	/* wait on child */

	while ((w = wait(&status)) != pid && w != -1)
		;
	if (w == -1)
		status = -1;

	return status;
}

/* fast popen() */
FILE* fast_popen(cmd, mode)
char* cmd;
char* mode;
{
	char fbuf[MAXPATH];
	char cmd2[MAXCBUF];

	if (cmd == NULL || !*cmd || mode == NULL || !*mode)
		fatal("bad argument to fast_popen()", 0, 0);

	/* dump into a temporary file */

	sprintf(fbuf, "%s/popen_buf", tdir);
	sprintf(cmd2, "%s 1>%s", cmd, fbuf);
	if (fast_system(cmd2))
		return NULL;

	/* return pointer to that file */

	return fopen(fbuf, "r");
}

/* fast pclose() */
int fast_pclose(fp)
FILE* fp;
{
	if (fp == NULL)
		fatal("bad argument to fast_pclose()", 0, 0);
	return fclose(fp);
}
#endif

/************************* STRING LISTS *************************/

/* add to a string list */
void slp_add(slp, name)
Slist* slp;
char* name;
{
	char** np;
	int i;

	if (slp == NULL || name == NULL || !*name)
		fatal("bad argument to slp_add()", 0, 0);

	/* first time */

	if (slp->n == 0) {
		slp->max = LISTSTART;
		slp->ptrs = (char**)gs(sizeof(char*) * slp->max);
	}

	/* grow list if have to */

	else if (slp->n >= slp->max) {
		np = (char**)gs(sizeof(char*) * slp->max * 2);
		for (i = 0; i < slp->n; i++)
			np[i] = slp->ptrs[i];
		slp->max *= 2;
		fs(slp->ptrs);
		slp->ptrs = np;
	}
	slp->ptrs[slp->n++] = copy(name);
}

/* add to a string list if not already there */
int slp_adduniq(slp, name)
Slist* slp;
char* name;
{
	int i;
	char** p;

	if (slp == NULL || name == NULL || !*name)
		fatal("bad argument to slp_adduniq()", 0, 0);

	for (i = slp->n - 1, p = slp->ptrs + i; i >= 0; i--, p--) {
		if (!STRCMP(*p, name))
			return 0;
	}

	slp_add(slp, name);

	return 1;
}

/* delete a string list */
void slp_del(slp)
Slist* slp;
{
	int i;

	if (slp == NULL)
		fatal("bad argument to slp_del()", 0, 0);
	if (slp->n < 0 || (slp->n > 0 && slp->ptrs == NULL))
		fatal("string list corrupted", 0, 0);

	for (i = 0; i < slp->n; i++)
		fs(slp->ptrs[i]);
	if (slp->n > 0)
		fs(slp->ptrs);
	slp->n = 0;
}

/* sort a list of strings */
void slp_sort(slp)
Slist* slp;
{
	int h;
	int i;
	int j;
	char* m;
	char** ms;

	if (slp == NULL)
		fatal("bad argument to slp_sort()", 0, 0);

	/* sort using a Shell sort */

	h = 1;
	ms = slp->ptrs - 1;
	do
		h = h * 3 + 1;
	while (h <= slp->n);
	do {
		h /= 3;
		for (i = h + 1; i <= slp->n; i++) {
			m = ms[i];
			j = i;
			while (strcmp(ms[j - h], m) > 0) {
				ms[j] = ms[j - h];
				j -= h;
				if (j <= h)
					break;
			}
			ms[j] = m;
		}
	} while (h != 1);
}

/* see if any members of p1 are found in p2 --> p3 */
int slp_eq(p1, p2, p3)
Slist* p1;
Slist* p2;
Slist* p3;
{
	int i;
	int j;

	if (p1 == NULL || p2 == NULL || p3 == NULL)
		fatal("bad argument to slp_eq()", 0, 0);

	for (i = 0; i < p1->n; i++) {
		for (j = 0; j < p2->n; j++) {
			if (!STRCMP(p1->ptrs[i], p2->ptrs[j])) {
				slp_add(p3, p1->ptrs[i]);
				break;
			}
		}
	}

	return p3->n > 0;
}

/* see if p1 is a subset of p2 with members that are not --> p3 */
int slp_subset(p1, p2, p3)
Slist* p1;
Slist* p2;
Slist* p3;
{
	int i;
	int j;

	if (p1 == NULL || p2 == NULL || p3 == NULL)
		fatal("bad argument to slp_subset()", 0, 0);

	for (i = 0; i < p1->n; i++) {
		for (j = 0; j < p2->n; j++) {
			if (!STRCMP(p1->ptrs[i], p2->ptrs[j]))
				break;
		}
		if (j == p2->n)
			slp_add(p3, p1->ptrs[i]);
	}

	return p3->n == 0;
}
