Manutenzione del mirror
Nel momento in cui Gaetano Paolone ha pensato di dividere la raccolta
delle FAQ in due parti (LDR free e LDR nonfree), i nomi dei vari file sono
stati cambiati e perciò un lunedì mi sono trovato con file nuovi insieme a
file vecchi nella directory ~ant/public_html/linuxfaq. Me ne sono accorto
perchè ogni lunedì vado a fare un:
$ less ~ant/public_html/linuxfaq/WGET.LOG
Inoltre i fogli di stile adesso erano due ed uno non era stato scaricato,
perciò Netscape non visualizzava proprio le pagine (a meno che di non
disattivare il supporto per i fogli di stile dal menù Edit/Preferences).
Questo dimostra come conviene che teniate sott'occhio il vostro mirror. Io
ho cancellato il contenuto della directory linuxfaq e poi ho cambiato il
mio cron file come segue:
# MIN HOUR DAY MONTH DAYOFWEEK COMMAND
0 4 * * 1 /usr/local/bin/wget -m -nH -P public_html
-o public_html/linuxfaq/WGET.LOG http://web.tiscalinet.it/linuxfaq
http://web.tiscalinet.it/linuxfaq/stile.css
Ho poi lanciato lo stesso comando a mano, per eliminare la fuffa ed avere
subito il nuovo mirror senza aspettare a lunedì prossimo. Con un po' di
attenzione quindi riuscite a mantenere un mirror in efficienza.
A questo punto penso di aver chiarito l'utilizzo di cron. Altri esempi di
crontab e le particolarità della specifica implementazione con cui avete a
che fare li potrete osservare nella manpage (man crontab) del vostro
sistema.
Personalizza wget
Oltre che dalla linea di comando, le opzioni possono essere fornite a wget
tramite un file di inizializzazione .wgetrc nella vostra home directory.
Il root potrà inoltre definire le opzioni di default per tutti gli utenti
e per ciascun sito creando /etc/wgetrc. Di seguito una traduzione molto
semplificata della sezione del manuale che spiega la struttura del file di
startup. Per accedere alla documentazione completa:
$ info wget "Startup File"
La sintassi di questi file è molto semplice:
variabile = valore
La "variabile" viene anche detta "comando". I "valori" validi differiscono
a seconda dei comandi. I comandi sono case-insensitive e
underscore-insensitive. Così ad es. `DIr__PrefiX' è lo stesso di
`dirprefix'. Le linee vuote, quelle contenenti solo spazi bianchi e quelle
che iniziano con # sono ignorate.
Se per esempio volete che per default siano abilitate le opzioni -m, -t inf e
-b, dovrete scrivere queste linee in ~/.wgetrc:
mirror = on # come -m
tries = inf # come -t inf
background = on # come -b
Dopodichè quando scrivete:
$ nohup wget URL
questo semplice comando equivarrà a:
$ nohup wget -m -t inf -b URL
Se poi per default non volete che il mirroring esca dall'ultima directory
specificata nell'URL, e che i link non-relativi siano convertiti in
relativi, aggiungete in .wgetrc:
convert links = on # come -k
no_parent = on # come -np
Esempi
Le opzioni di wget non finiscono qui. La guida completa è accessibile con:
$ info wget
Alcuni esempi con
$ info wget examples
Le opzioni viste sono tuttavie quelle principali, che userete di più.
Vi lascio tuttavia una serie di esempi pratici.
Il primo riguarda il bellissimo archivio di libri (visionabili
gratuitamente) che si trova a:
http://library.cs.tuiasi.ro/
Scegliete ad es. webmaster e poi il titolo Creating Commercial Web Sites.
Supponiamo di volerci scaricare questo libro per visione offline.
Navigando un po' vi accorgerete che sta tutto dentro la cartella:
http://library.cs.tuiasi.ro/webmaster/creating-commercial-websites/
Quindi basta fare:
$ wget -m -np -nH --cut-dirs=1 http://library.cs.tuiasi.ro/webmaster/creating-commercial-websites/
Tuttavia questo non funzionerà, dandovi un messagio del tipo:
--10:36:47--
http://library.cs.tuiasi.ro:80/webmaster/creating-commercial-websites/
=> `creating-commercial-websites/index.html'
Connecting to library.cs.tuiasi.ro:80... connected!
HTTP request sent, awaiting response... 403 Forbidden
10:36:50 ERROR 403: Forbidden.
Il loro server web è configurato in modo da non accettare invasioni da
parte degli spyder come wget. Per aggirare il problema utilizziamo
l'opzione -U di wget:
-U, --user-agent=AGENT identify as AGENT instead of Wget/VERSION
(per maggiori info a proposito, eseguite appunto
info wget Invoking "HTTP Options")
Ad es. per far sì che wget appaia come il mio Netscape di Linux basta
fare:
$ wget -m -np -nH --cut-dirs=1 \
http://library.cs.tuiasi.ro/webmaster/creating-commercial-websites/ \
-U "Mozilla/4.75 [en] (X11; U; Linux 2.2.14-5.0 i686)"
Per conoscere la stringa di identificazione del vostro browser un modo è
utilizzare questo JavaScript (per i browser che supportano JavaScript):
<html>
<body>
<script language=JavaScript>
<!--
document.write(navigator.userAgent)
// -->
</script>
<noscript>
hai JavaScript disattivato o il tuo browser non lo supporta
</noscript>
</body>
</html>
Altri siti (ad es. www.informit.com) usano il meccanismo dei cookie: non
vi fanno accedere alle pagine a meno che non restituite il biscottino che
viene sfornato dal server dopo che vi siete registrati :) Anche questi
sono riuscito a prenderli con wget, semplicemente andando a vedere il
"biscottino" nel file .netscape/cookies nella mia home e facendoglielo
restituire a wget, tramite l'opzione:
--header=STRING insert STRING among the headers.
dove al posto di string dovete mettere 'Cookie: contenuto_del_cookie'.
Altri ancora abusano del file robots.txt per impedirvi lo scaricamento.
Anche questi si possono "craccare" e scaricare comunque con wget (per
maggiori informazioni: info wget appendices robots). Non starò qui a
coprire tutti i casi, ma ci tengo a precisare che tutto quello che
riuscite a vedere col browser potete scaricarlo con wget e potete
automatizzare lo scaricamento di qualsiasi cosa con un opportuno script di
shell, anche se può costare parecchia fatica scriversi lo script. Questo
per me è molto di più di quello che si può fare con programmi per Windows
tipo Teleport ecc... E' la potenza di Unix...
Distribuzione
Come tutte le altre utility della GNU, l'ultima versione di Wget si
trova sempre nell'archivio principale della GNU nel sito prep.ai.mit.edu,
e nei suoi mirror. Per esempio, Wget versione 1.5.3 si trova a
`ftp://prep.ai.mit.edu/pub/gnu/wget/wget-1.5.3.tar.gz'
Esiste una mailing list per wget. Per informazioni:
$ info wget various "mailing list"
Wget e gli script
In questa sezione vedremo come wget si comporta in presenza di script in
un sito web. Inoltre questo paragrafo costituisce una breve introduzione
al mondo del web scripting.
Gli script sono dei programmi, scritti in un qualsiasi linguaggio di
programmazione disponibile sul server, che tramite lo standard input o le
variabili d'ambiente prendono dei dati in input passati dal server web (e
a loro volta trasmessi al web server dal browser di un client remoto) e
producono sullo standard output solitamente un testo in formato HTML (ma
anche suoni, immagini, ecc...).
Un esempio molto semplice, ma significativo di script è il
form-to-email-gateway ossia uno scriptino che riceve i dati da un modulo
html (sicuramente voi tutti ne avete visto uno) e, dopo averli
eventualmente riformattati, li invia, usando programmi di inoltro della
posta elettronica disponibili sul server ad un certo indirizzo. Altri
esempi sono gli script che generano pagine web in cui alcune informazioni
sono variabili e vengono "pescate" da una base di dati (residente sullo
stesso server che esegue lo script o su un altro apposito database
server), ad es. una rubrica telefonica accessibile via web: una pagina
html contiene la form che permette di ricercare per cognome e/o nome e/o
numero di telefono, ecc...; i dati che l'utente immette in questa form,
allorquando clicca sul tasto di submit, vengono opportunamente codificati
(tra un po' vedremo come) e mandati dal suo browser al server web remoto e
da questi allo script che effettua la ricerca nel database. Quest'ultimo
compone l'elenco di tutti i record che soddisfano la condizione di ricerca
imposta dall'utente sotto forma di documento html che il server web
restituisce infine al client che aveva fatto la richiesta.
Altro esempio: nel mio sito web (vedi indirizzo a fine documento) potete
trovare un semplice script che accetta in input un anno e genera una
pagina con il calendario di quell'anno. Di per se questo script non ha
richiesto molto lavoro, poichè furbescamente ho usato il comando di Unix
cal).
Una pagina generata da uno script può cambiare a seconda dei dati in input
dati allo script (banalmente il calendario cambia a seconda dell'anno) e
viene perciò detta in gergo "pagina dinamica". Per converso una pagina non
generata da uno script, che viene inviata sempre allo stesso modo ad ogni
client viene detta "pagina statica". Questa terminologia si riferisce
implicitamente al "lato server"; infatti, poichè il navigatore vede sempre
una pagina html, guardando le cose dal punto di vista del client, tutte le
pagine sono "statiche"; tuttavia se in una pagina compare ad es. il vostro
nome (che in una pagina precedente o in una sessione di navigazione
precedente avevate comunicato tramite form al sito in questione), nessuna
meraviglia: è chiaro che quella pagina è stata generata al volo dai
programmi (o script) presenti sul server.
Le pagine dinamiche sono quindi in grado di fornire contenuti variabili a
seconda di alcuni dati in input. Ogni pagina è generata da uno script o
programma che si interfaccia con il server web tramite un'interfaccia
detta Common Gateway Interface. Questi script sono infatti detti anche
"script CGI".
Vi mostro un semplice esempio di script, che non prende in esame alcun
dato in input passatogli insieme ad una richiesta del client, ma comunque
genera una pagina "dinamica", dipendente dal tipo di server su cui viene
eseguito. Questo script, che ho chiamato info.cgi, non è altro che un
comunissimo script della shell sh, che potete eseguire anche dalla riga di
comando e che stampa alcune informazioni sul computer che lo esegue,
appoggiandosi al comando UNIX uname:
#!/bin/sh
echo "Content type: text/html \
<HTML> \
<HEAD> \
<TITLE>Remote host information</TITLE> \
</HEAD> \
<BODY>Hello. This machine's data are: \
<TABLE><TR><TD>operating system</TD><TD>"
uname --sysname
echo "</TD></TR><TR><TD>hostname</TD><TD>"
uname --nodename
echo "</TD></TR><TR><TD>operating system release</TD><TD>"
uname --release
echo "</TD></TR><TR><TD>operating system version</TD><TD>"
uname -v
echo "</TD></TR><TR><TD>machine (hardware) type</TD><TD>"
uname --machine
echo "</TD></TR><TR><TD>processor type</TD><TD>"
uname --processor
echo "</TD></TR></TABLE>"
echo "<P><A HREF=\"/\">Document Root /</A> \
</BODY> \
</HTML>"
Eseguendolo dalla riga di comando si ottiene un output simile al seguente:
Content type: text/html
<HTML> <HEAD> <TITLE>Remote host information</TITLE> </HEAD>
<BODY>Hello. This machine's data are: <TABLE><TR><TD>operating system</TD><TD>
Linux
</TD></TR><TR><TD>hostname</TD><TD>
Peanut-Linux
</TD></TR><TR><TD>operating system release</TD><TD>
2.2.16
</TD></TR><TR><TD>operating system version</TD><TD>
#2 Mon Jun 12 20:43:26 UTC 2000
</TD></TR><TR><TD>machine (hardware) type</TD><TD>
i586
</TD></TR><TR><TD>processor type</TD><TD>
GenuineIntel
</TD></TR></TABLE>
<P><A HREF="/">Document Root /</A> </BODY> </HTML>
quando questo documento html viene interpretato da un browser testuale
molto semplice, come ad es. lynx, ecco come appare:
Remote host information
Hello. This machine's data are:
operating system Linux
hostname Peanut-Linux
operating system release 2.2.16
operating system version #2 Mon Jun 12 20:43:26 UTC 2000
machine (hardware) type i586
processor type GenuineIntel
Document Root /
Nota: per far sì che il file info.cgi venga eseguito come uno script,
occorre metterlo in una directory in cui la particolare configurazione del
server web abilita l'esecuzione degli script (di solito la principale è
detta cgi-bin e posta allo stesso livello della document root; nel caso
particolare di xitami la predefinita è /home/cgi-bin). Inoltre occorre
assicurarsi che il file sia un file eseguibile dall'utente sotto cui gira
il server web, altrimenti esso vedrà il codice dello script, anzichè il
suo output, poichè il file non verrà eseguito, ma rimandato come semplice
documento di testo (o peggio ancora, l'utente vedrà della "fuffa" se lo
script è un compilato binario, come se ad es. è stato scritto in C). In
pratica basta fare ad es. un
chmod 755 info.cgi
Anche se qui non starò a spiegare come scrivere script CGI più sofisticati
nei più disparati linguaggi, ecco un semplicissimo script (ancora di
shell) che riporta il suo standard input in output:
cat.cgi - uno script felino ;)
#!/bin/sh
echo -e "Content type: text/plain\n"
cat
potete usarlo per sperimentare le modalità con cui vengono passati i
parametri di un form con l'interfaccia CGI.
Eseguendo questo script tramite
lynx localhost/cgi-bin/cat.cgi
vedrete una pagina vuota. Ma provate ad eseguirlo come azione associata
all'invio di un semplice form. Preparate ad es. questo file html:
form.html
---------
<HTML>
<HEAD>
<TITLE>Form di prova</TITLE>
</HEAD>
<BODY>
<FORM ACTION=/cgi-bin/cat.cgi METHOD=POST>
Nome <INPUT TYPE=TEXT NAME=Nome><BR>
Età <INPUT TYPE=TEXT NAME=Eta SIZE=3><BR>
<INPUT TYPE=SUBMIT VALUE=Invia>
</FORM>
</BODY>
</HTML>
Lanciate poi questo file col browser lynx (lynx localhost/form.html),
inserite il vostro nome e la vostra età e cliccate sul tasto "Invia". Il
browser visualizzerà una pagina in cui è scritto qualcosa del tipo:
Nome=Antonio&Eta=23
Questo significa che il contenuto della casella Nome era Antonio e (&, il
simbolo dell'AND logico) quello della casella Eta era 34.
Il mio Lynx versione 2.8.3dev.16 (quello di Linux Peanut al momento in cui
scrivo) è bacato e dà come output:
Nome=AntonioY=23
Questo perchè, nonostante lo script dichiari un mime type text/plain, lynx
si ostina ad interpretare l'output come HTML e come forse sapete in HTML
il carattere & ha un significato speciale (ad es. è rappresenta la
e accentata così: è). Visto che lynx è bacato, ho provato con Netscape ed
ho ottenuto l'output giusto: Nome=Antonio&Eta=23.
Nota: quella versione di lynx è bacata, ma nelle nuove versioni hanno
risolto il problema. Ad es. 2.8.3dev.18 che uso al lavoro non ha più
questo baco. Per sapere la vostra versione di lynx fate:
$ lynx --version
Un altro metodo per trasmettere i parametri, detto methodo GET, che è quello
di default (è opzionale specificare METHOD=GET nel tag FORM) encoda i
parametri nell'URL, allo stesso modo del metodo post. Ad es. la stringa
Nome=Antonio&Eta=23 viene appesa all'url dello script subito dopo il
separatore ?. Quindi l'url chiamato sarà del tipo:
http://localhost/cgi-bin/cat.cgi?Nome=Antonio&Eta=23
(sicuramente avrete visto url di questo tipo navigando)
Dal punto di vista dello script, la stringa che encoda i parametri viene
passata tramite la variabile d'ambiente $QUERY_STRING. Ecco la form e lo
script per sperimentare anche questo metodo.
form.html (versione "GET")
--------------------------
<HTML>
<HEAD>
<TITLE>Form di prova</TITLE>
</HEAD>
<BODY>
<FORM ACTION=/cgi-bin/cat.cgi>
Nome <INPUT TYPE=TEXT NAME=Nome><BR>
Età <INPUT TYPE=TEXT NAME=Eta SIZE=3><BR>
<INPUT TYPE=SUBMIT VALUE=Invia>
</FORM>
</BODY>
</HTML>
cat.cgi (versione "GET")
------------------------------
#!/bin/sh
echo -e "Content type: text/plain\n \
$QUERY_STRING"
Notare che con questo metodo potete richiamare uno script e passargli dei
parametri anche senza usare la pagina form: conoscendo i nomi dei
parametri (che possono essere ricavati dall'html della pagina form), basta
semplicemente digitare nella casella location del browser un URL del tipo
visto prima. Questo è utile perchè potete avere ad es. un link che
richiama uno script con dei parametri costanti quando l'utente ci clicca
su:
<A HREF="http://localhost/cgi-bin/cat.cgi?Nome=Antonio&Eta=23">trasmetti
dati di Antonio</A>
Inoltre uno script CGI può essere scritto in modo da accettare l'input con
entrambi i metodi GET e POST, ottenendo così la massima flessibilità. C'è
da dire però che il vecchio metodo GET è meno sicuro del POST.
Infine questo script appoggiandosi al comando UNIX env visualizza tutto il
suo spazio d'ambiente:
env.cgi
-------
echo -e "Content type: text/html\n
<HTML>\n \
<BODY>\n \
<H1>Variabili d'ambiente</H1>\n \
<PRE>\n"
env
echo -e " </PRE>\n \
</BODY>\n \
</HTML>\n"
Tutto questo per farvi capire cosa è uno script CGI... naturalmente wget
non è in grado di prendere il sorgente degli script, come un qualsiasi
navigatore, esso vede gli script come pagine statiche. Così se fate
wget localhost/cgi-bin/info.cgi
quello che salvate sul disco è il seguente file html:
info.cgi
--------
<HTML> <HEAD> <TITLE>Remote host information</TITLE> </HEAD>
<BODY>Hello. This machine's data are: <TABLE><TR><TD>operating system</TD><TD>
Linux
</TD></TR><TR><TD>hostname</TD><TD>
Peanut-Linux
</TD></TR><TR><TD>operating system release</TD><TD>
2.2.16
</TD></TR><TR><TD>operating system version</TD><TD>
#2 Mon Jun 12 20:43:26 UTC 2000
</TD></TR><TR><TD>machine (hardware) type</TD><TD>
i586
</TD></TR><TR><TD>processor type</TD><TD>
GenuineIntel
</TD></TR></TABLE>
<P><A HREF="/">Document Root /</A> </BODY> </HTML>
D'altra parte se una pagina html contiene una form che richiama uno
script, wget, non essendo un navigatore "umano", non è in grado di capire
il modo corretto con cui riempire la form e non tenta nemmeno di
richiamare lo script senza argomenti: semplicemente lo script non verrà
scaricato. Così se fate:
wget -m localhost/form.html
non uscirete dalla pagina form.html vista prima. Tutto questo fa capire
che quando un sito contiene degli script e volete mirrorarlo, dovete
chiedere all'amministratore locale di mandarvi i sorgenti degli script (e
dovrete avere anche i tool per poterli eseguire sul vostro server, ad es.
dovrete avere il processore PHP (www.php.net) se sono scritti in PHP, un
linguaggio di scripting server-side incorporabile ("embedded")
direttamente nell'html). In ogni caso dovete sempre chiedere prima di fare
il mirror di un sito, a meno che non lo facciate per uso personale (come
per risparmiare sulla bolletta telefonica) o sia detto esplicitamente in
una delle pagine che non occorre chiedere il permesso: infatti molte
pagine web sono "copyrighted".
Un'ultima nota: poichè potete chiamare gli script in cgi-bin come vi pare,
non c'è modo per un utente web di capire con che linguaggio è stato
scritto uno script. Ad es. uno script chiamato script.html potrebbe
benissimo essere scritto in Perl (un linguaggio interpretato molto comune
in ambiente Unix) oppure in C compilato (il buon vecchio linguaggio base
di Unix), oppure ancora in C++, Pascal, in un linguaggetto di shell (come
quello di Bash), Python, TCL ecc.. Tuttavia, specie se utilizzate molti
linguaggi diversi, penso sia buona norma assegnare agli script una
estensione convenzionale che indica il linguaggio utilizzato (.c per il C,
.pl per il Perl, .sh per un linguaggio di shell unix, .tcl per il Tcl,
.pas per il Pascal, .py per il Python). Questo perchè così dal nome del
file uno capisce quale linguaggio è stato utilizzato, senza andar a vedere
il sorgente del programma sul server.
Riguardo al linguaggio da utilizzare io non sono dogmatico su questo
punto, appunto perchè la scelta penso sia molto soggettiva: probabilmente
il miglior linguaggio è quello che conoscete meglio e per cui disponete di
librerie che possono facilitarvi il lavoro (ad es. API per connettervi ad
un determinato database) e altri strumenti utili, come i debugger a
livello di sorgente. In generale i linguaggi compilati sono più efficienti
ma meno ricchi a livello sintattico e semantico, per cui alcuni compiti
potrebbero risultare più difficili in questi linguaggi (tuttavia se avete
le librerie adatte, questo potrebbe non essere vero). I linguaggi
interpretati producono invece programmi più lenti, ma sono spesso più
ricchi di funzionalità e più ad "alto livello". Inoltre gli script
interpretati possono essere spostati da una macchina ad una diversa senza
la necessità di ricompilarli. Se poi i linguaggi interpretati sono
embedded nell'html (come PHP, ASP o Lite di Msql) vuol dire che sono
studiati apposta per il web-scripting e quindi vi facilitano la vita:
potete mescolare codice HTML e di scripting a piacere e l'utente vedrà
tutto codice HTML. A voi la scelta, ricordate però che più linguaggi
conoscete, più siete in grado di valutare volta per volta quale fa al caso
vostro.
La documentazione presente in rete sugli script CGI in ogni linguaggio è a
dir poco mastodontica... praticamente arrivate a tutto quello che volete
sapere tramite qualsiasi grosso motore di ricerca. Alcuni siti notevoli li
trovate nella mia collezione di link.
Conclusioni
wget non è esente da mancanze. Oltre come visto a non scaricare i fogli di
stile, non riconosce JavaScript, perciò se ad es. una pagina contiene un
link del tipo:
<A HREF="javascript:popUp('figure_1.gif');"><B>figure 1</B></A>
dove popUp è una funzione javascript che visualizza un documento html o
un'immagine in una finestra di pop-up, contentente solo la barra del menù
del browser, l'immagine e nient'altro:
function popUp(url) {
window.open(url, "pop_up", "menubar=yes");
}
Per provarla, potete usare questo semplice file html:
js_prova.html
-------------
<HTML>
<HEAD>
<TITLE>Es. di pagina con JavaScript</TITLE>
<SCRIPT LANGUAGE="JavaScript">
function popUp(url) {
window.open(url, "pop_up", "menubar=yes");
}
</SCRIPT>
</HEAD>
<BODY>
<A HREF="javascript:popUp('figure_1.gif');">figure 1</A>
</BODY>
</HTML>
Copiate inoltre una immagine figure_1.gif a piacere nella stessa directory
in cui mettete questo file .html (che ho chiamato js_prova.html), che deve
essere sotto la document root del server, come al solito. Se volete
provarla, usate Netscape e non Lynx (che non supporta JavaScript). Io l'ho
messo direttamente sotto la document root e poi ho dato il comando:
wget -m localhost/js_prova.html
e vedrete che scaricherà solo localhost/prova.htm
Pretendere che wget capisca il JavaScript è un po' troppo per ora.
Nemmeno gli appletti java di una pagina vengono scaricati correttamente,
in quanto wget non ha la capacità di una Java Virtual Machine (JVM) di
capire quali sono i file di classe e di suoni, immagini, ecc... collegati
al file di classe principale e si limita sostanzialmente a scaricare
quest'ultimo. L'appletto risultante non funzionerà quindi se esso è
composto da più file e non sono stati tutti scaricati.
Se si vogliono scaricare tutte queste cose occorre farlo manualmente o
indicare manualmente tutti i percorsi a wget.
Inoltre l'opzione --spider, che dovrebbe trasformare wget in un puro
spider (in altre parole un link checker), non è ancora molto potente.
Nonostante ciò, probabilmente troverete utile wget.
Come contattare l'autore
_________________________________________
Antonio Bonifati
sviluppa pagine web statiche e dinamiche
e basi di dati sotto server di tipo UNIX.
Collabora gratuitamente a progetti
culturali e no-profit sul web.
Può essere contatto all'indirizzo:
posta elettronica antonio punto bonifati a google
sito web:
ninuzzo.freehostia.com
posta delle lumache :)
Via Ernesto Moneta, 11
87012 Castrovillari (CS)
tel. 0981/26247
-----------------------------------------