/*
 *  stuff -- ハードディスク消去のための詰め物データを作成
 *
 *  revision history:
 *	0.0:	Apr. 30, 2004 by Dai ISHIJIMA
 *	0.1:	May   1, 2004
 *	0.2:	Jun. 28, 2006 (ランダムパターンとその補数)
 *	0.3:	Jan. 12, 2017 (文字 'U' 0x55)
 *	0.4:	Sep. 23, 2021 (出力長の指定)
 */

#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>

#define BSIZ 1024
#define STDIN 0
#define STDOUT 1
#define STDERR 2

#define NEWLINE '\n'

#define ZERO 'z'
#define FILL 'f'
#define RAND 'r'
#define GENERATE 'g'
#define PATTERN  'p'
#define INVERT   'q'
#define CHAR_55  'U'	/* 0x55 */
#define CHAR_AA  'n'	/* 0xAA */

#define shift --argc; ++argv


#define swap(t,a,b) {t _swap_tmp; _swap_tmp=(a); a=b; b=_swap_tmp;}


/* 一文字出力 */
void putch(int fd, unsigned char ch)
{
    write(fd, &ch, 1);
}


/* 文字列出力 */
void myputs(int fd, char *s)
{
    while (*s) {
	putch(fd, *s);
	++s;
    }
}


/* 乱数でデータをかきまぜる */
void stair(int len, unsigned char *buf)
{
    int i, n;

    for (i = 0; i < len; i++) {
	n = random() & (len - 1);		/* len は 2^n と仮定 */
	swap(unsigned char, buf[i], buf[n]);
    }
}


/* 書き込み用データの初期化 */
void init(int len, unsigned char *buf, int mode)
{
    int i;

    if (mode == ZERO) {
	for (i = 0; i < len; i++) {
	    buf[i] = 0;
	}
    }
    else if (mode == FILL) {
	for (i = 0; i < len; i++) {
	    buf[i] = 0xff;
	}
    }
    else if (mode == CHAR_55) {
	for (i = 0; i < len; i++) {
	    buf[i] = 0x55;	/* 0x55 -> 0101 0101b */
	}
    }
    else if (mode == CHAR_AA) {
	for (i = 0; i < len; i++) {
	    buf[i] = 0xAA;	/* 0xAA -> 1010 1010bb */
	}
    }
    else if ((mode == RAND) || (mode == GENERATE)) {
	srandom(time(NULL));
	for (i = 0; i < len; i++) {
	    buf[i] = i;
	}
	stair(len, buf);
    }
    else if (mode == PATTERN) {
	for (i = 0; i < BSIZ; i++) {
	    buf[i] = 0;
	}
	read(STDIN, buf, BSIZ);
    }
    else if (mode == INVERT) {
	for (i = 0; i < BSIZ; i++) {
	    buf[i] = 0;
	}
	read(STDIN, buf, BSIZ);
	for (i = 0; i < len; i++) {
	    buf[i] = ~buf[i];
	}
    }
    else if (mode < 0) {		/* わかりにくいかな */
	for (i = 0; i < len; i++) {
	    buf[i] = -mode;
	}
    }
    else {
	/* ここには来ないはず */
	myputs(STDERR, "stuff: unknown mode: ");
	putch(STDERR, mode);
	putch(STDERR, NEWLINE);
	exit(1);
    }
}


/* コマンドライン指定に基づいて、0, 1, ランダムデータを出力 */    
int main(int argc, char *argv[])
{
    int mode;
    unsigned char buf[BSIZ];
    int len = 0;

    mode = ZERO;
    shift;
    while ((argc > 0) && (argv[0][0] == '-')) {
	if ((argv[0][1] == '0') || (argv[0][1] == 'z')) {
	    /* 0 を出力 */
	    mode = ZERO;
	}
	else if ((argv[0][1] == '1') || (argv[0][1] == 'f')) {
	    /* 1 を出力 */
	    mode = FILL;
	}
	else if ((argv[0][1] == 'R') || (argv[0][1] == 'r')) {
	    /* ランダムデータを出力 */
	    mode = RAND;
	}
	else if ((argv[0][1] == 'G') || (argv[0][1] == 'g')) {
	    /* ランダムパターンを生成 */
	    mode = GENERATE;
	}
	else if ((argv[0][1] == 'P') || (argv[0][1] == 'p')) {
	    /* パターンを繰り返し出力 */
	    mode = PATTERN;
	}
	else if (argv[0][1] == 'F') {
	    /* パターンを繰り返し出力 */
	    mode = PATTERN;
	}
	else if ((argv[0][1] == 'U') || (argv[0][1] == '5')) {
	    /* 文字 "U" 0x55 を繰り返し出力 */
	    mode = CHAR_55;
	}
	else if ((argv[0][1] == 'n') || (argv[0][1] == 'A')) {
	    /* 文字 "U" 0x55 のビット反転 0xAA を繰り返し出力 */
	    mode = CHAR_AA;
	}
	else if ((argv[0][1] == 'Q') || (argv[0][1] == 'q')) {
	    /* パターンの補数を繰り返し出力 */
	    mode = INVERT;
	}
	else if (argv[0][1] == 'l') {
	    shift;
	    len = atoi(*argv);
	}
	else if (argv[0][1] == 'v') {
	    shift;
	    mode = -atoi(*argv);
	}
	else {
	    myputs(STDERR, "Usage: stuff [-[01Rzfrgpqu]]\n");
	    exit(1);
	}
	shift;
    }
    if (argc > 0) {
	myputs(STDERR, "Usage: stuff [-[01Rzfrgpq]]\n");
	exit(1);
    }
    init(BSIZ, buf, mode);
    if (mode == GENERATE) {
	write(STDOUT, buf, BSIZ);
    }
    else {
	if (len == 0) {
	    for (;;) {
		if (mode == RAND) {
		    stair(BSIZ, buf);
		}
		write(STDOUT, buf, BSIZ);
	    }
	}
	else {
	    while (len > 0) {
		if (mode == RAND) {
		    stair(BSIZ, buf);
		}
		write(STDOUT, buf, BSIZ);
		--len;
	    }
	}
    }
    exit(0);
}

/* Local Variables: */
/* compile-command:"cc -Wall -O -s -static -o stuff stuff.c" */
/* End: */
