[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico] [home] [volume] [parte]


Capitolo 670.   M4: introduzione

M4 è un elaboratore di «macro», nel senso che la sua elaborazione consiste nell'espandere le macroistruzioni che incontra nell'input. In altri termini, si può dire che copia l'input nell'output, espandendo man mano le macroistruzioni che incontra.

La logica di funzionamento di M4 è completamente diversa dai linguaggi di programmazione comuni; inoltre, le sue potenzialità richiedono molta attenzione da parte del programmatore. Detto in maniera diversa, si tratta di un linguaggio macro molto potente, ma altrettanto difficile da gestire.

L'obiettivo di questo capitolo è solo quello di mostrarne i principi di funzionamento, per permettere la comprensione, parziale, del lavoro di altri. Per citare un caso significativo, la configurazione di Sendmail (capitolo 327) viene gestita attualmente attraverso una serie di macroistruzioni di M4, con le quali si genera il file /etc/sendmail.cf.

670.1   Principio di funzionamento

M4 è costituito in pratica dall'eseguibile m4, la cui sintassi per l'avvio può essere semplificata nel modo rappresentato dallo schema seguente:

m4 [opzioni] [file_da_elaborare]

Il file da elaborare può essere fornito come argomento, oppure attraverso lo standard input; il risultato viene emesso attraverso lo standard output e gli errori eventuali vengono segnalati attraverso lo standard error.

Per iniziare a comprendere il funzionamento di M4, si osservi il testo seguente:

Ciao, come stai ? dnl Che domanda!

# Questo è un commento ? dnl Sì.

Oggi è una giornata stupenda.

Supponendo di avere scritto questo in un file, precisamente prova.m4, lo si può rielaborare con M4 in uno dei due modi seguenti (sono equivalenti):

m4 prova.m4[Invio]

m4 < prova.m4[Invio]

In entrambi i casi, quello che si ottiene attraverso lo standard output è il testo seguente:

Ciao, come stai ?
# Questo è un commento ? dnl Sì.

Oggi è una giornata stupenda.

Tutto ciò che M4 non riesce a interpretare come una macroistruzione rimane inalterato. Anche se il simbolo di commento è previsto e corrisponde a # (a meno che siano state usate opzioni o istruzioni particolari), i commenti non vengono eliminati: servono solo a evitare che il testo sia interpretato da M4.

L'unico commento che funzioni in modo simile a quello dei linguaggi di programmazione comuni è la macro dnl (è stata usata nella prima riga), con la quale viene eliminato il testo a partire da quel punto fino al codice di interruzione di riga successivo. Dal momento che viene eliminato anche il codice di interruzione di riga, si può vedere dall'esempio che la seconda riga, quella vuota, viene inghiottita; invece, il «dnl» contenuto nella riga di commento non è stato considerato da M4.

670.2   Convenzioni generali

L'analisi di M4 sull'input viene condotta separando tutto in «elementi» (token), i quali possono essere classificati fondamentalmente in tre tipi: nomi, stringhe tra virgolette e caratteri singoli che non hanno significati particolari.

I nomi sono sequenze di lettere (compreso il trattino basso) e numeri, dove il primo carattere è una lettera. Una volta che M4 ha delimitato un nome, se questo viene riconosciuto come una macroistruzione, allora questa viene espansa (sostituendola al nome).

Le stringhe delimitate da virgolette richiedono l'uso di un apice di apertura e di uno di chiusura (` e '). Il risultato dell'elaborazione di una stringa di questo tipo è ciò che si ottiene eliminando il livello più esterno di apici. Per esempio:

`'

corrisponde alla stringa nulla;

`la mia stringa'

corrisponde al testo la mia stringa;

``tra virgolette''

corrisponde a `tra virgolette'.

È importante tenere presente che anche i simboli usati per delimitare le stringhe possono essere modificati attraverso istruzioni di M4.

Tutto ciò che non rientra nella classificazione di nomi e stringhe delimitate tra virgolette, sono elementi sui quali non si applica alcuna trasformazione.

I commenti per M4 rappresentano solo una parte di testo che non deve essere analizzato alla ricerca di macroistruzioni. Quello che si ottiene è la riproduzione di tale testo senza alcuna modifica. In linea di principio, i commenti sono delimitati dal simbolo # fino alla fine della riga, cioè fino al codice di interruzione di riga. M4 permette di modificare i simboli usati per delimitare i commenti, o di annullarli del tutto.

È il caso di soffermarsi un momento su questo concetto. Quando si utilizza M4, spesso lo si fa per generare un file di configurazione o un programma scritto in un altro linguaggio. Questi tipi di file potrebbero utilizzare dei commenti, ma può essere conveniente generare nel risultato dei commenti il cui contenuto cambia in funzione di situazioni determinate. Si immagini di voler realizzare uno script di shell, in cui notoriamente il commento si introduce con lo stesso simbolo #, volendo comporre il commento in base a delle macroistruzioni; diventa necessario fare in modo che M4 non consideri il simbolo # come l'inizio di un commento.

L'unico tipo di dati che M4 può gestire sono le stringhe alfanumeriche, indipendentemente dal fatto che si usino gli apici per delimitarle. Naturalmente, una stringa contenente un numero può avere un significato particolare che dipende dal contesto.

670.2.1   Macro

M4 è un linguaggio di programmazione il cui scopo principale è quello di gestire opportunamente la sostituzione di testo in base a delle macroistruzioni. Tuttavia, alcune macroistruzioni potrebbero servire a ottenere qualche funzione in più rispetto alla semplice sostituzione di testo. In generale, per uniformità, si parla sempre di «macro» anche quando il termine potrebbe essere improprio; per la precisione si distingue tra macroistruzioni interne (builtin), che pur non essendo dichiarate fanno parte di M4, e macroistruzioni normali, dichiarate esplicitamente.

Una macroistruzione può essere «invocata» attraverso due modi possibili:

nome
nome(parametro_1, parametro_2, ... parametro_n)

Nel primo caso si tratta di una macroistruzione senza parametri (ovvero senza argomenti); nel secondo si tratta di una macroistruzione con l'indicazione di parametri. È importante osservare che, quando si utilizzano i parametri, la parentesi aperta iniziale deve seguire immediatamente il nome della macroistruzione (senza spazi aggiuntivi); inoltre, se una macroistruzione non ha parametri, non si possono utilizzare le parentesi aperta e chiusa senza l'indicazione di parametri, perché questo sarebbe equivalente a fornire la stringa nulla come primo parametro.

La cosa più importante da apprendere è il modo in cui viene trattato il contenuto che appare tra parentesi, che serve a descrivere i parametri di una macroistruzione; infatti, prima di espandere la macroistruzione, viene espanso il contenuto che appare tra parentesi. Una volta espansa anche la macroistruzione con i parametri ottenuti, viene eseguita un'altra analisi del risultato, con il quale si possono eseguire altre espansioni di macroistruzioni, oppure si può ottenere la semplice eliminazione delle coppie di apici dalle stringhe delimitate. Le operazioni svolte da M4 per espandere una macroistruzione sono elencate dettagliatamente di seguito.

  1. Vengono suddivisi gli elementi contenuti tra parentesi ignorando gli spazi iniziali e includendo quelli finali. Si osservi l'esempio seguente:

    miamacro(a mio, d)
    

    Questo è equivalente a:

    miamacro(a mio,d)
    
  2. Vengono espanse le macroistruzioni contenute eventualmente tra i parametri. Continuando l'esempio precedente, si immagini che mio sia una macroistruzione che si espande nella stringa:

    , b, c
    

    A causa della sostituzione di mio, si ottiene in pratica quanto segue:

    miamacro(a , b, c,d)
    

    Infine, tutto si riduce a:

    miamacro(a ,b,c,d)
    

    Pertanto i parametri sono esattamente una a seguita da uno spazio e poi le altre lettere b, c e d.

  3. Una volta risolti i parametri, viene espansa la macroistruzione.

  4. Il risultato dell'espansione viene rianalizzato alla ricerca di stringhe delimitate a cui togliere gli apici esterni e di altre macroistruzioni da espandere.

In un certo senso si potrebbe dire che le stringhe, delimitate come previsto da M4, siano delle macroistruzioni che restituiscono il contenuto in modo letterale, perdendo quindi la coppia di apici più esterni. Questo significa che ciò che appare all'interno di una tale stringa non può essere interpretato come il nome di una macroistruzione; inoltre, nemmeno i commenti vengono presi in considerazione come tali. La differenza fondamentale rispetto alle macroistruzioni normali sta nel fatto che l'espansione avviene una volta sola.

Quando si usano le stringhe delimitate tra le opzioni di una macroistruzione normale, è necessario tenere presente che queste vengono trattate la prima volta nel modo appena descritto, allo scopo di fornire i parametri effettivi alla macroistruzione, ma dopo l'espansione della macroistruzione avviene un'ulteriore elaborazione del risultato.

In generale sarebbe conveniente e opportuno indicare i parametri di una macroistruzione sempre utilizzando le stringhe delimitate, a meno di voler indicare esplicitamente altre macroistruzioni. Ciò facilita la lettura umana di un linguaggio di programmazione già troppo complicato. In ogni caso, non si deve dimenticare il ruolo degli spazi finali che vengono sempre inclusi nei parametri. Per esempio, si osservi la macroistruzione miamacro:

miamacro(`a' , `b', `c', `d')

Questa ha sempre come primo parametro la lettera a seguita da uno spazio; a nulla serve in questo caso l'uso degli apici, o meglio, sarebbe stato più opportuno usarli nel modo seguente:

miamacro(`a ', `b', `c', `d')

È il caso di precisare che le sequenze di caratteri numerici sono comunque delle stringhe per M4, per cui miamacro(123) è perfettamente uguale a miamacro(`123'). Tuttavia, dal momento che un nome non può cominciare con un numero, non ci possono essere macroistruzioni il cui nome corrisponda a un numero; pertanto si può evitare di utilizzare gli apici di delimitazione perché sarebbe comunque inutile.

Le stringhe delimitate, oltre che per impedire l'espansione di nomi che corrispondono a delle macroistruzioni, permettono di «unire» due macroistruzioni. Si osservi l'esempio seguente:

miamacro_x`ciao'miamacro_y

L'intenzione è quella di fare rimpiazzare a M4 le macroistruzioni miamacro_x e miamacro_y con qualcosa, facendo in modo che queste due parti si uniscano avendo al centro la parola «ciao». Si può intuire che non sarebbe stato possibile scrivere il testo seguente:

miamacro_xciaomiamacro_y

Infatti, in tal modo non sarebbe stata riconosciuta alcuna macroistruzione. Secondo lo stesso principio, si può unire il risultato di due macroistruzioni senza spazi aggiuntivi, utilizzando apici che delimitano una stringa nulla.

miamacro_x`'miamacro_y

L'espansione delle macroistruzioni pone un problema in più a causa del fatto che dopo l'espansione il risultato viene riletto alla ricerca di altre macroistruzioni. Si osservi l'esempio seguente, supponendo che la macroistruzione miamacro_x restituisca la stringa miama nel caso in cui il suo unico parametro sia pari a 1:

miamacro_x(1)cro_z

Espandendo la macroistruzione si ottiene la stringa «miama», ma dal momento che viene fatta una scansione successiva, la parola «miamacro_z» potrebbe essere un'altra macroistruzione; se fosse questo il caso, la macroistruzione verrebbe espansa a sua volta. Per evitare che accada una cosa del genere si possono usare gli apici in uno dei due modi seguenti:

miamacro_x(1)`'cro_z
miamacro_x(1)`cro_z'

Il problema può essere visto anche in modo opposto, se l'espansione di una macroistruzione, quando questa è attaccata a un'altra, può impedire il riconoscimento della seconda. L'esempio seguente mostra infatti che la seconda macroistruzione, miamacro_y, non può essere riconosciuta a causa dell'espansione della prima.

miamacro_x(1)miamacro_y

Una considerazione finale va fatta sulle macroistruzioni che non restituiscono alcunché, ovvero che si traducono semplicemente nella stringa nulla. Spesso si tratta di macroistruzioni interne che svolgono in realtà altri compiti, come potrebbe fare una funzione void di un linguaggio di programmazione normale. In questo senso, per una macroistruzione che non restituisce alcun valore, viene anche detto che restituisce void, che in questo contesto è esattamente la stringa nulla.

670.2.2   Definizione di una macroistruzione

define(nome_macro[, espansione])

Come si può osservare dalla sintassi mostrata, la creazione di una macroistruzione avviene attraverso una macroistruzione interna, define, per la quale deve essere fornito un parametro obbligatorio, corrispondente al nome della macroistruzione da creare, a cui si può aggiungere il valore in cui questa si deve espandere. Se non viene specificato in che modo si deve espandere la macroistruzione, si intende che si tratti della stringa nulla.

La macroistruzione define non restituisce alcun valore (a parte la stringa nulla). Si osservi l'esempio seguente:

1 define(`CIAO', `Ciao a tutti.')
2 CIAO

Se questo file viene elaborato da M4, si ottiene il risultato seguente:

1
2 Ciao a tutti.

Come già affermato, define crea una macroistruzione ma non genera alcun risultato, pertanto viene semplicemente eliminata.

Per creare una macroistruzione che accetti delle opzioni, occorre indicare, nella stringa utilizzata per definire la sostituzione, uno o più simboli speciali. Si tratta precisamente di $1, $2,... $n. Il numero massimo di parametri gestibili da M4 dipende dalla sua versione. I sistemi GNU dispongono generalmente di M4 GNU (1) e questo non ha limiti particolari al riguardo, mentre le versioni presenti in altri sistemi Unix possono essere limitate a nove.

Questa simbologia richiama alla mente i parametri usati dalle shell comuni; e con la stessa analogia, il simbolo $0 si espande nel nome della macroistruzione stessa.

1 define(`CIAO', `Ciao $1, come stai?')
2 CIAO(`Tizio')

L'esempio è una variante di quello precedente, in cui si crea la macroistruzione CIAO che accetta un solo parametro. Il risultato dell'elaborazione del file appena mostrato è il seguente:

1
2 Ciao Tizio, come stai?

Prima di proseguire è opportuno rivedere il meccanismo dell'espansione di una macroistruzione attraverso un caso particolare. L'esempio seguente è leggermente diverso da quello precedente, in quanto vengono aggiunti gli apici attorno alla parola «come». Il risultato dell'elaborazione è però lo stesso.

1 define(`CIAO', `Ciao $1, `come' stai?')
2 CIAO(`Tizio')

Infatti, quando la macroistruzione CIAO viene espansa, subisce una rianalisi successiva; dal momento che viene trovata una stringa, questa viene «elaborata» restituendo semplicemente se stessa senza gli apici. Questo meccanismo ha comunque una fine, dal momento che non ci sono altre macroistruzioni, come si vede nell'esempio seguente:

1 define(`CIAO', `Ciao $1, ``come'' stai?')
2 CIAO(`Tizio')

Questo si traduce nel risultato:

1
2 Ciao Tizio, `come' stai?

670.2.3   Simboli speciali

All'interno della stringa di definizione di una macroistruzione, oltre ai simboli $n, si possono utilizzare altri codici simili, in un modo che assomiglia a quello delle shell più comuni.

$#

Rappresenta il numero di parametri passati effettivamente a una macroistruzione:

define(`CIAO', `$#')
CIAO
CIAO()
CIAO(primo, secondo)

L'esempio si traduce nel risultato seguente (si deve tenere presente che una macroistruzione chiamata con le parentesi senza alcun contenuto ha un parametro costituito dalla stringa nulla):

0
1
2
$*

Rappresenta tutti i parametri forniti effettivamente alla macroistruzione, separati da una virgola, ma soprattutto senza gli apici di delimitazione:

define(`ECHO', `$*')
ECHO(uno,   due  , tre)

L'esempio si traduce nel modo seguente; si osservi l'effetto degli spazi prima e dopo i parametri:

uno,due  ,tre
$@

Rappresenta tutti i parametri forniti effettivamente alla macroistruzione, separati da una virgola, con gli apici di delimitazione. La differenza rispetto a $* è sottile e l'esempio seguente dovrebbe permettere di comprenderne il significato:

define(`CIAO', `maramao')
define(ECHO1,`$1,$2,$3')
define(ECHO2,`$*')
define(ECHO3,`$@')
ECHO1(CIAO,`CIAO',``CIAO'')
ECHO2(CIAO,`CIAO',``CIAO'')
ECHO3(CIAO,`CIAO',``CIAO'')

Le ultime righe del risultato che si ottiene sono le seguenti:

maramao,maramao,CIAO
maramao,maramao,CIAO
maramao,CIAO,`CIAO'

670.2.4   Eliminazione di una macroistruzione

Una macroistruzione può essere eliminata attraverso la macroistruzione interna undefine, secondo la sintassi seguente:

undefine(nome_macro)

L'esempio seguente elimina la macroistruzione CIAO, per cui, da quel punto in poi, la parola CIAO mantiene il suo valore letterale:

undefine(`CIAO')

La macroistruzione undefine non restituisce alcun valore e può essere usata solo con un parametro, quello che rappresenta la macroistruzione che si vuole eliminare.

670.3   Istruzioni condizionali, iterazioni e ricorsioni

M4 non utilizza istruzioni vere e proprie, dal momento che tutto viene svolto attraverso delle «macro». Tuttavia, alcune macroistruzioni interne permettono di gestire delle strutture di controllo.

Dal momento che il risultato dell'espansione di una macroistruzione viene scandito successivamente alla ricerca di altre macroistruzioni da espandere, in qualche modo, è possibile anche la realizzazione di cicli ricorsivi. Resta il fatto che questo sia probabilmente un ottimo modo per costruire macroistruzioni molto difficili da leggere e da controllare.

670.3.1   Macro «ifdef»

ifdef(nome_macro, stringa_se_esiste[, stringa_se_non_esiste])

La macroistruzione interna ifdef permette di verificare l'esistenza di una macroistruzione. Il nome di questa viene indicato come primo parametro, mentre il secondo parametro serve a definire la stringa da restituire in caso la condizione di esistenza si avveri. Se si indica il terzo parametro, questo viene restituito se la condizione di esistenza fallisce.

L'esempio seguente verifica l'esistenza della macroistruzione CIAO; se questa non risulta già definita, la crea:

ifdef(`CIAO', `', `define(`CIAO', `maramao')')

670.3.2   Macro «ifelse»

ifelse(commento)
ifelse(stringa_1, stringa_2, risultato_se_uguali[, risultato_se_diverse])
ifelse(stringa_1, stringa_2, risultato_se_uguali, ... [, risultato_altrimenti])

La macroistruzione interna ifelse serve generalmente per confrontare una o più coppie di stringhe, restituendo un risultato se il confronto è valido o un altro risultato se il confronto fallisce.

Si tratta di una sorta di struttura di selezione (case, switch e simili) in cui, ogni terna di parametri rappresenta rispettivamente le due stringhe da controllare e il risultato se queste risultano uguali. Un ultimo parametro facoltativo serve a definire un risultato da emettere nel caso l'unica o tutte le coppie da controllare non risultino uguali.

Nella tradizione di M4, è comune utilizzare ifelse con un solo parametro; in tal caso non si può ottenere alcun risultato, pertanto questo fatto viene sfruttato per delimitare un commento.

Segue la descrizione di alcuni esempi.

670.3.3   Macro «shift»

shift(parametro[,...])

La macroistruzione interna shift permette di eliminare il primo parametro restituendo i rimanenti separati da una virgola. La convenienza di utilizzare questa macroistruzione sta probabilmente nell'uso assieme a $* e $#.

shift(mio, tuo, suo)

Dall'esempio appena mostrato, eliminando il primo parametro si ottiene il risultato seguente:

tuo,suo

670.3.4   Macro «forloop»

forloop(indice, inizio, fine, stringa_iterata)

La macroistruzione interna forloop permette di svolgere una sorta di ciclo in cui l'ultimo parametro, il quarto, viene eseguito tante volte quanto necessario a raggiungere il valore numerico espresso dal terzo parametro. Nel corso di questi cicli, il primo parametro viene trattato come una macroistruzione che di volta in volta restituisce un valore progressivo, a partire dal valore del secondo parametro, fino al raggiungimento di quello del terzo.

forloop(`i', 1, 7, `i; ')

L'esempio restituisce la sequenza dei numeri da uno a sette, seguiti da un punto e virgola:

1; 2; 3; 4; 5; 6; 7;

670.4   Altre macroistruzioni interne degne di nota

In questa introduzione a M4 ci sono altre macroistruzione interne che è importante conoscere per comprendere le possibilità di questo linguaggio. Attraverso queste macroistruzione, descritte nelle sezioni seguenti, è possibile eliminare un codice di interruzione di riga, inserire dei file, cambiare i delimitatori dei commenti e deviare l'andamento del flusso di output.

670.4.1   Macro «dnl»

dnl[commento]new-line

La macroistruzione interna dnl è anomala nel sistema di M4: non utilizza parametri ed elimina tutto quello che appare dopo di lei fino alla fine della riga, comprendendo anche il codice di interruzione di riga. La natura di M4, in cui tutto è fatto sotto forma di macroistruzione, fa sì che ci si trovi spesso di fronte al problema di righe vuote ottenute nell'output per il solo fatto di avere utilizzato macroistruzioni interne che non restituiscono alcun risultato. La macroistruzione dnl serve principalmente per questo: eliminando anche il codice di interruzione di riga si risolve il problema delle righe vuote inutili.

Teoricamente, dnl potrebbe essere utilizzata anche con l'aggiunta di parametri (tra parentesi). Il risultato che si ottiene è che i parametri vengono raccolti e interpretati come succederebbe con un'altra macroistruzione normale, senza però produrre risultati. Naturalmente, questo tipo di pratica è sconsigliabile.

dnl Questo è un commento vero e proprio
define(`CIAO', `maramao')dnl
CIAO

L'esempio mostra i due usi tipici di dnl: come introduzione di un commento fino alla fine della riga, oppure soltanto come un modo per sopprimere una riga che risulterebbe vuota nell'output. Il risultato dell'elaborazione è composto da una sola riga:

maramao

670.4.2   Macro «changecom»

changecom([simbolo_iniziale[, simbolo_finale]])

La macroistruzione interna changecom permette di modificare i simboli di apertura e di chiusura dei commenti. Solitamente, i commenti sono introdotti dal simbolo # e sono terminati dal codice di interruzione di riga. Quando si utilizza M4 per produrre il sorgente di un certo linguaggio di programmazione, o un file di configurazione, è probabile che i commenti di questi file debbano essere modificati attraverso le macroistruzioni stesse. In questo senso, spesso diventa utile cancellare la definizione dei commenti che impedirebbero la loro espansione.

L'esempio seguente cambia i simboli di apertura e chiusura dei commenti, facendo in modo di farli coincidere con quelli utilizzati dal linguaggio C:

changecom(`/*', `*/')

L'esempio seguente cancella la definizione dei commenti:

changecom

670.4.3   Macro «include» e «sinclude»

include(file)
sinclude(file)

Attraverso la macroistruzione include è possibile incorporare un file esterno nell'input in corso di elaborazione. Ciò permette di costruire file-macro di M4 strutturati. Tuttavia, è necessario fare attenzione alla posizione in cui si include un file esterno (si immagini un file che viene incluso nei parametri di una macroistruzione).

La differenza tra include e sinclude sta nel fatto che la seconda macroistruzione non segnala errori se il file non esiste.

L'esempio seguente include il file mio.m4:

include(`mio.m4')

670.4.4   Macro «divert» e «undivert»

M4 consente l'uso di uno strano meccanismo, detto deviazione, o diversion, attraverso il quale parte del flusso dell'output può essere accantonato temporaneamente per essere rilasciato in un momento diverso. Per ottenere questo si utilizzano due macroistruzioni interne: divert e undivert.

divert([numero_deviazione])
undivert([numero_deviazione[,...]])

La prima macroistruzione, divert, serve ad assegnare un numero di deviazione alla parte di output generato a partire dal punto in cui questa appare nell'input. Questo numero può essere omesso e in tal caso si intende lo zero in modo predefinito.

La deviazione zero corrisponde al flusso normale; ogni altro numero positivo rappresenta una deviazione differente. Quando termina l'input da elaborare vengono rilasciati i vari blocchi accumulati di output, in ordine numerico crescente. In alternativa, si può usare la macroistruzione undivert per richiedere espressamente il recupero di output deviato; se questa viene utilizzata senza parametri, si intende il recupero di tutte le deviazioni, altrimenti si ottengono solo quelle elencate nei parametri.

Esiste un caso particolare di deviazione che serve a eliminare l'output; si ottiene utilizzando il numero di deviazione -1. Questa tecnica viene usata spesso anche come un modo per delimitare un'area di commenti che non si vuole siano riprodotti nell'output.

Come si può intuire, queste macroistruzioni non restituiscono alcun valore.

Segue la descrizione di alcuni esempi.

670.5   Riferimenti

La documentazione di M4 GNU, cioè quanto distribuito normalmente con i sistemi GNU, è disponibile generalmente attraverso la documentazione Info: info m4.


1) GNU M4   GNU GPL


Appunti di informatica libera 2008 --- Copyright © 2000-2008 Daniele Giacomini -- <appunti2 (ad) gmail·com>


Dovrebbe essere possibile fare riferimento a questa pagina anche con il nome m4_introduzione.htm

[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico] [home]

Valid ISO-HTML!

CSS validator!

Gjlg Metamotore e Web Directory