/* mtrunc - logfile truncation utility
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include "lock.h"
#include "getopt.h"


void display_version(void);
int log_truncate(int, int, long, long, int);


/* Return the number contained in string "s", which could be suffixed by
 * 'B' for bytes, 'K' for kilobytes, or 'M' for megabytes, either upper or
 * lower case.
 */
long getnum(char *s)
{
	int l;
	if (!s)
		return (0);
	if (!*s)
		return (0);
	l = strspn(s, "0123456789");
	switch (s[l]) {
	case 'k':
	case 'K':
		return (atol(s) * 1024);
	case 'm':
	case 'M':
		return (atol(s) * 1024 * 1024);
	}
	return (atol(s));
}


/* Display command-line help for mtrunc.
 */
void display_help(void)
{
	printf("\
Usage: mtrunc [OPTIONS] FILE\n\
Truncate the log file FILE if it is over a certain size, leaving a little\n\
data still in it. Also locks FILE while operating on it.\n\
\n\
  -m, --maxsize SIZE    truncate files over SIZE bytes (K/M=kilo/megabytes)\n\
  -t, --truncto SIZE    leave SIZE bytes after truncation\n\
  -a, --archive FILE    append truncated data to FILE\n\
  -c, --commentfile     treat as comment file - leave file header intact\n\
\n\
      --help            show this help and exit\n\
  -V, --version         show version information and exit\n\
\n\
Report bugs to <mconv@ivarch.com>.\n\
");
}


/* Process command-line arguments and truncate the file given accordingly.
 */
int main(int argc, char *argv[])
{
	struct option long_options[] = {
		{"maxsize", 1, 0, 'm'},
		{"max", 1, 0, 'm'},
		{"truncto", 1, 0, 't'},
		{"to", 1, 0, 't'},
		{"archive", 1, 0, 'a'},
		{"commentfile", 0, 0, 'c'},
		{"help", 0, 0, '!'},
		{"version", 0, 0, 'V'},
		{0, 0, 0, 0}
	};
	char *short_options = "m:t:a:cV";
	int option_index = 0;
	char *archive = 0;
	long maxsize = 102400;
	long truncto = 10240;
	int commentfile = 0;
	int fd, ofd;
	char *file;
	char *pn;
	int c;

	pn = strrchr(argv[0], '/');
	if (pn)
		pn++;
	else
		pn = argv[0];

	do {				    /* read options */
		c = getopt_long(argc, argv, short_options, long_options,
				&option_index);
		if (c < 0)
			continue;
		switch (c) {
		case 'a':
			archive = optarg;
			break;
		case 'm':
			maxsize = getnum(optarg);
			break;
		case 't':
			truncto = getnum(optarg);
			break;
		case 'c':
			commentfile = 1;
			break;
		case 'V':
			display_version();
			return (0);
			break;
		case '!':
			display_help();
			return (0);
			break;
		default:
			fprintf(stderr,
				"Try `%s --help' for more information.\n\r",
				pn);
			return (1);
			break;
		}
	} while (c != -1);

	if ((optind >= argc) || (argc > (optind + 2))) {
		fprintf(stderr, "Usage: %s [OPTIONS] FILE\n\r", pn);
		fprintf(stderr,
			"Try `%s --help' for more information.\n\r", pn);
		return (1);
	}

	if (truncto >= maxsize) {
		fprintf(stderr,
			"%s: truncated size must be less than max size\n\r",
			pn);
		fprintf(stderr,
			"Try `%s --help' for more information.\n\r", pn);
		return (1);
	}

	file = argv[optind];

	fd = open(file, O_RDWR);
	if (fd < 0) {
		fprintf(stderr, "%s: %s: %s\n\r", pn, file,
			strerror(errno));
		return (1);
	}

	if (archive) {
		ofd =
		    open(archive, O_WRONLY | O_CREAT | O_APPEND,
			 S_IRUSR | S_IWUSR);
		if (ofd < 0) {
			fprintf(stderr, "%s: %s: %s\n\r", pn, archive,
				strerror(errno));
			close(fd);
			return (1);
		}
	} else
		ofd = -1;

	my_flock(file, fd, LOCK_EX);
	if (ofd >= 0)
		my_flock(archive, ofd, LOCK_EX);

	c = log_truncate(fd, ofd, maxsize, truncto, commentfile);
	if (c) {
		fprintf(stderr, "%s: %s: %s\n\r", pn, file,
			strerror(errno));
	}

	if (ofd >= 0)
		my_flock(archive, ofd, LOCK_UN);
	my_flock(file, fd, LOCK_UN);

	if (ofd >= 0)
		close(ofd);
	close(fd);

	return (c);
}

/* EOF */
