/* vim: set noet ts=4:
 *
 * Copyright (c) 2002-2024 Martin A. Godisch <martin@godisch.de>
 */

#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <X11/xpm.h>

#ifdef SOLARIS
#  include <strings.h>
#  define PID_T "%lu"
#else  /* SOLARIS */
#  include <getopt.h>
#  define PID_T "%u"
#endif /* SOLARIS */

#include "wmgeneral.h"
#include "wmwork.h"
#include "wmwork.xpm"

#define BUT_START (1)
#define BUT_PAUSE (2)
#define BUT_STOP  (3)
#define BUT_PREV  (4)
#define BUT_NEXT  (5)

#define CHAR_SRC_X1(N) ((N - 'A') * 5)
#define CHAR_SRC_Y1    (71)
#define CHAR_SRC_X2(N) ((N - ' ') * 5)
#define CHAR_SRC_Y2    (64)
#define CHAR_SRC_X3(N) ((N - '{') * 5 + 65)
#define CHAR_SRC_Y3    (57)
#define TIMER_SRC_X(N) (N * 5 + 80)
#define TIMER_SRC_Y    (64)

char
	wmwork_mask_bits[64*64],
	*dirName  = NULL,
	*logname  = NULL,
	*lockname = NULL;
struct timeval
	now;
struct timezone
	tz;
struct Project
	*current  = NULL,
	*first    = NULL;
time_t
	sess_time = 0;
int
	path_len  = 0,
	do_exit   = 0;

static void handler(int signo)
{
	do_exit = 1;
}

static void at_exit(void)
{
	write_log();
	write_record();
	if (unlink(lockname) < 0) {
		fprintf(stderr, "%s: cannot unlink '%s'\n", PACKAGE_NAME, lockname);
		fprintf(stderr, "%s: %s\n", PACKAGE_NAME, strerror(errno));
	}
}

int main(int argc, char *argv[])
{
	int n,
		show_days = 0,
		but_stat  = 0,
		microtm   = 0,
		running   = 0,
		force     = 0;
	time_t
		last_time = 0;
	char
		*geometry = NULL,
		*xdisplay = NULL;
	static int signals[] =
		{SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGTERM, SIGUSR1, SIGUSR2, 0};
	XEvent Event;

	assert(sizeof(char) == 1);

	if (getenv("HOME") == NULL || *getenv("HOME") == 0) {
		fprintf(stderr, "%s: unknown $HOME directory, please check your environment\n", PACKAGE_NAME);
		return -1;
	}

	umask(077);
	do_opts(argc, argv, &show_days, &xdisplay, &geometry, &force);

	path_len = strlen(getenv("HOME")) + strlen("/.wmwork/worklog") + 1;
	if ((dirName = malloc(path_len)) == NULL || (logname = malloc(path_len)) == NULL || (lockname = malloc(path_len)) == NULL) {
		fprintf(stderr, "%s: cannot allocate memory for path variable\n", PACKAGE_NAME);
		fprintf(stderr, "%s: %s\n", PACKAGE_NAME, strerror(errno));
		exit(1);
	}
	snprintf(dirName,  path_len, "%s/.wmwork", getenv("HOME"));
	snprintf(logname,  path_len, "%s/worklog", dirName);
	snprintf(lockname, path_len, "%s/.#LOCK",  dirName);
	if (chdir(dirName) < 0) {
		if (errno == ENOENT) {
			if (mkdir(dirName, 0777)) {
				fprintf(stderr, "%s: cannot mkdir '%s'\n", PACKAGE_NAME, dirName);
				fprintf(stderr, "%s: %s\n", PACKAGE_NAME, strerror(errno));
				exit(1);
			}
			compat();
		} else {
			fprintf(stderr, "%s: cannot chdir into '%s'\n", PACKAGE_NAME, dirName);
			fprintf(stderr, "%s: %s\n", PACKAGE_NAME, strerror(errno));
			exit(1);
		}
	}

	for (n = 0; signals[n]; n++) {
		if (signal(signals[n], handler) == SIG_ERR) {
			fprintf(stderr, "%s: cannot set handler for signal %d\n", PACKAGE_NAME, signals[n]);
			fprintf(stderr, "%s: %s\n", PACKAGE_NAME, strerror(errno));
		}
	}

	make_lock(force);
	atexit(at_exit);
	read_log();
	current = first;

	initXwindow(xdisplay);
	createXBMfromXPM(wmwork_mask_bits, wmwork_master_xpm, 64, 64);
	openXwindow(argc, argv, wmwork_master_xpm, wmwork_mask_bits, 64, 64, geometry, NULL);
	AddMouseRegion(BUT_START,  5, 48, 22, 58);
	AddMouseRegion(BUT_PAUSE, 23, 48, 40, 58);
	AddMouseRegion(BUT_STOP,  41, 48, 58, 58);
	AddMouseRegion(BUT_PREV,   5, 33, 16, 43);
	AddMouseRegion(BUT_NEXT,  47, 33, 58, 43);
	drawTime(current->time, sess_time, microtm, show_days, running);
	drawProject(current->name);

	while (1) {
		last_time = now.tv_sec;
		gettimeofday(&now, &tz);
		if (running) {
			current->time += now.tv_sec - last_time;
			sess_time     += now.tv_sec - last_time;
			microtm        = now.tv_usec;
			drawTime(current->time, sess_time, microtm, show_days, running);
			RedrawWindow();
		}
		while (XPending(display)) {
			XNextEvent(display, &Event);
			switch (Event.type) {
			case Expose:
				RedrawWindow();
				break;
			case DestroyNotify:
				XCloseDisplay(display);
				exit(0);
			case ButtonPress:
				n = CheckMouseRegion(Event.xbutton.x, Event.xbutton.y);
				switch (n) {
				case BUT_START:
				case BUT_PAUSE:
				case BUT_STOP:
				case BUT_PREV:
				case BUT_NEXT:
					ButtonDown(n);
					break;
				}
				but_stat = n;
				RedrawWindow();
				break;
			case ButtonRelease:
				n = CheckMouseRegion(Event.xbutton.x, Event.xbutton.y);
				switch (but_stat) {
				case BUT_START:
				case BUT_PAUSE:
				case BUT_STOP:
				case BUT_PREV:
				case BUT_NEXT:
					ButtonUp(but_stat);
					break;
				}
				if (but_stat && n == but_stat) {
					switch (but_stat) {
					case BUT_START:
						running = 1;
						break;
					case BUT_PAUSE:
						running = 0;
						break;
					case BUT_STOP:
						write_log();
						write_record();
						running   = 0;
						sess_time = 0;
						break;
					case BUT_PREV:
						if (!running && sess_time == 0)
							current = current->prev;
						break;
					case BUT_NEXT:
						if (!running && sess_time == 0)
							current = current->next;
						break;
					}
					drawTime(current->time, sess_time, microtm, show_days, running);
					drawProject(current->name);
				}
				RedrawWindow();
				but_stat = 0;
				break;
			}
		}
		usleep(50000L);
		if (do_exit)
			exit(0);
	}
}

void ButtonDown(int button)
{
	switch (button) {
	case BUT_START:
		copyXPMArea( 65, 25, 18, 11,  5, 48);
		break;
	case BUT_PAUSE:
		copyXPMArea( 84, 25, 18, 11, 23, 48);
		break;
	case BUT_STOP:
		copyXPMArea(103, 25, 18, 11, 41, 48);
		break;
	case BUT_PREV:
		copyXPMArea(122, 25, 12, 11,  5, 33);
		break;
	case BUT_NEXT:
		copyXPMArea(135, 25, 12, 11, 47, 33);
		break;
	}
}

void ButtonUp(int button)
{
	switch (button) {
	case BUT_START:
		copyXPMArea( 65, 13, 18, 11,  5, 48);
		break;
	case BUT_PAUSE:
		copyXPMArea( 84, 13, 18, 11, 23, 48);
		break;
	case BUT_STOP:
		copyXPMArea(103, 13, 18, 11, 41, 48);
		break;
	case BUT_PREV:
		copyXPMArea(122, 13, 12, 11,  5, 33);
		break;
	case BUT_NEXT:
		copyXPMArea(135, 13, 12, 11, 47, 33);
		break;
	}
}

void ButtonEnable(int button)
{
	return ButtonUp(button);
}

void ButtonDisable(int button)
{
	switch (button) {
	case BUT_START:
		copyXPMArea( 65,  1, 18, 11,  5, 48);
		break;
	case BUT_PAUSE:
		copyXPMArea( 84,  1, 18, 11, 23, 48);
		break;
	case BUT_STOP:
		copyXPMArea(103,  1, 18, 11, 41, 48);
		break;
	case BUT_PREV:
		copyXPMArea(122,  1, 12, 11,  5, 33);
		break;
	case BUT_NEXT:
		copyXPMArea(135,  1, 12, 11, 47, 33);
		break;
	}
}

void do_opts(int argc, char *argv[], int *show_days, char **xdisplay, char **geometry, int *force)
{
	static struct option long_opts[] = {
		{"days",     0, NULL, 'd'},
		{"force",    0, NULL, 'f'},
		{"help",     0, NULL, 'h'},
		{"version",  0, NULL, 'v'},
		{"display",  1, NULL,  0 },
		{"geometry", 1, NULL,  1 },
		{NULL,       0, NULL,  0}};
	int i, opt_index = 0;

#ifdef SOLARIS
	for (i = 1; i < argc; i++) {
		if (*argv[i] == '-') {
			if (argv[i][1] == 'd')
				*show_days = 1;
			else if (argv[i][1] == 'f')
				*force = 1;
			else if (argv[i][1] == 'h') {
#else /* SOLARIS */
	while (1) {
		if ((i = getopt_long(argc, argv, "dfhv", long_opts, &opt_index)) == -1)
			break;
		switch (i) {
		case 'd':
			*show_days = 1;
			break;
		case 'f':
			*force = 1;
			break;
		case 'h':
#endif /* SOLARIS */
			printf("usage: %s [options]\n", argv[0]);
			printf("  -d, --days        display time in ddd.hh:mm instead of hhh:mm:ss,\n");
			printf("  -f, --force       overwrite stale lock files,\n");
			printf("  -h, --help        display this command line summary,\n");
			printf("  -v, --version     display the version number,\n");
			printf("  --display=<id>    open the mini window on display <id>, e.g. ':0.0',\n");
			printf("  --geometry=<pos>  open the mini window at position <pos>, e.g. '+10+10'.\n");
			exit(0);
#ifdef SOLARIS
			} else if (argv[i][1] == 'v') {
#else /* SOLARIS */
		case 'v':
#endif /* SOLARIS */
			printf("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION);
			printf("copyright (c) 2002-2024 Martin A. Godisch <martin@godisch.de>\n");
			exit(0);
#ifdef SOLARIS
			} else if (!strcmp(argv[i], "--display"))
				*xdisplay = optarg;
			else if (!strcmp(argv[i], "--geometry"))
				*geometry = optarg;
			else break;
		} else
			break;
	}
	if (i < argc) {
		fprintf(stderr, "%s: invalid argument -- %s\n", PACKAGE_NAME, argv[i]);
		exit(1);
	}
#else /* SOLARIS */
		case 0:
			*xdisplay = optarg;
			break;
		case 1:
			*geometry = optarg;
			break;
		case '?':
			exit(1);
		}
	}
	if (optind < argc) {
		fprintf(stderr, "%s: invalid argument -- %s\n", PACKAGE_NAME, argv[optind]);
		exit(1);
	}
#endif /* SOLARIS */
}

void drawTime(time_t time1, time_t time2, int microtm, int show_days, const int running)
{
	time_t d1 = 0, d2 = 0, h1 = 0, h2 = 0;
	short  m1 = 0, m2 = 0, s1 = 0, s2 = 0;

	if (time1 >= 3600000 || time2 >= 3600000)
		show_days = 1;

	if (show_days) {
		d1 = time1 / 86400;
		d2 = time2 / 86400;
		time1 %=     86400;
		time2 %=     86400;
		if (d1 >= 1000 || d2 >= 1000) {
			d1 %= 1000;
			d2 %= 1000;
		}
	}
	h1 = time1 / 3600;
	h2 = time2 / 3600;
	time1 %=     3600;
	time2 %=     3600;
	m1 = time1 /   60;
	m2 = time2 /   60;
	s1 = time1 %   60;
	s2 = time2 %   60;

	if (show_days) {
		copyXPMArea(TIMER_SRC_X(d1 / 100), TIMER_SRC_Y, 5, 7,  7,  6);
		d1 %= 100;
		copyXPMArea(TIMER_SRC_X(d1 /  10), TIMER_SRC_Y, 5, 7, 13,  6);
		copyXPMArea(TIMER_SRC_X(d1 %  10), TIMER_SRC_Y, 5, 7, 19,  6);
		copyXPMArea(TIMER_SRC_X(h1 /  10), TIMER_SRC_Y, 5, 7, 29,  6);
		copyXPMArea(TIMER_SRC_X(h1 %  10), TIMER_SRC_Y, 5, 7, 35,  6);
		copyXPMArea(TIMER_SRC_X(m1 /  10), TIMER_SRC_Y, 5, 7, 45,  6);
		copyXPMArea(TIMER_SRC_X(m1 %  10), TIMER_SRC_Y, 5, 7, 51,  6);

		copyXPMArea(TIMER_SRC_X(d2 / 100), TIMER_SRC_Y, 5, 7,  7, 20);
		d2 %= 100;
		copyXPMArea(TIMER_SRC_X(d2 /  10), TIMER_SRC_Y, 5, 7, 13, 20);
		copyXPMArea(TIMER_SRC_X(d2 %  10), TIMER_SRC_Y, 5, 7, 19, 20);
		copyXPMArea(TIMER_SRC_X(h2 /  10), TIMER_SRC_Y, 5, 7, 29, 20);
		copyXPMArea(TIMER_SRC_X(h2 %  10), TIMER_SRC_Y, 5, 7, 35, 20);
		copyXPMArea(TIMER_SRC_X(m2 /  10), TIMER_SRC_Y, 5, 7, 45, 20);
		copyXPMArea(TIMER_SRC_X(m2 %  10), TIMER_SRC_Y, 5, 7, 51, 20);
	} else {
		copyXPMArea(TIMER_SRC_X(h1 / 100), TIMER_SRC_Y, 5, 7,  7,  6);
		h1 %= 100;
		copyXPMArea(TIMER_SRC_X(h1 /  10), TIMER_SRC_Y, 5, 7, 13,  6);
		copyXPMArea(TIMER_SRC_X(h1 %  10), TIMER_SRC_Y, 5, 7, 19,  6);
		copyXPMArea(TIMER_SRC_X(m1 /  10), TIMER_SRC_Y, 5, 7, 29,  6);
		copyXPMArea(TIMER_SRC_X(m1 %  10), TIMER_SRC_Y, 5, 7, 35,  6);
		copyXPMArea(TIMER_SRC_X(s1 /  10), TIMER_SRC_Y, 5, 7, 45,  6);
		copyXPMArea(TIMER_SRC_X(s1 %  10), TIMER_SRC_Y, 5, 7, 51,  6);

		copyXPMArea(TIMER_SRC_X(h2 / 100), TIMER_SRC_Y, 5, 7,  7, 20);
		h2 %= 100;
		copyXPMArea(TIMER_SRC_X(h2 /  10), TIMER_SRC_Y, 5, 7, 13, 20);
		copyXPMArea(TIMER_SRC_X(h2 %  10), TIMER_SRC_Y, 5, 7, 19, 20);
		copyXPMArea(TIMER_SRC_X(m2 /  10), TIMER_SRC_Y, 5, 7, 29, 20);
		copyXPMArea(TIMER_SRC_X(m2 %  10), TIMER_SRC_Y, 5, 7, 35, 20);
		copyXPMArea(TIMER_SRC_X(s2 /  10), TIMER_SRC_Y, 5, 7, 45, 20);
		copyXPMArea(TIMER_SRC_X(s2 %  10), TIMER_SRC_Y, 5, 7, 51, 20);
	}

	if (microtm < 500000 || !running) {
		if (show_days) {
			copyXPMArea(161, CHAR_SRC_Y1 + 4, 1, 3, 26, 10);
			copyXPMArea(161, CHAR_SRC_Y1 + 4, 1, 3, 26, 24);
		} else {
			copyXPMArea(161, CHAR_SRC_Y1, 1, 7, 26,  6);
			copyXPMArea(161, CHAR_SRC_Y1, 1, 7, 26, 20);
		}
		copyXPMArea(161, CHAR_SRC_Y1, 1, 7, 42,  6);
		copyXPMArea(161, CHAR_SRC_Y1, 1, 7, 42, 20);
	} else {
		if (!show_days) {
			copyXPMArea(163, CHAR_SRC_Y1, 1, 7, 26,  6);
			copyXPMArea(163, CHAR_SRC_Y1, 1, 7, 26, 20);
		}
		copyXPMArea(163, CHAR_SRC_Y1, 1, 7, 42,  6);
		copyXPMArea(163, CHAR_SRC_Y1, 1, 7, 42, 20);
	}
}

void drawProject(const char *name)
{
	int i;

	for (i = 0; i < 3; i++) {
		copyXPMArea(CHAR_SRC_X2(' '), CHAR_SRC_Y2, 5, 7, 23 + i * 6, 35);
		if (i >= strlen(name))
			continue;
		if (name[i] >= 'A' && name[i] <= '`')
			copyXPMArea(CHAR_SRC_X1(name[i]), CHAR_SRC_Y1, 5, 7, 23 + i * 6, 35);
		else if (name[i] >= ' ' && name[i] <= '@')
			copyXPMArea(CHAR_SRC_X2(name[i]), CHAR_SRC_Y2, 5, 7, 23 + i * 6, 35);
		else if (name[i] >= '{' && name[i] <= '~')
			copyXPMArea(CHAR_SRC_X3(name[i]), CHAR_SRC_Y3, 5, 7, 23 + i * 6, 35);
	}
}

int read_log(void)
{
	struct Project
		*p = NULL;
	FILE
		*F = NULL;
	char
		buffer[512],
		*colon = NULL,
		*s     = NULL;
	int i, line, n = 0;

	if ((first = malloc(sizeof(struct Project))) == NULL) {
		fprintf(stderr, "%s: cannot allocate memory for element %d\n", PACKAGE_NAME, n);
		fprintf(stderr, "%s: %s\n", PACKAGE_NAME, strerror(errno));
		exit(1);
	}
	strcpy(first->name, "---");
	first->time = 0;
	first->prev = first;
	first->next = first;

	if ((F = fopen(logname, "r")) == NULL) {
		if (errno != ENOENT) {
			fprintf(stderr, "%s: cannot open '%s' for reading\n", PACKAGE_NAME, logname);
			fprintf(stderr, "%s: %s\n", PACKAGE_NAME, strerror(errno));
		}
		return 1;
	}

	for (line = 1;; line++) {
		if (fgets(buffer, sizeof(buffer), F) == NULL)
			break;

		if (n == 0) {
			p = first;
		} else if ((p = malloc(sizeof(struct Project))) == NULL) {
			fprintf(stderr, "%s: cannot allocate memory for element %d\n", PACKAGE_NAME, n);
			fprintf(stderr, "%s: %s\n", PACKAGE_NAME, strerror(errno));
			return(n);
		}

		if ((s = index(buffer, '\n')) != NULL)
			*s = '\0';
		if (buffer[0] == '#' || buffer[0] == '\0') {
			if (n)
				free(p);
			continue;
		}
		if ((colon = index(buffer, ':')) == NULL) {
			fprintf(stderr, "%s: missing ':' in '%s' line %d\n", PACKAGE_NAME, logname, line);
			if (n)
				free(p);
			continue;
		}
		i = colon - buffer < 3 ? colon - buffer : 3;
		p->name[i] = '\0';
		for (i--; i >= 0; i--) {
			p->name[i] = toupper(buffer[i]);
			if (p->name[i] == '/') {
				fprintf(stderr, "%s: '/' is fs delimiter in '%s' line %d\n", PACKAGE_NAME, logname, line);
				fprintf(stderr, "%s: converting forbidden '/' in project id into '|'\n", PACKAGE_NAME);
				p->name[i] = '|';
			}
		}
		p->time = strtol(++colon, &s, 10);
		if (*s && *s != ':' && *s != ' ') {
			fprintf(stderr, "%s: error converting timestamp '%s' in '%s' line %d\n", PACKAGE_NAME, colon, logname, line);
			if (n)
				free(p);
			continue;
		}
		if (*s++ == ':') {
			if ((p->comment = strdup(s)) == NULL) {
				fprintf(stderr, "%s: ignored error while aquiring memory for comment string '%s' in '%s' line %d\n", PACKAGE_NAME, s, logname, line);
				fprintf(stderr, "%s: %s\n", PACKAGE_NAME, strerror(errno));
			}
		} else
			p->comment = NULL;

		p->prev = first->prev;
		p->next = first;
		p->prev->next = p;
		p->next->prev = p;
		n++;
	}

	fclose(F);
	return(n);
}

int write_log(void)
{
	struct Project
		*p = NULL;
	FILE
		*F = NULL;

	if ((F = fopen(logname, "w")) == NULL) {
		fprintf(stderr, "%s: cannot open '%s' for writing\n", PACKAGE_NAME, logname);
		fprintf(stderr, "%s: %s\n", PACKAGE_NAME, strerror(errno));
		F = stderr;
	} else {
		fprintf(F, "# wmwork configuration file\n");
		fprintf(F, "# do not edit while wmwork is running\n\n");
	}
	p = first;
	do {
		if (F == stderr)
			fprintf(F, "%s: > %s:%li:%s\n", PACKAGE_NAME, p->name, p->time, p->comment ? p->comment : "");
		else
			fprintf(F, "%s:%li:%s\n", p->name, p->time, p->comment ? p->comment : "");
		p = p->next;
	} while (p != first);

	if (F == stderr)
		return 0;
	fclose(F);
	return 1;
}

int write_record(void)
{
	char
		*fname = NULL,
		tbuff[64],
		rbuff[64];
	FILE
		*F = NULL;

	if (sess_time == 0)
		return 1;
	strftime(tbuff, sizeof(tbuff), "%a, %d %b %Y %H:%M:%S %z", localtime(&now.tv_sec));
	snprintf(rbuff, sizeof(rbuff), "%s %03li:%02li:%02li", tbuff, sess_time / 3600, sess_time / 60 % 60, sess_time % 60);

	if ((fname = malloc(path_len)) == NULL) {
		fprintf(stderr, "%s: cannot allocate memory for path variable\n", PACKAGE_NAME);
		fprintf(stderr, "%s: %s\n", PACKAGE_NAME, strerror(errno));
		fprintf(stderr, "%s: > %s\n", PACKAGE_NAME, rbuff);
		return 0;
	}
	snprintf(fname, path_len, "%s/.wmwork/%s", getenv("HOME"), current->name);
	if ((F = fopen(fname, "a")) == NULL) {
		fprintf(stderr, "%s: cannot open '%s' for writing\n", PACKAGE_NAME, fname);
		fprintf(stderr, "%s: %s\n", PACKAGE_NAME, strerror(errno));
		fprintf(stderr, "%s: > %s\n", PACKAGE_NAME, rbuff);
		free(fname);
		return 0;
	}
	fprintf(F, "%s\n", rbuff);
	fclose(F);
	free(fname);
	return 1;
}

int make_lock(int force)
{
	FILE
		*F  = NULL;
	pid_t
		pid = 0;
	int
		fd  = 0,
		i   = 0;
	char
		*c  = NULL,
		proc[32],
		buffer[256];

	if ((fd = open(lockname, O_WRONLY | O_CREAT | (force ? 0 : O_EXCL), 0666)) < 0) {
		if (errno == EEXIST) {
			if ((F = fopen(lockname, "r")) != NULL) {
				i = fscanf(F, PID_T, &pid);
				fclose(F);
				snprintf(proc, sizeof(proc), "/proc/"PID_T"/exe", pid);
				if (i == 1 && ((i = readlink(proc, buffer, sizeof(buffer)-1)) >= 0 || errno == ENOENT)) {
					if (i >= 0) {
						buffer[i] = 0;
					}
					if (i < 0 || ((c = rindex(buffer, '/')) != NULL && strcmp(c+1, "wmwork") != 0)) {
						fprintf(stderr, "%s: found stale lock file (pid "PID_T")\n", PACKAGE_NAME, pid);
						return make_lock(1);
					}
				}
			}
			fprintf(stderr, "%s: already running, --force will overwrite the lock file\n", PACKAGE_NAME);
			exit(1);
		}
		fprintf(stderr, "%s: cannot create '%s'\n", PACKAGE_NAME, lockname);
		fprintf(stderr, "%s: %s\n", PACKAGE_NAME, strerror(errno));
		return 0;
	}
	if ((F = fdopen(fd, "w")) != NULL) {
		fprintf(F, PID_T"\n", getpid());
		fclose(F);
	} else
		close(fd);
	return 1;
}

int compat(void)
{
	char
		*temp1 = NULL,
		*temp2 = NULL;
	DIR *dir   = NULL;
	struct dirent
		*entry = NULL;

	/* BEGIN compatibility section for wmwork < 0.2.0 */

	if ((temp1 = malloc(path_len)) == NULL || (temp2 = malloc(path_len)) == NULL) {
		fprintf(stderr, "%s: cannot allocate memory for path variable\n", PACKAGE_NAME);
		fprintf(stderr, "%s: %s\n", PACKAGE_NAME, strerror(errno));
		return -1;
	}

	snprintf(temp1, path_len, "%s/.wmworklog", getenv("HOME"));
	if (rename(temp1, logname) < 0) {
		if (errno == ENOENT)
			return 0;
		fprintf(stderr, "%s: cannot rename '%s' to '%s'\n", PACKAGE_NAME, temp1, logname);
		fprintf(stderr, "%s: %s\n", PACKAGE_NAME, strerror(errno));
		return -1;
	} else
		fprintf(stderr, "%s: moving '%s' -> '%s'\n", PACKAGE_NAME, temp1, logname);

	if ((dir = opendir(getenv("HOME"))) < 0) {
		fprintf(stderr, "%s: cannot read '%s'\n", PACKAGE_NAME, getenv("HOME"));
		fprintf(stderr, "%s: %s\n", PACKAGE_NAME, strerror(errno));
		return -1;
	}
	while ((entry = readdir(dir))) {
		if (strstr(entry->d_name, ".wmwork.") == entry->d_name &&
			strlen(entry->d_name) > 8 && strlen(entry->d_name) <= 11) {
			snprintf(temp1, path_len, "%s/%s", getenv("HOME"), entry->d_name);
			snprintf(temp2, path_len, "%s/%s", dirName, entry->d_name + 8);
			if (rename(temp1, temp2) < 0) {
				fprintf(stderr, "%s: cannot rename '%s' to '%s'\n", PACKAGE_NAME, temp1, temp2);
				fprintf(stderr, "%s: %s\n", PACKAGE_NAME, strerror(errno));
			} else
				fprintf(stderr, "%s: moving '%s' to '%s'\n", PACKAGE_NAME, temp1, temp2);
		}
	}
	closedir(dir);
	return 0;

	/* END compatibility section for wmwork < 0.2.0 */
}
