*
 * Ora parleremo dell'I/O, ossia Input/Output (Entrata/Uscita).
 * Il sistema I/O del C ha un'interfaccia indipendente dall'hardware, ossia
 * pone un certo livello di astrazione tra il programmatore e il dispositivo
 * in uso, per cui e' analogo accedere a terminali, unita' a disco, a nastro
 * eccetera. Benche' ogni dispositivo sia diverso dagli altri, vengono
 * trasformati dall'ANSI C in un dispositivo logico chiamato FLUSSO.
 * I tipi di flusso sono 2:
 *
 * 1) FLUSSO DI TESTO: e' una sequenza di caratteri, che pero' durante il
 *    trasferimento puo' subire anche delle conversioni secondo le necessita'
 *    dell'ambiente di destinazione (ad esempio il carattere di fine riga e'
 *    diverso in certi casi: puo' essere di newline o cr+lf) il che implica
 *    che il flusso eventualmente convertito puo' essere anche di dimensione
 *    diversa. Passando un file non di testo da questo flusso si danneggia!
 *
 * 2) FLUSSO BINARIO: e' una sequenza di byte avente una corrispondenza uno
 *    a uno con la sequenza rivevuta dal dispositivo esterno. Non avvenendo
 *    alcuna conversione il numero di byte scritti/letti rimane uguale.
 *
 * Abbiamo gia' visto comandi per I/O da e verso la console (tastiera/video),
 * ossia printf/scanf, gets/puts, per leggere o scrivere stringhe e caratteri
 * vari. A noi interessa maggiormente la lettura/scrittura nei FILE, in
 * particolare su Hard Disk/Floppy, ad esempio nel caso volessimo salvare
 * dei dati da un nostro programma, per poi rileggerli in un secondo momento
 * per un nuovo utilizzo (file di preferenze settaggio, di dati ecc.).
 * Veniamo quindi alla pratica: per leggere un file, le operazioni sono
 * queste: "aprire" quel file con fopen(), leggerlo con fread(), e chiuderlo
 * con fclose(). Una volta aperto un file (o piu' genericamente un flusso),
 * otteniamo un PUNTATORE associato a quel file.
 * Il PUNTATORE A UN FILE e' un vero e proprio puntatore, che punta ad
 * informazioni sul file aperto, come il nome, lo stato e la posizione
 * corrente. Un puntatore di file e' una variabile puntatore di tipo FILE:
 *
 *	FILE *puntailfile
 *
 * In questo modo abbiamo creato un puntatore chiamato puntailfile adatto
 * a contenere i dati di un file aperto con fopen in questo modo:
 *
 *	puntailfile = fopen("nomefile", modalita');
 *
 * Dove nomefile e' il nome del file, ed eventualmente il path, e "modalita'"
 * e' il modo di apertura desiderato (apertura di un file in lettura, oppure
 * creazione di un file in scrittura, ad esempio).
 * Ecco alcune delle modalita' piu' comuni:
 *
 *	"rt"	Apertura di un file di testo in lettura
 *	"wt"	Creazione di un file di testo in scrittura
 *	"rb"	Apertura di un file binario in lettura
 *	"wb"	Creazione di un file binario in scrittura
 *
 * "rt" e "rb" servono per leggere un file esistente, "wt" e "wb" invece
 * creano un file nuovo per scriverci qualcosa. Se esiste gia' un file
 * con quel nome, viene sovrascritto. Due esempi di modo di apertura:
 *
 *	puntailfile = fopen("c:\file.bau", "rb"); * file binario in lett.
 *	puntailfile = fopen("pippo.pip", "wt");   * file di testo in scritt.
 *
 * Dato che puo' verificarsi un errore nell'apertura, di solito si controlla:
 *
 *	if ((puntailfile = fopen("pippo.pip", "rb"))==NULL) {
 *		puts("\nImpossibile aprire il file.\n");
 *		errore = 1 }
 *
 * Questo significa: se l'apertura del file fallisce, segna nella variabile
 * errore il valore 1, che potra' in seguito essere controllato per sapere
 * che il file non e' stato aperto. Altrimenti si potrebbe usare il return()
 * con un valore particolare, o si fa terminare il programma.
 * NULL e' un alias per 0, solitamente, e si usa per controllare se il
 * puntatore e' rimasto NULL, ossia non inizializzato dopo l'apertura.
 * Una volta aperto lo stream, si puo' leggere e scrivere con fread() e
 * fwrite().
 *
 * Vediamo di mettere in pratica le ultime cose dette, leggendo questo
 * file (clez6a.c) e facendone una copia, chiamata clez6a.bak.
 * Se non trova il file, aggiungete il path.
 * Ci serve di sapere qualche altra istruzione: fputc(), e' un putchar per
 * i file, come si intuisce dal nome, e ci servira' per copiare un char
 * alla volta da un file all'altro. Per sapere quanti char copiare, ossia
 * quando finisce il primo file, possiamo usare feof().
 * Attenzione che si scrive su HD/FD, io vi ho avvertito!
 */

#include <stdio.h>	/* Includiamo la libreria standard */

/* Funzione principale e inizio del programma */

int main(void)		/* Funzione principale, eseguita per prima */
{			/* Inizio della funzione main() */

   int errore = 0;	/* Variabile che uso per segnare eventuali errori */
   FILE *leggoinput, *scrivooutput;  /* Definisco 2 strutture FILE */

/* Provo ad aprire il file CLEZ6A.C in modalita' lettura file di testo */

   if ((leggoinput = fopen("CLEZ6A.C", "rt")) == NULL) /* Se non si apre...*/
   {
      printf("\nNon posso aprire il file di input.\n");
      errore=1;	                           /* Segna l'errore */
   }

   if (errore==0) {	/* Se non si e' verificato l'errore apro la dest. */

/* Provo ad aprire il file CLEZ6A.BAK in modalita' scrittura file di testo */

   if ((scrivooutput = fopen("CLEZ6A.BAK", "wt")) == NULL) /* Si apre!? */
   {
      printf("\nNon posso aprire il file di output.\n");
      errore=2;	                                   /* Uscita con errore */
   }
  }

   if (errore==0) {	/* Se non si e' verificato l'errore copio */

/* Se sono riuscito ad aprire entrambi i file, posso copiare il primo nel
   secondo, char dopo char, fino alla fine. Per sapere quando sono arrivato
   alla fine, uso feof(), che ritorna 0 finche' non si raggiunge la fine
   del file (ossia EOF). Da notare l'operatore negazione "!". */

   while (!feof(leggoinput))    /* Finche' NON siamo alla fine del file...*/
      fputc(fgetc(leggoinput), scrivooutput);  /* Copia 1 char alla volta */
   }

/* File copiato, posso chiudere i due flussi (stream), sempre che sia
   riuscito ad aprirli senza errori! */

   if(errore!=1) fclose(leggoinput);
   if(errore!=2) fclose(scrivooutput);

/* Scriviamo un messaggio di felicitazioni, o di rammarico per errore.*/

   if(errore==0) printf("\nHo avuto successo!\n");
   else printf("\nInsuccesso totale!\n");

   return(0);	/* la funzione main restituisce uno 0 intero */
} 	/* Fine della funzione main() */

/* Facile, no?
 * In Java troveremo differenze di sintassi, ma la logica e' molto simile,
 * a parte le limitazioni sulla scrittura, per motivi di sicurezza.
 */