Successivo: Processori di output, Precedente: Stringa di versione Estensioni, Su: Funzioni di registrazione [Contenuti][Indice]
Per default, gawk
legge file di testo come input. Il valore della
variabile RS
è usato per determinare la fine di un record, e subito
dopo la variabile FS
(o FIELDWIDTHS
o FPAT
) viene usata
per suddividerlo in campi
(vedi la sezione Leggere file in input).
Viene inoltre impostato il valore di RT
(vedi la sezione Variabili predefinite).
Se lo si desidera, è possibile fornire un analizzatore di input
personalizzato. Il compito di un analizzatore di input è di restituire un
record al codice di gawk
, che poi lo elaborerà, accompagnato,
se necessario, da indicatori del valore e della lunghezza dei dati da usare
per RT
.
Per fornire un analizzatore personalizzato di input, occorre innanzitutto rendere disponibili due funzioni (dove XXX è un nome che fa da prefisso all’estensione intera):
awk_bool_t XXX_can_take_file(const awk_input_buf_t *iobuf);
Questa funzione esamina l’informazione disponibile in iobuf
(che vedremo tra poco). Basandosi su tale informazione,
decide se l’analizzatore di input personalizzato andrà usato per questo file.
Se questo è il caso, dovrebbe restituire true. Altrimenti, restituirà
false. Nessuno stato (valori di variabili, etc.) dovrebbe venire
modificato all’interno di gawk
.
awk_bool_t XXX_take_control_of(awk_input_buf_t *iobuf);
Quando gawk
decide di passare il controllo del file a questo
analizzatore di input, richiamerà questa funzione.
Questa funzione a sua volta deve assegnare un valore ad alcuni campi
nella struttura awk_input_buf_t
e assicurarsi che
alcune condizioni siano verificate. Dovrebbe poi restituire true.
Se si verifica un errore di qualche tipo, i campi in questione non dovrebbero
venire riempiti, e la funzione dovrebbe restituire false; in questo caso
gawk
non utilizzerà più l’analizzatore personalizzato di input.
I dettagli sono descritti più avanti.
L’estensione dovrebbe raccogliere queste funzioni all’interno di una
struttura awk_input_parser_t
, simile a questa:
typedef struct awk_input_parser { const char *nome; /* nome dell'analizzatore */ awk_bool_t (*can_take_file)(const awk_input_buf_t *iobuf); awk_bool_t (*take_control_of)(awk_input_buf_t *iobuf); awk_const struct awk_input_parser *awk_const next; /* per uso di gawk */ } awk_input_parser_t;
I campi sono:
const char *nome;
Il nome dell’analizzatore di input. Questa è una normale stringa di caratteri del linguaggio C.
awk_bool_t (*can_take_file)(const awk_input_buf_t *iobuf);
Un puntatore alla funzione XXX_can_take_file()
.
awk_bool_t (*take_control_of)(awk_input_buf_t *iobuf);
Un puntatore alla funzione XXX_take_control_of()
.
awk_const struct input_parser *awk_const next;
Questa struttura è per uso di gawk
;
per questo motivo è marcata awk_const
in modo che l’estensione non
possa modificarla.
I passi da seguire sono i seguenti:
static awk_input_parser_t
e inizializzarla
adeguatamente.
gawk
usando la funzione API register_input_parser()
(descritta più sotto).
La definizione di una struttura awk_input_buf_t
è simile a questa:
typedef struct awk_input { const char *nome; /* nome file */ int fd; /* descrittore di file */ #define INVALID_HANDLE (-1) void *opaque; /* area dati privata per l'analizzatore di input */ int (*get_record)(char **out, struct awk_input *iobuf, int *errcode, char **rt_start, size_t *rt_len, const awk_fieldwidth_info_t **field_width); ssize_t (*read_func)(); void (*close_func)(struct awk_input *iobuf); struct stat sbuf; /* buffer per stat */ } awk_input_buf_t;
I campi si possono dividere in due categorie: quelli che sono usati (almeno
inizialmente) da XXX_can_take_file()
, e quelli che sono usati da
XXX_take_control_of()
. Il primo gruppo di campi, e il loro uso,
è così definito:
const char *nome;
Il nome del file.
int fd;
Un descrittore di file per il file. Se gawk
riesce ad aprire
il file, il valore di fd
non sarà uguale a
INVALID_HANDLE
[descrittore non valido]. In caso contrario,
quello sarà il valore.
struct stat sbuf;
Se il descrittore di file è valido, gawk
avrà riempito i campi di
questa struttura invocando la chiamata di sistema fstat()
.
La funzione XXX_can_take_file()
dovrebbe esaminare i campi di
cui sopra e decidere se l’analizzatore di input vada usato per il file.
La decisione può dipendere da uno stato di gawk
(il valore
di una variabile definita in precedenza dall’estensione e impostata dal
codice awk
), dal nome del
file, dal fatto che il descrittore di file sia valido o no,
dalle informazioni contenute in struct stat
o da una qualsiasi
combinazione di questi fattori.
Una volta che XXX_can_take_file()
restituisce true, e
gawk
ha deciso di usare l’analizzatore personalizzato, viene
chiamata la funzione XXX_take_control_of()
. Tale funzione
si occupa di riempire il campo get_record
oppure il campo
read_func
nella struttura awk_input_buf_t
. La funzione si
assicura inoltre che fd
not sia impostato al valore
INVALID_HANDLE
. L’elenco seguente descrive i campi che
possono essere riempiti da XXX_take_control_of()
:
void *opaque;
Questo campo è usato per contenere qualiasi informazione di stato sia
necessaria per l’analizzatore di input
riguardo a questo file. Il campo è “opaco” per gawk
.
L’analizzatore di input non è obbligato a usare questo puntatore.
int (*get_record)(char **out,
struct awk_input *iobuf,
int *errcode,
char **rt_start,
size_t *rt_len,
const awk_fieldwidth_info_t **field_width);
Questo puntatore a funzione dovrebbe puntare a una funzione che crea i record in input. Tale funzione è il nucleo centrale dell’analizzatore di input. Il suo modo di operare è descritto nel testo che segue questo elenco.
ssize_t (*read_func)();
Questo puntatore a funzione dovrebbe puntare a una funzione che ha lo
stesso comportamento della chiamata di sistema standard POSIX read()
.
È in alternativa al puntatore a get_record
. Il relativo comportamento
è pure descritto nel testo che segue quest’elenco.
void (*close_func)(struct awk_input *iobuf);
Questo puntatore a funzione dovrebbe puntare a una funzione che fa
la “pulizia finale”. Dovrebbe liberare ogni risorsa allocata da
XXX_take_control_of()
. Può anche chiudere il file. Se lo fa,
dovrebbe impostare il campo fd
a INVALID_HANDLE
.
Se fd
è ancora diverso da INVALID_HANDLE
dopo la chiamata a
questa funzione, gawk
invoca la normale chiamata di sistema
close()
.
Avere una funzione di “pulizia” è opzionale. Se l’analizzatore di input
non ne ha bisogno, basta non impostare questo campo. In questo caso,
gawk
invoca la normale chiamata di sistema close()
per il
descrittore di file, che, quindi, dovrebbe essere valido.
La funzione XXX_get_record()
svolge il lavoro di creazione dei
record in input. I parametri sono i seguenti:
char **out
Questo è un puntatore a una variabile char *
che è impostatata in modo
da puntare al record. gawk
usa una sua copia locale dei dati,
quindi l’estensione deve gestire la relativa area di memoria.
struct awk_input *iobuf
Questa è la struttura awk_input_buf_t
per il file. I campi dovrebbero
essere usati per leggere i dati (fd
) e per gestire lo stato privato
(opaque
), se necessario.
int *errcode
Se si verifica un errore, *errcode
dovrebbe essere impostato a un
valore appropriato tra quelli contenuti in <errno.h>
.
char **rt_start
size_t *rt_len
Se il concetto “fine record” è applicabile,
*rt_start
dovrebbe essere impostato per puntare ai dati da usare come
RT
, e *rt_len
dovrebbe essere impostata alla lunghezza di quel
campo. In caso contrario, *rt_len
dovrebbe essere impostata a zero.
gawk
usa una sua copia di questi dati, quindi l’estensione deve
gestire tale memoria.
const awk_fieldwidth_info_t **field_width
Se field_width
non è NULL
, *field_width
sarà
inizializzato a NULL
, e la funzione può impostarlo per puntare a una
struttura che fornisca l’informazione sulla lunghezza del campo, che
sarà utilizzata al posto di quella determinata dall’analizzatore di default
del campo. Si noti che questa struttura non sarà copiata da gawk
;
inoltre essa deve rimanere disponibile almeno fino alla successiva chiamata a
get_record
o a close_func
. Si noti inoltre che
field_width
vale NULL
quando getline
sta assegnando
i risultati a una variabile, e quindi un’analisi del campo non è necessaria.
Se l’analizzatore imposta *field_width
,
allora gawk
usa questa descrizione per analizzare il record in
input, e il valore di PROCINFO["FS"]
sarà "API"
finché questo
record rimane corrente come $0
.
La struttura dati awk_fieldwidth_info_t
è descritta qui di seguito.
Il codice di ritorno è la lunghezza del buffer puntato da
*out
oppure EOF
, se è stata raggiunta la fine del file o se
si è verificato un errore.
Poiché errcode
è sicuramente un puntatore valido, non c’è
bisogno di controllare che il valore sia NULL
. gawk
imposta *errcode
a zero, quindi non c’è bisogno di impostarlo, a meno
che non si verifichi un errore.
In presenza di un errore, la funzione dovrebbe restituire EOF
e
impostare *errcode
a un valore maggiore di zero. In questo caso, se
*errcode
non è uguale a zero, gawk
automaticamente aggiorna
la variabile ERRNO
usando il valore contenuto in *errcode
.
(In generale, impostare ‘*errcode = errno’ dovrebbe essere la
cosa giusta da fare.)
Invece di fornire una funzione che restituisce un record in input,
è possibile fornirne una che semplicemente legge dei byte, e lascia
che sia gawk
ad analizzare i dati per farne dei record. In questo
caso, i dati dovrebbero essere restituiti nella codifica multibyte propria
della localizzazione corrente.
Una siffatta funzione dovrebbe imitare il comportamento della chiamata di
sistema read()
, e riempire il puntatore read_func
con
il proprio indirizzo nella struttura awk_input_buf_t
.
Per default, gawk
imposta il puntatore read_func
in modo che
punti alla chiamata di sistema read()
. In questo modo l’estensione
non deve preoccuparsi di impostare esplicitamente questo campo.
NOTA: Occorre decidere per l’uno o per l’altro metodo: o una funzione che restituisce un record o una che restituisce dei dati grezzi. Nel dettaglio, se si fornisce una funzione che prepara un record,
gawk
la invocherà, e non chiamerà mai la funzione che fa una lettura grezza.
gawk
viene distribuito con un’estensione di esempio che legge
delle directory, restituendo un record per ogni elemento contenuto nella
directory (vedi la sezione Leggere directory. Questo codice sorgente può
essere usato come modello per scrivere un analizzatore di input
personalizzato.
Quando si scrive un analizzatore di input, si dovrebbe progettare (e
documentare) il modo con cui si suppone che interagisca con il codice
awk
. Si può scegliere di utilizzarlo per tutte le letture, e
intervenire solo quando è necessario, (come fa l’estensione di
esempio readdir
). Oppure lo si può utilizzare a seconda del
valore preso da una variabile awk
, come fa l’estensione XML
inclusa nel progetto gawkextlib
(vedi la sezione Il progetto gawkextlib
).
In quest’ultimo caso, il codice in una regola BEGINFILE
può controllare FILENAME
ed ERRNO
per decidere se
attivare un analizzatore di input (vedi la sezione I criteri di ricerca speciali BEGINFILE
ed ENDFILE
) oppure no.
Un analizzatore di input va registrato usando la seguente funzione:
void register_input_parser(awk_input_parser_t *input_parser);
Registra l’analizzatore di input puntato da input_parser
con
gawk
.
Se si vuole ignorare il meccanismo di default per l’analisi dei campi
per un determinato record, si deve riempire una struttura
awk_fieldwidth_info_t
che ha questo aspetto:
typedef struct { awk_bool_t use_chars; /* falso ==> usare byte */ size_t nf; /* numero di campi nel record (NF) */ struct awk_field_info { size_t skip; /* da ignorare prima dell'inizio di un campo */ size_t len; /* lunghezza del campo */ } fields[1]; /* la dimensione effettiva dovrebbe essere nf */ } awk_fieldwidth_info_t;
I campi sono:
awk_bool_t use_chars;
Impostare ad awk_true
se le lunghezze di campo sono specificate in
unità di caratteri potenzialmente multi-byte, oppure impostarlo a
awk_false
se le lunghezze sono espresse in numero di byte.
L’efficienza del programma sarà maggiore utilizzando la dimensione in byte.
size_t nf;
Impostare al numero di campi nel record in input, cioè a NF
.
struct awk_field_info fields[nf];
Questo è un vettore di lunghezza variabile la cui dimensione effettiva
dovrebbe essere nf
.
Per ogni campo, l’elemento skip
dovrebbe essere impostato al numero
di caratteri o byte, come richiesto dal flag use_chars
,
da saltare prima dell’inizio di questo campo. L’elemento len
fornisce
la lunghezza del campo. I valori in fields[0]
forniscono l’informazione
per $1
, e così via, fino all’elemento fields[nf-1]
che contiene
l’informazione per $NF
.
Una macro di utilità awk_fieldwidth_info_size(numfields)
è disponibile
per calcolare la dimensione appropriata della struttura a lunghezza variabile
awk_fieldwidth_info_t
che contiene numfields
campi. Questa
dimensione può essere usata come argomento per malloc()
o in una
struttura union per allocare spazio staticamente.
Per un esempio si può vedere l’estensione di esempio readdir_test
.
Successivo: Processori di output, Precedente: Stringa di versione Estensioni, Su: Funzioni di registrazione [Contenuti][Indice]