usata ed in genere possono essere riletti senza problemi solo dallo
stesso programma che li ha prodotti.
-Infatti diversi compilatori possono eseguire ottimizzazioni diverse
-delle strutture dati e alcuni compilatori (come il \cmd{gcc}) possono
-anche scegliere se ottimizzare l'occupazione di spazio, impacchettando
-più strettamente i dati, o la velocità inserendo opportuni
-\textit{padding} per l'allineamento dei medesimi generando quindi output
-binari diversi. Inoltre altre incompatibilità si possono presentare
-quando entrano in gioco differenze di architettura hardware, come la
-dimensione del bus o la modalità di ordinamento dei bit o il formato
-delle variabili in floating point.
-
-Per questo motivo quando si usa l'input/output binario occorre sempre
-essere prendere le opportune precauzioni (in genere usare un formato di
-più alto livello che permetta di recuperare l'informazione completa),
-per assicurarsi che versioni diverse del programma siano in grado di
-rileggere i dati tenendo conto delle eventuali differenze.
+Infatti diversi compilatori possono eseguire ottimizzazioni diverse delle
+strutture dati e alcuni compilatori (come il \cmd{gcc}) possono anche
+scegliere se ottimizzare l'occupazione di spazio, impacchettando più
+strettamente i dati, o la velocità inserendo opportuni \textit{padding} per
+l'allineamento dei medesimi generando quindi output binari diversi. Inoltre
+altre incompatibilità si possono presentare quando entrano in gioco differenze
+di architettura hardware, come la dimensione del bus o la modalità di
+ordinamento dei bit o il formato delle variabili in floating point.
+
+Per questo motivo quando si usa l'input/output binario occorre sempre prendere
+le opportune precauzioni (in genere usare un formato di più alto livello che
+permetta di recuperare l'informazione completa), per assicurarsi che versioni
+diverse del programma siano in grado di rileggere i dati tenendo conto delle
+eventuali differenze.
Le \acr{glibc} definiscono altre due funzioni per l'I/O binario, che
evitano il lock implicito dello stream, usato per dalla librerie per la
Nelle estensioni GNU che provvedono la localizzazione sono definite tre
funzioni equivalenti alle precedenti che invece di un carattere di un
byte restituiscono un carattere in formato esteso (cioè di tipo
-\ctyp{wint\_t}, il loro prototipo è:
+\ctyp{wint\_t}), il loro prototipo è:
\begin{functions}
\headdecl{stdio.h}
\headdecl{wchar.h}
\func{fread} e \func{fwrite}, in quanto non è possibile distinguere il
valore -1 da una condizione di errore che restituisce \macro{EOF}.
-Una degli usi più frequenti dell'input/output a caratteri è nei
-programmi di \textit{parsing} in cui si analizza il testo; in questo
-contesto diventa utile poter analizzare il carattere successivo da uno
-stream senza estrarlo effettivamente (la tecnica è detta \textit{peeking
- ahead}) in modo che il programma possa regolarsi sulla base avendo
-dato una \textsl{sbirciatina} a quello che viene dopo.
+Uno degli usi più frequenti dell'input/output a caratteri è nei programmi di
+\textit{parsing} in cui si analizza il testo; in questo contesto diventa utile
+poter analizzare il carattere successivo da uno stream senza estrarlo
+effettivamente (la tecnica è detta \textit{peeking ahead}) in modo che il
+programma possa regolarsi avendo dato una \textsl{sbirciatina} a quello che
+viene dopo.
Nel nostro caso questo tipo di comportamento può essere realizzato prima
leggendo il carattere, e poi rimandandolo indietro, cosicché ridiventi
\subsection{Input/output di linea}
\label{sec:file_line_io}
-La terza ed ultima modalità di input/output non formattato è quella di
-linea, in cui legge o scrive una riga alla volta; questa è una modalità
-molto usata per l'I/O da terminale, ma che presenta le caratteristiche
-più controverse.
+La terza ed ultima modalità di input/output non formattato è quella di linea,
+in cui si legge o si scrive una riga alla volta; questa è una modalità molto
+usata per l'I/O da terminale, ma che presenta le caratteristiche più
+controverse.
Le funzioni previste dallo standard ANSI C per leggere una linea sono
sostanzialmente due, \func{gets} e \func{fgets}, i cui rispettivi
Entrambe le funzioni effettuano la lettura (dal file specificato \func{fgets},
dallo standard input \func{gets}) di una linea di caratteri (terminata dal
-carattere \textit{newline}, \verb|\n|, quello mappato sul tasto di ritorno a
-capo della tastiera), ma \func{gets} sostituisce \verb|\n| con uno zero,
+carattere \textit{newline}, \verb|'\n'|, quello mappato sul tasto di ritorno a
+capo della tastiera), ma \func{gets} sostituisce \verb|'\n'| con uno zero,
mentre \func{fgets} aggiunge uno zero dopo il \textit{newline}, che resta
dentro la stringa. Se la lettura incontra la fine del file (o c'è un errore)
viene restituito un \macro{NULL}, ed il buffer \param{buf} non viene toccato.
L'uso di \func{gets} è deprecato e deve essere assolutamente evitato; la
-funzione infatti non controlla il numero di byte letti, per cui nel caso
-la stringa letta superi le dimensioni del buffer, si avrà un
-\textit{buffer overflow}, con sovrascrittura della memoria del processo
-adiacente al buffer.
+funzione infatti non controlla il numero di byte letti, per cui nel caso la
+stringa letta superi le dimensioni del buffer, si avrà un \textit{buffer
+ overflow}\index{buffer overflow}, con sovrascrittura della memoria del
+processo adiacente al buffer.\footnote{questa tecnica è spiegata in dettaglio
+ e con molta efficacia nell'ormai famoso articolo di Aleph1 \cite{StS}.}
Questa è una delle vulnerabilità più sfruttate per guadagnare accessi
non autorizzati al sistema (i cosiddetti \textit{exploit}), basta
(supposto che la \func{gets} sia stata chiamata da una subroutine), in
modo da far ripartire l'esecuzione nel codice inviato nella stringa
stessa (in genere uno \textit{shell code} cioè una sezione di programma
-lancia una shell).
-
-La funzione \func{fgets} non ha i precedenti problemi di \func{gets} in
-quanto prende in input la dimensione del buffer \param{size}, che non
-verrà mai ecceduta in lettura. La funzione legge fino ad un massimo di
-\param{size} caratteri (newline compreso), ed aggiunge uno zero di
-terminazione; questo comporta che la stringa possa essere al massimo di
-\var{size-1} caratteri. Se la linea eccede la dimensione del buffer
-verranno letti solo \var{size-1} caratteri, ma la stringa sarà sempre
-terminata correttamente con uno zero finale; sarà possibile leggere i
-restanti caratteri in una chiamata successiva.
+che lancia una shell).
+
+La funzione \func{fgets} non ha i precedenti problemi di \func{gets} in quanto
+prende in input la dimensione del buffer \param{size}, che non verrà mai
+ecceduta in lettura. La funzione legge fino ad un massimo di \param{size}
+caratteri (newline compreso), ed aggiunge uno zero di terminazione; questo
+comporta che la stringa possa essere al massimo di \code{size-1} caratteri. Se
+la linea eccede la dimensione del buffer verranno letti solo \code{size-1}
+caratteri, ma la stringa sarà sempre terminata correttamente con uno zero
+finale; sarà possibile leggere i rimanenti caratteri in una chiamata
+successiva.
Per la scrittura di una linea lo standard ANSI C prevede altre due
funzioni, \func{fputs} e \func{puts}, analoghe a quelle di lettura, i
non negativo in caso di successo e \macro{NULL} o \macro{EOF} in
caso di errore o fine del file.}
\end{functions}
-\noindent il comportamento è identico a quello di \func{fgets} e
-\func{fputs} solo che tutto (numero di caratteri massimo, terminatore
-della stringa, newline) è espresso in termini di caratteri estesi
+\noindent il cui comportamento è identico a quello di \func{fgets} e
+\func{fputs} a parte il fatto che tutto (numero di caratteri massimo,
+terminatore della stringa, newline) è espresso in termini di caratteri estesi
anziché di caratteri ASCII.
Come nel caso dell'I/O binario e a caratteri nelle \acr{glibc} sono
leggere una linea terminata da un newline esattamente allo stesso modo
di \func{fgets}, il suo prototipo è:
\begin{prototype}{stdio.h}
- {ssize\_t getline(char **buffer, size\_t *n, FILE *stream)} Legge una
- linea dal file \param{stream} sul buffer indicato da \param{buffer}
- riallocandolo se necessario (l'indirizzo del buffer e la sua
- dimensione vengono sempre riscritte).
+ {ssize\_t getline(char **buffer, size\_t *n, FILE *stream)} Legge una linea
+ dal file \param{stream} copiandola sul buffer indicato da \param{buffer}
+ riallocandolo se necessario (l'indirizzo del buffer e la sua dimensione
+ vengono sempre riscritte).
\bodydesc{La funzione ritorna il numero di caratteri letti in caso di
successo e -1 in caso di errore o di raggiungimento della fine del
file.}
\end{prototype}
-La funzione permette di eseguire una lettura senza doversi preoccupare
-della eventuale lunghezza eccessiva della stringa da leggere. Essa
-prende come primo parametro l'indirizzo del puntatore al buffer su cui
-si vuole leggere la linea. Quest'ultimo \emph{deve} essere stato
-allocato in precedenza con una \func{malloc} (non si può passare
-l'indirizzo di un puntatore ad una variabile locale); come secondo
-parametro la funzione vuole l'indirizzo della variabile contenente le
-dimensioni del buffer suddetto.
+La funzione permette di eseguire una lettura senza doversi preoccupare della
+eventuale lunghezza eccessiva della stringa da leggere. Essa prende come primo
+parametro l'indirizzo del puntatore al buffer su cui si vuole copiare la
+linea. Quest'ultimo \emph{deve} essere stato allocato in precedenza con una
+\func{malloc} (non si può passare l'indirizzo di un puntatore ad una variabile
+locale); come secondo parametro la funzione vuole l'indirizzo della variabile
+contenente le dimensioni del buffer suddetto.
Se il buffer di destinazione è sufficientemente ampio la stringa viene
scritta subito, altrimenti il buffer viene allargato usando
\func{realloc} e la nuova dimensione ed il nuovo puntatore vengono
passata indietro (si noti infatti come per entrambi i parametri si siano
-usati dei \textit{value result argument}, passando dei puntatori anzichè
+usati dei \textit{value result argument}, passando dei puntatori anziché
i valori delle variabili, secondo la tecnica spiegata in
\secref{sec:proc_var_passing}).
-Se si passa alla funzione l'indirizzo ad un puntatore impostato a
-\macro{NULL} e \var{*n} è zero, la funzione provvede da sola
-all'allocazione della memoria necessaria a contenere la linea. In tutti
-i casi si ottiene dalla funzione un puntatore all'inizio del testo della
-linea. Un esempio di codice può essere il seguente:
+Se si passa alla funzione l'indirizzo di un puntatore impostato a \macro{NULL}
+e \var{*n} è zero, la funzione provvede da sola all'allocazione della memoria
+necessaria a contenere la linea. In tutti i casi si ottiene dalla funzione un
+puntatore all'inizio del testo della linea letta. Un esempio di codice può
+essere il seguente:
\begin{lstlisting}[labelstep=0,frame=,indent=1cm]{}
size_t n = 0;
char *ptr = NULL;
\item uno specificatore del parametro da usare (terminato da un \cmd{\$}),
\item uno o più flag (i cui valori possibili sono riassunti in
\tabref{tab:file_format_flag}) che controllano il formato di stampa della
- conversione
+ conversione,
\item uno specificatore di larghezza (un numero decimale), eventualmente
seguito (per i numeri in virgola mobile) da un specificatore di precisione
(un altro numero decimale),
Per eliminare alla radice questi problemi, le \acr{glibc} supportano una
-estensione specifica GNU che alloca dinamicamente tutto lo spazio necessario;
+specifica estensione GNU che alloca dinamicamente tutto lo spazio necessario;
l'estensione si attiva al solito definendo \macro{\_GNU\_SOURCE}, le due
funzioni sono:
\begin{functions}
relativamente semplice si preferisce usare l'input di linea ed effettuare
scansione e conversione di quanto serve direttamente con una delle funzioni di
conversione delle stringhe; se invece il formato è più complesso diventa più
-facile utilizzare uno strumento come il \cmd{flex} per generare un
-analizzatore lessicale o il \cmd{bison} per generare un parser.
+facile utilizzare uno strumento come \cmd{flex}\footnote{il programma
+ \cmd{flex}, è una implementazione libera di \cmd{lex} un generatore di
+ analizzatori lessicali, per i dettagli si può fare riferimento al manuale
+ \cite{flex}.} per generare un analizzatore lessicale o il
+\cmd{bison}\footnote{il programma \cmd{bison} è un clone del generatore di
+ parser \cmd{yacc}, maggiori dettagli possono essere trovati nel relativo
+ manuale \cite{bison}.} per generare un parser.
\subsection{Posizionamento su uno stream}
\label{sec:file_fseek}
-Come per i file descriptor è possibile anche con gli stream spostarsi
+Come per i file descriptor anche per gli stream è possibile spostarsi
all'interno di un file per effettuare operazioni di lettura o scrittura in un
-punto prestabilito; questo fintanto che l'operazione di riposizionamento è
-supportata dal file sottostante lo stream, quando cioè si ha a che fare con
-quello che viene detto un file ad \textsl{accesso casuale}.
+punto prestabilito; sempre che l'operazione di riposizionamento sia supportata
+dal file sottostante lo stream, quando cioè si ha a che fare con quello che
+viene detto un file ad \textsl{accesso casuale}.\footnote{dato che in un
+ sistema Unix esistono vari tipi di file, come le fifo ed i dispositivi, non
+ è scontato che questo sia sempre vero.}
In GNU/Linux ed in generale in ogni sistema unix-like la posizione nel file è
espressa da un intero positivo, rappresentato dal tipo \type{off\_t}, il
-problema è alcune delle funzioni usate per il riposizionamento sugli stream
-originano dalle prime versioni di Unix, in cui questo tipo non era ancora
-stato definito, e che in altri sistemi non è detto che la posizione su un file
-venga sempre rappresentata con il numero di caratteri dall'inizio (ad esempio
-in VMS può essere rappresentata come numero di record, e offset rispetto al
-record corrente).
+problema è che alcune delle funzioni usate per il riposizionamento sugli
+stream originano dalle prime versioni di Unix, in cui questo tipo non era
+ancora stato definito, e che in altri sistemi non è detto che la posizione su
+un file venga sempre rappresentata con il numero di caratteri dall'inizio (ad
+esempio in VMS può essere rappresentata come numero di record, più l'offset
+rispetto al record corrente).
Tutto questo comporta la presenza di diverse funzioni che eseguono
sostanzialmente le stesse operazioni, ma usano parametri di tipo
\noindent la funzione restituisce la posizione come numero di byte
dall'inizio dello stream.
-Queste funzioni esprimono tutte la posizione nel file come un \func{long int},
-dato che (ad esempio quando si usa un filesystem indicizzato a 64 bit) questo
+Queste funzioni esprimono tutte la posizione nel file come un \ctyp{long int}.
+Dato che (ad esempio quando si usa un filesystem indicizzato a 64 bit) questo
può non essere possibile lo standard POSIX ha introdotto le nuove funzioni
\func{fgetpos} e \func{fsetpos}, che invece usano il nuovo tipo
\type{fpos\_t}, ed i cui prototipi sono:
\funcdecl{int fsetpos(FILE *stream, fpos\_t *pos)} Imposta la posizione
corrente nello stream \param{stream} al valore specificato da \param{pos}.
- \funcdecl{int fgetpos(FILE *stream, fpos\_t *pos)} Scrive la posizione
- corrente nello stream \param{stream} in \param{pos}.
+ \funcdecl{int fgetpos(FILE *stream, fpos\_t *pos)} Legge la posizione
+ corrente nello stream \param{stream} e la scrive in \param{pos}.
\bodydesc{Le funzioni ritornano 0 in caso di successo e -1 in caso di
errore.}
\end{functions}
In Linux, a partire dalle glibc 2.1, sono presenti anche le due funzioni
-\func{fseeko} e \func{ftello}, che assolutamente identiche alle precedenti
-\func{fseek} e \func{ftell} ma hanno argomenti di tipo \type{off\_t} anziché
-di tipo \ctyp{long int}.
+\func{fseeko} e \func{ftello}, che sono assolutamente identiche alle
+precedenti \func{fseek} e \func{ftell} ma hanno argomenti di tipo
+\type{off\_t} anziché di tipo \ctyp{long int}.
\subsection{Le funzioni di controllo}
\label{sec:file_stream_cntrl}
-Al contrario di quanto avviene con i file descriptor le librerie standard del
+Al contrario di quanto avviene con i file descriptor, le librerie standard del
C non prevedono nessuna funzione come la \func{fcntl} per il controllo degli
-attributi dei file. Però siccome ogni stream si appoggia ad un file descriptor
-si può usare la funzione \func{fileno} per ottenere quest'ultimo, il prototipo
-della funzione è:
+attributi dei file. Però, dato che ogni stream si appoggia ad un file
+descriptor, si può usare la funzione \func{fileno} per ottenere quest'ultimo,
+il prototipo della funzione è:
\begin{prototype}{stdio.h}{int fileno(FILE *stream)}
Legge il file descriptor sottostante lo stream \param{stream}.
\noindent ed in questo modo diventa possibile usare direttamente \func{fcntl}.
Questo permette di accedere agli attributi del file descriptor sottostante lo
-stream, ma non ci da nessuna informazione riguardo alle proprietà dello stream
+stream, ma non ci dà nessuna informazione riguardo alle proprietà dello stream
medesimo. Le \acr{glibc} però supportano alcune estensioni derivate da
Solaris, che permettono di ottenere informazioni utili.
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 è specificato nulla la modalità di buffering viene decisa
+se non si è specificato nulla, la modalità di buffering viene decisa
autonomamente sulla base del tipo di file sottostante, ed i buffer vengono
allocati automaticamente.
\param{size}.
Ovviamente se si usa un buffer specificato dall'utente questo deve essere
-stato allocato e restare disponibile per tutto il tempo in cui si opera sullo
+stato allocato e rimanere disponibile per tutto il tempo in cui si opera sullo
stream. In genere conviene allocarlo con \func{malloc} e disallocarlo dopo la
chiusura del file; ma fintanto che il file è usato all'interno di una
-funzione, può anche essere usata una variabile automatica. In \file{stdio.h}
+funzione, può anche essere usata una variabile automatica. In \file{stdio.h} è
definita la macro \macro{BUFSIZ}, che indica le dimensioni generiche del
-buffer di uno stream; queste vengono usate dalla funzione \func{setbuf},
-questa però non è detto corrisponda in tutti i casi al valore ottimale (che
+buffer di uno stream; queste vengono usate dalla funzione \func{setbuf}. Non
+è detto però che tale dimensione corrisponda sempre al valore ottimale (che
può variare a seconda del dispositivo).
Dato che la procedura di allocazione manuale è macchinosa, comporta dei rischi
Oltre a \func{setvbuf} le \acr{glibc} definiscono altre tre funzioni per la
gestione della bufferizzazione di uno stream: \func{setbuf}, \func{setbuffer}
-e \func{setlinebuf}, i loro prototipi sono:
+e \func{setlinebuf}; i loro prototipi sono:
\begin{functions}
\headdecl{stdio.h}
usare \func{sync} o \func{fsync} (si veda~\secref{sec:file_sync}).
Infine esistono anche circostanze in cui si vuole scartare tutto l'output
-pendente, per questo si può usare \func{fpurge}, il cui prototipo è:
+pendente; per questo si può usare \func{fpurge}, il cui prototipo è:
\begin{prototype}{stdio.h}{int fpurge(FILE *stream)}
Cancella i buffer di input e di output dello stream \param{stream}.
modo in cui sono usati nelle applicazioni normali, ma si deve essere
consapevoli delle possibili complicazioni anche quando non si usano i
thread, dato che l'implementazione delle librerie è influenzata
-pesantemente dalle richieste necessarie per garantirne l'uso coi thread.
+pesantemente dalle richieste necessarie per garantirne l'uso con i thread.
Lo standard POSIX richiede che le operazioni sui file siano atomiche rispetto
ai thread, per questo le operazioni sui buffer effettuate dalle funzioni di
\begin{functions}
\headdecl{stdio.h}
- \funcdecl{void flockfile(FILE *stream)} Esegue l'acquisizione del
- lock dello stream \param{stream}, bloccandosi in caso il lock non
- disponibile.
+ \funcdecl{void flockfile(FILE *stream)} Esegue l'acquisizione del lock dello
+ stream \param{stream}, bloccandosi se il lock non è disponibile.
- \funcdecl{int ftrylockfile(FILE *stream)} Tenta l'acquisizione del
- lock dello stream \param{stream}, senza bloccarsi in caso il lock non sia
- disponibile. Ritorna zero in caso di acquisizione del lock, diverso da
- zero altrimenti.
+ \funcdecl{int ftrylockfile(FILE *stream)} Tenta l'acquisizione del lock
+ dello stream \param{stream}, senza bloccarsi se il lock non è disponibile.
+ Ritorna zero in caso di acquisizione del lock, diverso da zero altrimenti.
\funcdecl{void funlockfile(FILE *stream)} Rilascia il lock dello
stream \param{stream}.