X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=filestd.tex;h=c6be8eb040a1a795e22704147ff5ba58063ef3c5;hp=dcbb3381f146bb7cd6d0141571274232de019601;hb=6b236ee4592f05aabf7dbb4bfcba72d4217052d3;hpb=a76ef671288ddb656f3a4aa599cfda5e05491c39 diff --git a/filestd.tex b/filestd.tex index dcbb338..c6be8eb 100644 --- a/filestd.tex +++ b/filestd.tex @@ -89,14 +89,14 @@ 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 +\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 +\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 +\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. @@ -215,23 +215,23 @@ tre\footnote{\func{fopen} e \func{freopen} fanno parte dello standard prototipi sono: \begin{functions} \headdecl{stdio.h} - \funcdecl{FILE * fopen(const char * path, const char *mode)} + \funcdecl{FILE *fopen(const char *path, const char *mode)} Apre il file specificato da \param{path}. - \funcdecl{FILE * fdopen(int fildes, const char * mode)} + \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)} + \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}. + \bodydesc{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}, @@ -338,13 +338,14 @@ Una volta aperto lo stream, si pu 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)} +\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}). + \bodydesc{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 @@ -360,14 +361,14 @@ prototipo \begin{prototype}{stdio.h}{int fcloseall(void)} Chiude tutti gli stream. - Restituisce 0 se non ci sono errori ed \macro{EOF} altrimenti. + \bodydesc{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}). +\noindent 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} @@ -421,9 +422,9 @@ questi due flag possono essere riletti dalle funzioni: 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. + + \bodydesc{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 c'è stato un errore, o che si è raggiunta la fine del file in una @@ -432,7 +433,7 @@ ogni volta che si chiama una funzione di libreria. 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)} +\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 @@ -451,19 +452,18 @@ formattati. Le due funzioni che si usano per l'I/O binario sono: \begin{functions} \headdecl{stdio.h} - \funcdecl{size\_t fread(void * ptr, size\_t size, size\_t nmemb, FILE - * stream)} + \funcdecl{size\_t fread(void *ptr, size\_t size, size\_t nmemb, FILE + *stream)} - \funcdecl{size\_t fwrite(const void * ptr, size\_t size, size\_t - nmemb, FILE * stream)} + \funcdecl{size\_t fwrite(const void *ptr, size\_t size, size\_t + nmemb, FILE *stream)} - Le funzioni rispettivamente leggono e scrivono \param{nmemb} elementi - di dimensione \param{size} dal buffer \param{ptr} al file - \param{stream}. + Rispettivamente leggono e scrivono \param{nmemb} elementi di dimensione + \param{size} dal buffer \param{ptr} al file \param{stream}. - Entrambe le funzioni ritornano il numero di elementi letti o scritti, - in caso di errore o fine del file viene restituito un numero di - elementi inferiore al richiesto. + \bodydesc{Entrambe le funzioni ritornano il numero di elementi letti o + scritti, in caso di errore o fine del file viene restituito un numero di + elementi inferiore al richiesto.} \end{functions} In genere si usano queste funzioni quando si devono trasferire su file @@ -471,7 +471,7 @@ blocchi di dati binari in maniera compatta e veloce; un primo caso di uso tipico è quello in cui si salva un vettore (o un certo numero dei suoi elementi) con una chiamata del tipo: \begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} -int WriteVect(FILE * stream, double * vec, size_t nelem) +int WriteVect(FILE *stream, double *vec, size_t nelem) { int size, nread; size = sizeof(*vec); @@ -551,14 +551,14 @@ gestione delle applicazioni multi-thread (si veda \begin{functions} \headdecl{stdio.h} - \funcdecl{size\_t fread\_unlocked(void * ptr, size\_t size, size\_t - nmemb, FILE * stream)} + \funcdecl{size\_t fread\_unlocked(void *ptr, size\_t size, size\_t + nmemb, FILE *stream)} - \funcdecl{size\_t fwrite\_unlocked(const void * ptr, size\_t size, - size\_t nmemb, FILE * stream)} + \funcdecl{size\_t fwrite\_unlocked(const void *ptr, size\_t size, + size\_t nmemb, FILE *stream)} - Le funzioni sono identiche alle analoghe \func{fread} e \func{fwrite} - ma non acquisiscono il lock implicito sullo stream. + \bodydesc{Le funzioni sono identiche alle analoghe \func{fread} e + \func{fwrite} ma non acquisiscono il lock implicito sullo stream.} \end{functions} \noindent entrambe le funzioni sono estensioni GNU previste solo dalle \acr{glibc}. @@ -568,10 +568,9 @@ gestione delle applicazioni multi-thread (si veda \label{sec:file_char_io} La seconda modalità di input/output è quella a caratteri, in cui si -trasferisce un carattere alla volta. - -Le funzioni per la lettura a caratteri sono tre, \func{fgetc}, -\func{getc} e \func{getchar}, i rispettivi prototipi sono: +trasferisce un carattere alla volta. Le funzioni per la lettura a +caratteri sono tre, \func{fgetc}, \func{getc} e \func{getchar}, i +rispettivi prototipi sono: \begin{functions} \headdecl{stdio.h} @@ -581,11 +580,11 @@ Le funzioni per la lettura a caratteri sono tre, \func{fgetc}, \funcdecl{int fgetc(FILE *stream)} Legge un byte da \param{stream} e lo restituisce come intero. È una sempre una funzione. - \funcdecl{int getchar(void)} è equivalente a \func{getc(stdin)}. + \funcdecl{int getchar(void)} Equivalente a \func{getc(stdin)}. - Tutte queste funzioni leggono un byte alla volta, che viene restituito - come intero; in caso di errore o fine del file il valore di ritorno è - \macro{EOF}. + \bodydesc{Tutte queste funzioni leggono un byte alla volta, che viene + restituito come intero; in caso di errore o fine del file il valore + di ritorno è \macro{EOF}.} \end{functions} A parte \func{getchar}, che si usa in genere per leggere un carattere da @@ -608,12 +607,13 @@ ad \type{int} (si usa \type{unsigned char} in modo da evitare l'espansione del segno). In questo modo il valore di ritorno è sempre positivo, tranne in caso di errore o fine del file. -Nelle estenzioni GNU che provvedono la localizzazione sono definite tre +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 \type{wint\_t}, il loro prototipo è: \begin{functions} \headdecl{stdio.h} + \headdecl{wchar.h} \funcdecl{wint\_t getwc(FILE *stream)} Legge un carattere esteso da \param{stream}. In genere è implementata come una macro. @@ -621,10 +621,10 @@ byte restituiscono un carattere in formato esteso (cio \funcdecl{wint\_t fgetwc(FILE *stream)} Legge un carattere esteso da \param{stream} È una sempre una funzione. - \funcdecl{wint\_t getwchar(void)} è equivalente a \func{getwc(stdin)}. + \funcdecl{wint\_t getwchar(void)} Equivalente a \func{getwc(stdin)}. - Tutte queste funzioni leggono un carattere alla volta, in caso di - errore o fine del file il valore di ritorno è \macro{WEOF}. + \bodydesc{Tutte queste funzioni leggono un carattere alla volta, in + caso di errore o fine del file il valore di ritorno è \macro{WEOF}.} \end{functions} Per scrivere un carattere si possono usare tre funzioni analoghe alle @@ -639,11 +639,11 @@ precedenti usate per leggere: \func{putc}, \func{fputc} e \funcdecl{int fputc(FILE *stream)} Scrive il carattere \param{c} su \param{stream}. È una sempre una funzione. - \funcdecl{int putchar(void)} è equivalente a \func{putc(stdin)}. + \funcdecl{int putchar(void)} Equivalente a \func{putc(stdin)}. -\item \noindent Le funzioni scrivono sempre un carattere alla volta, - il cui valore viene restituito in caso di successo; in caso di errore - o fine del file il valore di ritorno è \macro{EOF}. + \bodydesc{Le funzioni scrivono sempre un carattere alla volta, il cui + valore viene restituito in caso di successo; in caso di errore o + fine del file il valore di ritorno è \macro{EOF}.} \end{functions} Tutte queste funzioni scrivono sempre un byte alla volta, anche se @@ -653,9 +653,10 @@ sempre un intero; in caso di errore o fine del file il valore di ritorno è \macro{EOF}. Come nel caso dell'I/O binario le \acr{glibc} provvedono per ciascuna -delle funzioni precedenti una seconda funzione, il cui nome è ottenuto -aggiungendo un \func{\_unlocked}, che esegue esattamente le stesse -operazioni evitando però il lock implicito dello stream. +delle funzioni precedenti, come estensione GNU, una seconda funzione, il +cui nome è ottenuto aggiungendo un \func{\_unlocked}, che esegue +esattamente le stesse operazioni evitando però il lock implicito dello +stream. Per compatibilità con SVID sono provviste anche due funzioni per leggere e scrivere una \textit{word} (che è sempre definita come \type{int}); i @@ -666,47 +667,356 @@ loro prototipi sono: \funcdecl{int getw(FILE *stream)} Legge una parola da \param{stream}. \funcdecl{int putw(int w, FILE *stream)} Scrive la parola \param{w} su \param{stream}. - - Le funzioni restituiscono la parola \param{w}, o \macro{EOF} in caso - di errore o di fine del file. + + \bodydesc{Le funzioni restituiscono la parola \param{w}, o \macro{EOF} + in caso di errore o di fine del file.} \end{functions} -l'uso di queste funzioni è deprecato in favore dell'uso di \func{fread} -e \func{fwrite}, in quanto non è possibile distinguere il valore -1 da una -condizione di errore che restituisce \macro{EOF}. +\noindent l'uso di queste funzioni è deprecato in favore dell'uso di +\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. + +Nel nostro caso questo tipo di comportamento può essere realizzato prima +leggendo il carattere, e poi rimandandolo indietro, cosicché ridiventi +disponibile per una lettura successiva; la funzione che inverte la +lettura si chiama \func{ungetc} ed il suo prototipo è: +\begin{prototype}{stdio.h}{int ungetc(int c, FILE *stream)} + Rimanda indietro il carattere \param{c}, con un cast a \type{unsigned + char}, sullo stream \param{stream}. + + \bodydesc{La funzione ritorna \param{c} in caso di successo e + \macro{EOF} in caso di errore.} +\end{prototype} +\noindent benché lo standard ANSI C preveda che l'operazione possa +essere ripetuta per un numero arbitrario di caratteri, alle +implementazioni è richiesto di garantire solo un livello; questo è +quello che fa la \acr{glibc}, che richiede che avvenga un'altra +operazione fra due \func{ungetc} successive. + +Non è necessario che il carattere che si manda indietro sia l'ultimo che +si è letto, e non è necessario neanche avere letto nessun carattere +prima di usare \func{ungetc}, ma di norma la funzione è intesa per +essere usata per rimandare indietro l'ultimo carattere letto. + +Nel caso \param{c} sia un \macro{EOF} la funzione non fa nulla, e +restituisce sempre \macro{EOF}; così si può usare \func{ungetc} anche +con il risultato di una lettura alla fine del file. + +Se si è alla fine del file si può comunque rimandare indietro un +carattere, il flag di end-of-file verrà automaticamente cancellato +perché c'è un nuovo carattere disponibile che potrà essere riletto +successivamente. + +Infine si tenga presente che \func{ungetc} non altera il contenuto del +file, ma opera esclusivamente sul buffer interno. Se si esegue una +qualunque delle operazioni di riposizionamento (vedi +\secref{sec:file_fseek}) i caratteri rimandati indietro vengono +scartati. \subsection{Input/output di linea} \label{sec:file_line_io} -La terza ed ultima modalità di input/output non formattato è quello di +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, ma che presenta le caratteristiche più controverse. +molto usata per l'I/O da terminale, ma che presenta le caratteristiche +più controverse. -Le funzioni per leggere una linea sono sostazialmente due, \func{gets} e -\func{fgets}, i rispettivi prototipi sono: +Le funzioni previste dallo standard ANSI C per leggere una linea sono +sostanzialmente due, \func{gets} e \func{fgets}, i cui rispettivi +prototipi sono: \begin{functions} \headdecl{stdio.h} - \funcdecl{char * gets(char * string)} Scrive su \param{string} una + \funcdecl{char *gets(char *string)} Scrive su \param{string} una linea letta da \var{stdin}. - \funcdecl{char * fgets(char * string, int size, FILE *stream)} + \funcdecl{char *fgets(char *string, int size, FILE *stream)} Scrive su \param{string} la linea letta da \param{stream} per un massimo di \param{size} byte. + + \bodydesc{Le funzioni restituiscono l'indirizzo \param{string} in caso + di successo o \macro{NULL} in caso di errore.} +\end{functions} - Le funzioni restituiscono della stringa letta o \macro{NULL} in caso - di errore. +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|), 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. + +Questa è una delle vulnerabilità più sfruttate per guadagnare accessi +non autorizzati al sistema (i cosiddetti \textit{exploit}), basta +infatti inviare una stringa sufficientemente lunga ed opportunamente +forgiata per sovrascrivere gli indirizzi di ritorno nello stack +(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. + +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 +rispettivi prototipi sono: +\begin{functions} + \headdecl{stdio.h} + + \funcdecl{int puts(const char *string)} Scrive su \var{stdout} la + linea \param{string}. + + \funcdecl{int fputs(const char *string, FILE *stream)} Scrive su + \param{stream} la linea \param{string}. + + \bodydesc{Le funzioni restituiscono un valore non negativo in caso di + successo o \macro{EOF} in caso di errore.} \end{functions} +Dato che in questo caso si scrivono i dati in uscita \func{puts} non ha +i problemi di \func{gets} ed è in genere la forma più immediata per +scrivere messaggi sullo standard output; la funzione prende una stringa +terminata da uno zero ed aggiunge automaticamente un newline. La +differenza con \func{fputs} (a parte la possibilità di specificare un +file diverso da \var{stdout}) è che quest'ultima non aggiunge il +newline, che deve essere previsto esplicitamente. + +Come per le funzioni di input/output a caratteri esistono le estensioni +per leggere e scrivere caratteri estesi, i loro prototipi sono: +\begin{functions} + \headdecl{whar.h} + \funcdecl{wchar\_t *fgetws(wchar\_t *ws, int n, FILE *stream)} + Legge un massimo di \param{n} caratteri estesi dal file + \param{stream} al buffer \param{ws}. + + \funcdecl{int fputws(const wchar\_t *ws, FILE *stream)} Scrive la + linea \param{ws} di caratteri estesi sul file \param{stream}. + + \bodydesc{Le funzioni ritornano rispettivamente \param{ws} o un numero + 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 +anziché di caratteri ASCII. + +Come nel caso dell'I/O binario e a caratteri nelle \acr{glibc} sono +previste una serie di altre funzioni, estensione di tutte quelle +illustrate finora (eccetto \func{gets} e \func{puts}), il cui nome si +ottiene aggiungendo un \func{\_unlocked}, e che eseguono esattamente le +stesse operazioni delle loro equivalenti, evitando però il lock +implicito dello stream (vedi \secref{sec:file_stream_thread}). + +Come abbiamo visto, le funzioni di lettura per l'input/output di linea +previste dallo standard ANSI C presentano svariati inconvenienti. Benché +\func{fgets} non abbia i gravissimi problemi di \func{gets}, può +comunque dare risultati ambigui se l'input contiene degli zeri; questi +infatti saranno scritti sul buffer di uscita e la stringa in output +apparirà come più corta dei bytes effettivamente letti. Questa è una +condizione che è sempre possibile controllare (deve essere presente un +newline prima della effettiva conclusione della stringa presente nel +buffer), ma a costo di una complicazione ulteriore della logica del +programma. Lo stesso dicasi quando si deve gestire il caso di stringa +che eccede le dimensioni del buffer. + +Per questo motivo le \acr{glibc} prevedono, come estensione GNU, due +nuove funzioni per la gestione dell'input/output di linea, il cui uso +permette di risolvere questi problemi. L'uso di queste funzioni deve +essere attivato definendo la macro \macro{\_GNU\_SOURCE} prima di +includere \file{stdio.h}. La prima delle due, \func{getline}, serve per +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). + + \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. + +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è +i valori delle variabili, secondo la tecnica spiegata in +\secref{sec:proc_var_passing}). + +Se si passa alla funzione l'indirizzo ad un puntatore settato 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: +\begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} + size_t n = 0; + char *ptr = NULL; + int nread; + FILE * file; + ... + nread = getline(&ptr, &n, file); +\end{lstlisting} +e per evitare memory leak occorre ricordarsi di liberare \var{ptr} con +una \func{free}. + +Il valore di ritorno della funzione indica il numero di caratteri letti +dallo stream (quindi compreso il newline, ma non lo zero di +terminazione); questo permette anche di distinguere eventuali zeri letti +dallo stream da quello inserito dalla funzione per terminare la linea. +Se si è alla fine del file e non si è potuto leggere nulla o c'è stato +un errore la funzione restituisce -1. + +La seconda estensione GNU è una generalizzazione di \func{getline} per +poter usare come separatore un carattere qualsiasi, la funzione si +chiama \func{getdelim} ed il suo prototipo è: +\begin{prototype}{stdio.h} +{ssize\_t getdelim(char **buffer, size\_t *n, int delim, FILE *stream)} + Identica a \func{getline} solo che usa \param{delim} al posto del + carattere di newline come separatore di linea. +\end{prototype} + +Il comportamento di \func{getdelim} è identico a quello di +\func{getline} (che può essere implementata da questa passando +\verb|'\n'| come valore di \param{delim}). \subsection{Input/output formattato} \label{sec:file_formatted_io} +L'ultima modalità di input/output è quella formattata, che è una delle +caratteristiche più utilizzate delle librerie standard del C; in genere +questa è la modalità in cui si esegue normalmente l'output su terminale +poichè permette di stampare in maniera facile e veloce dati, tabelle e +messaggi. + +L'output formattato viene eseguito con una delle 13 funzioni della +famiglia \func{printf}; le tre più usate sono le seguenti: +\begin{functions} + \headdecl{stdio.h} + \funcdecl{int printf(const char *format, ...)} Stampa su \var{stdout} + gli argomenti, secondo il formato specificato da \param{format}. + + \funcdecl{int fprintf(FILE *stream, const char *format, ...)} Stampa + su \param{stream} gli argomenti, secondo il formato specificato da + \param{format}. + + \funcdecl{int sprintf(char *str, const char *format, ...)} Stampa + sulla stringa \param{str} gli argomenti, secondo il formato + specificato da \param{format}. + + \bodydesc{Le funzioni ritornano il numero di caratteri stampati.} +\end{functions} +\noindent le prime due servono per stampare su file (lo standard output +o quallo specificato) la terza permette di stampare su una stringa, in +genere l'uso di \func{sprintf} è sconsigliato in quanto è possibile, se +non si ha la sicurezza assoluta sulle dimensioni del risultato della +stampa, eccedere le dimensioni di \param{str}; per questo motivo si +consiglia l'uso dell'alternativa: +\begin{prototype}{stdio.h} +{snprintf(char *str, size\_t size, const char *format, ...);} + Identica a \func{sprintf}, ma non scrive su \param{str} più di + \param{size} caratteri. +\end{prototype} + +La parte più complessa di queste funzioni è il formato della variabile +\param{format} che indica le conversioni da fare ed il numero di +parametri che dovranno essere specificati a seguire. + + + + + + + + + + \subsection{Posizionamento su uno stream} \label{sec:file_fseek} +Come per i file descriptor è possibile anche con gli stream 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 quelio che viene detto un file ad +\textsl{accesso casuale}. + +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 +un numero di caratteri (ad esempio in VMS può essere rappresentata come +numero di record, e offset rispetto al record corrente). + +Tutto questo comporta la presenza di diverse funzioni che seguono +sostanzialmente le stesse operazioni, ma usano parametri di tipo +diverso. Le funzioni tradizionali usate per il riposizionamento della +posizione in uno stream sono: +\begin{functions} + \headdecl{stdio.h} + + \funcdecl{int fseek(FILE *stream, long offset, int whence)} Sposta la + posizione nello stream secondo quanto specificato tramite \param{offset} + e \param{whence}. + + \funcdecl{void rewind(FILE *stream)} Riporta la posizione nello stream + all'inizio del file. +\end{functions} + +L'uso di \func{fseek} è del tutto analogo a quello di \func{lseek} per i +file descriptor, ed i parametri, a parte il tipo, hanno lo stesso +significato; in particolare \param{whence} assume gli stessi valori già +visti in \secref{sec:file_lseek}. La funzione restituisce 0 in caso di +successo e -1 in caso di errore. + +Per leggere la posizione corrente invece la funzione \func{ftell}, il +cui prototipo è: +\begin{prototype}{stdio.h}{long ftell(FILE *stream)} + Legge la posizione attuale nello stream. + + \bodydesc{La funzione restituisce la posizione corrente, o -1 in caso + di fallimento, che può esser dovuto sia al fatto che il file non + supporta il riposizionamento che al fatto che la posizione non può + essere espressa con un \type{long int}} +\end{prototype} + + \section{Funzioni avanzate} @@ -734,10 +1044,10 @@ stream; questo pu \func{\_\_fwritable} i cui prototipi sono: \begin{functions} \headdecl{stdio\_ext.h} - \funcdecl{int \_\_freadable (FILE * stream)} + \funcdecl{int \_\_freadable (FILE *stream)} Restituisce un valore diverso da zero se \param{stream} consente la lettura. - \funcdecl{int \_\_fwritable(FILE * stream)} + \funcdecl{int \_\_fwritable(FILE *stream)} Restituisce un valore diverso da zero se \param{stream} consente la scrittura. \end{functions} @@ -746,11 +1056,11 @@ Altre due funzioni, \func{\_\_freading} e \func{\_\_fwriting} servono ad un uso ancora più specialistico, il loro prototipo è: \begin{functions} \headdecl{stdio\_ext.h} - \funcdecl{int \_\_freading (FILE * stream)} + \funcdecl{int \_\_freading (FILE *stream)} Restituisce un valore diverso da zero se \param{stream} è aperto in sola lettura o se l'ultima operazione è stata di lettura. - \funcdecl{int \_\_fwriting(FILE * stream)} + \funcdecl{int \_\_fwriting(FILE *stream)} Restituisce un valore diverso da zero se \param{stream} è aperto in sola scrittura o se l'ultima operazione è stata di scrittura. \end{functions} @@ -780,7 +1090,7 @@ stream; se non si Gli stream possono essere usati in applicazioni multi-thread allo stesso modo in cui sono usati nelle applicazioni normali, ma si deve essere -consapovoli delle possibili complicazioni anche quando non si usano i +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. @@ -789,16 +1099,16 @@ pesantemente dalle richieste necessarie per garantirne l'uso coi thread. \begin{functions} \headdecl{stdio.h} - \funcdecl{void flockfile(FILE * stream)} Esegue l'acquisizione del + \funcdecl{void flockfile(FILE *stream)} Esegue l'acquisizione del lock dello stream \param{stream}, bloccandosi in caso il lock non disponibile. - \funcdecl{int ftrylockfile(FILE * stream)} Tenta l'acquisizione del + \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{void funlockfile(FILE * stream)} Rilascia il lock dello + \funcdecl{void funlockfile(FILE *stream)} Rilascia il lock dello stream \param{stream}. \end{functions}