/*----------------------------------------------------------------
                              termio

 Tramite la system call ioctl e'  possibile modificare le caratte-
 ristiche di un  terminale.  Ad  esempio e'  possibile  sopprimere
 l'echo  dei  caratteri digitati sulla tastiera. E' possibile pure
 effettuare l'input puntuale. Se ad esempio e'  necessario  immet-
 tere  1  solo  carattere, e' possibile far si' che non sia neces-
 sario battere il tasto di invio. La chiamata:
    ioctl (0, TCGETS, &termbuf)
 salva  in struct termio termbuf le caratteristiche del  terminale
 mentre la chiamata:
    ioctl (0, TCSETS, &termbuf)
 posiziona  le  caratteristiche come stabilito in termbuf.  Per il
 ripristino la chiamata con le caratteristiche originali e':
    ioctl  (0,  TCSETSF, &termbuf)
 ove la F finale del 2^ parametro signica Force.

 Normalmente sono attivate certe caratteristiche quali:
 * l'echo dei tasti battuti sulla tastiera
 * l'uso del tasto Invio (o Enter)  per rendere disponibile quanto
   battuto sulla tastiera.
 Per cambiare le impostazione e' necessario passare  nella  cosid-
 detta  modalita' NON canonica e togliere l'echo dei tasti. Sempre
 im modalita' non canonica e' necessario specificare  2  quantita'
 (tempo  e  caratteri).  Trascorsa una delle quali l'input e' reso
 disponibile al programma senza che sia necessario battere Invio.

 Per passare in modalita' non canonica e togliere l'echo e' neces-
 sario azzerare i bit che  le  attivano.   Si  agisce  sul  membro
 c_lflag  della struttura che contiene le caratteristiche dell'in-
 put:
      xxxx.c_flags &= ~ (ICANON | ECHO)
 Con ICANON | ECHO si specificano su quali  bit  agire.  La  tilde
 opera il complemento, quindi tutti i bit, eccetto quelli specifi-
 cati, verranno posti ad  1.  Infine  si  esegue  un  AND  con  la
 maschera  dei  flag.   Il risultato e' che quelli della modalita'
 canonica e dell'echo sono stati azzerati.
 Si passa a modificare 2 voci del vettore c_cc, quelle individuate
 da VTIME e VMIN. Vanno poste ad un valore > 0. Con:
      xxxx.c_cc [VMIN] =
      xxxx.c_cc [VTIME] =
 si  specificano rispettivamente  il numero minimo di caratteri ed
 il timeout in decimi di secondo.  Battuti un numero di  caratteri
 pari  ad  almeno c_cc [VMIN] oppure se e' trascorso un tempo pari
 ad almeno c_cc [VTIME] decimi di secondo, la  lettura  termina  e
 l'input passa al programma.


      Messaggistica

 errore ioctl
      la system call ha restituito un errore

 errore ripristino
      ripristino delle caratteristiche fallito

 errore settaggio terminale
      modifica delle caratteristiche fallita
----------------------------------------------------------------*/

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <termios.h>

/*-----configurazione-----*/
const int dimpw = 16;
/*---fineconfigurazione---*/

int main (void)
{
  struct termios orig, save (void), tmp;
  void           modify (struct termios), restore (struct termios);
  void           typepwd (int);

  orig = save ();
  tmp = orig;
  modify (tmp);
  typepwd (dimpw);
  restore (orig);
  exit (EXIT_SUCCESS);
}

struct termios save (void)
{
  struct termios termbuf;

  if ((ioctl (0, TCGETS, &termbuf)) == -1)
    {
      fprintf (stderr, "errore ioctl\n");
      exit (EXIT_FAILURE);
    }
  return termbuf;
}

void restore (struct termios termbuf)
{

  if ((ioctl (0, TCSETSF, &termbuf)) == -1)
    {
      fprintf (stderr, "errore ripristino\n");
      exit (EXIT_FAILURE);
    }
}

void modify (struct termios termbuf)
{
  termbuf.c_lflag &= ~ (ICANON | ECHO);
  termbuf.c_cc [VMIN] = 1;
  termbuf.c_cc [VTIME] = 1;
  if ((ioctl (0, TCSETS, &termbuf)) == -1)
    {
      fprintf (stderr, "errore settaggio terminale\n");
      exit (EXIT_FAILURE);
    }
}

void typepwd (const int dimpw)
{
  char *pw;
  int  i = 0, unchar;

  pw = (char *) calloc (dimpw, 1);
  printf ("immettere la password (backspace = CTRL/H) > ");
  do
    {
      fflush ((FILE *) NULL);
      unchar = fgetc (stdin);
      switch (unchar)
        {
          case '\n':
          case '\0':
            break;
          case '\b':
            if (i > 0 && i < dimpw)
              {
                printf ("\b \b");
                pw [--i] = '\0';
              }
            break;
          default:
            if (! isprint (unchar))
              break;
            if (i >= 0 && i < dimpw)
              {
                pw [i++] = unchar;
                printf ("*");
              }
            break;
        }
    }
  while (unchar != '\n'); 
  printf ("\npassword immessa: %s\n", pw);
  free ((void *) pw);
}