Successivo: , Precedente: , Su: Programmi vari   [Contenuti][Indice]


11.3.7 Estrarre programmi da un file sorgente Texinfo

Sia questo capitolo che il precedente (Funzioni di libreria) presentano un numero elevato di programmi awk. Se si vuole fare pratica con questi programmi, è fastidioso doverli digitare di nuovo manualmente. È per questo che abbiamo pensato a un programma in grado di estrarre parti di un file in input Texinfo e metterli in file separati.

Questo Documento è scritto in Texinfo, il programma di formattazione di documenti del progetto GNU. Un solo file sorgente Texinfo può essere usato per produrre sia la documentazione stampata, usando TeX, sia quella online. (Texinfo è esaurientemente documentato nel libro Texinfo—The GNU Documentation Format, disponibile alla Free Software Foundation, e anche online.)

Per quel che ci riguarda, è sufficiente sapere tre cose riguardo ai file di input Texinfo:

Il programma seguente, extract.awk, legge un file sorgente Texinfo e fa due cose, basandosi sui commenti speciali. Dopo aver visto il commento ‘@c system …’, esegue un comando, usando il testo del comando contenuto nella riga di controllo e passandolo alla funzione system() (vedi Funzioni di I/O). Dopo aver trovato il commento ‘@c file nome_file’, ogni riga successiva è spedita al file nome_file, fino a che si trova un commento ‘@c endfile’. Le regole in extract.awk sono soddisfatte sia quando incontrano ‘@c’ che quando incontrano ‘@comment’ e quindi la parte ‘omment’ è opzionale. Le righe che contengono ‘@group’ e ‘@end group’ sono semplicemente ignorate. extract.awk usa la funzione di libreria join() (vedi Funzione join).

I programmi di esempio nel sorgente Texinfo online di GAWK: Programmare efficacemente in AWK (gawktexi.in) sono stati tutti inseriti tra righe ‘file’ e righe ‘endfile’. La distribuzione di gawk usa una copia di extract.awk per estrarre i programmi di esempio e per installarne molti in una particolare directory dove gawk li può trovare. Il file Texinfo ha un aspetto simile a questo:

…
Questo programma ha una regola @code{BEGIN}
che stampa un messaggio scherzoso:

@example
@c file esempi/messages.awk
BEGIN @{ print "Non v'allarmate!" @}
@c endfile
@end example

Stampa anche qualche avviso conclusivo:

@example
@c file esempi/messages.awk
END @{ print "Evitate sempre gli archeologi annoiati!" @}
@c endfile
@end example
…

Il programma extract.awk inizia con l’impostare IGNORECASE a uno, in modo che un miscuglio di lettere maiuscole e minuscole nelle direttive non faccia differenza.

La prima regola gestisce le chiamate a system(), controllando che sia stato fornito un comando (NF dev’essere almeno tre) e controllando anche che il comando termini con un codice di ritorno uguale a zero, che sta a significare che tutto è andato bene:

# extract.awk --- estrae file ed esegue programmi dal file Texinfo

BEGIN    { IGNORECASE = 1 }

/^@c(omment)?[ \t]+system/ {
    if (NF < 3) {
        e = ("extract: " FILENAME ":" FNR)
        e = (e  ": riga `system' con formato errato")
        print e > "/dev/stderr"
        next
    }
    $1 = ""
    $2 = ""
    stat = system($0)
    if (stat != 0) {
        e = ("extract: " FILENAME ":" FNR)
        e = (e ": attenzione: system ha restituito " stat)
        print e > "/dev/stderr"
    }
}

La variabile e è stata usata per far sì che la regola sia agevolemente contenuta nella videata.

La seconda regola gestisce il trasferimento di dati in un file. Verifica che nella direttiva sia stato fornito un nome-file. Se il nome del file non è quello del file corrente, il file corrente viene chiuso. Mantenere aperto il file corrente finché non si trova un nuovo nome file permette di usare la ridirezione ‘>’ per stampare i contenuti nel file, semplificando la gestione dei file aperti.

Il ciclo for esegue il lavoro. Legge le righe usando getline (vedi Getline). Se si raggiunge una fine-file inattesa, viene chiamata la funzione fine_file_inattesa(). Se la riga è una riga “endfile”, il ciclo viene abbandonato. Se la riga inizia con ‘@group’ o ‘@end group’, la riga viene ignorata, e si passa a quella seguente. Allo stesso modo, eventuali commenti all’interno degli esempi vengono ignorati.

Il grosso del lavoro è nelle poche righe che seguono. Se la riga non ha simboli ‘@’, il programma la può stampare così com’è. Altrimenti, ogni ‘@’ a inizio parola dev’essere eliminato. Per rimuovere i simboli ‘@’, la riga viene divisa nei singoli elementi del vettore a, usando la funzione split() (vedi Funzioni per stringhe). Il simbolo ‘@’ è usato come carattere di separazione. Ogni elemento del vettore a che risulti vuoto indica due caratteri ‘@’ contigui nella riga originale. Per ogni due elementi vuoti (‘@@’ nel file originale), va inserito un solo simbolo ‘@’ nel file in output.

Una volta terminato di esaminare il vettore, viene chiamata la funzione join() specificando nella chiamata il valore di SUBSEP (vedi Vettori multidimensionali), per riunire nuovamente i pezzi in una riga sola. La riga è poi stampata nel file di output:

/^@c(omment)?[ \t]+file/ {
    if (NF != 3) {
        e = ("extract: " FILENAME ":" FNR ": riga `file' con formato errato")
        print e > "/dev/stderr"
        next
    }
    if ($3 != file_corrente) {
        if (file_corrente != "")
            close(file_corrente)
        file_corrente = $3
    }

    for (;;) {
        if ((getline riga) <= 0)
            fine_file_inattesa()
        if (riga ~ /^@c(omment)?[ \t]+endfile/)
            break
        else if (riga ~ /^@(end[ \t]+)?group/)
            continue
        else if (riga ~ /^@c(omment+)?[ \t]+/)
            continue
        if (index(riga, "@") == 0) {
            print riga > file_corrente
            continue
        }
        n = split(riga, a, "@")
        # if a[1] == "", vuol dire riga che inizia per @,
        # non salvare un @
        for (i = 2; i <= n; i++) {
            if (a[i] == "") { # era un @@
                a[i] = "@"
                if (a[i+1] == "")
                    i++
            }
        }
        print join(a, 1, n, SUBSEP) > file_corrente
    }
}

È importante notare l’uso della ridirezione ‘>’ . L’output fatto usando ‘>’ apre il file solo la prima volta; il file resta poi aperto, e ogni scrittura successiva è aggiunta in fondo al file. (vedi Ridirezione). Ciò rende possibile mischiare testo del programm e commenti esplicativi (come è stato fatto qui) nello stesso file sorgente, senza nessun problema. Il file viene chiuso solo quando viene trovato un nuovo nome di file-dati oppure alla fine del file in input.

Per finire, la funzione fine_file_inattesa() stampa un appropriato messaggio di errore ed esce. La regola END gestisce la pulizia finale, chiudendo il file aperto:

function fine_file_inattesa()
{
    printf("extract: %s:%d: fine-file inattesa, o errore\n",
                     FILENAME, FNR) > "/dev/stderr"
    exit 1
}

END {
    if (file_corrente)
        close(file_corrente)
}

Successivo: , Precedente: , Su: Programmi vari   [Contenuti][Indice]