/* Translated from Basic into C by 
 * Peter Conrad <conrad@unix-ag.uni-kl.de
 * 30-jun-94
 *
 * has been tested on Solaris 2.2 using gcc -O -o enigma enigma.c -lucb -lelf
 * If you don't have ftime() #define NOFTIME below
 *
	'This program implements a simulation of the famous ENIGMA cipher
	'machine which was used by the Germans before and during WWII.
	'Information used in writing the program was obtained from the
	'following references:
	'
	'       Garlinski, Joseph, The Enigma War. Charles Scribner's
	'         Sons, New York, NY, 1980.
	'       Diffie,W. and M.E. Hellman, "Privacy and Authentication:
	'         An Introduction to Cryptography", Proc. IEEE, vol. 67,
	'         No. 3, March 1979, pp. 397-427
	'
	'The original ENIGMA machine operated something like a typewriter.
	'The cipher clerk would press a key corresponding to a plaintext
	'letter, and a light would light up to indicate the ciphertext
	'letter.  This program operates on sequential ASCII files. You choose
	'the file to you wish to encrypt, and where you would like the
	'output writtem, and the program does the rest.  The program
	'encrypts only capital letters, and ignores any other characters
	'which may be present in the input file.
	'
	'The file DATA.RND contains data for ten rotors and three reflectors.
	'You choose which three rotors and which reflector you wish to use.
	'The connections for the rotors and reflectors were generated randomly.
	'
	'This program is released to the public domain with the understanding
	'that it shall not be sold or resold for profit.
	'
	'If you have any comments, write to:
	'
	'       John A. Shonder
	'       P.O. Box 4102
	'       Oak Ridge, TN 37830
	'  <padrote@delphi.com>
   '
*/
/* Remove this comment if you don't have ftime() * /
#define	NOFTIME */

#include <stdio.h>

#ifndef NOFTIME
#include <sys/time.h>
#include <sys/timeb.h>
#endif

/*
	'Array W implements the forward rotor and reflector connections
	'Array IW implements the inverse of the rotor connections
	'Array N holds the initial rotor positions
	'Array P implements the plug connections
*/
int	W[5][26], IW[4][26], N[4], P[26];

char	buffer[1024];

void incRotor( int rotor )
{
/* 'This subroutine increments the rotors */

int	T, j;

	T = W[rotor][25];
	for( j=25; j>=1; j-- )
		W[rotor][j] = W[rotor][j-1];
	W[rotor][0] = T;
	for( j=0; j<=25; j++ )
		IW[rotor][j] = (IW[rotor][j]+1)%26;
}

int processLine( FILE *out, int x )
{
/* 'This subroutine processes one line of text
   The text is in the global array buffer. Ciphertext is
   written to the given file. Return value is number of processed
   characters. */

int	i, j, w, u;

	for( i=0; i<strlen(buffer); i++ )
	{
		w = buffer[i]; /* 'Pick off a character and convert to integer */
		w = (w>='A' && w<='Z')?w-'A':w-'a';

		if( w>=0 && w<=25) /* 'If not a capital letter, get next character */
		{
			u = P[w];	/* 'Send character through plug connection */
			for( j=1; j<=4; j++ )
				u = W[j][u];	/* 'Send character through rotors and reflector */
			for( j=3; j>=1; j-- )
				u = IW[j][u];	/* 'Send character back through rotors*/
			u = P[u] + 'A';		/* 'Send character through plug connection*/
			x++;			/* 'Count characters for reference */
			fputc( u, out );	/* 'Print transformed character to output file*/
			if( x % 5 == 0 ) fputc( ' ', out ); /* 'Print in blocks of five */
			if( x %65 == 0 ) fputc( '\n', out); /* 'Print 13 blocks per line */

			incRotor( 3 );		/* 'Increment rotor 3 */
			if( W[3][0]==N[3] ) 	/* 'Check if rotor 3 has made a complete revolution */
			{
				incRotor( 2 );	/* If so, then increment rotor 2 */
				if( W[2][0]==N[2] )/* 'Check if rotor 2 has made a complete revolution */
					incRotor( 1 );	/* 'If so, then increment rotor 1*/
			}
		}
	}

	return x;
}

void main()
{
FILE		*data, *in, *out;
int		i, j, x, T, P1, P2;
long		LOL, OEL;
char		q[8], infile[81], outfile[81];

#ifndef NOFTIME
struct timeb	t1, t2;
#endif

	printf( "ENIGMA SIMULATOR\n" );
	printf( "Written by John A. Shonder\n" );
	printf( "Translated into C by Peter Conrad <conrad@unix-ag.uni-kl.de>\n" );

/*	'DATA.RND contains the connections for the rotors and reflectors */
	data = fopen( "data.rnd", "r" );
	if( !data )
	{
		printf("\nCan't find data.rnd file!\n" );
		exit();
	}

	printf( "\n\n\t\t\t\tROTOR SELECTION\n\n" );
	printf( "     The Enigma uses three rotors, one each in positions one, two, and three.\n" );
	printf( "The basket contains ten rotors labeled 0 through 9. Pick any three.\n\n\n" );

	for( i = 1; i<=3; i++ )
	{
                /* 'Get the connections for each rotor */
		do{
			printf( "Rotor to be used in position %d: ", i );
			scanf( "%d", &x );
			if( x<0 || x>9) printf( "Enter a number from 0 to 9.\n" );
		}while( x<0 || x>9 );
		LOL = 26 * x;
		fseek( data, LOL, SEEK_SET );
		for( j=0; j<=25; j++ )
		{
			W[i][j] = fgetc( data );
		}
	}

	printf( "\n\n\t\t\t      REFLECTOR SELECTION\n\n" );
	printf( "     The basket also contains three different reflectors labeled 1 through 3.\n" );
	printf( "Choose any one.\n\n" );
	/* 'Get the connections for the reflector */
	do{
		printf( "Which reflector: " );
		scanf( "%d", &x );
		if( x<1 || x>3 ) printf( "Enter a number from 1 to 3.\n" );
	}while(  x<1 || x>3 );

	LOL = 26 * (x + 9);
	fseek( data, LOL, SEEK_SET );
	for( i=0; i<=25; i++ )
	{
		W[4][i] = fgetc( data );
	}
	fclose( data );

	printf( "\n\n\t\t\t\t RING SETTINGS\n\n" );
	printf( "     The ring on each rotor is initially set to position 26. Enter the setting\n" );
	printf( "for each ring. Enter an integer between 1 and 26.\n\n" );
	for( i=1; i <= 3; i++ )
	{
		do{
			printf( "Initial setting of rotor %d: ", i );
			scanf( "%d", &x );
			if( x<1 || x>26 ) printf( "Enter a number between 1 and 26.\n" );
		}while( x<1 || x>26 );
		for( j=0; j<=25; j++ )
			W[i][j] = (W[i][j] + x) % 26;
	}

	printf( "\n\n\t\t\t     INITIAL ROTOR SETTINGS\n\n" );
	printf( "     Enter the initial setting for each rotor. Enter a letter.\n\n" );
	for( i=1; i<=3; i++ )
	{
		N[i] = -1;
		do{
			printf( "Initial position of rotor %d: ", i );
			scanf( "%1s", q );
			if( (q[0]>='A' && q[0]<='Z') || (q[0]>='a' && q[0]<='z') )
				N[i] = (q[0]>='A' && q[0]<='Z')?q[0]-'A':q[0]-'a';
			else
				printf( "Enter a letter [a-zA-Z].\n" );
		}while( N[i] < 0 );
		while( W[i][0] != N[i] )
		{
			T = W[i][25];
			for( j=25; j>=1; j--)
				W[i][j] = W[i][j-1];
			W[i][0] = T;
		}
		for( j=0; j<=25; j++ )
			IW[i][W[i][j]] = j;
	}

	printf( "\n\n\t\t\t\tPLUG CONNECTIONS\n\n" );
	printf( "     Now set the plug connections. The Enigma can be set with from zero to\n" );
	printf( "thirteen plugs. At the prompt, enter the pair of letters to be connected,\n" );
	printf( "for example, AX. Enter '--' when you are finished.\n\n" );
	for( i=0; i<=25; i++ )
		P[i] = -1;
	for( i=1; i<=13 && strcmp( "--", q ); i++ )
	{
		do{
			printf( "Plug connection %d: ", i );
			scanf( "%2s", q );
			if( strcmp( "--", q ) )
			{
				P1 = (q[0]>='A' && q[0]<='Z')?q[0]-'A':q[0]-'a';
				P2 = (q[1]>='A' && q[1]<='Z')?q[1]-'A':q[1]-'a';
				if( P1<0 || P1>25 || P2<0 || P2>25 )
					printf( "Enter a pair of letters.\n" );
				else if( P1==P2 ) 
					printf( "The two letters must be distinct.\n" );
				else if( P[P1]!=-1 || P[P2]!=-1 )
					printf( "One of the letters you entered has already been used. Enter a different pair.\n" );
				else
				{
					P[P1] = P2;
					P[P2] = P1;
				}
			}
		}while( strcmp( "--", q ) && 
			( P1==P2 || P1<0 || P1>25 || P2<0 || P2>25 || P[P1]!=-1 || P[P2]!=-1 ) );
	}

	for( i=0; i<=25; i++ )
		if( P[i]==-1 ) P[i] = i;

	printf( "\n\nInput file name:  " );
	scanf( "%80s", infile );
	printf(     "Output file name: " );
	scanf( "%80s", outfile );

	in = fopen( infile, "r" );
	if( !in )
	{
		printf( "Can't open input file %s!\n", infile );
		exit();
	}
	out = fopen( outfile, "w" );
	if( !out )
	{
		printf( "Can't open output file %s!\n", outfile );
		exit();
	}

	x = 0;
	printf( "\n\nProcessing...\n" );
#ifndef NOFTIME
	ftime( &t1 );
#endif
	while( !feof( in ) )
	{
		if( fgets( buffer, 1024, in ) )
			x = processLine( out, x );	/* 'Process a line of text */
	}
	fclose( in );
	fclose( out );

#ifndef NOFTIME
	ftime( &t2 );
#endif
	printf( "File %s has been written\n\n", outfile );
#ifndef NOFTIME
	OEL = 1000 * (t2.time - t1.time) + t2.millitm - t1.millitm;
	printf( "\t\t\t      Elapsed time: %d ms.\n", OEL );
#endif
	printf( "\t\t\t    %d characters processed.\n", x );
#ifndef NOFTIME
	if( x > 0 )
		printf( "\t\t\t  Average speed: %d ms/char.\n", OEL / x );
#endif
}

