/**
 * strip-null-bytes: A small C program which strips null bytes
 * at beginning and end of STDIN and echos the data (without the
 * null bytes) to STDOUT.
 *
 * why not a perl tool? perl is nice for complex jobs, but not for
 * something like this low level thing ;-)
 *
 * (c) 14.06.08 Sven Köppel
 *
 **/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define DPRINTF(bla...) { if(flag_debug) fprintf(stderr,bla); }

int stream_get_contents(FILE *stream, unsigned char **content) {
	/**
	 * helper function to read in stdin into heap array; from
	 * PaperTape cairo project -> from Glib.
	 **/

	unsigned char buf[4096];
	size_t bytes; // gerade eben eingelesene bytes
	unsigned char *str = NULL;
	size_t total_bytes = 0;     // alle bis jetzt eingelesenen bytes
	size_t total_allocated = 0;
	unsigned char *tmp; // fuers realloc

	while(!feof(stream)) {
		bytes = fread(buf, 1, sizeof(buf), stream);

		while( (total_bytes + bytes) > total_allocated) {
			if(str)
				total_allocated *= 2;
			else
				total_allocated = bytes > sizeof(buf) ? bytes : sizeof(buf);

			tmp = realloc(str, total_allocated);

			if(tmp == NULL) {
				fprintf(stderr, "*** file_get_contents ERROR *** Could not reallocate\n");
				exit(1);
			}

			str = tmp;
		} // while innen

		memcpy(str + total_bytes, buf, bytes);
		total_bytes += bytes;
	} // while

	if(total_allocated == 0)
		str = malloc(1); // ein leerer String halt.

	*content = str;
	return total_bytes;
}

int main(int argc, char **argv) {
	unsigned char *bytes;
	int length;
	int x_start, x_end, x;
	int flag_start, flag_end, flag_debug;
	flag_debug = flag_start = flag_end = 0;
	
	int c;
	while( (c = getopt(argc, argv, "bsehd")) != -1)
		switch(c) {
			case 'd':
				flag_debug = 1;
				break;
			case 'h':
				fprintf(stderr, "Usage: %s [-d] [-s/b] [-e] [-h]\n"
					"  optional parameters:\n"
					"   -d   debug mode (print everything to stderr)\n"
					"   -h   display this help message\n\n"
					"   -b   same as -s\n"
					"   -s   strip null bytes at START of stdin\n"
					"   -e   strip null bytes a END of stdin\n\n"
					"  without any parameters, null bytes will be stripped\n"
					"  both at start and at end of stdin.\n"
					,argv[0]);
				return 0;
			case 'b':
			case 's':
				flag_start = 1;
				break;
			case 'e':
				flag_end = 1;
		}
		
	DPRINTF("Reading in from stdin, finish with eof/[STRG] + [D]\n");
	length = stream_get_contents(stdin, &bytes);
	DPRINTF("Finished reading in.\n");
	
	/* stripping at start */
	if( flag_start || (!flag_start && !flag_end) )
		for(x_start = 0; x_start < length; x_start++) {
			DPRINTF("start char %d/%d: 0x%x\n", x_start, length, bytes[x_start]);
			if(bytes[x_start])
				break;
		}
	else {
		x_start = 0;
		DPRINTF("skipping stripping at start\n");
	}
	
	/* stripping at end */
	if( flag_end || (!flag_start && !flag_end) )
		for(x_end = length; x_end > 0; x_end--) {
			DPRINTF("end char %d/%d: 0x%x\n", x_end, length, bytes[x_end]);
			if(bytes[x_end])
				break;
		}
	else {
		x_end = length;
		DPRINTF("skipping stripping at end\n");
	}
	
	DPRINTF("found data chunk: begin at %d, end at %d\n", x_start, x_end);
	
	for(x=x_start; x<=x_end; x++)
		fputc(bytes[x], stdout);
	
	return 0;
}

