/*  fsplit - a crude file-splitting program.
   (C) Tony Sanderson, Bluehaze Solutions, January 2001

   Type "make fsplit" on any half reasonable unix system to build it.
   Should also build okay under DOS/Windoze, although BUF_SIZE may need to be
   dropped to 32000 if using older "16 bit" compilers (I use an old (1987)
   Borland Turbo C which works fine).  But I now use "Winsplit" with Windows :-)

   No charge for non-commercial use.  Contact author for commercial usage
   (admin@bluehaze.com.au).

   Type "fsplit -h" for help.

   Changes:
   Vers 2.3, Sep 27, 2005:
	Add -d (debug) arg
   Vers 2.2, Oct 29, 2004:
   Change the long ints to long long ints to allow chunk sizes of more than
   2^32 (2147483848 signed) bytes.  2Gb is not big enough for DVDs.
   Also had to define _LARGEFILE_SOURCE & _FILE_OFFSET_BITS 64 ahead of
   other defs.  Google search: libc "_LARGEFILE_SOURCE" " _FILE_OFFSET_BITS"
   Or just go to: http://www.ece.utexas.edu/~luo/linux_lfs.html

	Vers 2.1, Mar 10, 2001:
	Chunk size requested (via -s) now honoured exactly.  Vers 1 would round
	it to the nearest Mb if the requested size was > BUF_SIZE.
	Also change to allow stdin if no input file specified.
*/

#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE
#define _FILE_OFFSET_BITS 64
#include <gnu/libc-version.h>
#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include "getopt.h"
#include "malloc.h"
#include "stdarg.h"
#include "stddef.h"
#include "stdlib.h"
#include "signal.h"

#define VERS "2.3"
#define FBUF 12
#define NFILES 1
#define RDBIN "rb"
#define RDTXT "rt"
#define WRBIN "wb"
#define WRTXT "wt"
#define false 0
#define true !false
#define BUF_SIZE 2000000
unsigned int *buf, i, j, k, suffix;
long long fsize, bytes_processed, chunksize, write_size, bread, remaining;
extern	int	opterr,		/* stop errors if set to 0. Defaults to 1 */
		optind;		/* argv index of next un-taken string 	*/
extern int	getopt();
extern char	*optarg;	/* argument string. For -mxxx, is "xxx"	*/
unsigned int max_suffix=999;

char	c, *of_ptr;
char helpmsg[] = { "usage: fsplit -s N [-i infile] [-o outfile] [-d]\n\
\n\
Splits input into chunks of N bytes.\n\
\n\
The chunks are created with suffixes of 000, 001, 002 etc, up to a max of 999\n\
\n\
If no input file name is specified, fsplit reads from stdin.\n\
\n\
If no output file name is specified, fsplit uses the input file name as the\n\
output file name base where an input was specified, otherwise it just uses\n\
\"xx\" as a base (ie: \"xx.000\", \"xx.001\" etc).\n\
\n\
Specify -d for debugging.\n\
"};

main(int argc, char *argv[])
{
	FILE *output_file, *input_file;
	char infile[128] = { "" };
	char outfile[128] = { "" };
	int debugf = 0;
	fsize = 0;
	
	while ((c = getopt (argc, argv, "s:o:i:hd")) != EOF)
	{
		switch (c)
		{
			case 's':
				sscanf(optarg,"%lld",&fsize);
				break;
			case 'o':
				strcpy (outfile,optarg);
				break;
			case 'h':
				printf ("fsplit %s %s", VERS, helpmsg);
				exit(0);
			case 'i':
				strcpy (infile,optarg);
				break;
			case 'd':
				debugf = 1;
				break;
			case '?':
				/* getopt will have printed an error message if here */
				exit(1);
				break;
		}
	}

	if ((strlen(outfile) == 0) && (strlen(infile) == 0))
		strcpy (outfile,"xx\0");
	
	if (strlen(outfile) == 0)
	{
		strcpy (outfile,infile);
	}
	strcat(outfile,".000\0");
	of_ptr=outfile+strlen(outfile)-3;

	if (fsize == 0)
	{
		fprintf(stderr, "Error - no chunk size\n");
		exit(1);
	}
	buf = (unsigned int *)calloc (BUF_SIZE, sizeof (unsigned int));
	if (buf == NULL)
	{
		fprintf(stderr, "Bad memory alloc for buffer\n");
		exit (1);
	}
	if ((output_file = fopen (outfile, WRBIN)) == NULL)
	{
		fprintf(stderr, "Could not open %s", outfile[0]);
		exit (1);
	}
	if (strlen(infile) > 0)
	{
		if ((input_file = fopen (infile, RDBIN)) == NULL)
		{
			fprintf (stderr, "\nCould not open input file %s\n", infile);
			exit (1);
		}
	}
	else
		input_file=stdin;
	bytes_processed = 0;
	suffix = 0;
	if (fsize < BUF_SIZE)
	{
		chunksize=fsize;
	}
	else
	{
		chunksize=BUF_SIZE;
	}

	if (debugf)
	{
		fprintf (stderr, "Have fsize=%lld and chunksize=%lld\n", fsize, chunksize);
	}
	while ((bread=fread(buf,sizeof (unsigned char),chunksize,input_file)) != 0)
	{
		if (debugf)
		{
			fprintf (stderr, "And have read in %lld bytes\n", bread);
		}
		bytes_processed = bytes_processed + (bread * sizeof(unsigned char));
		if (debugf)
		{
			fprintf (stderr, "Writing %lld bytes to %s\n", bread, outfile);
		}
		fwrite(buf,sizeof (unsigned char),bread,output_file);

		remaining=fsize-bytes_processed;
		if (debugf)
		{
			fprintf (stderr, "fsize=%lld, bytes_processed=%lld, bytes remaining=%lld \n", fsize, bytes_processed,remaining);
		}
		/* Close this chunk if full and open next */
		if (remaining <= 0)
		{
			if (debugf)
			{
				fprintf (stderr, "And now closing this chunk\n");
			}
			bytes_processed=0;
			fclose (output_file);
			sprintf ((of_ptr), "%03d\0", ++suffix);
			if (suffix > max_suffix)
			{
				fprintf (stderr, "Suffix now exceeds %d - sorry\n", max_suffix);
				exit (1);
			}
			if (debugf)
			{
				fprintf (stderr, "Opening new chunk %3d\n", suffix);
			}
			if ((output_file = fopen (outfile, WRBIN)) == NULL)
			{
				fprintf (stderr, "Could not open %s", outfile);
				return (false);
			}
			if (fsize > BUF_SIZE)
			{
				chunksize=BUF_SIZE;
			}
			else
			{
				chunksize=fsize;
			}
			remaining=fsize;
		}
		else
		{
		/* Calc chunksize to read/write for next block */
			if (remaining > BUF_SIZE)
			{
				chunksize=BUF_SIZE;
			}
			else
			{
				chunksize=remaining;
			}
		}
	}
	fclose (output_file);
	if (bytes_processed == 0)
	{
		if (debugf)
		{
			fprintf (stderr, "Un-linking %s\n", outfile);
		}
		unlink(outfile);  /* Remove any empties */
	}
}
