/* primitive arbitrary-data frontend for netcat.  0.9 960226
   only handles one value per ascii line, but at least parses 0xNN too
   an input line containing "%r" during "-g" generates a random byte

   todo:
        make work on msloss jus' for kicks [workin' on it...]

   syntax: data -X [limit]
   where X is one of
        d: dump raw bytes to ascii format
        g: generate raw bytes from ascii input
        c: generate ??? of value -- NOTYET
        r: generate all random bytes
   and limit is how many bytes to generate or dump [unspecified = infinite]

 * Hobbit*, started 951004 or so and randomly screwed around with since */

#include <stdio.h>

#ifdef MSDOS                            /* for MSC only at the moment... */
#include <fcntl.h>
#else /* MSDOS */
#include <sys/file.h>
#define HAVE_RANDOM                     /* XXX: might have to change */
#endif /* MSDOS */

static char buf_in [128];
static char buf_raw [8192];
static char surveysez[] = "survey sez... XXX\n";

/* fgetss :
   wrapper for fgets, that yanks trailing newlines.  Doing the work ourselves
   instead of calling strchr/strlen/whatever */
char * fgetss(buf, len, from)
char * buf;
size_t len;
FILE * from;
{
	register int x;
	register char * p, * q;
	p = fgets(buf, len, from);      /* returns ptr to buf */
	if (!p)
		return NULL;

	q = p;
	for (x = 0; x < len; x++) {
		*p = (*p & 0x7f);       /* rip parity, just in case */
		switch (*p) {
		case '\n':
		case '\r':
		case '\0':
			*p = '\0';
			return q;
		}       /* switch */
		p++;
	}               /* for */
}                       /* fgetss */

/* randint:
   swiped from rndb.c.  Generates an INT, you have to mask down to char. */
int randint()
{
	register int q;
	register int x;

#ifndef HAVE_RANDOM
	q = rand();
#else
	q = random();
#endif
	x = ((q >> 8) & 0xff);  /* perturb low byte using some higher bits */
	x = q ^ x;
	return x;
}

main(argc, argv)
int argc;
char ** argv;
{
	register unsigned char * p;
	register char * q;
	register int x;
	int bc = 0;
	int limit = 0;          /* num to gen, or 0 = infinite */
	register int xlimit;    /* running limit */
	FILE * txt;             /* line-by-line ascii file */
	int raw;                /* raw bytes fd */
	int dumping = 0;        /* cmd flags ... */
	int genning = 0;
	int randing = 0;

	memset(buf_in, 0, sizeof(buf_in));
	memset(buf_raw, 0, sizeof(buf_raw));

	xlimit = 1;                     /* doubles as "exit flag" */
	bc = 1;                         /* preload, assuming "dump" */
	x = getpid() + 687319;
/* if your library doesnt have srandom/random, use srand/rand. [from rnd.c] */
#ifndef HAVE_RANDOM
	srand(time(0) + x);
#else
	srandom(time(0) + x);
#endif

#ifdef O_BINARY
/* DOS stupidity */
/* Aha: *here's* where that setmode() lib call conflict in ?BSD came from */
	x = setmode(0, O_BINARY);       /* make stdin raw */
	if (x < 0) {
		fprintf(stderr, "stdin binary setmode oops: %d\n", x);
		exit(1);
	}
	x = setmode(1, O_BINARY);       /* make stdout raw */
	if (x < 0) {
		fprintf(stderr, "stdout binary setmode oops: %d\n", x);
		exit(1);
	}
#endif  /* O_BINARY */

	if (argv[1]) {
		p = argv[1];    /* shit-simple single arg parser... */
		if (*p == '-')  /* dash is optional, we'll deal */
			p++;
		if (*p == 'd')
			dumping++;
		if (*p == 'g')
			genning++;
		if (*p == 'r')
			randing++;
	} /* if argv 1 */

/* optional second argument: limit # of bytes shoveled either way */
	if (argv[2]) {
		x = atoi(argv[2]);
		if (x)
			limit = x;
		else
			goto wrong;
		xlimit = limit;
	}

/* Since this prog would likely best be written in assmbler, I'm gonna
   write it *like* assembler.  So there. */

	if (randing)
		goto do_rand;

nextbuf:                                /* loop sleaze */

	if (dumping) {                  /* switch off to wherever */
		if (genning)
			goto wrong;
		goto do_dump;
	}
	if (genning)
		goto do_gen;
wrong:
	fprintf(stderr, surveysez);     /* if both or neither */
	exit(1);

do_gen:
/* here if genning -- original functionality */
	q = buf_raw;
	bc = 0;
/* suck up lines until eof or buf_raw is full */
	while (1) {
		p = fgetss(buf_in, 120, stdin);
		if (!p)
			break;                  /* EOF */
/* super-primitive version first: one thingie per line */
		if (*p == '#')                  /* comment */
			continue;
		if (*p == '\0')                 /* blank line */
			continue;
		if (*p == '%') {                /* escape char? */
			p++;
			if (*p == 'r') {        /* random byte */
				x = randint();
				goto stuff;
			}                       /* %r */
		}                               /* if "%" escape */
		if (*p == '0')
			if (*(p + 1) == 'x')    /* 0x?? */
				goto hex;
		x = atoi(p);                    /* reg'lar decimal number */
		goto stuff;

hex:
/* A 65   a 97 */
/* xxx: use a conversion table for this or something.  Since we ripped the
   parity bit, we only need a preset array of 128 with downconversion factors
   loaded in *once*.   maybe look at scanf... */
		p++;
		p++;                            /* point at hex-chars */
		x = 0;
		if ((*p > 96) && (*p < 123))    /* a-z */
			*p = (*p - 32);         /* this is massively clumsy */
		if ((*p > 64) && (*p < 71))     /* A-F */
			x = (*p - 55);
		if ((*p > 47) && (*p < 58))     /* digits */
			x = (*p - 48);
		p++;
		if (*p)                         /* another digit? */
			x = (x << 4);           /* shift to hi half */
		if ((*p > 96) && (*p < 123))    /* a-z */
			*p = (*p - 32);
		if ((*p > 64) && (*p < 71))     /* A-F */
			x = (x | (*p - 55));    /* lo half */
		if ((*p > 47) && (*p < 58))     /* digits */
			x = (x | (*p - 48));

/* fall thru */
stuff:                                  /* cvt to byte and add to buffer */
		*q = (x & 0xff);
		q++;
		bc++;
		if (limit) {
			xlimit--;
			if (xlimit == 0) /* max num reached */
				break;
		} /* limit */
		if (bc >= sizeof(buf_raw)) /* buffer full */
			break;
	} /* while 1 */

/* now in theory we have our buffer formed; shovel it out */
	x = write(1, buf_raw, bc);
	if (x <= 0) {
		fprintf(stderr, "write oops: %d\n", x);
		exit(1);
	}
	if (xlimit && p)
		goto nextbuf;           /* go get some more */
	exit(0);

do_dump:
/* here if dumping raw stuff into an ascii file */
/* gad, this is *so* much simpler!  can we say "don't rewrite printf"? */
	x = read(0, buf_raw, 8192);
	if (x <= 0)
		exit(0);
	q = buf_raw;
	for (; x > 0; x--) {
		p = q;
		printf("%-3.3d # 0x%-2.2x # ", *p, *p);
		if ((*p > 31) && (*p < 127))
			printf("%c %d\n", *p, bc);
		else
			printf(". %d\n", bc);
		q++;
		bc++;
		if (limit) {
			xlimit--;
			if (xlimit == 0) {
				fflush(stdout);
				exit(0);
			}
		}       /* limit */
	}               /* for */
	goto nextbuf;

do_rand:
/* here if generating all-random bytes.  Stays in this loop */
	p = buf_raw;
	while (1) {
		*p = (randint() & 0xff);
		write(1, p, 1);         /* makes very slow! */
		if (limit) {
			xlimit--;
			if (xlimit == 0)
				break;
		}
	} /* while */
	exit(0);

} /* main */
