Successivo: , Precedente: , Su: Esempio di estensione   [Contenuti][Indice]


17.6.2 Codice C per eseguire chdir() e stat()

Questo è il codice C per queste estensioni.115

Il file include alcuni file di intestazione standard, e poi il file di intestazione gawkapi.h, che fornisce le definizioni dell’API. A queste seguono le dichiarazioni di variabili, necessarie per usare le macro dell’API e il codice predefinito (vedi la sezione Codice predefinito di interfaccia API):

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/stat.h>

#include "gawkapi.h"

#include "gettext.h"
#define _(msgid)  gettext(msgid)
#define N_(msgid) msgid

#include "gawkfts.h"
#include "stack.h"

static const gawk_api_t *api;    /* per consentire il funzionamento
                                    delle macro di utilità */
static awk_ext_id_t ext_id;
static awk_bool_t init_filefuncs(void);
static awk_bool_t (*init_func)(void) = init_filefuncs;
static const char *ext_version = "filefuncs extension: version 1.0";

int plugin_is_GPL_compatible;

Per convenzione, per una funzione awk di nome pippo(), la funzione C che la implementa è chiamata do_pippo(). La funzione dovrebbe avere due argomenti. Il primo è un numero int, chiamato nargs, che rappresenta il numero di argomenti passato alla funzione. Il secondo è un puntatore a una struttura awk_value_t, normalmente chiamata risultato:

/*  do_chdir --- fornisce funzione chdir()
                 caricata dinamicamente per gawk */

static awk_value_t *
do_chdir(int nargs, awk_value_t *risultato, struct awk_ext_func *non_usata)
{
    awk_value_t newdir;
    int ret = -1;

    assert(risultato != NULL);

La variabile newdir rappresenta la nuova directory nella quale cambiare, che è ottenuta tramite la funzione get_argument(). Si noti che il primo argomento è quello numero zero.

Se l’argomento è stato trovato con successo, la funzione invoca la chiamata di sistema chdir(). In caso contrario, se la chdir() non riesce, viene aggiornata la variabile ERRNO:

    if (get_argument(0, AWK_STRING, & newdir)) {
        ret = chdir(newdir.str_value.str);
        if (ret < 0)
            update_ERRNO_int(errno);
    }

Infine, la funzione restituisce il codice di ritorno da chdir a livello di awk:

    return make_number(ret, risultato);
}

L’estensione stat() è più impegnativa. Dapprima abbiamo una funzione che trasforma la stringa di autorizzazione numerica (mode) in una rappresentazione stampabile (p.es., il codice ottale 0644 diviene ‘-rw-r--r--’). Questa parte è qui omessa per brevità.

/* format_mode --- trasforma il campo mode di stat
                   in qualcosa di leggibile */

static char *
format_mode(unsigned long fmode)
{
    …
}

Viene poi una funzione per leggere dei collegamenti simbolici, anche questa omessa per brevità:

/* read_symlink --- legge un collegamento simbolico in un buffer
   allocato.
   … */

static char *
read_symlink(const char *fname, size_t bufsize, ssize_t *linksize)
{
    …
}

Due funzioni ausiliarie semplificano l’immissione di valori nel vettore che conterrà il risultato della chiamata a stat():

/* array_set --- imposta un elemento di un vettore */

static void
array_set(awk_array_t vettore, const char *sub, awk_value_t *valore)
{
    awk_value_t index;

    set_array_element(vettore,
                      make_const_string(sub, strlen(sub), & index),
                      valore);

}

/* array_set_numeric --- imposta un elemento di un vettore con un
   numero */

static void
array_set_numeric(awk_array_t vettore, const char *sub, double num)
{
    awk_value_t tmp;

    array_set(vettore, sub, make_number(num, & tmp));
}

La seguente funzione fa il grosso del lavoro per riempire il vettore dei risultati awk_array_t con valori ottenuti da una struct stat valida. Questo lavoro è fatto in una funzione separata per supportare sia la funzione stat() per gawk, che l’estensione fts(), che è inclusa nello stesso file, ma non è mostrata qui (vedi la sezione Funzioni relative ai file).

La prima parte della funzione è la dichiarazione delle variabili, compresa una tabella per tradurre i tipi di file in stringhe:

/* fill_stat_array --- fa il lavoro di riempire un
                       vettore con informazioni da stat */

static int
fill_stat_array(const char *nome, awk_array_t vettore, struct stat *sbuf)
{
    char *pmode;    /* mode stampabile */
    const char *type = "unknown";
    awk_value_t tmp;
    static struct ftype_map {
        unsigned int mask;
        const char *type;
    } ftype_map[] = {
        { S_IFREG, "file" },
        { S_IFBLK, "blockdev" },
        { S_IFCHR, "chardev" },
        { S_IFDIR, "directory" },
#ifdef S_IFSOCK
        { S_IFSOCK, "socket" },
#endif
#ifdef S_IFIFO
        { S_IFIFO, "fifo" },
#endif
#ifdef S_IFLNK
        { S_IFLNK, "symlink" },
#endif
#ifdef S_IFDOOR /* Stranezza Solaris */
        { S_IFDOOR, "door" },
#endif
    };
    int j, k;

Il vettore di destinazione è svuotato di elementi, e poi il codice riempie i vari elementi prendendoli dai valori presenti in struct stat:

    /* svuota il vettore */
    clear_array(vettore);

    /* riempie il vettore */
    array_set(vettore, "name", make_const_string(nome, strlen(nome),
                                               & tmp));
    array_set_numeric(vettore, "dev", sbuf->st_dev);
    array_set_numeric(vettore, "ino", sbuf->st_ino);
    array_set_numeric(vettore, "mode", sbuf->st_mode);
    array_set_numeric(vettore, "nlink", sbuf->st_nlink);
    array_set_numeric(vettore, "uid", sbuf->st_uid);
    array_set_numeric(vettore, "gid", sbuf->st_gid);
    array_set_numeric(vettore, "size", sbuf->st_size);
    array_set_numeric(vettore, "blocks", sbuf->st_blocks);
    array_set_numeric(vettore, "atime", sbuf->st_atime);
    array_set_numeric(vettore, "mtime", sbuf->st_mtime);
    array_set_numeric(vettore, "ctime", sbuf->st_ctime);

    /* per dispositivi a blocchi o carattere, aggiunge rdev,
       e il numero principale e secondario */
    if (S_ISBLK(sbuf->st_mode) || S_ISCHR(sbuf->st_mode)) {
        array_set_numeric(vettore, "rdev", sbuf->st_rdev);
        array_set_numeric(vettore, "major", major(sbuf->st_rdev));
        array_set_numeric(vettore, "minor", minor(sbuf->st_rdev));
    }

L’ultima parte della funzione fa alcune aggiunte selettive al vettore di destinazione, a seconda che siano disponibili o no certi campi e/o il tipo del file. Viene poi restituito zero, per indicare che tutto è andato bene:

#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
    array_set_numeric(vettore, "blksize", sbuf->st_blksize);
#endif

    pmode = format_mode(sbuf->st_mode);
    array_set(vettore, "pmode", make_const_string(pmode, strlen(pmode),
                                                & tmp));

    /* per collegamenti simbolici, si aggiunge un campo linkval */
    if (S_ISLNK(sbuf->st_mode)) {
        char *buf;
        ssize_t linksize;

        if ((buf = read_symlink(nome, sbuf->st_size,
                    & linksize)) != NULL)
            array_set(vettore, "linkval",
                      make_malloced_string(buf, linksize, & tmp));
        else
            warning(ext_id, _("stat: non riesco a leggere il \
collegamento simbolico `%s'"),
                    nome);
    }

    /* aggiunge il tipo di campo */
   type = "unknown";   /* non dovrebbe succedere */
   for (j = 0, k = sizeof(ftype_map)/sizeof(ftype_map[0]); j < k; j++) {
       if ((sbuf->st_mode & S_IFMT) == ftype_map[j].mask) {
           type = ftype_map[j].type;
           break;
       }
   }

   array_set(vettore, "type", make_const_string(type, strlen(type), & tmp));

   return 0;
}

Del terzo argomento passato a stat() non si era ancora parlato. Questo argomento è opzionale. Se presente, dice a do_stat() di usare la chiamata di sistema stat() invece della chiamata di sistema lstat(). Questo avviene attraverso un puntatore a funzione: statfunc. statfunc è inizializzato per puntare a lstat() (invece che a stat()) per ottenere le informazioni relative al file, nel caso che il file in questione sia un collegamento simbolico. Tuttavia, se il terzo argomento è specificato, statfunc viene modificato in modo da puntare a stat().

Ecco la funzione do_stat(), che inizia con la dichiarazione delle variabili e un controllo degli argomenti passati dal chiamante:

/* do_stat --- fornisce una funzione stat() per gawk */

static awk_value_t *
do_stat(int nargs, awk_value_t *risultato, struct awk_ext_func *non_usata)
{
    awk_value_t file_param, array_param;
    char *nome;
    awk_array_t vettore;
    int ret;
    struct stat sbuf;
    /* per default si usa lstat() */
    int (*statfunc)(const char *path, struct stat *sbuf) = lstat;

    assert(risultato != NULL);

A questo punto inizia l’elaborazione vera e propria. Per prima cosa, la funzione esamina gli argomenti. Poi, ottiene le informazioni relative al file. Se la funzione chiamata (lstat() o stat()) restituisce un errore, il codice imposta ERRNO e torna al chiamante:

    /* file è il primo argomento,
       il vettore per contenere i risultati è il secondo */
    if (   ! get_argument(0, AWK_STRING, & file_param)
        || ! get_argument(1, AWK_ARRAY, & array_param)) {
        warning(ext_id, _("stat: parametri errati"));
        return make_number(-1, risultato);
    }

    if (nargs == 3) {
        statfunc = stat;
    }

    nome = file_param.str_value.str;
    vettore = array_param.array_cookie;

    /* svuota sempre il vettore all'inizio */
    clear_array(vettore);

    /* chiama stat per il file;
       in caso di errore,
       imposta ERRNO e ritorna */
    ret = statfunc(nome, & sbuf);
    if (ret < 0) {
        update_ERRNO_int(errno);
        return make_number(ret, risultato);
    }

Il lavoro noioso è svolto da fill_stat_array(), visto in precedenza. Alla fine, la funzione restituisce il codice di ritorno impostato da fill_stat_array():

    ret = fill_stat_array(nome, vettore, & sbuf);

    return make_number(ret, risultato);
}

Infine, è necessario fornire la “colla” che aggrega le nuove funzioni a gawk.

L’estensione filefuncs comprende anche una funzione fts(), qui omessa (vedi la sezione Funzioni relative ai file). È anche prevista una funzione di inizializzazione:

/* init_filefuncs --- routine di initializazione */

static awk_bool_t
init_filefuncs(void)
{
    …
}

Siamo quasi alla fine. Serve un vettore di strutture awk_ext_func_t per caricare ogni funzione in gawk:

static awk_ext_func_t func_table[] = {
    { "chdir", do_chdir, 1, 1, awk_false, NULL },
    { "stat",  do_stat, 3, 2, awk_false, NULL },
    …
};

Ogni estensione deve avere una routine di nome dl_load() per caricare tutto ciò che occorre caricare. La cosa più semplice è di usare la macro dl_load_func() in gawkapi.h:

/* definizione della funzione dl_load()
   usando la macro standard */

dl_load_func(func_table, filefuncs, "")

Abbiamo finito!


Note a piè di pagina

(115)

La versione qui presentata è lievemente modificata per amor di semplicità. Si veda extension/filefuncs.c nella distribuzione gawk per la versione completa.


Successivo: , Precedente: , Su: Esempio di estensione   [Contenuti][Indice]