/**************************************************************************
 Peggy Dagley
 
 CMSC 443
 Programming assignment #2 (rotor machine)

  This program is designed to emulate a 2-rotor encryption machine that
 uses a reflector, so that if an A is encrypted to a W, then a W must 
 also encrypt to an A.  I have assigned a 2 dimensional array to hold
 the rotor alphabets.  The user must call the program with a.out followed
 by an input filename and an output filename.  The user will then be prompted
 to provide a key.  This key is two alphabetic uppercase characters separated
 by a space.  This program operates in UPPERCASE only, if a lowercase letter
 is detected, that letter will be converted to uppercase.
 **************************************************************************/


#include <stdio.h>
#include <stdlib.h>


int crypt_key(void);       
int crypt_file(char *, char *);
void set_up_rotors(void);
int encrypt (int);
void print_welcome(void);


static int pos[2];         /* holds the position of the rotors */
static int ltr[2][26];     /* holds the alphabetic letters on the rotors */ 


main(int argc, char *argv[])
{
  int error;

  if(argc != 3)
  {
    printf("USAGE:  <program name> [input file] [output file]\n");
    printf("Exiting\n");
    exit(0);
  }
  else
  {
    print_welcome();
    error = crypt_key();     /* get key */  
    if(error == 0)           /* if valid key, proceed, else exit */
      error = crypt_file(argv[1], argv[2]);  /* perform encryption/decryption*/
    if(error == 0)
      printf("The program has successfully completed\n");
    else
      printf("There has been an error, please re-run program\n");
  }
}
/***************************************************************************
  Function:  print_welcome()
  This function prints a welcome message that explains the program in
  broad terms
  *************************************************************************/

void print_welcome(void)
{
  printf("\n*************************************************************");
  printf("\n              WELCOME TO PEGGY'S VERSION OF");
  printf("\n                      2-ROTOR MACHINE");
  printf("\n\nThis device prompts the user for a two (2) letter key");
  printf("\nThen sets up the rotors accordingly.  This device will");
  printf("\nperform encryption or decryption because it uses a ");
  printf("\nreflector.  The output text will be printed in the");
  printf("\noutput file specified.  GOOD LUCK\n");
  printf("\n************************************************************\n\n");
}


/****************************************************************************
 Function:  crypt_key
 This function prompts the user for a 2-character (uppercase) key.
 Then sets the positions of the rotors according to the key.  In the
 event that two uppercase letters are not entered, an error message
 is displayed and the program exits. If a valid key is entered, then
 a 0 is returned signifying success.  1 signifies failure.
 ***********************************************************************/
int crypt_key ()
{
  int i = 0;                              /* number of chars in key*/
  int idx;                                /* index/counter */
  char a[2];                              /* temporarily hold input*/
  char ch;                                /* holds key character */
  
    fflush(stdout);
    fflush(stdin);
    printf ("Initial rotor position (2 letters separated by spaces): ");
    while((ch = getchar()) != '\n')
    {
	if(isalpha(ch))
	{
	  ch = (toupper(ch));
	  a[i] = ch;
	  i++;
	}
    }
    if(i > 2)   /* Too many letters entered for key */
    {
      printf("You must enter 2 letters separated by spaces for the key\n");
      printf("This program is exiting.\n");
      return 1;
    }
    else                                  /*good key - proceed*/
    {
      for (idx = 0; idx < 2; idx++)      /*setting positions chars*/
	pos[idx] = (a[idx]) - 'A';       /*   according to key*/
      return 0;
    }
}

/*************************************************************************
 Function:  crypt_file
 This function takes in the input filename and output filenames as args.
 It sets up the rotors by calling the set_up_rotors function and then
 attempts to open both input and output files for reading and writing
 respectively.  If success function continues, otherwise the function
 returns a 1, specifying failure and exits program.  Once the files are
 successfully opened, the input file is read character-by-character and
 the function encrypt is called to encrypt/decrypt each character. Upon
 completion of encrypting/decrypting a character, that character is 
 written to the output file. When the entire input file is processed,
 the files are closed and the program exits.
 *************************************************************************/
int crypt_file (char *source, char *dest)
{

  int c;                      /*input char changed to int*/
  FILE *ifp;                  /* input filename */  
  FILE *ofp;                  /* output filename */
  

  set_up_rotors();            /*function call to set up machine*/

  if ((ifp = fopen (source, "rb")) == NULL)
  {
    fprintf (stderr, "Can not open %s for reading.\n", source);
    return 1;
  }
  
  if ((ofp = fopen (dest, "wb")) == NULL)
  {
    fprintf (stderr, "Can not open %s for writing.\n", dest);
    fclose (ifp);
    return 1;
  }
  pos[0] = 0;               /* ensuring positions of rotors are set to 0 */
  pos[1] = 0;               /* before starting encrypt/decrypt phase*/
  while((c = fgetc (ifp)) != EOF)
  {
    if (isalpha (c))                          
    {
      c = encrypt ((int) (toupper(c)));/* function call to encrypt/decrypt */
    }
    if (fputc (c, ofp) == EOF)  /* unable to write to output file*/
    {
      fprintf (stderr, "Could not write to output file %s\n", dest);
      fclose (ifp);
      fclose (ofp);
      return 1;
    }
  }
  
  fclose (ifp);                 
  fclose (ofp);
  return 0;
}

/*************************************************************************
 Function:  set_up_rotors
 This function sets up the rotors so that the starting positions of the
 rotors correspond to the desired key and then assigns the remaining
 positions accordingly.
 **************************************************************************/
void set_up_rotors (void)
{
  int i, j;                  
 
  for (j = 0; j < 26; j++)    /* setting up first rotor to correspond to key */
  {
    ltr[0][j] = pos[0] % 26;
    pos[0]++;
  }

  for (j = 0; j < 26; j++)   /* setting up second rotor to correspond to key */
  {
    ltr[1][j] = pos[1] % 26;
    pos[1]++;
  }
}


/************************************************************************
 Function: encrypt
 This function actually performs the encryption/decryption of each character
 in the input file.  It uses the Affine Cipher algorithm ax+b mod 26.  The
 character from the input file is multiplied by a constant number (which must
 have an inverse in mod 26) then added to the present positions corresponding
 character.  This output of the first rotor is then mutliplied by the inverse
 of the first rotors' constant number, then its added to the present positions
 corresponding character in the second rotor and this result is the 
 ciphertext/plaintext character.  This character is then returned for writing 
 to the output file.
 ***************************************************************************/  
int encrypt (int c)
{
  static int i, j;             /* counters */
  int f;                       /* holds result of output of first rotor */
  int k;                       /* holds result of output of second rotor */
    
    if (isalpha (c))
    {
      i = pos[1];              /* start to encipher */
      j = pos[0];
      c = c - 'A';             /* put char in mod 26 format */
      f = ((c*1) + ltr[0][j]) % 26;  
      k = ((f*25) + ltr[1][i]) % 26;
      k += 'A';               /* put char back into ASCII equivalent */
    }
    pos[0] = (pos[0]+1) %26;  /* increment first rotor forward once */
    j = (j+1) %26;
    if(pos[0] == 0)           /* if first rotor has completed a complete */
    {                         /* cycle, increment second rotor once */
      pos[1] = (pos[1] + 1) % 26;;
      i++;
    }
    return (k);               /* return ciphertext/plaintext */
}











