/* solitaire.c */

/*
 * The Solitaire encryption algorithm programmed in C.
 * solitaire encryption system by Bruce Schneier
 * based on a deck of cards
 * See <http://www.counterpane.com/solitaire.html> for details.
 * programming by Lloyd Miller, Sept 2000
*/

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

/*
 *   usage :
 *       to encrypt
 *              solitair -e key <plaintext >cyphertext
 *       to decrypt
 *              solitair -d key <cyphertext >plaintext
 *
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <ctype.h>

void usage(void)
{
  fprintf(stderr,
	  "usage:\n"
	  "       to encrypt\n"
	  "              solitair -e key <plaintext >cyphertext\n"
	  "       to decrypt\n"
	  "              solitair -d key <cyphertext >plaintext\n");
  exit(EXIT_FAILURE);
}

int getalpha(void)
{
  int a;

  while (1) {
    a = getchar();
    if (a == EOF) return 0;
    if (a >= 'A' && a <= 'Z')
      return a;
    if (a >= 'a' && a <= 'z')
      return a - 'a' + 'A';
  }
}

int findit(char *deck, int val, int siz)
{
  int i = 0;
  while(siz--)
    {
      if (*deck == val)
	return i;
      deck++;
      i++;
    }
  return -1;
}

int step(char *deck)
{
  int c;
  int b;
  int a;
  char tmp[54];

  do {

    /* step 1 */

    c = findit(deck, 53, 54);
    if (c < 53)
      {
	a = c + 1;
	deck[c] = deck[a];
      }
    else
      {
	for (a = 53; a > 1; a--)
	  deck[a] = deck[a - 1];
	a = 1;
      }
    deck[a] = 53;

    /* step 2 */

    b = findit(deck, 54, 54);
    if (b < 52)
      {
	c = b + 1;
	deck[b] = deck[c];
	if (a == c)
	  a = b;
	b = c + 1;
	deck[c] = deck[b];
	if (a == b)
	  a = c;
      }
    else
      {
	c = b;
	b = b - 51;
	for (; c > b; c--)
	  {
	    deck[c] = deck[c - 1];
	    if (a == c - 1)
	      a = c;
	  }
      }
    deck[b] = 54;

    /* step 3 */

    if (a > b)
      {
	c = a;
	a = b;
	b = c;
      }
    tmp[53] = deck[b++];

    c = 0;
    while (b < 54)
      tmp[c++] = deck[b++];

    b = a;
    while (deck[b] != tmp[53])
      tmp[c++] = deck[b++];

    tmp[c++] = tmp[53];
    b = 0;
    while (b < a)
      tmp[c++] = deck[b++];

    assert(c == 54);

    /* step 4 */

    b = tmp[53];
    if (b == 54)
      b = 53;

    a = 0;
    for (c = b; c < 53; c++)
      deck[a++] = tmp[c];
    for (c = 0; c < b; c++)
      deck[a++] = tmp[c];
    assert(a == 53);

    deck[53] = tmp[53];

    /* step 5 */

    a = deck[0];
    if (a == 54)
      a = 53;
    a = deck[a];
  } while (a > 52);
  return (a - 1) % 26 + 1;
}

int sumchar(int a, int b)
{
  return 'A' + (toupper(a) - 'A' + b + 26) % 26;
}

int main(int argc, char **argv)
{
  char deck[54];
  int i, j;
  int mode = 0;  /* default mode is encode, non-zero is decode */

  for (i = 0; i < 54; i++)
    deck[i] = i + 1;

  if (argc > 1 && argv[1][0] == '-')
    {
      if(argv[1][1] == 'd')
	{
	  mode = 1;
	}
      else if (argv[1][1] == 'e')
	{
	  mode = 0;
	}
      else usage();
    }
  else usage();

  argc--;
  argv++;

  /* do key */
  while (argc-- > 1)
    {
      char *ap = *++argv;
      while (*ap)
	{
	  int c = *ap++;
	  char tmp[53];

	  if (c >= 'a' && c <= 'z')
	    c = c - 'a' + 'A';
	  if (c >= 'A' && c <= 'Z')
	    {
	      c = c - 'A' + 1;
	      step(deck);
	      i = 0;
	      for (j = c; j < 53; j++)
		tmp[i++] = deck[j];
	      for (j = 0; j < c; j++)
		tmp[i++] = deck[j];
	      for (j = 0; j < 53; j++)
		deck[j] = tmp[j];
	    }
	}
    }

  j = 0;
  while ((i = getalpha()) != 0)
    {
      putchar(mode ? sumchar(i, -step(deck)) : sumchar(i, step(deck)));
      j++;
      if (j % 5 == 0)
	{
	  if (j == 50)
	    {
	      j = 0;
	      putchar('\n');
	    }
	  else
	    putchar (' ');
	}
    }

  j = j % 5;
  if (j)
    while (j < 5)
      {
	i = 'X';
	putchar(mode ? sumchar(i, -step(deck)) : sumchar(i, step(deck)));
	j++;
      }

  printf("\n");

  return EXIT_SUCCESS;
}

/* end of file */
