/*****************************************************//**
 * @brief	Vigenere cipher of texts or text-files, with flexible alphabet.
 * @file	vigenere.c
 * @version	0.3
 * @date	19 Feb, 2012
 * @author 	migf1 <mig_f1@hotmail.com>
 * @par Language:
 *		ANSI C (C89)
 * @remark	Source code tab-size: 8
 * @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>
#include <limits.h>

const short int DEBUGMODE = 1;		/* set it to 0 to disable own error messages */
const short int ABSHUFFLE = 1;		/* set it to 0 to disable alphabet shuffling */

#define MAXINPUT	(1024+1)	/* limit in chars for reading the stdin line */

/* Macro for outputting our own error-messages */
#define MSG_ERROR( msg )							\
do {										\
	extern const short int DEBUGMODE;					\
	if ( DEBUGMODE ) {							\
		fprintf(stderr, "\n*** ERR [%s()|ln:%d]: %s\n",			\
			__func__, __LINE__, msg );				\
		fputc('\n', stderr);						\
	}									\
} while(0)

/* Portable alternative to Windows system("pause"); */
#define pressENTER()								\
do{									\
	char mYcHAr;							\
	printf( "press ENTER..." );					\
	while ( (mYcHAr=getchar()) != '\n' && mYcHAr != EOF )		\
		;							\
}while(0)

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

/*********************************************************//**
 * @par Prototype:
 * 	int fname_exists( const char *fname );
 *
 * @brief		Check whether a file exits or not.
 * @param[in] fname	(const char *) A c-string holding the name of the file
 *			to be chacked.
 * @return		1 (TRUE) if the file exists, 0 (FALSE) otherwise.
 *************************************************************
 */
int fname_exists( const char *fname )
{
	FILE *fp = NULL;

	if ( !fname || !*fname || !(fp=fopen(fname,"r")) )
		return 0;	/* FALSE */

	if ( fp )
		fclose(fp);

	return 1;		/* TRUE */
}

/*********************************************************//**
 * @par Prototype:
 * 	int int_read( int *intvar );
 *
 * @brief			Read an integer from stdin.
 * @param[in,out] intvar	(int *) A pointer to the integer variable to be read.
 * @return			The integer value read, or INT_MAX on error.
 *************************************************************
 */
int int_read( int *intvar )
{
	int try = 0;
	char input[ MAXINPUT ] = {'\0'};

	if ( !fgets(input, MAXINPUT, stdin) )
		return INT_MAX;

	if ( EOF == (try=sscanf(input, "%d", intvar)) || try < 1 )
		return INT_MAX;

	return *intvar;
}

/*********************************************************//**
 * @par Prototype:
 * 	char *s_read( char *s );
 *
 * @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;
}

/*********************************************************//**
 * @par Prototype:
 * 	int s_cindex( const char *s, const char c );
 *
 * @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;
	char *str = (char *)s;

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

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

/*********************************************************//**
 * @par Prototype:
 * 	char *s_shuffle( char *s );
 *
 * @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 )
{
	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;
	}

	return s;
}

/*********************************************************//**
 * @par Prototype:
 * 	char *s_rotate_left( char *s, int rotby, const size_t len );
 *
 * @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 size_t 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;
}

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

	if ( !sarr )
		return;

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

	return;
}

/*********************************************************//**
 * @par Prototype:
 * 	void sarr_free( char ***sarr );
 *
 * @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 )
{
	size_t i = 0;

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

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

	*sarr = NULL;
}

/*********************************************************//**
 * @par Prototype:
 * 	char **sarr_new( const int nstrings, const int lenstr );
 *
 * @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;
}

/*********************************************************//**
 * @par Prototype:
 * unsigned char c_encrypt_vigenere(
 *	const unsigned char c, const unsigned char kc, ConstUString tabrecta[] );
 *
 * @brief		Encrypt an unsigned character according to its corresponding
 * 			Vigenere key-character.
 * @param[in] c		(const unsigned char) The character to be encrypted.
 * @param[in] kc	(const unsigned char) The corresponding Vigenere key-character.
 * @param[in] tabrecta	(ConstUString *) The Vigenere tabula recta, holding all the
 * 			alphabets.
 * @return		The encrypted character, or '\0' on error (if either c or kc
 * 			is out of the basic alphabet, c is returned unchanged).
 * @remark		http://e...content-available-to-author-only...a.org/wiki/Vigen%C3%A8re_cipher
 * @sa			s_encrypt_vigenere(), f_encrypt_vigenere().
 ************************************************************/
unsigned char c_encrypt_vigenere(
	const unsigned char c, const unsigned char kc, ConstUString 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] || c == '\0' )
		return '\0';

	/* characters out of alphabet are returned unchanged */
	if ( -1 == (ci=s_cindex( (const char *)tabrecta[0], (const char)c ))
	|| -1 == (kci=s_cindex( (const char *)tabrecta[0], (const char)kc)) )
		return c;

	return tabrecta[kci][ci];
}

/*********************************************************//**
 * @par Prototype:
 * unsigned char c_decrypt_vigenere(
 *	const unsigned char c, const unsigned char kc, ConstUString tabrecta[] );
 *
 * @brief		Decrypt an unsigned character according to its corresponding
 * 			Vigenere key-character.
 * @param[in] c		(const unsigned char) The enrypted character.
 * @param[in] kc	(const unsigned char) The corresponding Vigenere key-character.
 * @param[in] tabrecta	(ConstUString *) The Vigenere tabula recta, holding all the
 * 			alphabets.
 * @return		The decrypted character, or '\0' on error (if either c or kc
 * 			is out of the basic alphabet, c is returned unchanged).
 * @remark		http://e...content-available-to-author-only...a.org/wiki/Vigen%C3%A8re_cipher
 * @sa			s_decrypt_vigenere(), f_decrypt_vigenere().
 ************************************************************/
unsigned char c_decrypt_vigenere(
	const unsigned char c, const unsigned char kc, ConstUString 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] || c == '\0' )
		return '\0';

	/* characters out of alphabet are returned unchanged */
	if ( -1 == (ci=s_cindex((const char *)tabrecta[0], (const char)c))
	|| -1 == (kci=s_cindex((const char *)tabrecta[0], (const char)kc)) )
		return c;

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

	return tabrecta[0][ci];			/* ci'th char in the basic alphabet  */
}

/*********************************************************//**
 * @par Prototype:
 * 	int vigkey_repeat( unsigned char *skey, const size_t txtlen );
 *
 * @brief			Repeat or truncate a Vigenere base key-string
 *				so that its length matches a given length.
 * @param[in,out] skey		(unsigned char *) The Vigenere base key-string
 *				to be modified.
 * @param[in] txtlen		(const int) The length to be matched.
 * @return			1 (TRUE) or 0 (FALSE) on error.
 * @remark			The length to be matched should be equal to
 *				the length of the encrypted/decrypted text.
 *************************************************************
 */
int vigkey_repeat( unsigned char *skey, const size_t txtlen )
{
	size_t i=0, len=0;

	/* sanity checks */
	if ( !skey || !*skey || txtlen > MAXINPUT-1)
		return 0;	/* FALSE */

	/* when base key is longer than plain text, truncate it */
	len = strlen( (char *)skey );
	if (len > txtlen) {
		skey[ txtlen ] = '\0'; 
	}
	/* when base key is shorter than plain text, repeat it */
	else {
		for (i=len; i+len < txtlen; i += len)
			memmove( &skey[i], skey, len * sizeof(char) );
		memmove( &skey[i], skey, (txtlen-i) * sizeof(char) );
	}

	return 1;		/* TRUE */
}

/*********************************************************//**
 * @par Prototype:
 * 	unsigned char *s_encrypt_vigenere(
 *		unsigned char *s, const unsigned char *repkey, ConstUString tabrecta[]
 *	);
 *
 * @brief		Encrypt an unsigned c-string according to a Vigenere
 *			repetitive key c-string.
 * @param[in,out] s	(unsigned char *) The unsigned c-string to be encrypted.
 * @param[in] repkey	(const unsigned char *) The Vigenere repetitive key c-string.
 * @param[in] tabrecta	(ConstUString *) The Vigenere tabula recta, holding all the
 * 			alphabets.
 * @return		The encrypted unsigned c-string, or NULL on error.
 * @note		Contrary to the function f_encrypt_vigenere() which expects the
 *			base key-string, this one expects the repetitive key-string.
 * @sa			c_encrypt_vigenere(), f_encrypt_vigenere().
 ************************************************************/
unsigned char *s_encrypt_vigenere(
	unsigned char *s, const unsigned char *repkey, ConstUString tabrecta[] )
{
	size_t  i = 0;				/* to parse s & repkey */

	/* sanity checks */
	if ( !s || !*s || !repkey || !*repkey || !tabrecta ) {
		MSG_ERROR("Sanity check failed!");
		return NULL;
	}
	if (  strlen( (char *)s) != strlen((char *)repkey) ) {
		MSG_ERROR("Key length does not match text length!");
		return NULL;
	}

	for (i=0; s[i]; i++)
	{
		s[i] = c_encrypt_vigenere(s[i], repkey[i], (ArrayOfConstUString)tabrecta);
		if ( '\0' == s[i] ) {
			MSG_ERROR("Invalid character found during encryption!");
			return NULL;
		}
	}

	return s;
	
}

/*********************************************************//**
 * @par Prototype:
 * 	unsigned char *s_decrypt_vigenere(
 *		unsigned char *s, const unsigned char *repkey, ConstUString tabrecta[]
 *	);
 *
 * @brief		Decrypt an unsigned c-string according to a Vigenere
 * 			repetitive key c-string.
 * @param[in,out] s	(unsigned char *) The unsigned c-string to be decrypted.
 * @param[in] repkey	(const unsigned char *) The repetitive Vigenere key c-string.
 * @param[in] tabrecta	(ConstUString *) The Vigenere tabula recta, holding all the
 * 			alphabets.
 * @return		The decrypted unsigned c-string, or NULL on error.
 * @note		Contrary to the function f_decrypt_vigenere() which expects the
 *			base key-string, this one expects the repetitive key-string.
 * @sa			c_decrypt_vigenere(), f_decrypt_vigenere().
 ************************************************************/
unsigned char *s_decrypt_vigenere(
	unsigned char *s, const unsigned char *repkey, ConstUString tabrecta[] )
{
	int  i = 0;				/* to parse s & repkey */

	/* sanity checks */
	if ( !s || !*s || !repkey || !*repkey || !tabrecta
	|| strlen((char *)s) != strlen((char *)repkey) ) {
		MSG_ERROR("Sanity check failed!");
		return NULL;
	}

	for (i=0; s[i]; i++)
	{
		if ( '\0' == (s[i] = c_decrypt_vigenere(s[i], repkey[i], tabrecta)) ) {
			MSG_ERROR("Invalid character found during decryption!");
			return NULL;
		}
	}

	return s;
	
}

/*********************************************************//**
 * @par Prototype:
 * 	int f_encrypt_vigenere(
 *		const char *, const unsigned char *, ConstUString *, const char *
 *	);
 *
 * @brief		Encrypt the contents of a text-file, and save them to another
 * 			text-file.
 * @param[in] fname	(const char *fname) A c-string holding the name of the file
 * 			whose contents are about to get encrypted.
 * @param[in] skey	(const unsigned char *) An unsigned c-string holding the
 *			Vigenere base key (that is, without being repititive).
 * @param[in] tabrecta	(ConstUString *) The Vigenere tabula recta, holding all the
 * 			alphabets.
 * @param[in] decfname	(const char *fname) A c-string holding the name of the file
 * 			to be created with the encrypted contents.
 * @return		1 (TRUE) or 0 (FALSE) on error.
 * @note		Contrary to the function s_encrypt_vigenere() which expects the
 *			repetitive key-string, this one expects the base key-string.
 * @sa			c_encrypt_vigenere(), s_encrypt_vigenere().
 *************************************************************
 */
int f_encrypt_vigenere(
	const char 		*fname,
	const unsigned char 	*skey,
	ConstUString 		tabrecta[],
	const char 		*encfname )
{
	FILE *fp = NULL, *encfp = NULL;
	int cin = '\0', cout = '\0';
	size_t i = 0;

	if ( !fname || !*fname || NULL == (fp = fopen(fname, "r")) )
		goto ret_failure;
	if ( !encfname || !*encfname || NULL == (encfp = fopen(encfname, "w")) )
		goto ret_failure;

	while ( EOF != (cin = fgetc(fp)) )
	{
		if ( '\0' == skey[i] )
			i = 0;

		cout = (int)c_encrypt_vigenere((unsigned char)cin, skey[i], tabrecta);
		if ( 0 == cout )
			goto ret_failure;

		if ( EOF == fputc(cout, encfp) )
			goto ret_failure;

		i++;
	}

	if ( !feof(fp) )
		fputs("\nf_encrypt_vigenere() failed!\n", stderr);

	fflush(encfp);
	fclose(encfp);
	fclose(fp);

	return 1;		/* TRUE */

ret_failure:
	if ( encfp ) {
		fflush( encfp );
		fclose( encfp );
	}
	if ( fp )
		fclose( fp );
	return 0;		/* FALSE */
}

/*********************************************************//**
 *
 *************************************************************
 */
#if 0
int _f_encrypt_vigenere(
	const char 	*fname,
	char 		*skey,
	ConstUString 	tabrecta[],
	const char 	*encfname )
{
	FILE *fp = NULL, *encfp = NULL;
	char line[ MAXINPUT ] = {'\0'};
	char repkey[ MAXINPUT ] = {'\0'};
	size_t lenskey = 0;

	if ( !fname || !*fname || NULL == (fp = fopen(fname, "r")) )
		goto ret_failure;
	if ( !encfname || !*encfname || NULL == (encfp = fopen(encfname, "w")) )
		goto ret_failure;

	lenskey = strlen(skey);
	while ( fgets(line, MAXINPUT, fp) )
	{
		strncpy( repkey, skey, MAXINPUT-1 );
		if ( !vigkey_repeat(repkey, strlen(line)) )
			goto ret_failure;
		if ( !s_encrypt_vigenere(line, repkey, tabrecta ) )
			goto ret_failure;
		if ( EOF == fputs(line, encfp) )
			goto ret_failure;
	}

	if ( ferror(fp) )
		goto ret_failure;

	fclose(encfp);
	fclose(fp);
	return 1;		/* TRUE */

ret_failure:
	if ( encfp )
		fclose( encfp );
	if ( fp )
		fclose( fp );
	return 0;		/* FALSE */
}
#endif

/*********************************************************//**
 * @par Prototype:
 * 	int f_decrypt_vigenere(
 *		const char *, const unsigned char *, ConstUString *, const char *
 *	);
 *
 * @brief		Decrypt the contents of a text-file, and save them to another
 * 			text-file.
 * @param[in] fname	(const char *fname) A c-string holding the name of the file
 * 			whose contents are about to get decrypted.
 * @param[in] skey	(const unsigned char *) An unsigned c-string holding the
 *			Vigenere base key (that is, without being repititive).
 * @param[in] tabrecta	(ConstUString *) The Vigenere tabula recta, holding all the
 * 			alphabets.
 * @param[in] decfname	(const char *fname) A c-string holding the name of the file
 * 			to be created with the decrypted contents.
 * @return		1 (TRUE) or 0 (FALSE) on error.
 * @note		Contrary to the function s_decrypt_vigenere() which expects the
 *			repetitive key-string, this one expects the base key-string.
 * @sa			c_decrypt_vigenere(), s_decrypt_vigenere().
 *************************************************************
 */
int f_decrypt_vigenere(
	const char 		*fname,
	const unsigned char 	*skey,
	ConstUString 		tabrecta[],
	const char 		*decfname )
{
	FILE *fp = NULL, *decfp = NULL;
	int cin = 0, cout = 0;
	size_t i = 0;

	if ( !skey || !fname || !*fname || !(fp = fopen(fname, "r")) )
		goto ret_failure;
	if ( !decfname || !*decfname || !(decfp = fopen(decfname, "w")) )
		goto ret_failure;

	i = 0;
	while ( EOF != (cin = fgetc(fp)) )
	{
		if ( '\0' == skey[i] )
			i = 0;

		cout = (int)c_decrypt_vigenere((unsigned char)cin, skey[i], tabrecta);
		if ( 0 == cout )
			goto ret_failure;

		if ( EOF == fputc(cout, decfp) )
			goto ret_failure;

		i++;
	}

	if ( !feof(fp) )
		goto ret_failure;

	fflush( decfp );
	fclose(decfp);
	fclose(fp);
	return 1;		/* TRUE */

ret_failure:
	if ( decfp ) {
		fflush( decfp );
		fclose( decfp );
	}
	if ( fp )
		fclose( fp );
	return 0;		/* FALSE */
}

/*********************************************************//**
 * @par Prototype:
 * 	int do_file_vigenere( ConstUString tabrecta[] );
 *
 * @brief		Handle menu-selection "input from file".
 * @param[in] tabrecta	(ConstUString *) The Vigenere tabula recta, holding all the
 * 			alphabets.
 * @return		1 (TRUE) or 0 (FALSE) on error.
 *************************************************************
 */
int do_file_vigenere( ConstUString tabrecta[] )
{
	char fname[ MAXINPUT ] = {'\0'};
	char *encfname = "enc.txt";
	char *decfname = "dec.txt";
	unsigned char vigkey[ MAXINPUT ] = {'\0'};

	/* get the name of the text file to be encrypted */
	do
		printf("Enter the name of the file: ");
	while ( '\0' == *s_read(fname) );

	if (  !fname_exists(fname)) {
		perror(NULL);
		MSG_ERROR("No such file, try again...");
		return 0;	/* FALSE */
	}

	/* get a Vigenere base key-string */
	do
		printf("Enter a base key-string: ");
	while ( '\0' == *s_read((char *)vigkey) );

	/* encrypt the file fname */
	if ( !f_encrypt_vigenere( fname, vigkey, (ArrayOfConstUString)tabrecta, encfname ) ) {
		MSG_ERROR("f_encrypt_vigenere() failed!");
		return 0;	/* FALSE */
	}
	printf( "\nfile %s encrypted successfully, the resulting file is %s\n",
		fname, encfname);

	/* decrypt the file out.txt */
	if ( !f_decrypt_vigenere( encfname, vigkey, (ArrayOfConstUString)tabrecta, decfname ) ) {
		MSG_ERROR("f_decrypt_vigenere() failed!");
		return 0;	/* FALSE */
	}
	printf( "file %s decrypted successfully, the resulting file is %s\n",
		encfname, decfname);

	return 1;	/* TRUE */
}

/*********************************************************//**
 * @par Prototype:
 * 	int do_stdin_vigenere( ConstUString tabrecta[] );
 *
 * @brief		Handle menu-selection "input from keyboard".
 * @param[in] tabrecta	(ConstUString *) The Vigenere tabula recta, holding all the
 * 			alphabets.
 * @return		1 (TRUE) or 0 (FALSE) on error.
 *************************************************************
 */
int do_stdin_vigenere( ConstUString tabrecta[] )
{
	unsigned char text[ MAXINPUT ] = {'\0'};	/* plain text to en(de)crypt */
	unsigned char vigkey[ MAXINPUT ] = {'\0'};

	/* get the text to be encrypted */
	do
		printf("Text to be encrypted : ");
	while ( *s_read((char *)text) == '\0' );

	/* get a Vigenere base key-string */
	do
		printf("Enter base key-string: ");
	while ( *s_read((char *)vigkey) == '\0' );

	/* convert the base key-string to a repetitive key-string */
	if ( !vigkey_repeat(vigkey, strlen((char *)text)) ) {
		MSG_ERROR("Key repetition failed, nothing more to do!");
		return 0;	/* FALSE */
	}
	printf("\nRepetitive key-string: %s\n", vigkey);

	/* encrypt given text with the given Vigenere key */
	if ( NULL == s_encrypt_vigenere(text, vigkey, (ArrayOfConstUString)tabrecta) ) {
		MSG_ERROR("encryption error (perhaps invalid char in text)!");
		return 0;	/* FALSE */
	}
	printf("\nEncrypted text: %s\n", text );

	/* decrypt given text using given Vigenere key */
	if ( NULL == s_decrypt_vigenere(text, vigkey, (ArrayOfConstUString)tabrecta) ) {
		MSG_ERROR("\tdecryption error (perhaps invalid char in text)");
		return 0;	/* FALSE */
	}
	printf("Decrypted text: %s\n\n", text );

	return 1;	/* TRUE */
}

/*********************************************************//**
 * @par Prototype:
 * 	char menu_selection( void );
 *
 * @brief		Display the commands menu and the get user selection.
 * @return		(char) The menu selection, as a character.
 *************************************************************
 */
char menu_selection( void )
{
	char input[ MAXINPUT ] = {'\0'};

	puts("\n-----------------------");
	puts("migf1 @ Vigenere Cipher");
	puts("-----------------------");
	puts("1. display the alphabet");
	puts("2. input from keyboard");
	puts("3. input form text file");
	puts("0. exit");
	puts("-----------------------");

	printf("> ");
	s_read( input );

	return *input;
}

/*********************************************************//**
 * @par Prototype:
 * 	short int tabrecta_make(
 *		unsigned char ***, const unsigned char *, const unsigned char,const int
 *	);
 *
 * @brief			Create & fill the Vigenere tabula recta (table of
 * 				unsigned alphabets).
 * @param[in,out] tabrecta	(unsigned char ***) Pointer to an array of unsigned
 *				c-strings (tabula recta) to be created & filled-in.
 * 				If the array is created successfully, its last
 *				c-string will be a NULL pointer (NULL terminated
 * 				array of unsigned c-strings).
 * @param[in] ab		(const unsigned char *) An unsigned c-string to be
 * 				used as basic alphabet (see notes).
 * @param[in] cstart		(const unsigned char) The 1st character in the basic
 * 				alphabet (see notes).
 * @param[in] ablen		(const 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.
 * @sa				tabrecta_extend().
 *************************************************************
 */
short int tabrecta_make(
	unsigned char 		***tabrecta,
	const unsigned char 	*ab,
	const unsigned char 	cstart,
	const int 		ablen )
{
	short int i, len = 0;

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

	/* when tabrecta already exists, just return its alphabet length */
	if ( *tabrecta && (*tabrecta)[0] ) {
		MSG_ERROR("TabulaRecta already existed (stayed unchanged)");
		return strlen( (char *)(*tabrecta)[0] );
	}

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

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

		/* remaining strings have basic alphabet rotated by 1 char at a time */
		for (i=1; (*tabrecta)[i]; i++) {
			strcpy( (char *)(*tabrecta)[i], (char *)(*tabrecta)[0] );
			s_rotate_left( (char *)(*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 = (unsigned char **) 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;
		if ( ABSHUFFLE )
			s_shuffle( (char *)(*tabrecta)[0] );	/* shuffle alphabet  */

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

	/* error */
	else
		return 0;


	return len;
}

/*********************************************************//**
 * @par Prototype:
 * 	short int tabrecta_extend( unsigned char ***, const unsigned char *extension );
 *
 * @brief			Extend the alphabet of an existing tabula recta.
 * @param[in,out] tabrecta	(unsigned char ***) Pointer to a NULL terminated array
 * 				of unsigned c-strings, holding the tabula recta to be
 * 				modified.
 * @param[in] extension		(char **) An unsigned c-string holding the characters
 *				to be added to the existing tabula recta.
 * @return			The length of the extended 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
 * 				extended number of c-string alphabets, excluding
 *				the final NULL string.
 * @note			After the needed houskeeping and error-checking is done,
 *				the function comoletely destroys the old tabula recta
 * 				and re-builds it from scratch. Thus, it is crucial to
 *				always check if it succeeded, before attempting to
 *				move on with the rest of the proggram.
 *************************************************************
 */
short int tabrecta_extend( unsigned char ***tabrecta, const unsigned char *extension )
{
	short int ablen = 0, newlen = 0;
	unsigned char *abnew = NULL;		/* temp c-str for extended alphabet */

	/* sanity checks */
	if ( !tabrecta || !*tabrecta )
		return 0;

	/* when tabula recta exists, get its alphabet length */
	if ( (*tabrecta)[0] )
		ablen = strlen( (char *)(*tabrecta)[0] );

	/* more sanity checks */
	if ( !extension || !*extension )
		return ablen;

	/* allocate memory for the extended alphabet */
	newlen = ablen + strlen( (char *)extension );
	if ( NULL == (abnew = calloc( newlen+1, sizeof(unsigned char) )) )
		return 0;
	/* construct the extended alphabet */
	strcpy( (char *)abnew, (char *)(*tabrecta)[0] );
	strcat( (char *)abnew, (char *)extension );

	/* destroy old tabula recta and re-build it with the extended alphabet */
	sarr_free( (char ***)tabrecta );
	newlen = tabrecta_make( tabrecta, abnew, 0, 0 );

	/* free memory temporarily reserved for the c-string of the extended alphabet */
	free( abnew );

	return newlen;
}

/*********************************************************//**
 *
 *************************************************************
 */
int main( void )
{
	char		msel = '0';			/* menu selection            */
	unsigned char 	**tabrecta = NULL;		/* Vigenere tabula recta     */
	short int 	ablen = 0;			/* length of basic alphabet  */

	/* create the Vigenere tabula recta, with alphabet from ' ' to ASCII(254) */
	if ( 0 == (ablen=tabrecta_make( &tabrecta, NULL, ' ', 223 )) ) {
		MSG_ERROR("Tabula recta initialization failed!");
		goto exit_failure;
	}
	/* extend the alphabet of the created Vigenere tabula recta with 5 more chars*/
	if ( 0 == (ablen=tabrecta_extend( &tabrecta, (unsigned char *)"\t\v\n\r\f")) )
	{
		MSG_ERROR("Tabula recta extension failed!");
		goto exit_failure;
	}

	/* display the commands menu and apply user selection */
	for (;;)
	{
		if ( '\0' == (msel=menu_selection()) )	/* just an ENTER was pressed */
			continue;

		if ( '0' == msel )			/* exit was selected         */
			break;

		if ( '1' == msel )			/* display the alphabet      */
			printf("\nAlphabet (%d chars):\n%s\n", ablen, tabrecta[0] );

		else if ( '2' == msel )	{		/* use stdin for input       */
			do_stdin_vigenere( (ArrayOfConstUString)tabrecta );
			pressENTER();
		}

		else if ( '3' == msel )	{		/* use a file for input      */
			do_file_vigenere( (ArrayOfConstUString)tabrecta );
			pressENTER();
		}

		else
			puts("\ninvalid option, try again...");

	}

	sarr_free( (char ***)&tabrecta );
	exit( EXIT_SUCCESS );

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