sabato 26 febbraio 2011

Compilare GNU Octave 3.4.0

Lo scorso 8 febbraio è stata rilasciata la versione 3.4.0 di GNU Octave, sostituto libero di MATLAB. Trovate le novità di questa versione qui.

In questo post spiegherò come compilare questa versione di GNU Octave. Qualche mese fa avevamo visto come compilare la versione 3.2.2. Non sarà molto diverso compilare la 3.4.0, anzi, personalmente ho incontrato molti meno problemi nel compilare la 3.4.0 rispetto alla 3.2.2. La guida è stata sperimentata su Ubuntu 10.10, non dovrebbero esserci praticamente differenze per compilare GNU Octave su Debian Sid. Per le altre distribuzioni GNU/Linux le uniche cose differenti saranno probabilmente i comandi da usare per richiamare il gestore pacchetti utilizzato.

Per prima cosa installiamo il compilatore installando il metapacchetto build-essential. Questo può essere installato da Synaptic, oppure da terminale con il comando:
sudo apt-get install build-essential
Il metapacchetto build-essential installa i compilatori se non già presenti, se sono già installati... non fa praticamente nulla. In questo modo comunque ci assicuriamo di averli installati. Dopo di ciò installiamo le dipendenze, ovvero le librerie necessarie per la compilazione di GNU Octave. Possiamo fare ciò molto velocemente da terminale con il comando:
sudo apt-get build-dep octave3.2
Questo comando installerà le dipendenze necessarie per compilare il ramo 3.2 di GNU Octave, ma queste sono praticamente identiche a quelle del ramo 3.4. Manca solo una libreria da installare. Non impedisce la compilazione, però potrebbe essere utile in alcuni casi. Si chiama bison e può essere installata come al solito da Synaptic oppure da terminale con il comando
sudo apt-get install bison
Scarichiamo ora il codice sorgente di GNU OCtave: http://www.gnu.org/software/octave/download.html. La versione 3.4.0 può essere scaricata da terminale con il comando
wget ftp://ftp.gnu.org/gnu/octave/octave-3.4.0.tar.bz2
Scompattiamo l'archivio. Lo si può fare per via grafica oppure da terminale con il comando
tar xjvf octave-3.4.0.tar.bz2
Entriamo con il terminale nella cartella principale del codice sorgente di GNU Octave:
cdd octave-3.4.0/
Leggiamo i file README e INSTALL che spiegano come si compila il programma. Per prima cosa dobbiamo configurare il programma usando lo script configure. Potete vedere l'elenco delle opzioni di configurazione possibili richiamando lo script con l'opzione --help:
./configure --help
Possiamo ora configurare GNU Octave:
./configure
Se lo ritenete necessario potete, naturalmente, aggiungere le opzioni che preferite. Se tutto è andato a buon fine dovreste leggere alla fine dell'output di ./configure qualcosa di questo tipo:
Octave is now configured for i686-pc-linux-gnu

  Source directory:            .
  Installation prefix:         /usr/local
  C compiler:                  gcc  -mieee-fp  -Wall -W -Wshadow -Wformat -Wpointer-arith -Wmissing-prototypes -Wstrict-prototypes -Wwrite-strings -Wcast-align -Wcast-qual -g -O2 -pthread
  C++ compiler:                g++  -mieee-fp  -I/usr/include/freetype2    -Wall -W -Wshadow -Wold-style-cast -Wformat -Wpointer-arith -Wwrite-strings -Wcast-align -Wcast-qual -g -O2
  Fortran compiler:            gfortran -O -mieee-fp
  Fortran libraries:            -L/usr/lib/gcc/i686-linux-gnu/4.4.5 -L/usr/lib/gcc/i686-linux-gnu/4.4.5/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/i686-linux-gnu/4.4.5/../../.. -L/usr/lib/i686-linux-gnu -lgfortranbegin -lgfortran -lm
  Lex libraries:               
  LIBS:                        -lm  

  AMD CPPFLAGS:                
  AMD LDFLAGS:                 
  AMD libraries:               -lamd
  BLAS libraries:              -lblas
  CAMD CPPFLAGS:               
  CAMD LDFLAGS:                
  CAMD libraries:              -lcamd
  CARBON libraries:            
  CCOLAMD CPPFLAGS:            
  CCOLAMD LDFLAGS:             
  CCOLAMD libraries:           -lccolamd
  CHOLMOD CPPFLAGS:            
  CHOLMOD LDFLAGS:             
  CHOLMOD libraries:           -lcholmod
  COLAMD CPPFLAGS:             
  COLAMD LDFLAGS:              
  COLAMD libraries:            -lcolamd
  CURL CPPFLAGS:               
  CURL LDFLAGS:                
  CURL libraries:              -lcurl
  CXSPARSE CPPFLAGS:           
  CXSPARSE LDFLAGS:            
  CXSPARSE libraries:          -lcxsparse
  DL libraries:                -ldl
  FFTW3 CPPFLAGS:              
  FFTW3 LDFLAGS:               
  FFTW3 libraries:             -lfftw3
  FFTW3F CPPFLAGS:             
  FFTW3F LDFLAGS:              
  FFTW3F libraries:            -lfftw3f
  fontconfig CFLAGS:            
  fontconfig LIBS:             -lfontconfig  
  FT2_CFLAGS:                  -I/usr/include/freetype2
  FT2_LIBS:                    -lfreetype -lz
  GLPK CPPFLAGS:               
  GLPK LDFLAGS:                
  GLPK libraries:              -lglpk
  graphics CFLAGS:             -g -O2 -D_THREAD_SAFE -D_REENTRANT
  graphics LIBS:               -Wl,-Bsymbolic-functions -lfltk_gl -lfltk
  Magick++ CPPFLAGS:           -I/usr/include/GraphicsMagick  
  Magick++ LDFLAGS:             
  Magick++ libraries:          -lGraphicsMagick++ -lGraphicsMagick  
  HDF5 CPPFLAGS:               
  HDF5 LDFLAGS:                
  HDF5 libraries:              -lhdf5
  LAPACK libraries:            -llapack
  OPENGL libraries:            -lfontconfig   -lGL -lGLU
  PTHREAD flags:               -pthread
  PTHREAD libraries:           
  QHULL CPPFLAGS:              
  QHULL LDFLAGS:               
  QHULL libraries:             -lqhull
  QRUPDATE libraries:          -lqrupdate
  READLINE libraries:          -lreadline
  REGEX libraries:             -L/usr/lib -lpcre
  TERM libraries:              -lncurses
  UMFPACK libraries:           -lumfpack
  X11 include flags:           
  X11 libraries:               -lX11
  Z CPPFLAGS:                  
  Z LDFLAGS:                   
  Z libraries:                 -lz

  Default pager:               less
  gnuplot:                     gnuplot

  Do internal array bounds checking:  false
  Build static libraries:             false
  Build shared libraries:             true
  Dynamic Linking:                    true (dlopen)
  Include support for GNU readline:   true
  64-bit array dims and indexing:     false
Se non avessimo installato bison avremmo inoltre letto un avviso di questo tipo:
configure: WARNING: I didn't find bison, but it's only a problem if you need to reconstruct parse.cc
configure: WARNING: OpenGL libs (GL and GLU) not found. Native graphics will be disabled.
configure: WARNING: 
configure: WARNING: I didn't find the necessary libraries to compile native
configure: WARNING: graphics.  It isn't necessary to have native graphics,
configure: WARNING: but you will need to have gnuplot installed or you won't
configure: WARNING: be able to use any of Octave's plotting commands
configure: WARNING: 
configure: 
configure: NOTE: libraries may be skipped if a library is not found OR
configure: NOTE: if the library on your system is missing required features.
Questo è il messaggio che ho ottenuto io dopo la prima configurazione (non avevo però ancora installato bison). Come potete vedere mi è stata segnalata l'assenza delle librerie OpenGL. Ho risolto questo "problema" (non è un vero e proprio problema perché si tratta solo di un avviso, non è un errore) installando i pacchetti libgl1-mesa-dev e libglu1-mesa-dev:
sudo apt-get install libgl1-mesa-dev libglu1-mesa-dev
Normalmente questo problema non dovrebbe verificarsi perché questi pacchetti dovrebbero essere installati usando apt-get build-dep octave3.2, non so bene per quale motivo a me sono stati rimossi.

Dopo aver finalmente configurato GNU Octave possiamo compilarlo con il comando
make
Per rendere più veloce la compilazione, chi ha un processore multicore può passare a make l'opzione -j n, dove n è il numero di core del processore (o meglio di job simultanei che può gestire, però per semplificare il discorso parliamo di core). Quindi chi, per esempio, ha un processore con quattro core potrà usare per compilare GNU Octave il comando
make -j 4
Se la compilazione va a buon fine (a seconda della potenza di calcolo del proprio computer questa operazione può richiedere da poche decine di minuti a qualche ora) alla fine dell'operazione leggerete sul terminale un messaggio di questo tipo:
Octave successfully built.  Now choose from the following:

   ./run-octave    - to run in place to test before installing
   make check      - to run the tests
   make install    - to install (PREFIX=/usr/local)

make[2]: uscita dalla directory "/[...]/octave-3.4.0"
make[1]: uscita dalla directory "/[...]/octave-3.4.0"
Come potete vedere, prima di installare Octave potete avviarlo per verificare se funzioni con il comando
./run-octave
Infine possiamo installarlo con il comando
sudo make install
Se invece vogliamo crare un pacchetto .deb per rendere più semplice la successiva installazione o rimozione possiamo usare checkinstall:
sudo checkinstall

domenica 20 febbraio 2011

Compilare QtiPlot 0.9.8.4

NOTA: Renato Rivoira, che ringrazio, ha curato la traduzione in italiano di QtiPlot e del suo manuale. Nei commenti a questo post chiede, a chi sia in grado di farlo, di aiutarlo a revisionare le sue traduzioni. Potete contattarlo mandandogli una mail all'indirizzo renatoriv AT ​libero PUNTO it.

Il 18 febbraio 2011 è stata rilasciata la versione finale di QtiPlot 0.9.8.4, un programma per l'analisi dei dati, equivalente libero di Origin. Questa versione porta con sé numerose novità che potete leggere qui.

QtiPlot è un programma un po' particolare perché il codice sorgente è rilasciato con licenza GPL ma lo sviluppatore distribuisce i binari precompilati a pagamento. Ciò non è in alcun modo in contrasto con il concetto di software libero perché nella sua definizione non si parla di prezzo. La confusione di "software libero" con "software gratuito" è maggiormente presente nelle persone di lingua inglese, poiché in questa la parola "free" ha il duplice significato di "libero" e "gratuito". Anche chi usa questo software avendolo compilato da sé può fare una donazione a favore del progetto all'indirizzo http://soft.proindependent.com/why_donate.html per permettere allo sviluppatore di portare avanti il suo progetto.

In questo post spiegherò come fare a compilare QtiPlot. Questa guida è per Ubuntu Maverick Meerkat 10.10 (e probabilmente funzionerà senza troppi problemi anche per Debian), per le altre distribuzioni ci potranno essere piccoli differenze per quanto riguarda i comandi che richiamano il gestore dei pacchetti, i nomi dei pacchetti e la disponibilità di questi.

Innanzitutto cominciamo con l'installare tutte le dipendenze necessarie per la compilazione. In un terminale dare
sudo apt-get install build-essential
sudo apt-get build-dep qtiplot
Il primo comando installa (se non già presente, ma è difficile che non lo sia già) le utility necessarie per compilare la maggior parte dei programmi, il secondo installa le principali librerie che necessita QtiPlot per essere compilato. Nelle ultime versioni QtiPlot ha aggiunto altre dipendenze i cui pacchetti non sono disponibili nella versione di Ubuntu considerata, quindi prima di procedere con la compilazione di QtiPlot stesso dovremo compilare altri programmi.

Scarichiamo il codice sorgente di QtiPlot all'indirizzo http://download.berlios.de/qtiplot/qtiplot-0.9.8.4.tar.bz2. Da terminale si può usare
wget http://download.berlios.de/qtiplot/qtiplot-0.9.8.4.tar.bz2
Scompattiamo l'archivio dove ci pare (se non siete in grado di farlo da terminale, vi ricordo che è possibile farlo attraverso interfaccia grafica) e spostiamoci con il terminale nella cartella in cui si trova il sorgente:
tar xjvf qtiplot-0.9.8.4.tar.bz2
cd qtiplot-0.9.8.4/
Aprite il file README.html presente nella cartella del sorgente (è sempre buona norma leggere i file README e/o INSTALL, o simili, prima di procedere con la compilazione di un programma). Come potete vedere le dipendenze necessarie sono le seguenti: Qt (>= 4.5.0), GSL, muParser (1.32), zlib e libpng. Tutti questi pacchetti sono stati installati (se non erano già presenti) con il comando sudo apt-get build-dep qtiplot. Inoltre servono delle versioni leggermente modificate delle librerie Qwt (5.2) e QwtPlot3D, ma queste sono presenti nella sottocartella 3rdparty/ del codice sorgente di QtiPlot, quindi non bisogna scaricare nulla di nuovo per questo elencato finora. Altre librerie suggerite sono QTeXEngine, ALGLIB (2.6) e TAMUANOVA.

Compiliamo queste librerie. Salveremo i codici sorgenti di queste nella sottocartella 3rdparty/, per comodità, quindi spostiamoci in questa cartella:
cd 3rdparty/
Scarichiamo e scompattiamo l'archivio contenente il codice sorgente di QTeXEngine:
wget http://download.berlios.de/qtiplot/QTeXEngine-0.3-opensource.zip
unzip QTeXEngine-0.3-opensource.zip
cd QTeXEngine
Compiliamo la libreria (le istruzioni sono presenti nel file README.txt:
qmake
make
Se tutto è andato bene (lo potete controllare guardando l'exit status con il comando echo $?, se è 0 allora l'operazione si è conclusa correttamente) torniamo nella cartella superiore, scarichiamo il codice sorgente di ALGLIB e compiliamolo:
cd ..
wget http://www.alglib.net/translator/re/alglib-2.6.0.cpp.zip
unzip alglib-2.6.0.cpp.zip ; mv cpp alglib
cd alglib
Le istruzioni per la compilazione di questa libreria sono presenti nel file manual.cpp.html. Bisogna eseguire lo script build passando come argomento il compilatore da usare, nel nostro caso GCC. Lo script build non ha i permessi di esecuzione, quindi prima di avviarlo dobbiamo rendere il file eseguibile:
chmox +x build
./build gcc
Se anche questa operazione si è conclusa con successo (controlliamo l'exit status con echo $?: se otteniamo 0 allora è andato tutto bene) andiamo avanti possiamo compilare TAMUANOVA. Spostiamoci nella cartella superiore, scarichiamo il codice sorgente di TAMUANOVA, scompattiamo l'archivio ed entriamo nella cartella del codice sorgente di questa libreria:
cd ..
wget http://www.stat.tamu.edu/~aredd/tamuanova/tamu_anova-0.2.tar.gz
tar xzvf tamu_anova-0.2.tar.gz ; mv tamu_anova-0.2/ tamu_anova/
cd tamu_anova/
Compiliamo questa libreria (le istruzioni sono presenti nel file INSTALL):
./configure
make
Controllate l'exit status con echo $?. Se ottenete 0 la compilazione è andata a buon fine. Non è necessario installare la libreria (come neanche le precedenti), è sufficiente averla compilata. Prima di procedere dobbiamo modificare un file: 3rdparty/qwtplot3d/qwtplot3d.pro. Questo file contiene le istruzioni per la compilazione della libreria QwtPlot3D (che avverrà successivamente quando compileremo QtiPlot). Alla fine di questo file bisogna aggiungere la seguente riga:
unix:CONFIG += staticlib
affinché venga compilata staticamente (suggerimento tratto da qui).

Ritorniamo con il terminale nella cartella principale del codice sorgente di QtiPlot (che si trova due cartelle superiori rispetto alla carella di ALGLIB in cui ci trovavamo prima):
cd ../..
Prima di iniziare la compilazione vera e propria ci sono ancora diverse operazioni da fare. Prima di tutto dobbiamo creare un file chiamato build.conf da posizionare nella cartella del codice sorgente di QtiPlot e che contiene alcune istruzioni che servono per la compilazione. Questo file dovrà essere realizzato sulla base del modello build.conf.example che si trova nella stessa cartella. Per la precisione, build.conf contiene alcune opzioni e variabili per la compilazione e i percorsi delle librerie da utilizzare (oppure le corrispondenti opzioni di compilazione). In questo file la variabile QTI_ROOT è uguale al percorso della cartella principale del codice sorgente di QtiPlot. Ho modificato build.conf.example in base ai reali percorsi delle librerie utilizzate ed ecco il mio build.conf:
isEmpty( QTI_ROOT ) {
  message( "each file including this config needs to set QTI_ROOT to the dir containing this file!" )
}

##########################################################
##     System specific configuration
##########################################################

# Global include path which is always added at the end of the INCLUDEPATH
SYS_INCLUDEPATH = /opt/local/include
# Global lib path and libs which is ls always added at the end of LIBS
SYS_LIBS = -L/opt/local/lib

##########################################################
## zlib (http://www.zlib.net/)
##########################################################

# include path. leave it blank to use SYS_INCLUDE
ZLIB_INCLUDEPATH = $$QTI_ROOT/3rdparty/zlib/

##########################################################
## muParser (http://muparser.sourceforge.net/)
##########################################################

# include path. leave it blank to use SYS_INCLUDE
MUPARSER_INCLUDEPATH = /usr/include/muParser/
# link statically against a copy in 3rdparty/
MUPARSER_LIBS = /usr/lib/libmuparser.so
# or dynamically against a system-wide installation
# MUPARSER_LIBS = -lmuparser

##########################################################
## GNU Sientific Library (http://www.gnu.org/software/gsl/)
##########################################################

# include path. leave it blank to use SYS_INCLUDE
# GSL_INCLUDEPATH = $$QTI_ROOT/3rdparty/gsl/include
# link statically against a copy in 3rdparty/
# GSL_LIBS = $$QTI_ROOT/3rdparty/gsl/lib/libgsl.a \
#            $$QTI_ROOT/3rdparty/gsl/lib/libgslcblas.a
# or dynamically against a system-wide installation
GSL_LIBS = -lgsl -lgslcblas

##########################################################
## QWT - use local copy till upstream catches up
# http://qwt.sourceforge.net/index.html
##########################################################

# include path.
QWT_INCLUDEPATH = $$QTI_ROOT/3rdparty/qwt/src
# link locally against a copy in 3rdparty/
QWT_LIBS = $$QTI_ROOT/3rdparty/qwt/lib/libqwt.a

##########################################################
## QwtPlot3D - use local copy till upstream catches up
# http://qwtplot3d.sourceforge.net/
##########################################################

# include path.
QWT3D_INCLUDEPATH = $$QTI_ROOT/3rdparty/qwtplot3d/include
# link locally against a copy in 3rdparty/
win32:QWT3D_LIBS = $$QTI_ROOT/3rdparty/qwtplot3d/lib/qwtplot3d.dll
unix:QWT3D_LIBS = $$QTI_ROOT/3rdparty/qwtplot3d/lib/libqwtplot3d.a

##########################################################
## libpng - optional. you don't have to set these variables
##########################################################

# include path. leave it blank to use SYS_INCLUDE
# LIBPNG_INCLUDEPATH = $$QTI_ROOT/3rdparty/libpng/
# link statically against a copy in 3rdparty/
# LIBPNG_LIBS = $$QTI_ROOT/3rdparty/libpng/libpng.a
# or dynamically against a system-wide installation
LIBPNG_LIBS = -lpng

##########################################################
## QTeXEngine - optional. you don't have to set these variables
# http://soft.proindependent.com/qtexengine/
##########################################################

# include path.
TEX_ENGINE_INCLUDEPATH = $$QTI_ROOT/3rdparty/QTeXEngine/src
# link locally against a copy in 3rdparty/
TEX_ENGINE_LIBS = $$QTI_ROOT/3rdparty/QTeXEngine/libQTeXEngine.a

##########################################################
## ALGLIB (2.6) - optional. you don't have to set these variables
# http://www.alglib.net/
##########################################################

# include path.
ALGLIB_INCLUDEPATH = $$QTI_ROOT/3rdparty/alglib/src
# link locally against a copy in 3rdparty/
ALGLIB_LIBS = $$QTI_ROOT/3rdparty/alglib/out/libalglib.a

##########################################################
## TAMUANOVA - optional. you don't have to set these variables
# http://www.stat.tamu.edu/~aredd/tamuanova/
##########################################################

# include path.
TAMUANOVA_INCLUDEPATH = $$QTI_ROOT/3rdparty/tamu_anova/
# link locally against a copy in 3rdparty/
TAMUANOVA_LIBS = $$QTI_ROOT/3rdparty/tamu_anova/libtamuanova.a

##########################################################
## python - only used if python is needed
##########################################################

# the python interpreter to use
# (unix only, windows will use what ever is configured to execute .py files!)
PYTHON = python

##########################################################
## Qt tools - allows to use specific versions
##########################################################

LUPDATE = lupdate
LRELEASE = lrelease

############################################################
##  Target specific configuration: configure Qtiplot itself
############################################################

contains( TARGET, qtiplot ) {
  # building without muParser doesn't work yet
  SCRIPTING_LANGS += muParser
  SCRIPTING_LANGS += Python

  # a console displaying output of scripts; particularly useful on Windows
  # where running QtiPlot from a terminal is inconvenient
  DEFINES         += SCRIPTING_CONSOLE

  #DEFINES         += QTIPLOT_DEMO

  # Uncomment the following line if you want to perform a custom installation using the *.path variables defined in ./qtiplot.pro.
  #CONFIG          += CustomInstall

  # Uncomment the following line if you want to build QtiPlot as a browser plugin (not working on Internet Explorer).
  #CONFIG          += BrowserPlugin
  
  CONFIG          += release
  #CONFIG          += debug
  
  # Uncomment the following line if you want to link statically against Qt.
  #CONFIG           += StaticBuild
  #win32: CONFIG   += console
}

Non abbiamo ancora finito, dobbiamo fare un'ultima cosa. Modifichiamo il file qtiplot.pro che si trova nella cartella principale del codice sorgente. Questo contiene il nome di quali cartelle contengono del codice che dovrà essere compilato. Togliamo dall'elenco fitPlugins e manual (eventualmente potranno essere compilati successivamente), quindi il file apparirà più o meno così:
TEMPLATE = subdirs

SUBDIRS = 3rdparty/qwt \
        3rdparty/qwtplot3d \
        qtiplot
A questo punto dovremmo essere in grado, finalmente di compilare QtiPlot. Come spiegato nel file README.html i comandi da dare sono
qmake
make
Per rendere più veloce la compilazione, chi ha un processore multicore può passare a make l'opzione -j n, dove n è il numero di core del processore (o meglio di job simultanei che può gestire, però per semplificare il discorso parliamo di core). Quindi chi, per esempio, ha un processore con quattro core potrà usare per compilare QtiPlot il comando
make -j 4
Se la compilazione va a buon fine (come al solito possiamo controllarlo con echo $?) possiamo direttamente avviare QtiPlot, per vedere se funziona, inserendo il percorso relativo dell'eseguibile appena generato:
qtiplot/qtiplot
Infine si può, se lo si desidera, installare il programma con
sudo make install
oppure utilizzando checkinstall:
sudo checkinstall
che crea un semplice pacchetto .deb (il risultato, però, non è "professionale").

sabato 19 febbraio 2011

Emacs: aprire il file manager nella cartelle del buffer corrente

In Emacs è presente una modalità chiamata Dired che funge da file manager. Tuttavia in qualche caso si potrebbe voler aprire il file manager dell'ambiente desktop in uso (o comunque un file manager esterno) direttamente nella cartella in cui si trova il file attualmente modificato (o meglio, il buffer corrispondente). Per fare ciò si può aggiungere il seguente codice all'interno del proprio file di inizializzazione (a meno che questa funzione non sia già presente in Emacs e io non sia in grado di trovarla):
(defun open-buffer-path ()
  "Run Nautilus on the directory of the current buffer."
  (interactive)
  (shell-command (concat "nautilus " default-directory)))
Questo codice definisce la funzione open-buffer-path che aprirà il file manager Nautilus (il predefinito di GNOME) nella cartella predefinita (che normalmente corrisponde alla cartella in cui si trova il file in uso, oppure alla propria home se il buffer aperto non corrisponde a un file). Se si vuole usare un file manager diverso basta sostituire il comando che compare come argomento di concat (e magari anche il Nautilus che compare nel rigo di descrizione della funzione).

Se lo si desidera si può associare una combinazione di tasti a questa funzione aggiungendo al proprio file di inizializzazione:
(global-set-key (kbd "<M-f3>") 'open-buffer-path)
Con questo codice verrà associata la combinazione M-f3 alla funzione open-buffer-path, se si vuole usare una combinazione diversa basta sostituire l'occorrenza di M-f3 nel codice precedente.

Questo post prende spunto da http://zhangda.wordpress.com/2010/02/03/open-the-path-of-the-current-buffer-within-emacs/ in cui è definita una funzione per aprire il file manager Explorer in Windows. A questo proposito trovate un codice alternativo qui: http://www.factsandpeople.com/facts-mainmenu-5/2-editor-emacs/80-switching-between-windows-explorer-and-emacs.

sabato 15 gennaio 2011

info: Impossibile trovare il nodo «Top». [Risolto]

Non so bene per quale motivo (ma ho l'impressione che sia colpa di un aggiornamento di Ubuntu) non sono più in grado di usare info. Come spiegai in uno dei primi post di questo blog, per consultare da terminale i manuali (se presenti) dei comandi si può usare man:
man nomecomando
In aggiunta a man, molti programmi (soprattutto del progetto GNU) forniscono una guida (spesso più completa del manuale consultabile con man) che può essere letta con info:
info nomecomando
In questi giorni, dando il comando (per esempio)
info coreutils
(per leggere la guida delle principale utilità di sistema, chiamate appunto coreutils) ricevo dal terminale questa risposta:
info: Impossibile trovare il nodo «Top».
Il messaggio in lingua inglese è il seguente:
info: Cannot find node `Top'.
Il problema è che per qualche motivo si è corrotto il file /usr/share/info/dir che contiene (o, nel mio caso, dovrebbe contenere) l'elenco dei manuali che possono essere consultati con info. Il mio file /usr/share/info/dir conteneva solo questo:
Questo è il file .../info/dir, che contiene il
nodo più in alto della gerarchia di Info, chiamata (dir)Top.
La prima volta che invochi il programma Info esso parte da questo nodo.
^_
File: dir, Node: Top\tQuesto è il nodo più alto dell'albero INFO

Questo (il nodo principale) fornisce un menù che contiene le voci principali.
Digitando "q" esce, digitando "?" si produce un elenco di tutti i comandi Info,
"d" ritorna alla presente schermata, "h" fornisce informazioni ai nuovi utenti,
"mEmacs<return>" visualizza il manuale di Emacs, ecc.
All'interno di Emacs, puoi cliccare con il pulsante sinistro del mouse su una voce di menù
o su un riferimento incrociato per selezionarlo.

* Menu:
Come vedete, manca l'elenco dei manuali disponibili, che invece dovrebbe essere presente dopo la riga * Menu:.

Per risolvere il problema si può cancellare il file incriminato e rigenerarlo con il comando da terminale install-info (in Debian, Ubuntu e derivate bisogna richiamare il comando con ginstall-info). install-info accetta come primo argomento il file (in genere questi file hanno estensione .info e potrebbero essere compressi con gzip) che contiene il manuale che si vuole aggiungere all'elenco (questi file in genere si trovano nella cartella /usr/share/info/ e sue sottocartelle), come secondo argomento il file di elenco (cioè /usr/share/info/dir). Queste due operazioni possono essere automatizzate con i due seguenti comandi (richiedono i diritti di amministratore perché si modificherà un file di sistema):
sudo mv /usr/share/info/dir ~/dir
for file in /usr/share/info/*.{gz,info} /usr/share/info/*/*.{gz,info}; do sudo install-info "$file" /usr/share/info/dir; done
Se usate sistemi, come Debian o Ubuntu, che richiedono ginstall-info al posto di install-info, il secondo dei comandi precedenti va cambiato in
for file in /usr/share/info/*.{gz,info} /usr/share/info/*/*.{gz,info}; do sudo ginstall-info "$file" /usr/share/info/dir ; done
Breve spiegazione dei comandi consigliati: il primo comando (mv) serve per spostare il file nella propria home, conservando una copia di sicurezza (anche se il file è corrotto non fa male creare una copia, dopo aver risolto il problema la copia può essere tranquillamente cancellata); il secondo esegue un ciclo for su tutti i file con estensioni .gz o .info presenti nella cartella /usr/share/info sue sottocartelle. Se in questo modo non dovessero essere inclusi alcuni file di manuali, potete farlo manualmente tramite install-info (o ginstall-info) con la sintassi esposta precedentemente.

Aggiornamento importante: dopo il commento di Zio LoneWolf (che ringrazio) mi sono accorto che la questione era più complicata di quanto mi sembrasse inizialmente. Per risolvere il problema non è affatto sufficiente usare i comandi presentati in precedenza perché pare che sia presente un bug nella traduzione italiana di Texinfo. Si può aggirare (e questa volta per davvero) il problema impostando nella shell la lingua inglese e poi utilizzando nuovamente i comandi precedenti:
LANG=en_US.UTF-8
for file in /usr/share/info/*.{gz,info} /usr/share/info/*/*.{gz,info}; do sudo install-info "$file" /usr/share/info/dir; done
oppure
LANG=en_US.UTF-8
for file in /usr/share/info/*.{gz,info} /usr/share/info/*/*.{gz,info}; do sudo ginstall-info "$file" /usr/share/info/dir; done
L'aggiornamento del file /usr/share/info/dir può anche essere fatto con il comando update-info-dir (dato come al solito con privilegi di amministratore). Si può fare in modo che questo script utilizzi sempre la lingua inglese senza doverla impostare manualmente ogni volta. Aprite con il vostro editor di testo preferito (e con i diritti di amministratore) lo script /usr/sbin/update-info-dir. Per esempio con gedit si userà:
gksudo gedit /usr/sbin/update-info-dir
A questo punto inserite la stringa
unset LANG # https://bugs.launchpad.net/ubuntu/+source/texinfo/+bug/707931
(nel commento c'è un riferimento alla segnalazione del bug su Launchpad) per esempio dopo il rigo
unset LANGUAGE
Dopo di ciò sarà possibile aggiornare il file /usr/share/info/dir semplicemente con il comando
sudo update-info-dir
Altro aggiornamento: Giovanni segnala nei commenti che ha realizzato uno script per risolvere automaticamente il problema. Trovate lo script e le istruzioni per l'uso sul sito sito: http://www.ubaweb.it/miniguide/info_non_va.php.

venerdì 5 novembre 2010

Confrontare le differenze fra due documenti PDF (2)

Qualche giorno fa ho illustrato un semplice script bash che permette di eseguire un confronto elementare fra due documenti PDF per cercarne le differenze. Tuttavia, come ho spiegato, questo script ha numerosi difetti perché cerca essenzialmente differenze nel testo dei due documenti (se è in grado di estrarre correttamente il testo da essi). Esiste però un programma a interfaccia grafica che permette di eseguire un confronto più efficace: DiffPDF. Questo pacchetto è fornito dalle principali distribuzioni GNU/Linux; in Debian, Ubuntu & Co. bisogna installare il pacchetto diffpdf con il Gestore pacchetti Synaptic oppure da terminale con il solito comando:
sudo apt-get install diffpdf
Per le altre distribuzioni fare riferimento al proprio gestore pacchetti.

Il programma può essere avviato dal lanciatore (in Ubuntu: ApplicazioniAccessoriDiffPDF) oppure da terminale con il comando
diffpdf
È possibile passare come argomenti i percorsi dei documenti che si vogliono confrontare:
diffpdf /percorso/del/file1.pdf /percorso/del/file2.pdf



Normalmente DiffPDF mette a confronto i testi dei documenti, ma, come potete vedere dallo screenshot seguente, è possibile anche effettuare un confronto visuale fra due documenti, per poter cercare differenze fra le immagini. Per fare ciò bisogna selezionare Appearance nel menu a tendina Comparison Mode in alto a destra e rieseguire il confronto con il pulsante Compare situato più in basso, sempre sul pannello di destra. Questa modalità potrebbe fornire dei falsi positivi (come succede nell'immagine proposta) perché non viene più confrontato solo del testo, ma comunque i risultati sono in genere molto buoni.



Anche qui propongo uno Script per Nautilus che permette di avviare DiffPDF con pochi clic selezionando i file che si desidera confrontare:
#!/bin/bash

titolo="Confronta PDF"

function controlla() {
    estensione=$(echo "$1" | cut -d. --complement -f1)
    case $estensione in
        [pP][dD][fF])
            ;;
        *)
            zenity --error --title="$titolo" --text="Devi selezionare due file con estensione \".pdf\"."
            exit 1
    esac
}

if [ $# -ne 2 ]; then
    zenity --error --title="$titolo" --text="Devi selezionare due file PDF alla volta."
    exit 1
fi
pdf1="$1"
pdf2="$2"
controlla "$pdf1" && controlla "$pdf2"
if [ $? -eq 0 ]; then
    diffpdf "$pdf1" "$pdf2"
else # non dovrebbe arrivare mai qui, metto per sicurezza
    zenity --error  --title="$titolo" --text="Si è verificato un errore."
    exit 1
fi
exit 0
Per utilizzare questo script aprite un file vuoto con un editor di testo, copiate lo script nell'editor e salvatelo nella cartella ~/.gnome2/nautilus-scripts (ricordo che la tilde ~ è un'abbreviazione della cartella home dell'utente corrente) chiamandolo, per esempio, "Confronta PDF". Rendete eseguibile lo script e così potrete selezionare due file PDF da confrontare che si trovano nella stessa cartella e, facendo clic con il tasto destro, scegliete ScriptConfronta PDF (o il nome che avete dato allo script).

martedì 26 ottobre 2010

Confrontare le differenze fra due documenti PDF

Nota: qui suggerisco il programma a interfaccia grafica DiffPDF che permette di effettuare un confronto più efficace fra due documenti PDF.

Un semplice metodo per confrontare due documenti in formato PDF è sfruttare l'accoppiata pdftotext (per convertire i PDF in testo puro) + diff (per controllare le differenze). Mi ero stancato di eseguire tutti i soliti comandi allora ho scritto questo script fresco fresco:
#!/bin/bash

function converti() {
    txt=$(eval "expr \$"$1" ")
    pdf=$(eval "expr \$"$2" ")
    scelta=""
    while [ -f "$txt" ]; do
        read -p "Il file $txt esiste già, sovrascriverlo? [s/n]: " scelta
        case "$scelta" in
            s|S|y|Y)
                break 2
                ;;
            n|N)
                read -p "Specifica un file di output differente: " txt
                ;;
        esac
    done
    if ! pdftotext "$pdf" "$txt"; then
        exit 1
    fi
    eval "$1=$txt"
}

if [ $# -ne 2 ]; then
    echo "Uso: $0 PDF1 PDF2"
    exit 1
fi
pdf1=$1
pdf2=$2
txt1=$(dirname "$pdf1")/$(basename "$pdf1" .pdf).txt
txt2=$(dirname "$pdf1")/$(basename "$pdf2" .pdf).txt
converti txt1 pdf1 && converti txt2 pdf2
if [ $? -eq 0 ]; then
    diff -u "$txt1" "$txt2" | less
fi
rm -f "$txt1" "$txt2"
exit 0
Per utilizzarlo basta copiare lo script in un file con un editor di testo a piacere, salvare il file chiamandolo, per esempio, pdf-diff, renderlo eseguibile e posizionarlo nella cartella ~/bin (ricordo che la tilde ~ è un'abbreviazione della cartella home dell'utente corrente), creandola se eventualmente non esiste. Date nel terminale il comando
. ~/.profile
(per i curiosi serve a caricare il file ~/.profile che aggiunge la cartella $HOME/bin alla variabile d'ambiente $PATH), dopo di ciò dovrebbe essere sufficiente dare nel terminale il comando
pdf-diff /percorso/del/primo/file /percorso/del/secondo/file
(o il nome che avete dato allo script).

Non sarà perfetto, ha tutti i limiti che si riscontrano nel passaggio da un PDF a un file di testo puro (per esempio se cambia un'immagine non se ne accorge e la buona riuscita del confronto dipende da quanto bene pdftotext riesce a leggere i documenti) ma è abbastanza veloce e ho trovato questo sistema spesso molto utile.

Ho scritto anche il corrispondente script Nautilus (utilizza zenity):
#!/bin/bash

titolo="Confronto PDF"

function converti() {
    txt=$(eval "expr \$"$1" ")
    pdf=$(eval "expr \$"$2" ")
    while [ -f "$txt" ]; do
        if ! zenity --question --title="$titolo" --text="Il file $txt esiste già, sovrascriverlo?"; then
            txt=$(zenity --entry  --title="$titolo" --text="Specifica un file di output differente: ")
        else
            break 2
        fi
    done
    pdftotext "$pdf" "$txt"
    case $? in
        0)
            ;;
        1)
            zenity --error  --title="$titolo" --text="Si è verificato un errore nell'apertura di $pdf."
            exit 1
            ;;
        2)
            zenity --error  --title="$titolo" --text="Si è verificato un errore nell'apertura di $txt."
            exit 1
            ;;
        3)
            zenity --error  --title="$titolo" --text="Si è verificato un errore collegato ai permessi di $pdf."
            exit 1
            ;;
        *)
            zenity --error  --title="$titolo" --text="Si è verificato un errore!"
            exit 1
    esac
    eval "$1=$txt"
}

if [ $# -ne 2 ]; then
    zenity --error --title="$titolo" --text="Devi selezionare due file alla volta"
    exit 1
fi
pdf1=$1
pdf2=$2
txt1=$(dirname "$pdf1")/$(basename "$pdf1" .pdf).txt
txt2=$(dirname "$pdf1")/$(basename "$pdf2" .pdf).txt
converti txt1 pdf1 && converti txt2 pdf2
if [ $? -eq 0 ]; then
    diff -u "$txt1" "$txt2" | zenity --text-info  --title="$titolo" --filename=/dev/stdin
    rm -f "$txt1" "$txt2"
else # non dovrebbe arrivare mai qui, metto per sicurezza
    zenity --error  --title="$titolo" --text="Si è verificato un errore!"
    exit 1
fi
exit 0
Per utilizzare questo secondo script aprite un file vuoto con un editor di testo, copiate lo script nell'editor e salvatelo nella cartella ~/.gnome2/nautilus-scripts chiamandolo, per esempio, "Confronta PDF". Rendete quindi eseguibile lo script. Così potrete selezionare due file PDF da confrontare e, facendo clic con il tasto destro, scegliete ScriptConfronta PDF (o il nome che avete dato allo script).

Dal punto di vista tecnico, gli script presentano un aspetto un po' particolare: la funzione converti (che serve per convertire il pdf in testo ed eseguire i controlli del caso) presenta una sorta di passaggio di variabile per riferimento e non per valore. Il "trucco" sta nel passare alla funzione non il valore della variabile ma il suo nome (chi ha familiarità con altri linguaggi di programmazione potrebbe trovare ciò naturale). I dettagli possono essere visti qui: http://www.pluto.it/files/ildp/guide/abs/functions.html#DEREFERENCECL.

sabato 23 ottobre 2010

Importare in Gmail la posta inviata di un altro account

Questa volta parlerò di un problema non legato al sistema operativo GNU/Linux, però è un problema che mi ha tenuto impegnato per un po' e voglio spiegare il barbatrucco con cui l'ho risolto: importare in Gmail la posta inviata di un altro account.

In Gmail è possibile importare le email ricevute da un qualsiasi altro account di posta elettronico nel proprio account su Gmail. Se si vuole è possibile anche continuare a scaricare automaticamente la posta da altri account (al massimo 5) con la funzione chiamata Mail Fetcher. Purtroppo né l'importazione manuale della posta né Mail Fetcher importano anche le email inviate dagli altri account, come spiegato nella sezione di Risoluzione dei problemi della guida. È vero che non è possibile farlo automaticamente, ma con qualche giro sono riuscito nel mio intento. Per fare ciò ho dovuto dare un'occhiata all'organizzazione della posta su Gmail. Il barbatrucco funziona pienamente se l'account da cui si vogliono importare le email verrà aggiunto ai mittenti personalizzati.

Le mail (che normalmente sono raccolte per conversazioni, se non vi piace questa visualizzazione potete disattivarla da Impostazioni e nella scheda Generali selezionate Visualizzazione per conversazione disattivata) non sono suddivise in cartelle ma hanno delle "etichette" che vengono loro attribuite. L'uso è praticamente lo stesso, la differenza è che a una mail possono essere attribuite più etichette, è come se si trovasse in più cartelle contemporaneamente. Tutte le mail si trovano nello spazio chiamato Tutti i messaggi (il collegamento sta nella barra sulla sinistra, se non la vede premete Altre), non vengono però visualizzate le email eliminate (le quali hanno l'etichetta Cestino) e quelle segnate come spam (che hanno l'etichetta Spam). (quasi) Tutti i "gruppi" che vediamo sulla sinistra non fanno altro che raccogliere le email secondo le etichette che le sono state attribuite. Così, le email posizionate in Posta in arrivo sono semplicemente delle mail che hanno l'etichetta Posta in arrivo (per quanto possa sembrare strano, una email inviata può trovarsi in Posta in arrivo: basta che abbia la corrispondente etichetta), le eventuali email presenti in Personale, Lavoro, ecc. hanno le rispettive etichette e, come detto, una mail può anche avere più etichette contemporaneamente (e quindi comparirà in tutti i rispettivi "gruppi"). Invece le mail presenti in Posta inviata hanno come caratteristica di avere come mittente l'account in uso o uno dei mittenti personalizzati.

Vi starete chiedendo: cosa c'entra tutto questo discorso con l'importazione delle email inviate? Calma, arriviamo subito alla soluzione del problema facendo un breve riepilogo di quello che abbiamo appreso. Sappiamo che Gmail può importare dagli altri account la posta arrivata, ma non quella inviata, allora potremmo pensare di spostare nel precedente account le email inviate nella cartella di Posta in arrivo (in genere questa è una operazione possibile, naturalmente non posso spiegare i dettagli perché variano da provider a provider), ma questa operazione crea l'inconveniente che importando la posta nell'account Gmail le email inviate avranno l'etichetta Posta in arrivo e quindi si troveranno in quel gruppo (a meno che nella fase di importazione non si sia selezionata l'opzione Archivia messaggi in arrivo (Ignora Posta in arrivo)). Sappiamo però anche che per togliere una mail dalla Posta in arrivo basta rimuovere la corrispondente etichetta, niente di più semplice! Ho già specificato che il barbatrucco funziona se l'indirizzo da cui stiamo importando la posta è uno dei mittenti personalizzati, quindi le email si troveranno già automaticamente in Posta inviata, altrimenti non compariranno in questa "cartella" ma solo in Tutti i messaggi (e se questa eventualità non è un problema allora non diventa neanche necessario che l'account da cui si importa sia fra i mittenti personalizzati).

Per non dover rimuovere manualmente le etichette a ciascun messaggio inviato consiglio di suddividere l'operazione di importazione in due punti:
1) importare normalmente nell'account Gmail tutta la posta arrivata al precedente account;
2) dopo aver concluso questa operazione, spostare (nel vecchio account) le email inviate nella cartella ed eseguire nuovamente lo scaricamento in Gmail (tranquilli, le email già importate non saranno scaricate nuovamente) selezionando l'opzione Archivia messaggi in arrivo (Ignora Posta in arrivo). In questo modo, come spiegato già abbastanza bene dal nome dell'opzione, le nuove email che saranno scaricate (nella speranza che ci siano solo le email inviate e non compaia qualche nuova mail :-D ) verranno archiviate, cioè non sarà aggiunta l'etichetta Posta in arrivo.

Nel vecchio account da cui avete importato le email potete ora fare quello che vi pare: riportare le email inviate nella cartella di posta inviata, cancellarle, ecc. Ovviamente è possibile selezionare l'opzione Archivia messaggi in arrivo (Ignora Posta in arrivo) già nell'importazione di tutte le mail ricevute, in questo caso non si rende neanche necessaria una doppia importazione, si possono importare tutte insieme email ricevute e inviate (naturalmente dopo aver spostato queste ultime nella cartella di posta ricevuta). Se si utilizza Mail Fetcher, dopo aver effettuato l'importazione dei messaggi inviati potreste voler togliere l'opzione Archivia messaggi in arrivo: ricordatevi di farlo, altrimenti le nuove mail ricevute sul vecchio account non passeranno da Posta in arrivo!

Questo è il barbatrucco che ho escogitato io, non escludo che esista qualche metodo più semplice, in questo caso sarei felice di conoscerlo! :-)