/*****************************************************//**
 * @brief	Sample Vigenere cipher, with flexible alphabet.
 *
 * @file	vigenere.c
 * @version	0.2
 * @date	16 Feb, 2012
 * @author 	migf1 <mig_f1@hotmail.com>
 * @par Language:
 *		ANSI C (C89)
 *
 * @note	http://e...content-available-to-author-only...a.org/wiki/Vigen%C3%A8re_cipher.
 *********************************************************
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define SHUFFLE		1		/* set it to 0 to disable alphabet shuffling */
#define MAXINPUT	(1024+1)

#define pressENTER()								\
	do{									\
		char mYcHAr;							\
		printf( "press ENTER..." );					\
		while ( (mYcHAr=getchar()) != '\n' && mYcHAr != EOF )		\
			;							\
	}while(0)

/*
 * Typedefs for passing arrays of strings as arrays of constant strings to functions.
 * (this makes sure that those functions will not alter any of the array contents).
 */
typedef const char *const 	ConstString;
typedef const char *const * 	ArrayOfConstString;


/*********************************************************//**
 * @brief		Read a c-string from stdin.
 * @param[in,out] s	(char *) The c-string to be read.
 * @return		The c-string (it remains unchanged on error)	
 *************************************************************
 */
char *s_read( char *s )
{
	int lenstr = 0;

	if ( !s || !fgets(s, MAXINPUT, stdin) )
		return s;

	lenstr = strlen(s);
	if ( s[ lenstr - 1 ] == '\n' )
		s[ lenstr -1 ] = '\0';

	return s;
}

/*********************************************************//**
 * @brief		Get the index of a given char c in a given c-string s.
 * @param[in] s 	(const char *) The c-string to be searched.
 * @param[in] c		(const char ) The character to be looked for.
 * @return		The zero-based index of c in s, or -1 on error.
 *************************************************************
 */
int s_cindex( const char *s, const char c )
{
	char *cp = NULL;

	if ( !s || !*s )
		return -1;

	return (cp = strchr(s,c)) ? (int)(cp - s) : -1;
}

/*********************************************************//**
 * @brief		Shuffle the contents of a given c-string s.
 * @param[in,out] s	(char *) The c-string to be shuffled.
 * @return		The shuffled c-string (it remains unchanged on error).
 * @note		It requires the constant SHUFFLE to contain an non-zero value.
 *************************************************************
 */
char *s_shuffle( char *s )
{
#if SHUFFLE
	char	*cp = NULL, c = '\0';
	int	len = 0;
	int	i = 0;

	/* sanity check */
	if ( !s || !*s || (len = strlen(s)) < 2 )
		return s;

	srand( time(NULL) );

	for (cp=s; *cp; cp++)
	{
		i = rand() % len;
		c = *cp;
		*cp = s[i];
		s[i] = c;
	}
#endif
	return s;
}

/*********************************************************//**
 * @brief		Roatate to the left a given c-string s of length len,
 *			by rotby characters.
 * @param[in,out] s	(char *) The c-string to be rotated.
 * @param[in] rotby	(int) The amount of characters the c-string will be rotated by.
 * @param[in] len	(const int) The length of the c-string in characters,
 *			excluding the nil terminator.
 * @return		The rotated c-string (remains unchanged on error).
 *************************************************************
 */
char *s_rotate_left( char *s, int rotby, const int len )
{
	char *temp = NULL;

	if ( !s || rotby < 1 || len < 2 || (rotby %= len) == 0 )
		return s;

	if ( NULL == (temp = malloc( rotby * sizeof(char) )) )
		return s;
	memcpy( temp, s, rotby * sizeof(char) );

	memmove( s, &s[ rotby ], (len-rotby) * sizeof(char) );
	memcpy( &s[ len-rotby ], temp, rotby * sizeof(char) );

	free( temp );
	return s;
}

/*********************************************************//**
 * @brief		Print to stdout the contents of a NULL terminated
 *			array of c-strings.
 * @param[in] sarr	(ConstString *) The NULL terminated array of c-strings.
 *************************************************************
 */
void sarr_print( ConstString sarr[] )
{
	int i = 0;

	if ( !sarr )
		return;

	for (i=0; sarr[i]; i++ )
		printf("%2d: %s\n", i, sarr[i] );

	return;
}

/*********************************************************//**
 * @brief		Free memory reserved for a NULL terminated array of c-strings.
 * @param[in,out] sarr	(char ***) Pointer to the NULL terminated array of c-strings.
 * @remark		The array pointer \a *sarr is reset to NULL (that's why it
 * 			passed <em>by reference</em> to the function).
 *************************************************************
 */
void sarr_free( char ***sarr )
{
	int i = 0;

	if ( !sarr || !*sarr )
		return;

	while ( (*sarr)[i] )
		free( (*sarr)[i++] );

	*sarr = NULL;
}

/*********************************************************//**
 * @brief		Reserve memory for a NULL terminated array of c-strings.
 * @param[in] nstrings	(const int) The number of c-strings to be allocated,
 * 			not counting the terminating NULL string.
 * @param[in] lenstr	(const int) The number of character to be allocated for
 * 			each c-string, excluding the nil-terminator character.
 * @return		A pointer to the newly allocated memory, or NULL on error.
 *************************************************************
 */
char **sarr_new( const int nstrings, const int lenstr )
{
	char **sarr = NULL;
	int i=0;

	if ( nstrings < 1 || lenstr < 1 )
		return NULL;

	if ( NULL == (sarr = calloc( nstrings+1, sizeof(char *) )) )
		return NULL;

	for (i=0; i < nstrings; i++)
	{
		if ( NULL == (sarr[i] = calloc( lenstr+1, sizeof(char) )) )
		{
			int j;
			for (j=i-1; j > -1; j--)
				free( sarr[j] );
			free( sarr );
			return NULL;
		}
	}

	return sarr;
}

/*********************************************************//**
 * @brief		Encrypt a character according to its corresponding Vigenere
 *			key-character.
 * @param[in] c		(const char) The character to be encrypted.
 * @param[in] kc	(const char) The corresponding Vigenere key-character.
 * @param[in] tabrecta	(ConstString *) The Vigenere tabula recta, holding all the
 * 			alphabets.
 * @return		The encrypted character, or '\0' on error (if either c or kc
 * 			is not part of the basic alphabet it is considered an error).
 * @remark		http://e...content-available-to-author-only...a.org/wiki/Vigen%C3%A8re_cipher
 ************************************************************/
char c_encrypt_vigenere( const char c, const char kc, ConstString tabrecta[] )
{
	int ci  = -1;				/* index of c in the basic alphabet  */
	int kci = -1;				/* index of kc in the basic alphabet */

	/* sanity checks */
	if ( !tabrecta || !tabrecta[0]
	|| -1 == (ci=s_cindex( tabrecta[0], c ))
	|| -1 == (kci=s_cindex(tabrecta[0], kc)) )
		return '\0';

	return tabrecta[kci][ci];
}

/*********************************************************//**
 * @brief		Decrypt a character according to its corresponding Vigenere
 *			key-character.
 * @param[in] c		(const char) The character to be decrypted.
 * @param[in] kc	(const char) The corresponding Vigenere key-character.
 * @param[in] tabrecta	(ConstString *) The Vigenere tabula recta, holding all the
 * 			alphabets.
 * @return		The decrypted character, or '\0' on error (if either c or kc
 * 			is not part of the basic alphabet it is considered an error).
 * @remark		http://e...content-available-to-author-only...a.org/wiki/Vigen%C3%A8re_cipher
 ************************************************************/
char c_decrypt_vigenere( const char c, const char kc, ConstString tabrecta[] )
{
	int ci  = -1;				/* index of c  */
	int kci = -1;				/* index of kc */

	/* sanity checks (including both c & kc being valid chars of basic alphabet) */
	if ( !tabrecta || !tabrecta[0]
	|| -1 == (ci=s_cindex(tabrecta[0], c))
	|| -1 == (kci=s_cindex(tabrecta[0], kc)) )
		return '\0';

	ci = s_cindex(tabrecta[kci], c);	/* index of c in the kci'th alphabet */
	return tabrecta[0][ci];			/* ci'th char in the basic alphabet  */
}

/*********************************************************//**
 * @brief		Encrypt a c-string according to a Vigenere key c-string.
 * @param[in,out] s	(char *) The c-string to be encrypted.
 * @param[in] skey	(const char *) The Vigenere key c-string.
 * @param[in] tabrecta	(ConstString *) The Vigenere tabula recta, holding all the
 * 			alphabets.
 * @return		The encrypted c-stsring, or NULL on error.
 * @sa			c_encrypt_vigenere()
 ************************************************************/
char *s_encrypt_vigenere(char *s, const char *skey, ConstString tabrecta[])
{
	int  i = 0;				/* to parse s & skey */

	/* sanity checks */
	if ( !s || !*s || !skey || !*skey || !tabrecta || strlen(s) != strlen(skey) )
		return NULL;

	for (i=0; s[i]; i++)
	{
		s[i] = c_encrypt_vigenere(s[i], skey[i], (ArrayOfConstString)tabrecta);
		if ( '\0' == s[i] )
			return NULL;
	}

	return s;
	
}

/*********************************************************//**
 * @brief		Decrypt a c-string according to a Vigenere key c-string.
 * @param[in,out] s	(char *) The c-string to be decrypted.
 * @param[in] skey	(const char *) The Vigenere key c-string.
 * @param[in] tabrecta	(ConstString *) The Vigenere tabula recta, holding all the
 * 			alphabets.
 * @return		The decrypted c-stsring, or NULL on error.
 * @sa			c_decrypt_vigenere()
 ************************************************************/
char *s_decrypt_vigenere( char *s, const char *skey, ConstString tabrecta[] )
{
	int  i = 0;				/* to parse s & skey */

	/* sanity checks */
	if ( !s || !*s || !skey || !*skey || !tabrecta || strlen(s) != strlen(skey) )
		return NULL;

	for (i=0; s[i]; i++)
		if ( '\0' == (s[i] = c_decrypt_vigenere(s[i], skey[i], tabrecta)) )
			return NULL;
	return s;
	
}

/*********************************************************//**
 * @brief		Create & fill the Vigenere tabula recta (table of alphabets).
 *
 * @param[in,out] tabrecta	(char ***) Pointer to an array of c-strings (tabula
 * 				recta) to be created & filled-in. If the array is
 * 				created successfully, its last c-string will be a 
 *				NULL string (NULL terminated array of c-strings).
 * @param[in] ab		(const char *) A c-string to be used as basic
 * 				alphabet (see notes).
 * @param[in] cstart		(const char) The 1st character in the basic alphabet
 * 				(see notes).
 * @param[in] ablen		(cont int) The basic alphabet length, in characters
 *				(see notes).
 *
 * @return			The length of the newly created basic alphabet in
 * 				characters, excluding the nil-terminator, or 0 on
 *				error. Since tabula recta is a square table, the
 * 				return value of the function equals also to the
 * 				number of c-string alphabets created, excluding the
 * 				final NULL string.
 *
 * @note			You may use this function differently depending on
 *				the arguments you pass to it. If you specify a valid,
 *				non-empty, c-string as the 2nd argument, then it is
 * 				used as the basic alphabet and the rest of the
 *				arguments are ignored.
 *				However, if you pass the 2nd argument as NULL or as
 *				an empty c-string, then the basic alphabet is
 * 				constructed from consecutive characters in the
 * 				current ASCII table, starting from character \a
 * 				cstart up to character <em>(cstart + ablen-1)</em>.
 * 				Please note that in this case, the zero character
 * 				'\0' is not allowed (the function returns error).
 *				In general, you should never include the zero
 * 				character in your basic alphabet.
 *************************************************************
 */
int tabrecta_make(
	char ***tabrecta, const char *ab, const char cstart, const int ablen )
{
	int i, len = 0;

	/* sanity check */
	if ( !tabrecta )
		return 0;

	/* when ab exists and it is not an empty c-string */
	if ( ab && *ab )
	{
		/* reserve memory for the tablula recta */
		len = strlen( ab );
		if ( NULL == (*tabrecta = sarr_new(len, len)) )
			return 0;

		/* copy the basic alphabet ab to the 1st string of the tabula recta */
		strcpy( (*tabrecta)[0], ab );
		s_shuffle( (*tabrecta)[0] );

		/* remaining strings have basic alphabet rotated by 1 char at a time */
		for (i=1; (*tabrecta)[i]; i++) {
			strcpy( (*tabrecta)[i], (*tabrecta)[0] );
			s_rotate_left( (*tabrecta)[i], i, len );
		}
	}

	/* when ab is NULL or empty, make alphabet from cstart to (cstart+ablen-1) */
	else if (cstart > '\0' && ablen > 0 && cstart + ablen - 1 < 257)
	{
		/* reserve memory for the tablula recta */
		len = ablen;
		if (NULL == (*tabrecta = sarr_new(len, len)) )
			return 0;

		/* construct the basic alphabet into the 1st string of tabula recta */
		for (i=0; i < len; i++)
			(*tabrecta)[0][i] = cstart + i;
		s_shuffle( (*tabrecta)[0] );		/* shuffle the alphabet   */

		/* remaining strings have basic alphabet rotated by 1 char at a time */
		for (i=1; (*tabrecta)[i]; i++) {
			strcpy( (*tabrecta)[i], (*tabrecta)[0] );
			s_rotate_left( (*tabrecta)[i], i, len );
		}
	}

	/* error */
	else
		return 0;

	return len;
}

/*********************************************************//**
 *
 *************************************************************
 */
int main( void )
{
	char 	**tabrecta = NULL;			/* Vigenere tabula recta     */
	int 	ablen = 0;				/* length of basic alphabet  */
	char 	text[ MAXINPUT ] = {'\0'};		/* text to be encrypted      */
	char 	vigkey[ MAXINPUT ] = {'\0'};		/* Vigenere key              */

	/* create the Vigenere tabula recta, with alphabet from '\t' to ASCII(254) */
	if ( 0 == (ablen=tabrecta_make( &tabrecta, NULL, '\t', 246 )) ) {
		puts("\nTabula recta initialization failed!");
		goto exit_failure;
	}
	printf("Alphabet (%d chars):\n%s\n\n", ablen, tabrecta[0] );

	/* get the text to be encrypted */
	printf("Text to be encrypted: ");
	if ( *s_read(text) == '\0' ) {
		puts("\nempty text was given, nothing to do!");
		goto exit_failure;
	}

	/* get a valid Vigenere key string */
	do {
		printf("Enter the key-string: ");
		s_read(vigkey);
	} while( strlen(vigkey) != strlen(text) );

	/* encrypt given text with the given Vigenere key */
	if ( NULL == s_encrypt_vigenere(text, vigkey, (ArrayOfConstString)tabrecta) ) {
		puts("\tencryption error (perhaps an invalid char was found in text)");
		goto exit_failure;
	}
	printf("\nEncrypted text: %s\n", text );

	/* decrypt given text using given Vigenere key */
	if ( NULL == s_decrypt_vigenere(text, vigkey, (ArrayOfConstString)tabrecta) ) {
		puts("\tdecryption error (perhaps an invalid char was found in text)");
		goto exit_failure;
	}
	printf("Decrypted text: %s\n\n", text );

	sarr_free( &tabrecta );
	pressENTER();
	exit( EXIT_SUCCESS );

exit_failure:
	sarr_free( &tabrecta );
	pressENTER();
	exit( EXIT_FAILURE );
}
