/* A spartan editor */
/*   ENJOY!    */

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

#define BIN_1 '1'
#define BIN_0 '0'
#define BIN_MARK ' ' /* ' ' will not allow use in input */
#define BIN_SEPSTR "   "
#define LLINE_BREAK "\n\n"
#define SLINE_BREAK "\n"
#define TMP_FILE_NAME "./sparta_tmp234doe"

void dump(unsigned char);
void close_f(FILE *fp);
void empty_file(char *name, FILE **fpp);

char bin_1 = BIN_1;
char bin_0 = BIN_0;

int main(int argc, char **argv)
{
	long unsigned offset = 0; 
	int tmp;
	int i, j;
	long unsigned ltmp;

	long unsigned show_off = 0;
	     unsigned show_len = 0;
	     unsigned end_show_len = 0;
	long unsigned change_off = 0;
	long unsigned del_off = 0;
	     unsigned del_len = 0;
	       signed change_bit_use, change_bit = 0;
	long unsigned insert_off = 0, insert_len = 0;

	unsigned char c;
	char in_data[9];
	FILE *fp;
	FILE *tmpfile;
	FILE **fpp = &fp;

	char slb[] = SLINE_BREAK;
	char llb[] = LLINE_BREAK;
	char *lb = slb;

	if (!(fp = fopen (*(argv+1),"r+") ) )  {
		fprintf(stderr, "\nspartacus: couldn't open file\n");
		exit (1);
	}
	goto menu;
	/*---------------------------------------------------------*/
insert:
	printf ("\nInsert: offset$ ");
	scanf (" %lx", &insert_off);	
	printf (" +num$ ");
	scanf (" %lx", &insert_len);
	if (!(tmpfile = fopen (TMP_FILE_NAME, "w+") ) )  {
		fprintf(stderr, "\nspartacus: couldn't open tmp-file %s\n"
				, TMP_FILE_NAME);
		goto menu;
	}
	if (0 != fseek(fp, insert_off, 0)  ||  insert_off != ftell(fp))  {
		fprintf(stderr, "\nspartacus: file-seek failed\n");
		goto menu;
	}
	while (EOF != (tmp = getc(fp)))  {
		putc(tmp, tmpfile);
	}
	if (0 != fseek(fp, insert_off, 0)  ||  insert_off != ftell(fp))  {
		fprintf(stderr, "\nspartacus: file-seek failed\n");
		goto menu;
	}
	for (ltmp=0; ltmp<insert_len; ltmp++)  {
		putc(0, fp);
	}
	if (0 != fseek(tmpfile, 0, 0)  ||  0 != ftell(tmpfile))  {
		fprintf(stderr, "\nspartacus: file-seek-tmpfile failed"
				", bad momend!\n");
		goto menu;
	}
	while (EOF != (tmp = getc(tmpfile)))  {
		putc(tmp, fp);
	}
	if (EOF == fclose(tmpfile))  {
		fprintf(stderr, "\nspartacus: couldn't close tmp-file %s\n"
				, TMP_FILE_NAME);
	}
	goto menu;
	/*---------------------------------------------------------*/
delete:
	printf("\nDelete!: offset$ ");
	scanf (" %lx", &del_off);
	printf(" +num$ ");
	scanf (" %x", &del_len);
	if (0 != fseek(fp, 0, 0)  ||  0 != ftell(fp))  {
		fprintf(stderr, "\nspartacus: file-seek failed\n");
		goto menu;
	}
	if (!(tmpfile = fopen (TMP_FILE_NAME, "w+") ) )  {
		fprintf(stderr, "\nspartacus: couldn't open tmp-file %s\n"
				, TMP_FILE_NAME);
		goto menu;
	}
	while (EOF != (tmp = getc(fp)))  {
		putc(tmp, tmpfile);
	}
	if (0 != fseek(tmpfile, 0, 0)  ||  0 != ftell(tmpfile))  {
		fprintf(stderr, "\nspartacus: file-seek-tmpfile failed"
			", bad momend!\n");
		goto menu;
	}
	empty_file(*(argv+1), fpp);
	for (offset=0; offset < del_off; offset++)  {
		tmp = getc(tmpfile);
		putc(tmp, fp);
	}
	for (; offset < del_off + del_len; offset++)  {
		getc(tmpfile);
	}
	while ( EOF != (tmp = getc(tmpfile)) )  {
		putc(tmp, fp);
	}
	if (EOF == fclose(tmpfile))  {
		fprintf(stderr, "\nspartacus: couldn't close tmp-file %s\n"
				, TMP_FILE_NAME);
	}
	goto menu;
	/*---------------------------------------------------------*/
show:
	printf("\nShow: offset$ ");
	scanf (" %lx", &show_off);
	printf(" +num$ ");
	scanf (" %x", &show_len);/* do..while, so '0' will get >1<*/
show_no_input:
	if (0 != fseek (fp, show_off, 0)  ||  show_off != ftell(fp))  {
		fprintf(stderr, "\nspartacus: file-seek failed\n");
		goto menu;
	}
	offset = ftell(fp);
	if (offset % 4)  {  printf ("\n%08lx  ", offset); }
	do  {
		fscanf(fp, "%c", &c);	
		if (!(offset % 4))  { printf ("%s%08lx  "
				, lb, offset); }
		printf (BIN_SEPSTR);
		dump (c);
		offset++;
	} while ( offset < show_off + show_len);
	goto menu;
	/*---------------------------------------------------------*/
show_end:
	printf("\nEnd-show: -num$ ");
	scanf (" %x", &end_show_len);
show_end_redo:
	if (0 == end_show_len)  { 
		end_show_len = 1; 
	}
	if (0 != fseek (fp, - end_show_len, SEEK_END))  {
		fprintf(stderr, "\nspartacus: file-seek failed\n");
		goto menu;
	}
	offset = ftell(fp);
	if (offset % 4)  {  printf ("\n%08lx  ", offset); }
	i=0;
	do  {
		c = getc(fp);
		if (!(offset % 4))  { printf ("%s%08lx  "
				, lb, offset); }
		printf (BIN_SEPSTR);
		dump (c);
		offset++;
		i++;
	} while ( i < end_show_len );
	goto menu;
	/*---------------------------------------------------------*/
change:
	printf ("\nChange: offset$ ");
	scanf (" %lx", &change_off);
	printf (" +bitnr_7654.3210$ ");
	scanf (" %x", &change_bit);
	printf (" bit(s)$ ");
change_next:
	scanf ("%9s", in_data);
	change_bit_use = change_bit;
	if (0 != fseek (fp, change_off, 0)  ||  
			change_off != (offset = ftell(fp)))  {
		fprintf(stderr, "\nspartacus: file-seek failed\n");
		goto menu;
	}
	fscanf (fp, "%c", &c);
	if (0 != fseek (fp, change_off, 0)  ||  
			change_off != ftell (fp) )  {
		fprintf(stderr, "\nspartacus: file-seek failed\n");
		goto menu;
	}
	for (i=0; change_bit_use >= 0  &&  i<9  &&  in_data[i]
			; change_bit_use--,i++)  {
		if (bin_0 == in_data[i])  {
			c |= (tmp = 0x1 << change_bit_use);
			c ^= tmp;
		}
		else if (bin_1 == in_data[i])  {
			c |= (0x1 << change_bit_use);
		}
		else  if (BIN_MARK == in_data[i])  {
			change_bit_use++;
		}
		else  {
			printf ("\nspartacus: %c or %c please, not '%c'"
					, bin_1, bin_0, in_data[i]);
			goto menu;
		}
	}
	putc(c, fp);
	/*fprintf (fp, "%c", c);*/
	goto menu;
	/*---------------------------------------------------------*/
append:
	printf ("\nAppend: bit(s)$ ");
	scanf ("%9s", in_data);
	if (0 != fseek (fp, 0, SEEK_END))  {
		fprintf(stderr, "\nspartacus(append,1): file-seek failed\n");
		goto menu;
	}
	c = 0;
	for ( i=0, j=7; i < 9 && in_data[i]; i++,j--)  {
		if (bin_1 == in_data[i])  { c |= (0x1 << j); }
		else if (BIN_MARK == in_data[i])  {
			j++;
		}
		else if (bin_0 != in_data[i])  {
			printf ( "\nspartacus: %c or %c please, not '%c'"
					, bin_1 , bin_0 , in_data[i]);
			goto menu;
		}
	}
	fprintf (fp, "%c", c);
	goto menu;
	/*---------------------------------------------------------*/
bin_01:
	printf ("\nDifferent format 0,1: char'1'$ ");
	scanf (" %c", &bin_1);
	printf ("\nDifferent 0,1: char'0'$ ");
	scanf (" %c", &bin_0);
        /* -------------------------------------------------------------- */
menu:
	printf ( "\n"                                                          "[s]how [r]eshow [m]ore [b]ack [e]ndshow [w]hatend\n"                           "[d]elete [i]nsert-0space [a]ppend [c]ange-[n]ext               [f]ormat [q]uit$\n");

	lb = slb; 
	do  { 
		tmp = getchar();
	} while ('\n' == tmp  ||
			'\t' == tmp  ||
			' ' == tmp);
	switch (tmp)  {
		case 'B':
			lb = llb;
		case 'b':
			show_off -= show_len;
			goto show_no_input;
		case 'M':
			lb = llb;
		case 'm':
			show_off += show_len;
			goto show_no_input;
		case 'd':
		case 'D':
			goto delete;
		case 'i':
		case 'I':
			goto insert;
		case 'S':
			lb = llb;
		case 's':
			goto show;
		case 'R':
			lb = llb;
		case 'r':
			goto show_no_input;
		case 'E':
			lb = llb;
		case 'e':
			goto show_end;
		case 'W':
			lb = llb;
		case 'w':
			goto show_end_redo;
		case 'n':
		case 'N':
			change_off++;
			printf("\nChange-Next: bit(s)$ ");
			goto change_next;
		case 'c':
		case 'C':
			goto change;
		case 'a':
		case 'A':
			goto append;
		case 'f':
		case 'F':
			goto bin_01;
		case 'q':
		case 'Q':
			goto end; 
		default:
			printf ("spartacus: not recognized '%c'",tmp);
			goto menu; 
	}

end:
	printf ("\n");
	close_f(fp);
	return 0;
}

void dump(unsigned char c)
{
	printf ("%c",( c & 0x80 )? bin_1:bin_0 );
	printf ("%c",( c & 0x40 )? bin_1:bin_0 );
	printf ("%c",( c & 0x20 )? bin_1:bin_0 );
	printf ("%c%c",( c & 0x10 )? bin_1:bin_0, BIN_MARK );
	printf ("%c",( c & 0x08 )? bin_1:bin_0 );
	printf ("%c",( c & 0x04 )? bin_1:bin_0 );
	printf ("%c",( c & 0x02 )? bin_1:bin_0 );
	printf ("%c",( c & 0x01 )? bin_1:bin_0 );
	if (0 == c)  { printf ("    "); }
	else  { printf (" %2X ", c); }
	if (32 < c  &&  127 > c)  { printf ("%c", c); }
	else  printf (" ");
	return;
}

void close_f(FILE *fp)
{
	if (EOF == fclose(fp))  {
		fprintf(stderr, "\nspartacus: coultn't close file\n");
		exit (1);
	}
	return;
}

void empty_file(char *name, FILE **fpp)
{
	close_f(*fpp);
	if (!fopen(name, "w"))  {
		fprintf(stderr, "\nspartacus: coultn't re-open file\n");
		exit (1);
	}
	close_f(*fpp);
	if (!(*fpp=fopen(name, "r+")))  {	
		fprintf(stderr, "\nspartacus: coultn't re-open file\n");
		exit (1);
	}
	return;
}


