shuffle

あると便利なんだけどなんか見つからなかったコマンド。shuffle
標準入力を行毎にシャッフルするようなコマンド。
ありそうなんだけどなぁ...

無かったので作ってみた↓

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include <assert.h>
#include <unistd.h>
#include <getopt.h>

//Listは循環リスト
typedef struct _List {
	struct _List* next;
	char* str;
} List;

int main ( int argc, char* argv[] )
{
	//乱数にseedをあたえる
	timeval tv;
	gettimeofday( &tv , NULL ); 
	unsigned int seed = tv.tv_usec;

	while( 1 ) {
		static struct option longopts[] = {
			{ "seed", required_argument, NULL, 's' },
			{ 0, 0, 0, 0 }
		};
		int c = getopt_long( argc, argv, "s:", longopts, NULL );

		if( c == -1 ) {
			break;
		}
		switch( c ) {
		case 's':
			seed = atoi( optarg );	
			break;
		default:
			return -1;
		}
	}
	srand( seed );


	//ファイル(もしくは標準入力)を開く
	FILE* fp = stdin;
	if( optind + 1 == argc ) {
		fp = fopen( argv[optind], "r" );
		if( fp == NULL ) {
			return 1;
		}
	}

	//読み込み
	List* list_cur = NULL;
	char* line = NULL;
	size_t len = 0;
	int lings = 0;
	while( getline( &line, &len, fp ) != -1 ) {
		lings++;
		if( list_cur == NULL ) {
			//リングリスト作成
			list_cur = (List*)malloc( sizeof( List ) );
			if( list_cur == NULL ) {
				free( line );
				break;
			}
			list_cur->next = list_cur;
			list_cur->str = line;
		} else {
			//リングリスト挿入
			List* newNode = (List*)malloc( sizeof( List ) );
			if( list_cur == NULL ) {
				free( line );
				break;
			}
			newNode->next = list_cur->next;
			newNode->str = line;
			list_cur->next = newNode;
		}
		//lineにNULLを代入しないとgetlineでreallocされてしまう
		line = NULL;
		//リングを回す。
		for( int i = 0; i < ( (double)lings * rand() ) / ( RAND_MAX + 1.0 ); i++ ) {
			list_cur = list_cur->next;
		}
	}
	//XXX:getlineはEOFで終了したのかエラーで終了したのか?

	//リングが空ではない
	if( list_cur != NULL ) {
		//getlineでallocされたメモリとリスト用のメモリの開放
		//list_nextはfreeされたメモリを参照しないためのテンポラリ
		//ついでに出力(ついでかよ
		List* list_next = list_cur->next;
		list_cur->next = NULL;	//リングを切る
		list_cur = list_next;
		while( list_cur != NULL ) {
			list_next = list_cur->next;
			printf( "%s", list_cur->str );
			free( list_cur->str );
			free( list_cur );
			list_cur = list_next;
		}
	}
	return 0;
}

せっかくC++なのに殆どCと言う...。ってかこれCでコンパイル通る気がしてきた...。
ん〜STLとか使えるようになりたいなぁ...。
まぁ、これ作ったのかなり前なんで(と言い訳してみる。

"-s"でシードを与えられるので同じ入力与えられれば、同じ結果を返すようにもできます。
まぁ、地味に有用。
mplayerとかシャッフル機能の無いプレーヤなんかでシャッフルしたい場合なんかに、プレイリストファイル自体をシャッフル(w


ということで今回も無駄に長くなったのでここまでノシ