\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
idetificabili 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 il \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 \textit{flush} dei
dati, poiché non è detto che ad una scrittura sullo stream corrisponda una
immediata scrittura sul dispositivo.

Per risondere 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 un \textit{flush} 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 il
\textit{flush} 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 ed il
\textit{flush} 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}


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


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


\subsection{Input/output binario}
\label{sec:file_binary_io}


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


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


\subsection{Chiusura di uno stream}
\label{sec:file_fclose}


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


\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}









