/*===========================================================================
  Copyright (C) 2002-2006 European Southern Observatory (ESO)
 
  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., 675 Massachusetts Ave, Cambridge, 
  MA 02139, USA.
 
  Correspondence concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

.IDENT        stegano.c
.LANGUAGE     C
.AUTHOR       K. Banse   ESO/IPG
.KEYWORDS     Steganography, Images
.PURPOSE      mix in a text with a (floating point) image

.VERSION      1.00  021013:   Creation 
 061010		last modif

$ stegano.exe textfile container-image [encr_flag]
$ stegano.exe textfile = container-image
$ stegano.exe textfile =/h container-image	(to just get the hint)

if container-image = NULL, the text file is only encrypted, 
not mixed into a container image afterwards

if encr_flag = NO, then no encryption

or in Midas:
> insert/stegano textfile container-image
> extract/stegano textfile = container-image 

---------------------------------------------------------------------*/

#include <sys/types.h>
#include <sys/stat.h>

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <termios.h>

#include   <fileexts.h>

#define MAXREC 256
#define MAXF MAXREC*8
#define MAXMEM 100000			/* default size for text (in bytes) */




static int inlen, mtrlen, xlen;		/* needed for blowcrab */
static int mantra[200], xmantra[410];


static struct termios SavedAttributes;	/* needed for terminal control */

/*

*/

/* this is the function to insert/extract the "text-bits" 
   into/from the last bit of image pixels */

void mixer(itext,datform,data,nlen,what)
int *itext;     /* IN/OUT: integer array with single bits */
int datform;	/* IN: format of array `data' */
char *data;	/* IN/OUT: array with info in last bit */
int nlen;       /* IN: size of int array `itext' */
char *what;	/* IN: I/O for encode/decode */

{
int  *idata;
register int n;
unsigned int hihi;

short int *jdata, kuku;
unsigned short int *ujdata, huhu;

float  *fdata;

union
   {
   unsigned int itemp;
   float rtemp;
   int ntemp;
   } yeah;

union
   {
   unsigned short int ujtemp;
   short int jtemp;
   } wow;



/* insert bits from `itext' and set as last bits of pixels `data' */

if (*what == 'I')
   {
   if (datform == 1)			/* floating point pixels */
      {				
      fdata = (float *)data;
      for (n=0; n<nlen; n++)
         {
         yeah.rtemp = *(fdata+n);
         yeah.itemp = yeah.itemp & 0xfffffffe;
         yeah.itemp = yeah.itemp | itext[n];
         *(fdata+n) = yeah.rtemp;
         }
      }
   else if (datform == 2)		/* integer pixels */
      {			
      idata = (int *)data;
      for (n=0; n<nlen; n++)
         {
         yeah.ntemp = *(idata+n);
         yeah.itemp = yeah.itemp & 0xfffffffe;
         yeah.itemp = yeah.itemp | itext[n];
         *(idata+n) = yeah.ntemp;
         }
      }
   else if (datform == 3)		/* short integer pixels */
      {			
      jdata = (short int *)data;
      for (n=0; n<nlen; n++)
         {
         wow.jtemp = *(jdata+n);
         wow.jtemp = wow.jtemp & 0xfffe;
         wow.jtemp = wow.jtemp | ((short int) itext[n]);
         *(jdata+n) = wow.jtemp;
         }
      }
   else if (datform == 4)		/* unsigned short integer pixels */
      {			
      ujdata = (unsigned short int *)data;
      for (n=0; n<nlen; n++)
         {
         wow.ujtemp = *(ujdata+n);
         wow.ujtemp = wow.ujtemp & 0xfffe;
         wow.ujtemp = wow.ujtemp | ((unsigned short int) itext[n]);
         *(ujdata+n) = wow.ujtemp;
         }
      }
   }

/* pull out last bits from pixels `data' and insert into `itext' */

else
   {
   if (datform == 1)			/* floating point pixels */
      {
      fdata = (float *)data;
      for (n=0; n<nlen; n++)
         {
         yeah.rtemp = *(fdata+n);
         hihi = yeah.itemp;
         yeah.itemp = hihi & 0x00000001;
         itext[n] = yeah.itemp;
         }
      }
   else if (datform == 2)               /* integer pixels */
      {        
      idata = (int *)data;
      for (n=0; n<nlen; n++)
         {
         yeah.ntemp = *(idata+n);
         hihi = yeah.itemp;
         yeah.itemp = hihi & 0x00000001;
         itext[n] = yeah.itemp;
         }
      }
   else if (datform == 3)               /* short integer pixels */
      {
      jdata = (short int *)data;
      for (n=0; n<nlen; n++)
         {
         kuku = *(jdata+n);
         wow.jtemp = kuku & 0x0001;
         itext[n] = wow.jtemp;
         }
      }
   else if (datform == 4)               /* unsigned short integer pixels */
      {
      ujdata = (unsigned short int *)data;
      for (n=0; n<nlen; n++)
         {
         huhu = *(ujdata+n);
         wow.ujtemp = huhu & 0x0001;
         itext[n] = wow.ujtemp;
         }
      }

   }
}
/*

*/

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


{
int  stat, nval, leni, action;
int  uni, nulo, stegano_flag, encr_flag;

char  imfile[120], txtfile[120], tmpfile[124];
char  *xp, *xh;




encr_flag = stegano_flag = 1;

if (argc > 1)				/* we're with command line args. */
   {
   if (strcmp(argv[1],"??") == 0)
      {
      (void) printf
      ("usage: stegano.exe textfile image [encr_flag]	(for encoding)\n");
      (void) printf
      ("       stegano.exe textfile = image [encr_flag]	(for decoding)\n");
      (void) printf
      ("       if image = NULL, file is only en/decrypted...\n");
      (void) printf
  ("       encrypt_flag = NO -> no en/decryption of text (default = YES)\n");
      exit(0);
      }


/* insert:  ascii image [encr_flag]
   extract: ascii = image [encr_flag] */


   (void) strcpy(txtfile,argv[1]);
   tmpfile[0] = '\0';

   if (argv[2][0] == '=')
      {
      action = 2;                              /* action flag = decode */
      leni = (int) strlen(argv[2]);
      if (leni > 2) 
         {
         char  *ptr;

         ptr = argv[2];
         ptr ++;
         if ( (*ptr == '/') && (*(ptr+1) == 'h') )
            {
            action = 20;		/* we just want to pull out the hint */
            }
         }

      (void) strcpy(imfile,argv[3]);
      if (argc > 4) (void) strcpy(tmpfile,argv[4]);
      if ( (argc > 5) &&
           ((argv[5][0] == 'F') || (argv[5][0] == 'f')) )
         action ++;
      }
   else
      {
      (void) strcpy(imfile,argv[2]);
      action = 1;				/* action flag = encode */
      if (argc > 3) (void) strcpy(tmpfile,argv[3]);
      }
     
   if ( (strcmp(imfile,"NULL") == 0) ||
        (strcmp(imfile,"null") == 0) ) stegano_flag = 0;
   if ((tmpfile[0] == 'N') || (tmpfile[0] == 'n')) encr_flag = 0;

   SCSPRO("-1");
   }
else
   {
   SCSPRO("stegano");
   uni = nulo = -1;
   stat = SCKGETC("IN_A",1,120,&nval,txtfile);		/* get textfile   */
   stat = SCKGETC("IN_B",1,120,&nval,imfile);		/* get image carrier */
   stat = SCKRDI("INPUTI",1,1,&nval,&action,&uni,&nulo);      /* get action */
   }

if ((stegano_flag == 0) && (encr_flag == 0))
   {
   (void) printf("nothing to do...\n");
   exit(9);
   }


/* see, if password, hint are stored in the environment */

xp = getenv("XPASSWD");
xh = getenv("XHINT");

(void) strcpy(tmpfile,txtfile);
nval = CGN_JNDEXC(tmpfile,'/');		/* look for dir. marker */
leni = CGN_JNDEXC(tmpfile,'.');		/* look for file type marker */
if (leni > nval)
   tmpfile[leni] = ',';			/* abc.typ => abc,typ */
else
   (void) strcat(tmpfile,",");		/* abc => abc, */


/* --- encrypt the text file and hide in image --- */

if (action == 1)
   {	
   if (stegano_flag != 0) 
      {			/* check, for write access of container image */
      if (access(imfile,W_OK) != 0)
         {					 /* and its existence */
         if (access(imfile,F_OK) != 0)
            (void) printf("cannot find Midas image %s ...\n",imfile);
         else
            (void) printf("cannot write into Midas image %s ...\n",imfile);
          
         stat = 2;
         goto end_of_it;
         }
      }

   if (encr_flag == 0)
      {
      (void) strcpy(tmpfile,txtfile);	/* use original text file */
      stat = 0;
      }
   else					/* first encrypt the text */
      stat = blowcrab(action,txtfile,tmpfile,xp,xh);

   if (stat == 0) 			/* encryption went o.k. */
      {		
      if (stegano_flag == 0) 
         {				/* just encrypt - no image used */
         (void) printf("%s encrypted to %s\n",txtfile,tmpfile);
         }
      else
         {				/* hide encrypted/original text */
         stat = stegano(action,tmpfile,imfile,&leni);
         if (stat == 0)
            (void) printf("%d pixels in %s modified\n",leni,imfile);
         if (encr_flag != 0) (void) unlink(tmpfile);
         }
      }
   else
      (void) printf("blowcrab returned %d\n",stat);
   }


/* --- extract from image and decrypt the text file --- */

else
   {
   char   *myptr;

   if (action == 20)
      {					/* get space for hint */
      myptr = (char *) malloc((size_t) 200);
      }
   else
      {
      myptr = (char *)0;
      }

   if (stegano_flag == 0)
      {				/* just decrypt - no image used */
      stat = blowcrab(action,txtfile,tmpfile,xp,myptr);
      if (stat == 0)
         (void) printf("%s decrypted to %s\n",tmpfile,txtfile);
      }
   else
      {				 /* extract (encrypted) text from image */
      if (encr_flag == 0)
         {
         stat = stegano(action,txtfile,imfile,&leni);
         if (stat == 0)                         /* already done */
            (void) printf("%d chars in %s written\n",leni,txtfile);
         }
      else
         {
         stat = stegano(action,tmpfile,imfile,&leni);
         if (stat == 0)                         /* then decrypt text */
            {
            (void) printf("%d chars in %s written\n",leni,tmpfile);
            stat = blowcrab(action,txtfile,tmpfile,xp,myptr);
            }
         }
      (void) unlink(tmpfile);
      }
   if (action == 20)
      {
      (void) printf("%s\n",myptr);
      (void) free(myptr);
      }
   }


end_of_it:
if (stat != 0)
   exit(1);
else
   SCSEPI();			/* implies exit(0) */
}

/*

*/

int stegano(actio,txtnam,imnam,sizzy)
char  *txtnam, *imnam;
int   actio;		/* IN: 1 = encode, 2 = decode, 3 = decode always */
int   *sizzy;		/* OUT: no. of pixels used */

{
int  td;
int  endy, n, nlim, nsize, noff, m, itemp;
int  ioff, actvals, unit, total, imnoa, naxis, npix[3];
int  ibuf[4], secret[MAXF];		/* bits of char string from file */
int  imsize, ncbytes, ncbits, lendm, mspace, space_off;
int  ec, el, ed, ln1, ln0;
int  datformat, retstat;

char *impntr, *space, cc, myrec[MAXREC+1], endmark[80];

struct stat filstat;





(void) SCECNT("GET",&ec,&el,&ed);
ln1 = 1; ln0 = 0;
(void) SCECNT("PUT",&ln1,&ln0,&ln0);		/* now, ignore errors */

m = SCFOPN(imnam,D_OLD_FORMAT,0,F_OLD_TYPE,&imnoa);
if (m != 0)
   {
   (void) printf("could not open image %s ...\n",imnam);
   return (2);
   }

ibuf[0] = imnoa;                     /* use SCFINF the fast way... */
(void) SCFINF(imnam,-3,ibuf);
if (ibuf[0] != F_IMA_TYPE)
   {
   (void) printf("file %s is not a FITS/Midas image %s ...\n",imnam);
   return (1);
   }

if (ibuf[1] == D_R4_FORMAT)		/* image is R*4 data */
   {
   (void) strcpy(myrec,"real");
   datformat = 1;
   m = sizeof (float);
   }
else if (ibuf[1] == D_I4_FORMAT)	/* image is I*4 data */
   {
   (void) strcpy(myrec,"int");
   datformat = 2;
   m = sizeof (int);
   }
else if (ibuf[1] == D_I2_FORMAT)	/* image is I*2 data */
   {
   (void) strcpy(myrec,"short int");
   datformat = 3;
   m = sizeof (short int);
   }
else if (ibuf[1] == D_UI2_FORMAT)	/* image is unsigned I*2 data */
   {
   (void) strcpy(myrec,"unsigned short int");
   datformat = 4;
   m = sizeof (short unsigned int);
   }
else
   {
   (void) printf("image has unsupported data format ...\n");
   return (4);
   }


/* currently fixed string as end marker */

(void) strcpy(endmark,"<<>>");

impntr = (char *) malloc((size_t) (m * MAXF));
if (impntr == (char *) 0)
   {
   (void) printf("problems allocating space for %d pixels...\n",m);
   return (9);
   }


unit = m = -1;
(void) SCDRDI(imnoa,"NAXIS",1,1,&actvals,&naxis,&unit,&m);
(void) SCDRDI(imnoa,"NPIX",1,naxis,&actvals,npix,&unit,&m);
imsize = npix[0];
for (n=1; n<naxis; n++)
   {
   imsize *= npix[n];
   }

endy = 0;				/* end marker set */
lendm = (int) strlen(endmark) * 8;
total = (imsize - lendm) / 8;		/* get max. text size */


/***** encode the text *****/

if (actio == 1) 
   {
   (void)printf("max. text size = %d chars. for this image\n",total);
 
   td = open(txtnam,O_RDONLY);
   if (td == -1) 
      {
      (void) printf("could not open text: %s\n",txtnam);
      return (1);
      }

   n = stat(txtnam,&filstat);              /* get size of textfile */
   if (n != 0)
      {
      (void) printf("could not get size of text: %s\n",txtnam);
      return (1);
      }
   total = (int)filstat.st_size;           /* no chars in textfile */
   ncbits = total * 8;                     /* that's so many bits/pixels */

   /* check, that carrier image has enough pixels for encoded text 
      min size = 8 * (nochars of text + encoded length string) */

   n = ncbits + lendm;

   /*
   (void) printf("Midas image %s with %s data format \n",imnam,myrec);
   (void)printf("length of ascii text = %d chars (%d bits)\n",total,ncbits);
   (void)printf("length of end marker = %d chars (%d bits)\n",lendm,lendm*8);
   */

   if (n > imsize)
      {
      (void)
      printf("image (%d pixels) not large enough for text (%d bits) ...\n",
             imsize,n);
      return (3);
      }

   total = 0;
   ioff = 1;


   /* here the reading loop for the text file */

  read_text:
   ncbytes = read(td,(void *)myrec,(size_t)MAXREC);
   if (ncbytes <= 0)
      {
      (void) close(td);				/* EOF reached... */
      endy = 1;
      ncbytes = CGN_COPY(myrec,endmark);
      }


   /* get the bits of the text record */

  encode_text:
   noff = 0;
   for (n=0; n<ncbytes; n++)
      {
      for (m=0; m<8; m++)
         {				/* move bits to int array */
         secret[noff++] = (int) ((myrec[n] >> m) & 0x1);
         }
      }
   total += noff;
   

   /* to mix in the bits from the text record with the image data
      read corresponding no. of float pixels from image  */

   (void) SCFGET(imnoa,ioff,noff,&actvals,impntr);

   /*
   printf("noff =  %d ints for the bits of text just read\n",noff);
   printf("from ioff = %d, get %d (noff) pixels in\n",ioff,noff);
   */


   /* function `mixer' en/decodes */

   mixer(secret,datformat,impntr,noff,"IN");


   /* write modified image back to disk */

   (void) SCFPUT(imnoa,ioff,noff,impntr);

   ioff += noff;			/* increase offset + read next */
   if (endy == 0) goto read_text;
           

   (void) SCFCLO(imnoa);
   *sizzy = total;
   return (0);
   }


/***** decode the text *****/

else
   {
   retstat = *sizzy = 0;

   td = open(txtnam,O_WRONLY | O_CREAT | O_TRUNC, 0644);
   if (td == -1)
      {
      (void) printf("could not create %s ...\n",txtnam);
      return(1);
      }

   mspace = MAXMEM;
   space = (char *) malloc((size_t) mspace);
   if (space == (char *) 0)
      {
      (void) printf("could not allocate memory...\n");
      return (1);
      }
   space_off = 0;

   total = 0;
   ioff = 1;

  read_mixed:			/* read loop for float pixels */
   if (ioff >= imsize)
      {				/* EOF reached */
      if (actio == 3) 
         {
         retstat = -1;
         goto write_file;	/* forced write */
         }
      else
         goto end_of_it;
      }

   (void) SCFGET(imnoa,ioff,MAXF,&nlim,impntr);
   ioff += nlim;

   /* move pixel bits to int array */

   mixer(secret,datformat,impntr,nlim,"OUT");


   noff = 0;
   nsize = nlim/8;
   for (n=0; n< nsize; n++)
      {
      cc = 0;
      for (m=0; m<8; m++)
         {
         itemp = secret[noff] << m;
         cc = cc | itemp;
         noff ++;
         }
      myrec[n] = cc;
      }

   /* check for end of text */

   myrec[nsize] = '\0';
   m = CGN_INDEXS(myrec,endmark);
   if (m > -1)
      {
      nsize = m;
      nlim = nsize *8;
      endy = 1;
      }
   
   /*
   printf("write %d chars to result text file\n",nsize);
   */

   if ((space_off + nsize) > mspace)
      {					/* out of space? double alloc mamory */
      mspace = mspace * 2;
      space = realloc((void *)space,(size_t)mspace);
      }

   (void) memcpy(space+space_off,myrec,(size_t) nsize);
   space_off += nsize;
   total += nsize;

   if (endy == 0) goto read_mixed;

  write_file:
   m = write(td,(void *)space,total);
   free((void *) space);
   if (m != total)
      {
      (void) printf("problems in writing text ...\n");
      close(td);
      return (5);
      }
   *sizzy = total;  

  end_of_it:
   SCFCLO(imnoa);
   close(td);

   return (retstat);
   }
   
}
/*

*/

int SetInputMode (void)
{
struct termios attr;


if (!(isatty (fileno (stdin))))
   return 0;                    /* Make sure stdin is a terminal... */


/* save current attributes */

tcgetattr (fileno (stdin), &SavedAttributes);


/* set terminal modes */

tcgetattr (fileno (stdin), &attr);

attr.c_lflag &= ~(ICANON | ECHO);       /* goto raw mode, turn echo off */
attr.c_cc[VMIN] = 1;
attr.c_cc[VTIME] = 0;
tcsetattr (fileno (stdin), TCSADRAIN, &attr);

return 1;
}




void ResetInputMode (void)
{
tcsetattr (fileno (stdin), TCSANOW, &SavedAttributes);
}




int getch (void)
{
char c;
while (read (fileno (stdin), &c, 1) == 0);
return (int) c;
}


static int readpassw(int flag, char *s, int lim)

{
int c, nr, d;


SetInputMode ();


if (flag == 0)
   fputs("Enter password phrase (min. 6 chars): ",stdout);
else
   fputs("Enter password confirmation: ",stdout);

fflush (stdout);



nr = 0;
lim --;				/* lim = max. no. of chars including '\0' */

in_loop:
c = getch () ;
if ((c != '\r') && (c != '\n'))
   {
   if (nr < lim)
      {
      s[nr++] = c;
      d = '*';
      fputc (d, stdout);
      fflush (stdout);
      goto in_loop;
      }
   }

s[nr] = '\0';
d = '\n';
fputc (d, stdout) ;
fflush (stdout) ;

ResetInputMode ();

return(nr);
}
/*

*/

/* blowcrab.c

  is NOT the blowfish algorithm to encrypt/decrypt text files

  K. Banse		ESO - Garching
  010107

  use as: blowcrab(action,filea,fileb,mantra,hint)

      a Password Phrase (mantra) serves as key for the en/decryption algorithm
      min. length = 6 chars, but should be longer
      max. length = 200 chars
      a Password Hint is not necessary, but VERY useful...

      the text file should not have lines longer than 200 chars

  Note: in case you have problems in editing the Password or Hint
        on the terminal, do
        $ stty erase ^H
        just before launching the calling main


*/

int blowcrab(iactio,filea,fileb,mtenv,hhenv)
int iactio;	/* IN: = 1 for encoding, plain -> encrypted
		       = 2 for decoding, encrypted -> plain */
char *filea;	/* IN: name of plain ASCII text file */
char *fileb;	/* IN: name of encrypted ASCII text file */
char *mtenv;	/* IN: Password Phrase or NULL ptr */
char *hhenv;	/* IN: Password Hint or NULL ptr */

{
char   dir, fname[84], resname[84];
char   mtbuf[200], hint[200], inbuf[200], outbuf[200];
char   *ppasswd, *phint, alpha[235], in[8];
register char cc;

int    status, k, lincount;
int    ival,myshift, save1, save2, alpdim, koff;
int    start;
register int nr, mr;

FILE  *fid, *gid, *fopen();

 


/* build alphabet string */

alpdim = 235;
koff = alpdim - 1;
for (nr=0; nr<95; nr++)			/* avoid DEL (127) */
   alpha[nr] = (char) (nr+32);
for (nr=95; nr<223; nr++)
   alpha[nr] = (char) (nr+33);
alpha[223] = 7;				/* bell */
alpha[224] = '\t';			/* horiz. tab */
alpha[225] = '\v';			/* vert. tab */
alpha[226] = '\b';			/* backspace */
alpha[227] = '\f';			/* form feed */
alpha[228] = 31;                        /* US */
alpha[229] = 2;				/* STX (start of text) */
alpha[230] = 3;				/* ETX (end of text) */
alpha[231] = 1;				/* SOH (start of heading) */
alpha[232] = 28;			/* FS */
alpha[233] = 29;			/* GS */
alpha[234] = 30;			/* RS */


/* check direction */

if (iactio == 1)
   {
   (void) strcpy(fname,filea);
   (void) strcpy(resname,fileb);
   dir = 'F';			/* forward */
   }
else
   {
   (void) strcpy(resname,filea);
   (void) strcpy(fname,fileb);
   dir = 'R';			/* reverse */
   }


fid = fopen(fname,"r");                 /* open input file */
if (fid == NULL)
   {
   fprintf(stderr, "Could not open %s\n",fname);
   return(1);
   }


if (iactio == 20)                       /* we just wanted to pull out the hint string */
   {
   inlen = myread(fid,hint,198);             /* read hint */
   if (strncmp(hint,"... ",4) == 0)
      {
      (void) printf("password hint: %s\n",&hint[4]);
      (void) strcpy(hhenv,hint);             /* save the hint */
      }

   fclose(fid);
   return (0);
   }



/* get password phrase */

if (mtenv == (char *) 0)
   {
   char  sav_mtbuf[200];

   if (dir == 'F')
      {
     p_again:
      mtrlen = readpassw(0,sav_mtbuf,198);
      mtrlen = readpassw(1,mtbuf,198);
      if (strcmp(sav_mtbuf,mtbuf) != 0)
         {
         (void) printf("password strings not matching - try again\n");
         goto p_again;
         }

      (void) printf("Enter a password hint ... \n");
      (void) strcpy(hint,"... ");
      k = Getline(&hint[4],190);
      }
   else
      {
      inlen = myread(fid,hint,198);		/* read hint */
      if (strncmp(hint,"... ",4) == 0)
         {
         if (inlen > 4)
         (void) printf("password hint: %s\n",&hint[4]);
         }

      mtrlen = readpassw(0,mtbuf,198);
      }
   }

else				/* password in env. var. XPASSWD */
   {
   mtrlen = CGN_COPY(mtbuf,mtenv);
   if (dir == 'F')
      {					/* also store a hint */
      (void) strcpy(hint,"... ");
      if (hhenv != (char *) 0)		/* hint in env. var. XHINT */
         (void) strcat(hint,hhenv);
      }
   else
      {
      inlen = myread(fid,hint,198);		/* read hint */
      if (strncmp(hint,"... ",4) == 0)
         {
         if (inlen > 4)
         (void) printf("password hint: %s\n",&hint[4]);
         }
      }
   }


/*
printf("I got mantra: %s, length = %d, dir = %c\n",mtbuf,mtrlen,dir);
*/


/* remove trailing blanks (is dangerous...) */

for (nr=mtrlen-1; nr>0; nr--)
   {
   if (mtbuf[nr] != ' ')
      {
      mtrlen = nr + 1;
      mtbuf[mtrlen] = '\0';
      break;
      }
   }


if (mtrlen < 6)
   {
   fprintf(stderr,
   "password phrase has length = %d, must be at least 6 chars long...\n",
   mtrlen);
   return (1);
   }



/* usually there are quite a few blanks in a long password,
   so let's overwrite them                               */

ival = (int) mtbuf[3];			/* take 4th element */
ival += mtrlen;

k = 0;				/* replace all blanks (except first) */
for (nr=0; nr<mtrlen; nr++)
   {
   if (mtbuf[nr] == ' ')
      {
      if (k == 0) 
         k = 1;
      else
         {
         mtbuf[nr] = (char) ival;
         ival += mtrlen;
         }
      }
   }


/* now convert to int values in [0,255]  */

for (nr=0; nr<200; nr++) mantra[nr] = 0;
for (nr=0; nr<410; nr++) xmantra[nr] = 0;

for (nr=0; nr<mtrlen; nr++) 
   {				/* char keyphrase -> int offset array */
   ival = (int) mtbuf[nr];
   xmantra[nr] = mantra[nr] = ival;
   }


/* open result file */

gid = fopen(resname,"w");
if (gid == NULL)
   {
   fprintf(stderr, "Could not create %s\n",resname);
   fclose(fid);
   return (1);
   }

if (dir == 'F')					/* save hint as 1st line */
   {
   if (hint[0] != '\0')
      {
      printf("hint = %s,\n",hint);
      mywrite(gid,hint,(int)strlen(hint));
      }
   }


lincount = 0;
status = 0;

while ((inlen = myread(fid,inbuf,198)) > -1)
   {
   if (inlen == 0) goto write_it;

   start = method(lincount);		/* build up `xmantra' */
   lincount ++;

   for (nr=0; nr<inlen; nr++)
      {
      cc = inbuf[nr];
      for (mr=0; mr<alpdim; mr++)
         {
         if (cc == alpha[mr])
            {
            save1 = ival = mr;
            goto ok_1;
            }
         }
      fprintf(stderr,
    "File %s, line %d: \nCould not match\n char %c (%d) with alpha string...\n",
      fname,lincount,cc,(int)cc);
      status = 1;
      goto end_of_it;

     ok_1:
      myshift = xmantra[start++];
      if (dir == 'F')
         ival += myshift;
      else
         ival -= myshift;
     loop_F:
      if (ival < 0)
         {
         ival += alpdim;
         goto loop_F;
         }
      if (ival > koff)
         {
         ival -= alpdim;
         goto loop_F;
         }

      save2 = ival;
      outbuf[nr] = alpha[ival];


      /* check, if we get back correctly 

      if (dir == 'F')
         {
         cc = outbuf[nr];
         ival -= myshift;
        loop_R:
         if (ival < 0)
            {
            ival += alpdim;
            goto loop_R;
            }
         if (ival > koff)
            {
            ival -= alpdim;
            goto loop_R;
            }
         cc = alpha[ival];
         if (cc != inbuf[nr])
            {
            (void) printf("with myshift = %d, and nr = %d\n",myshift,nr);
            (void) printf
            ("`%c' (indx %d of alpha) -> (indx %d) `%c' -> (indx %d) `%c'\n",
            inbuf[nr],save1,save2,outbuf[nr],ival,cc);
            (void) Getline(hint,8);
            }
         } */

         					/* xlen = size of xmantra */
      if (start > (xlen-1)) start -= xlen;
      } 


  write_it:
   outbuf[inlen] = '\0';
   mywrite(gid,outbuf,inlen);
   }


end_of_it:
fclose(gid);
fclose(fid);

return (status);
}
/*

*/

method(no)
int  no;

{
int  ioff, mystart, nbra;
int  j, nm, nomethod;
int  iwork[204];
register int k, nr;




/* we have to expand xmantra to be at least as long as the input line */

nomethod = 7;		/* max. methods in fummel() */

j = inlen + mtrlen;
nbra = j%nomethod;		/* (inlen+mtrlen) => nbra, in [0 ... nom-1] */


k = reorg(nbra,mantra,iwork);	/* reorganize mantra, reslength = mtrlen */
for (nr=0; nr<k; nr++) xmantra[nr] = iwork[nr];

if (mtrlen >= inlen)		/* key is already long enough */
   xlen = mtrlen;

else
   {
   ioff = mtrlen;		/* xmantra is already that long */
   while (ioff < inlen) 	/* compare with length of input line */
      {
      k = fummel(nbra,xmantra,iwork);	/* build up new array from key */

      for (nr=0; nr<k; nr++)
          xmantra[nr+ioff] = iwork[nr];

      ioff += k;
      }

   xlen = ioff;
   }


/* determine index in xmantra (which has now length=xlen) */

mystart = xlen - inlen + no;


/* for debugging ...


   (void) 
   printf("nbra = %d, xlen = %d, inlen =%d, linecount = %d, mystart = %d\n",
          nbra,xlen,inlen,no,mystart);
    
   k = 0;
   for (nr=0; nr<xlen/4; nr++)
      {
      printf("%d     %d     %d     %d\n",xmantra[k],xmantra[k+1],
                                         xmantra[k+2],xmantra[k+3]);
      k += 4;
      }

   (void) printf("all o.k.? ");
   (void) Getline(in,8);
   */


while (mystart >= xlen) mystart -= xlen;

return mystart;
}
/*

*/

int reorg(flag,a,b)
int  flag;		/* IN: which method to choose */
int  *a;		/* IN: the key from which to build the new string */
int  *b;		/* OUT: built up from `a' according to 
				method no. `flag'     */

{
int *j1, *j2;
int  mine[200];
int  mt, mk, m, total;
register int  nr;



j1 = a;				/* point to key */
j2 = b;				/* point to new array */
total = 4;			/* max. functions */


otra_vez:
if (flag == 0)			/* just copy */
   {
   for (nr=0; nr<mtrlen; nr++) *j2++ = *j1++;
   }

else if (flag == 1)			/* reverse */
   {
   mt = mtrlen - 1;
   mk = (mtrlen+1) /2;			/* get midpoint */

   for (nr=0; nr<mk; nr++)
      {
      m = mt - nr;		/* move from end to midpoint */
      *(j2+nr) = j1[m];
      *(j2+m) = j1[nr];
      }
   }

else if (flag == 2)		/* switch pairs */
   {
   mt = mtrlen - 1;
   j2[mt] = j1[mt];			/* in case of odd length ... */

   for (nr=0; nr<mt; nr+=2) 
      {
      j2[nr] = j1[nr+1];
      j2[nr+1] = j1[nr];
      }
   }

else if (flag == 3) 
   {
   mt = mtrlen - 1;
   mk = (mtrlen+1) /2;			/* get midpoint */

   for (nr=0; nr<mk; nr++)
      {
      m = mt - nr;		/* move from end to midpoint */
      mine[nr] = j1[m];
      mine[m] = j1[nr];
      }

   j2[mt] = mine[mt];			/* in case of odd length ... */

   for (nr=0; nr<mt; nr+=2) 
      {
      j2[nr] = mine[nr+1];
      j2[nr+1] = mine[nr];
      }
   }
else
   {
   flag = flag%total;
   goto otra_vez;
   }
   
return mtrlen;
}
/*

*/

int fummel(flag,a,b)
int  flag;              /* IN: which method to choose */
int  *a;                /* IN: the key from which to build the new string */
int  *b;                /* OUT: built up from `a' according to
                                method no. `flag'     */

{
int *j1, *j2;
int  count, mt, mk, m, total;
register int  nr;



j1 = a;                         /* point to key */
j2 = b;                         /* point to new array */
total = 7;			/* max. functions */
count = 0;


otra_vez:
if (flag == 0)                  /* just copy */
   {
   for (nr=0; nr<mtrlen; nr++) *j2++ = *j1++;
   count += mtrlen;
   }

else if (flag == 1)                     /* reverse */
   {
   mt = mtrlen - 1;
   mk = (mtrlen+1) /2;                  /* get midpoint */

   for (nr=0; nr<mk; nr++)
      {
      m = mt - nr;              /* move from end to midpoint */
      *(j2+nr) = j1[m];
      *(j2+m) = j1[nr];
      }
   count += mtrlen;
   }

else if (flag == 2)                     /* add length + copy */
   {
   *j2++ = mtrlen;
   count ++;
   flag = 0;
   goto otra_vez;
   }

else if (flag == 3)		/* add length + reverse */
   {
   *j2++ = mtrlen;
   count ++;
   flag = 1;
   goto otra_vez;
   }

else if (flag == 4)		/* add length + switch pairwise */
   {
   *j2++ = mtrlen;
   mt = mtrlen%2;		/* 1 = odd, 0 = even */
   for (nr=0; nr<(mtrlen-1); nr+=2)
      {
      *j2++ = j1[nr+1];
      *j2++ = j1[nr];
      }
   if (mt == 1) *j2++ = j1[mtrlen-1];
   count += (mtrlen +1);
   }

else if (flag == 5)		/* alternate lengths + values */	
   {
   mt = mtrlen;
   mk = -1;
   for (nr=0; nr<mtrlen; nr++)
      {
      *j2++ = mk * mt;
      *j2++ = j1[nr];
      mt += 5;
      mk = -mk;
      }
   count += 2*mtrlen;
   }

else if (flag == 6)		
   {
   mk = (mtrlen+1) /2;                  /* get midpoint */
   m = j1[mk];				/* and value there */
   mk = -1;
   for (nr=0; nr<mtrlen; nr++)
      {
      *j2++ = j1[nr] + (mk *m);
      m += 2;
      mk = -mk;
      }
   count += mtrlen;
   }

else
   {
   flag = flag%total;
   goto otra_vez;
   }

return count;
}
/*

*/

int Getline(s,lim)

/*++++++++++++++++++++++++++++++++++++++++++++++++++
.KEYWORDS
  terminal
.PURPOSE
  get an input line from the terminal
.ALGORITHM
  use C-function getchar
.RETURNS
  nothing
--------------------------------------------------*/

char s[];        /* OUT: input line from terminal  */
int  lim;        /* IN: max. length of input string  */

{
int  nr;



lim --;
for (nr=0; nr<lim; nr++)
   {
   s[nr] = getchar();
   if ( (s[nr] == '\n') || (s[nr] == '\r') )
      {
      s[nr] = '\0';
      return (nr);
      }
   }

if (lim < 0)            
   lim = 0;

s[lim] = '\0';
return (lim);
}
/*

*/

int myread(fid, pbuf, nochar)
FILE    *fid;                   /* IN : file identifier */
char    *pbuf;                  /* IN : pointer to i/o buffer */
int     nochar;                 /* IN : number of i/o characters */

{
char    *pc, *pe;
int     c;


if (fgets(pbuf, nochar, fid) == (char *) 0) return(-1);


pe = pbuf + nochar - 1;         /* Last Available Byte */
       

/* Continue to read if the end-of-record not found */

for (pc=pbuf; (pc <= pe) && (*pc != '\n') && (*pc != '\0'); pc++ ) ;
       
if (pc > pe)                            /* Not Found */
   *pe = '\0', pc = pe;


if (*pc == '\n')        /* Replace the newline + carriage-return */
   {
   while ( (--pc >= pbuf) && (*pc == '\r')) ;
   *++pc = '\0';
   }
else   
   {
   for (c=0,--pc; (c != EOF) && (c != '\n'); pc++) c = getc(fid);
   }

return((pc - pbuf));
}




int mywrite(fid, pbuf, nochar)
FILE  *fid;                     /* IN : file identifier */
char *pbuf;                     /* IN : pointer to i/o buffer */
int nochar;                     /* IN : number of i/o characters */

{
int     ret, add_newline;


add_newline = 0;
ret = 0;

if (nochar)             /* There are bytes to write... */
   {
   ret = fwrite(pbuf, sizeof(char), nochar, fid);
   if (ret == 0)
      ;                                         /* Error */
   else if (*(pbuf+nochar-1) != '\n')
      add_newline = 1;
   }
else   
   add_newline = 1;

if (add_newline)
   ret += fwrite("\n", 1, 1, fid);

return(ret);
}


