/*
 * Zeroer (for usage see "man zero").
 * This software is licensed under the GNU
 * public license. See LICENSE for details.
 *
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <popt.h>

/* use a 10 MB buffer by default (can be changed by -b option*/
long buffersize=10485760;
const char *filename;
poptContext optCtx;

/* option table; for details, see <man popt> */
static struct poptOption commandOptions[] = {
	 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ 
	{"buffersize", 'b', POPT_ARG_LONG, &buffersize, 'b', NULL, NULL},
	POPT_AUTOHELP /* Pre-implement '-?', '--help', '--usage' */
	POPT_TABLEEND /* Needed to mark end of this structure */
};

/* escape with system error message */
void errExit(char *message) {
	perror(message);
	poptFreeContext(optCtx);	
	exit(EXIT_FAILURE);
}

/*
 * This function writes the zero-filled memory blocks 
 * to the destination file. If the file system is going
 * to be full, the write size is reduced, so that smaller
 * blocks in the unallocated disk sector space can still
 * be overwritten.
 */
void blockWriteLoop(FILE *f, char *pBuf) {
	int writeSize=buffersize;
	int loopCount=0;

	while(writeSize > 0) {
		printf(
			"\nSTATS: loopCount=%d, writeSize=%d ",
			++loopCount,
			writeSize
			);
		fwrite((void *)pBuf, (size_t)sizeof(char), writeSize, f);

		if (ferror(f)) {
			printf("write_error, reducing writeSize");
			writeSize=writeSize/2;
			clearerr(f);
		}
	}	
}

/*
 * The <filename> argument specifies the name of a
 * file to be created. This will be the file in which
 * the zero's are written to.
 */
void initBlockWriter(const char *filename) {
	FILE *f=NULL;
	char *pBuf=NULL;
	
	f=fopen(filename, "wb");	
	if (f == NULL) {
		errExit("\nCould not open destfile");
	}

	pBuf=(char *)malloc((size_t)(sizeof(char) * buffersize));
	if (pBuf == NULL) {
		errExit("\nCould not allocate memory");
	}

	memset((void *)pBuf, 0, (size_t)(sizeof(char) * buffersize));
	blockWriteLoop(f, pBuf);

	free((void *)pBuf);
	fclose(f);
}

/*
 * In the main function, command-line arguments
 * are parsed and each switchoption's value is
 * implicitly stored in the corresponding global
 * variable as specified in the commandOptions
 * array.
 */
int main(int argc, char *argv[]) {
	int opt;

	/* acquire popt context */	
	optCtx=poptGetContext(
		NULL,
		argc,
		(const char **)argv,
		commandOptions,
		0
		);
	
	/* 
	 * The syntax for additional non-switchoption
	 * parameters. this is glued together with the
	 * automatically popt-generated options syntax
	 * list.
	 */
	poptSetOtherOptionHelp(optCtx, "filename");

	/* if no argument has been specified at all...*/
	if (argc < 2) {
		poptPrintUsage(optCtx, stderr, 0);
		poptFreeContext(optCtx);
                exit(EXIT_FAILURE);
	}

	/* parse switched options */
	while ((opt = poptGetNextOpt(optCtx)) >= 0) {
		switch (opt) {
		
		case 'b':
			fprintf(stderr, "buffersize set to %d\n", (int)buffersize);
			break;
		}
    	}

	/* parse filename (non-switchoption-parameter) */
	filename=poptGetArg(optCtx);
	if (filename == NULL) {
		fprintf(stderr, "no filename specified\n");
		poptPrintUsage(optCtx, stderr, 0);
		poptFreeContext(optCtx);
		exit(EXIT_FAILURE);
	}
	
	/* if a switchoption is malformed, i.e. wrong type */
	if (opt < -1) {
		fprintf(
			stderr,
			"%s: %s\n",
			poptBadOption(optCtx, POPT_BADOPTION_NOALIAS),
			poptStrerror(opt)
			);

		poptPrintUsage(optCtx, stderr, 0);
		poptFreeContext(optCtx);
		exit(EXIT_FAILURE);
	}

	/* run application */
	initBlockWriter(filename);
	
	/* release popt context */
	poptFreeContext(optCtx);
	return EXIT_SUCCESS;
}


syntax highlighted by Code2HTML, v. 0.9.1