\chapter{I file: l'interfaccia standard ANSI C}
\label{cha:files_std_interface}

Esamineremo in questo capitolo l'interfaccia standard ANSI C per i file,
quella che viene comunemente detta interfaccia degli \textit{stream}.
Dopo una breve sezione introduttiva tratteremo le funzioni base per la
gestione dell'input/output, mentre tratteremo le caratteristiche più avanzate
dell'interfaccia nell'ultima sezione.


\section{Introduzione}
\label{sec:file_stream_intro}

Come visto in \capref{cap:file_unix_interface} le operazioni di I/O sui file
sono gestibili a basso livello con l'interfaccia standard unix, che ricorre
direttamente alle system call messe a disposizione dal kernel.

Questa interfaccia però non provvede le funzionalità previste dallo standard
ANSI C, che invece sono realizzate attraverso opportune funzioni di libreria,
queste, insieme alle altre funzioni definite dallo standard, vengono a
costituire il nucleo\footnote{queste funzioni sono state implementate la prima
  volta da Ritchie nel 1976 e da allora sono rimaste sostanzialmente
  immutate.} delle \acr{glibc}.


\subsection{I \textit{file stream}}
\label{sec:file_stream}

Come più volte ribadito l'interfaccia dei file descriptor è una interfaccia di
basso livello, che non provvede nessuna forma di formattazione dei dati e
nessuna forma di bufferizzazione per ottimizzare le operazioni di I/O.

In \textit{Advanced Programming in the Unix Environment} Stevens descrive una
serie di test sull'influenza delle dimensioni del blocco di dati (il parametro
\param{buf} di \func{read} e \func{write}) nell'efficienza nelle operazioni di
I/O con i file descriptor, evidenziando come le prestazioni ottimali si
ottengano a partire da dimensioni del buffer dei dati pari a quelle dei
blocchi del filesystem (il valore dato dal campo \var{st\_blksize} di
\var{fstat}).

Se il programmatore non si cura di effettuare le operazioni in blocchi di
dimensioni adeguate, le prestazioni sono inferiori.  La caratteristica
principale dell'interfaccia degli stream è che essa provvede da sola alla
gestione dei dettagli della bufferizzazione e all'esecuzione delle operazioni
di lettura e scrittura in blocchi di dimensioni appropriate all'ottenimento
della massima efficienza.

Per questo motivo l'interfaccia viene chiamata anche interfaccia dei
\textit{file stream}, dato che non è più necessario doversi preoccupare di dei
dettagli della comunicazione con il tipo di hardware sottostante (come nel
caso della dimensione dei blocchi del filesystem), ed un file può essere
sempre considerato come composto da un flusso continuo (da cui il nome
\textit{stream}) di dati.

A parte i dettagli legati alla gestione delle operazioni di lettura e
scrittura (sia per quel che riguarda la bufferizzazione, che le
formattazioni), i file stream restano del tutto equivalenti ai file descriptor
(sui quali sono basati), ed in particolare continua a valere quanto visto in
\secref{sec:file_sharing} a proposito dell'accesso condiviso ed in
\secref{sec:file_access_control} per il controllo di accesso.


\subsection{Gli oggetti \type{FILE}}
\label{sec:file_FILE}

Per ragioni storiche la struttura di dati che rappresenta uno stream è stata
chiamata \type{FILE}, questi oggetti sono creati dalle funzioni di libreria e
contengono tutte le informazioni necessarie a gestire le operazioni sugli
stream, come la posizione corrente, lo stato del buffer e degli indicatori di
stato e di fine del file.

Per questo motivo gli utenti non devono mai utilizzare direttamente o allocare
queste strutture, ma usare sempre puntatori del tipo \type{FILE *} ottenuti
dalla libreria stessa (tanto che in certi casi il termine di puntatore a file
è diventato sinonimo di stream). 

Tutte le funzioni della libreria che operano sui file accettano come parametri
solo variabili di questo tipo, che diventa accessibile includendo l'header
file \file{stdio.h}.



\subsection{Gli stream standard}
\label{sec:file_std_stream}

Ai tre file descriptor standard (vedi \secref{sec:file_std_descr}) aperti per
ogni processo, corrispondono altrettanti stream, che rappresentano i canali
standard di input/output prestabiliti; anche questi tre stream sono
identificabili attraverso dei nomi simbolici definiti nell'header
\file{stdio.h} che sono:

\begin{itemize}
\item \var{FILE * stdin} Lo \textit{standard input} cioè lo stream da cui
  il processo riceve ordinariamente i dati in ingresso. Normalmente è associato
  dalla shell all'input del terminale e prende i caratteri dalla tastiera.
\item \var{FILE * stdout} Lo \textit{standard input} cioè lo stream su cui
  il processo invia ordinariamente i dati in uscita. Normalmente è associato
  dalla shell all'output del terminale e scrive sullo schermo.
\item \var{FILE * stderr} Lo \textit{standard input} cioè lo stream su cui
  il processo è supposto inviare i messaggi di errore. Normalmente anch'esso
  è associato dalla shell all'output del terminale e scrive sullo schermo.
\end{itemize}

Nelle \acr{glibc} \var{stdin}, \var{stdout} e \var{stderr} sono effettivamente
tre variabili di tipo \type{FILE *} che possono essere usate come tutte le
altre, ad esempio si può effettuare una redirezione dell'output di un
programma con il semplice codice:
\begin{lstlisting}[labelstep=0,frame=,indent=1cm]{}
    fclose (stdout);
    stdout = fopen ("standard-output-file", "w");
\end{lstlisting}
ma in altri sistemi possono essere definite come macro, se si hanno problemi
di portabilità e si vuole essere sicuri, diventa opportuno usare la funzione
\func{freopen}.


\subsection{Le modalità di bufferizzazione}
\label{sec:file_buffering}

La bufferizzazione è una delle caratteristiche principali della interfaccia
degli stream; lo scopo è quello di ridurre al minimo il numero di system call
(\func{read} o \func{write}) eseguite nelle operazioni di input/output. Questa
funzionalità è assicurata automaticamente dalla libreria, ma costituisce anche
una degli aspetti più comunemente fraintesi, in particolare per quello che
riguarda l'aspetto della scrittura dei dati sul file.

I caratteri che vengono scritti su uno stream normalmente vengono accumulati
in un buffer e poi trasmessi in blocco in maniera asincrona rispetto alla
scrittura (quello che viene chiamato lo \textsl{scarico}, dall'ingelese 
\textit{flush}, dei dati) tutte le volte che il buffer viene riempito. Un
comportamento analogo avviene anche in lettura (cioè dal file viene letto un
blocco di dati, anche se se ne sono richiesti una quantità inferiore), ma la
cosa ovviamente ha rilevanza inferiore, dato che i dati letti sono sempre gli
stessi; in caso di scrittura invece, quando si ha un accesso contemporaneo
allo stesso file (ad esempio da parte di un altro processo) si potranno vedere
solo le parti effettivamente scritte, e non quelle ancora presenti nel buffer.

Allo stesso modo, se si sta facendo dell'input/output interattivo bisognerà
tenere presente le caratteristiche delle operazioni di scarico dei dati,
poiché non è detto che ad una scrittura sullo stream corrisponda una immediata
scrittura sul dispositivo.

Per rispondere ad esigenze diverse, lo standard definisce tre distinte modalità
in cui può essere eseguita la bufferizzazione, delle quali occorre essere ben
consapevoli, specie in caso di lettura e scrittura da dispositivi interattivi:
\begin{itemize}
\item \textit{unbuffered}: in questo caso non c'è bufferizzazione ed i
  caratteri vengono trasmessi direttamente al file non appena possibile
  (effettuando immediatamente una \func{write}).
\item \textit{line buffered}: in questo caso i caratteri vengono normalmente
  trasmessi al file in blocco ogni volta che viene incontrato un carattere di
  \textit{newline} (il carattere ASCII \verb|\n|).
\item \textit{fully buffered}: in questo caso i caratteri vengono trasmessi da
  e verso il file in blocchi di dimensione opportuna.
\end{itemize}

Lo standard ANSI C specifica inoltre che lo standard output e lo standard
input siano aperti in modalità \textit{fully buffered} quando non fanno
riferimento ad un dispositivo interattivo, e che lo standard error non sia mai
aperto in modalità \textit{fully buffered}.

Linux, come BSD e SVr4, specifica il comportamento di default in maniera
ancora più precisa, e cioè impone che lo standard error sia sempre
\textit{unbuffered} (in modo che i messaggi di errore siano mostrati il più
rapidamente possibile) e che standard input e standard output siano aperti in
modalità \textit{line buffered} quando sono associati ad un terminale (od
altro dispositivo interattivo) ed in modalità \textit{fully buffered}
altrimenti.

Il comportamento specificato per standard input e standard output vale anche
per tutti i nuovi stream aperti da un processo; la selezione comunque avviene
automaticamente, e la libreria apre lo stream nella modalità più opportuna a
seconda del file o del dispositivo scelto.

La modalità \textit{line buffered} è quella che necessita di maggiori
chiarimenti e attenzioni per quel che concerne il suo funzionamento. Come già
accennato nella descrizione, \emph{di norma} i dati vengono inviati al kernel
alla ricezione di un carattere di a capo; questo non è vero in tutti i casi,
infatti, dato che le dimensioni del buffer usato dalle librerie sono fisse, se
le si eccedono si può avere uno scarico dei dati anche prima che sia stato
inviato un carattere di \textit{newline}.

Un secondo punto da tenere presente, particolarmente quando si ha a che fare
con I/O interattivo, è che quando si effettua una lettura su uno stream che
comporta l'accesso al kernel\footnote{questo vuol dire sempre se lo stream da
  cui si legge è in modalità \textit{unbuffered}.} viene anche eseguito lo
scarico di tutti i buffer degli stream in scrittura.

In \secref{sec:file_buffering_ctrl} vedremo come la libreria definisca delle
opportune funzioni per controllare le modalità di bufferizzazione e lo scarico
dei dati.



\section{Funzioni base}
\label{sec:file_ansi_base_func}

Esamineremo in questa sezione le funzioni base dell'interfaccia degli stream,
analoghe a quelle di \secref{sec:file_base_func} per i file descriptor. In
particolare vedremo come aprire, leggere, scrivere e cambiare la posizione
corrente in uno stream.


\subsection{Apertura e chiusura di uno stream}
\label{sec:file_fopen}

Le funzioni che permettono di aprire uno stream sono tre\footnote{\func{fopen}
  e \func{freopen} fanno parte dello standard ANSI C, \func{fdopen} è parte
dello standard POSIX.1.}, i loro prototipi sono:
\begin{functions}
  \headdecl{stdio.h}
  \funcdecl{FILE * fopen(const char * path, const char *mode)}
  Apre il file specificato da \param{path}.
  \funcdecl{FILE * fdopen(int fildes, const char * mode)}
  Associa uno stream al file descriptor \param{fildes}.
  \funcdecl{FILE * freopen(const char * path, const char * mode, FILE *
  stream)}
  Apre il file specificato da \param{path} associandolo allo stream
  specificato da \param{stream}, se questo è già aperto prima lo chiude.

  Le funzioni ritornano un puntatore valido in caso di successo e \macro{NULL}
  in caso di errore, in tal caso \var{errno} viene settata al valore ricevuto
  dalla funzione sottostante di cui è fallita l'esecuzione.
  
  Gli errori pertanto possono essere quelli di \func{malloc} per tutte e tre
  le funzioni, quelli \func{open} per \func{fopen}, quelli di \func{fcntl} per
  \func{fdopen} e quelli di \func{fopen}, \func{fclose} e \func{fflush} per
  \func{freopen}. 
\end{functions}

Normalmente la funzione che si usa per aprire uno stream è \func{fopen}, essa
apre il file specificato nella modalità specificata da \param{mode} che è una
delle stringhe elencate in \tabref{tab:file_fopen_mode}. 

L'uso di \func{freopen} è in genere per redirigere uno dei tre file standard
(vedi \secref{sec:file_std_stream}): il file \param{path} viene associato a
\param{stream} e se questo è uno stream aperto prima viene chiuso.  

Infine \func{fdopen} viene usato per associare uno stream ad un file
descriptor esistente ottenuto tramite una altra funzione (come \func{open},
\func{dup}, \func{pipe}) e serve quando si vogliono usare gli stream con file
speciali come le fifo od i socket, che non possono essere aperti con le
funzioni delle librerie standard del C.

\begin{table}[htb]
  \centering
  \begin{tabular}[c]{|l|p{8cm}|}
    \hline
    \textbf{Valore} & \textbf{Significato}\\
    \hline
    \hline
    \texttt{r} & Il file viene aperto in sola lettura; lo stream è posizionato
    all'inizio del file.\\
    \texttt{r+} & Il file viene aperto in lettura e scrittura; lo stream è
    posizionato all'inizio del file. \\
%    \hline
    \texttt{w} & Il file viene troncato a lunghezza nulla (o creato se non 
    esiste), ed aperto in sola lettura; lo stream è posizionato all'inizio del
    file.\\
    \texttt{w+} & Il file viene troncato a lunghezza nulla (o creato se non
    esiste), ed aperto in scrittura e lettura; lo stream è posizionato 
    all'inizio del file.\\
%    \hline
    \texttt{a} & Il file è aperto in \textit{append mode} in sola scrittura
    (o creato se non esiste). \\
    \texttt{a+} & Il file è aperto in \textit{append mode} in lettura e 
    scrittura (o creato se non esiste). \\
    \hline
  \end{tabular}
  \caption{Modalità di apertura di uno stream}
  \label{tab:file_fopen_mode}
\end{table}

In realtà lo standard ANSI C prevede un totale di 15 possibili valori
diversi per \param{mode}, ma in \tabref{tab:file_fopen_mode} si sono
riportati solo i sei valori effettivi, ad essi può essere aggiunto pure
il carattere \func{b} (come ultimo carattere o nel mezzo agli altri per
le stringhe di due caratteri) che in altri sistemi operativi serve a
distinguere i file binari dai file di testo; in un sistema POSIX questa
distinzione non esiste e il valore viene accettato solo per
compatibilità, ma non ha alcun effetto.

Inoltre nel caso si usi \func{fdopen} i valori specificati da
\param{mode} devono essere compatibili con quelli con cui il file
descriptor è stato aperto. Inoltre i modi \cmd{w} e \cmd{w+} non
troncano il file. La posizione nello stream viene settata a quella
corrente nel file descriptor, e le variabili di errore e di fine del
file sono cancellate. Il file non viene duplicato e verrà chiuso alla
chiusura dello stream.

I nuovi file saranno creati secondo quanto visto in
\secref{sec:file_ownership} ed avranno i permessi di accesso settati al
valore \macro{S\_IRUSR|S\_IWUSR|S\_IRGRP|S\_IWGRP|S\_IROTH|S\_IWOTH}
(pari a \macro{0666}) modificato secondo il valore di \acr{umask} per il
processo (si veda \secref{sec:file_umask}).

In caso di file aperti in lettura e scrittura occorre ricordarsi che c'è
di messo una bufferizzazione; per questo motivo lo standard ANSI C
richiede che ci sia una operazione di posizionamento fra una operazione
di output ed una di input o viceversa (eccetto il caso in cui l'input ha
incontrato la fine del file), altrimenti una lettura può ritornare anche
il risultato di scritture precedenti l'ultima effettuata.

Per questo motivo è una buona pratica (e talvolta necessario) far seguire ad
una scrittura una delle funzioni \func{fflush}, \func{fseek}, \func{fsetpos} o
\func{rewind} prima di eseguire una rilettura; viceversa nel caso in cui si
voglia fare una scrittura subito dopo aver eseguito una lettura occorre prima
usare una delle funzioni \func{fseek}, \func{fsetpos} o \func{rewind}. Anche
una operazione nominalmente nulla come \func{fseek(file, 0, SEEK\_CUR)} è
sufficiente a garantire la sincronizzazione.

Una volta aperto lo stream, si può cambiare la modalità di bufferizzazione
(vedi \secref{sec:file_buffering_ctrl}) fintanto che non si è effettuato
alcuna operazione di I/O sul file.

Uno stream viene chiuso con la funzione \func{fclose} il cui prototipo è:
\begin{prototype}{stdio.h}{int fclose(FILE * stream)}
  Chiude lo stream \param{stream}. 
  
  Restituisce 0 in caso di successo e \macro{EOF} in caso di errore, nel qual
  caso setta \var{errno} a \macro{EBADF} se il file descriptor indicato da
  \param{stream} non è valido, o uno dei valori specificati dalla sottostante
  funzione che è fallita (\func{close}, \func{write} o \func{fflush}).
\end{prototype}

La funzione effettua uno scarico di tutti i dati presenti nei buffer di uscita
e scarta tutti i dati in ingresso, se era stato allocato un buffer per lo
stream questo verrà rilasciato. La funzione effettua lo scarico solo per i
dati presenti nei buffer in user space usati dalle \acr{glibc}; se si essere
sicuri che il kernel forzi la scrittura su disco occorrà effettuare . 

Linux supporta, come estensione implementata dalle \acr{glibc}, anche una
altra funzione, \func{fcloseall}, che serve a chiudere tutti i file, il suo
prototipo è:
\begin{prototype}{stdio.h}{int fcloseall(void)}
  Chiude tutti gli stream. 
  
  Restituisce 0 se non ci sono errori ed \macro{EOF} altrimenti.
\end{prototype}
la funzione esegue lo scarico dei dati bufferizzati in uscita e scarta quelli
in ingresso, chiudendo tutti i file. Questa funzione è provvista solo per i
casi di emergenza, quando si è verificato un errore ed il programma deve
essere abortito, ma si vuole compiere qualche altra operazione dopo aver
chiuso i file e prima di uscire (si ricordi quanto visto in
\secref{sec:proc_exit}).


\subsection{Lettura e scrittura su uno stream}
\label{sec:file_io}

Una delle caratteristiche più utili dell'interfaccia degli stream è la
ricchezza delle funzioni disponibili per le operazioni di lettura e
scrittura sui file. Sono infatti previste ben tre diverse modalità
modalità di input/output non formattato:
\begin{enumerate}
\item\textsl{a blocchi} in cui legge/scrive un blocco di dati alla
  volta, vedi \secref{sec:file_block_io}.
\item\textsl{di linea} in cui si legge/scrive una linea (terminata dal
  carattere di newline \verb|\n|) alla volta, vedi
  \secref{sec:file_line_io}.
\item\textsl{a catatteri} in cui si legge/scrive un carattere alla
  volta (con la bufferizzazione gestita automaticamente dalla libreria),
  vedi \secref{sec:file_char_io}.
\end{enumerate}
e una modalità di input/output formattato.

A differenza dell'interfaccia dei file descriptor il raggiungimento
della fine del file è considerato un errore, e viene notificato come
tale dai valori di uscita delle varie funzioni; nella maggior parte dei
casi questo avviene con la restituzione del valore intero (di tipo
\type{int}) \macro{EOF}\footnote{la costante deve essere negativa, le
  \acr{glibc} usano -1, altre implementazioni possono avere valori
  diversi.}  definito anch'esso nell'header \func{stdlib.h}.

Dato che le funzioni dell'interfaccia degli stream sono funzioni di
libreria, esse non settano la variabile \var{errno}, che mantiene il
valore settato dalla system call che ha riportato l'errore, ma siccome
la condizione di end-of-file è anch'essa segnalata come errore, non
esisterebbe alcuna possibilità di distinguerla da un errore (\var{errno}
potrebbe essere stata settata in una altra occasione, vedi
\secref{sec:sys_errno}). 

Per questo motivo tutte le implementazioni delle librerie standard
mantengono per ogni stream almeno due flag all'interno dell'oggetto
\type{FILE}, il flag di \textit{end-of-file}, che segnala che si è
raggiunta la fine del file in lettura, e quello di errore, che segnala
la presenza di un qualche errore nelle operazioni di input/output;
questi flag possono essere riletti dalle funzioni:
\begin{functions}
  \headdecl{stdio.h}
  \funcdecl{int feof(FILE *stream)}
  Controlla il flag di end-of-file di \param{stream}.
  \funcdecl{int ferror(FILE *stream)}
  Controlla il flag di errore di \param{stream}.

  Entrambe le funzioni ritornano un valore diverso da zero se i relativi
  flag sono settati. 
\end{functions}
\noindent si tenga presente comunque che la lettura di questi flag segnala
soltanto che si è avuto un errore, o si è raggiunta la fine del file, in
una precedente operazione sullo stream.

Entrambi i flag (di errore e di end-of-file) possono essere cancellati
usando la funzione \func{clearerr}, il cui prototipo è:
\begin{prototype}{stdio.h}{void clearerr( FILE *stream)}
  Cancella i flag di errore ed end-of-file di \param{stream}. 
\end{prototype}
\noindent in genere si usa questa funziona una volta che si sia identificata e
corretta la causa di un errore per evitare di mantenere i flag attivi,
così da poter rilevare una successiva ulteriore condizione di errore.


\subsection{Input/output a blocchi}
\label{sec:file_block_io}


\subsection{Input/output a caratteri singoli}
\label{sec:file_char_io}


\subsection{Input/output di linea}
\label{sec:file_line_io}


\subsection{Input/output formattato}
\label{sec:file_formatted_io}


\subsection{Posizionamento su uno stream}
\label{sec:file_fseek}



\section{Funzioni avanzate}
\label{sec:file_stream_adv_func}

In questa sezione esamineremo le funzioni che permettono di controllare alcune
caratteristiche più particolari degli stream, come la lettura degli attributi,
le modalità di bufferizzazione, etc.


\subsection{Le funzioni di controllo}
\label{sec:file_stream_cntrl}

Al contrario di quanto avviene con i file descriptor le librerie standard del
C non prevedono nessuna funzione come la \func{fcntl} per la lettura degli
attributi degli stream; le \acr{glibc} però supportano alcune estensioni
derivate da Solaris, che permettono di ottenere informazioni utili.

In certi casi può essere necessario sapere se un certo stream è accessibile in
lettura o scrittura. In genere questa informazione non è disponibile, e si
deve ricordare come il file è stato aperto. La cosa può essere complessa se le
operazioni vengono effettuate un una subroutine, che a questo punto
necessiterà di informazioni aggiuntive rispetto al semplice puntatore allo
stream; questo può essere evitato con le due funzioni \func{\_\_freadable} e
\func{\_\_fwritable} i cui prototipi sono:

\begin{functions}
  \headdecl{stdio\_ext.h}
  \funcdecl{int \_\_freadable (FILE * stream)}
  \funcdecl{int \_\_fwritable(FILE * stream)}
\end{functions}



\subsection{Il controllo della bufferizzazione}
\label{sec:file_buffering_ctrl}

Come accennato in \secref{sec:file_buffering} le librerie definiscono una
serie di funzioni che permettono di controllare il comportamento degli
stream; se non si è 


\subsection{Dettagli dell'implementazione}
\label{sec:file_stream_details}


\subsection{File temporanei}
\label{sec:file_temp_file}


\subsection{Efficienza}
\label{sec:file_stream_efficiency}

