/* Copyright (c)1994-2000 Begemot Computer Associates. All rights reserved.
 * See the file COPYRIGHT for details of redistribution and use. */

/*
 * Program to make a tape file
 */
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <string.h>
# include <errno.h>
# include <fcntl.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <netinet/in.h>
# ifdef HAVE_GETOPT_H
#  include <getopt.h>
# endif
# include <begemot.h>
# include "cdefs.h"
# include "util.h"

RCSID("$Id: mktape.c 476 2001-07-09 08:09:10Z hbb $")

typedef struct file_t file_t;
struct file_t {
	char	*name;
	u_int	blksiz;
	off_t	fsize;
	off_t	recs;
};

file_t	*files;
u_int	nfiles, afiles;
off_t	total;
int	verbose;

static void parse(void);
static void dump(void);
static void whdr(void);
static void wfile(file_t *fp);
static void usage(void) DEAD_FUNC;

static char usgtxt[] =
"Usage: mktape [-v] file\n"
"Options:\n"
"	-v		increment verbose level\n"
"	-h		print this info\n";

int
main(int argc, char *argv[])
{
	int opt;
	file_t *fp;

	set_argv0(argv[0]);
	while((opt = getopt(argc, argv, "hv")) != EOF)
		switch(opt) {

		  case 'h':
			usage();

		  case 'v':
			verbose++;
			break;
		}

	argc -= optind;
	argv += optind;

	if(argc != 1)
		usage();
	if(strcmp(argv[0], "-") && freopen(argv[0], "r", stdin) == NULL)
		panic("%s: %s", argv[0], strerror(errno));

	parse();
	if(verbose)
		dump();
	if(verbose)
		fprintf(stderr, "write header ...\n");
	whdr();
	for(fp = files; fp < &files[nfiles]; fp++)
		if(fp->name) {
			if(verbose)
				fprintf(stderr, "write %s ...\n", fp->name);
			wfile(fp);
		}

	return 0;
}

static void
usage()
{
	fprintf(stderr, usgtxt);
	exit(1);
}

static void
parse()
{
	char line[2048];
	char *fields[4];
	char *f, *end;
	int lno, n;
	struct stat statb;
	file_t *fp;

	lno = 0;
	f = setfields(" \t\n");
	while(fgets(line, sizeof(line), stdin) != NULL) {
		if(afiles == nfiles) {
			afiles += 100;
			files = xrealloc(files, afiles * sizeof(file_t));
		}
		fp = &files[nfiles++];
		lno++;
		n = getmfields(line, fields, 4);
		if(n == 1) {
			if(strcmp(fields[0], "*"))
				panic("bad EOF field in line %d", lno);
			fp->name = NULL;
			total++;
		} else if(n == 2) {
			if(stat(fields[0], &statb))
				panic("cannot stat %s: %s", fields[0],
					strerror(errno));
			if((statb.st_mode & S_IFMT) != S_IFREG)
				panic("%s: not a regular file", fields[0]);
			fp->fsize = statb.st_size;
			fp->name = strcpy(xalloc(strlen(fields[0])+1),
				fields[0]);

			fp->blksiz = strtoul(fields[1], &end, 0);
			if(*end != 0)
				panic("bad number %s in line %d",fields[1],lno);
			if(fp->blksiz == 0 || fp->blksiz >= 32768)
				panic("illegal blksiz in line %s", fields[1]);
			fp->recs = (fp->fsize + fp->blksiz - 1) / fp->blksiz;
			total += fp->recs;
		} else
			panic("bad number of fields in line %d", lno);
	}
	(void)setfields(f);
	if(ferror(stdin))
		panic("file: %s", strerror(errno));
}

static void
dump()
{
	file_t *fp;
	off_t recno = 0;

	fprintf(stderr, "%d files/EOF's; %qd records/EOF's\n",
		nfiles, (unsigned long long)total);
	for(fp = files; fp < &files[nfiles]; fp++) {
		if(fp->name) {
			fprintf(stderr, "%s: %qu bytes, %qd records "
				"of %u bytes",
				fp->name, (unsigned long long)fp->fsize,
				(long long)fp->recs,
				fp->blksiz);
			fprintf(stderr, " (%qd ... %qd)\n",
				(unsigned long long)recno,
				(unsigned long long)(recno + fp->recs - 1));
			recno += fp->recs;
		} else {
			fprintf(stderr, "<EOF>");
			fprintf(stderr, " (%qd ... %qd)\n",
				(long long)recno, (long long)recno);
			recno++;
		}
	}
}

static void
whdr()
{
	u_short *tab, *tp;
	u_int recs;
	file_t *fp;
	off_t i;

	tp = tab = xalloc(total * sizeof(u_short));
	for(fp = files; fp < &files[nfiles]; fp++) {
		if(fp->name) {
			for(i = 0; i < fp->recs; i++)
				*tp++ = htons(fp->blksiz);
		} else {
			*tp++ = htons(0);
		}
	}
	recs = htonl((u_int)total);
	if(write(1, &recs, sizeof(u_int)) != sizeof(u_int))
		panic("write no of records: %s", strerror(errno));
	if(write(1, tab, total * sizeof(u_short)) != (ssize_t)(total * sizeof(u_short)))
		panic("write record table: %s", strerror(errno));
}

static void
wfile(file_t *fp)
{
	int fd;
	char *buf;
	off_t rno;
	int ret;

	if((fd = open(fp->name, O_RDONLY, 0)) < 0)
		panic("%s: %s", fp->name, strerror(errno));
	buf = xalloc(fp->blksiz);

	rno = 0;
	while((ret = read(fd, buf, fp->blksiz)) > 0) {
		if((u_int)ret < fp->blksiz)
			memset(buf + ret, 0, fp->blksiz - ret);
		if((ret = write(1, buf, fp->blksiz)) < 0 || (u_int)ret != fp->blksiz)
			panic("write record: %s", strerror(errno));
		rno++;
	}
	if(ret < 0)
		panic("%s: %s", fp->name, strerror(errno));
	if(rno != fp->recs)
		panic("%s: got %lu recs, expected %u", fp->name, (u_long)rno, (u_int)fp->recs);

	free(buf);
	(void)close(fd);
}
