Olivier Mengué – Code & rando

Aller au contenu | Aller au menu | Aller à la recherche

Code

Informatique : développement, administration, logiciels...

Fil des billets - Fil des commentaires

vendredi 12 août 2011

ARGV::URL - a new tool for Perl one-liners

I published a new module on the CPAN : ARGV::URL. At import time it'll process @ARGV to allow handling of URLs as transparently as filenames in one-liners. Here are a few examples of its power.

Show the count of fans for Delicious's user dolmen:

$ perl -MARGV::URL -MJSON -E 'say "Fans: ", (decode_json <>)->[2]->{n}' http://feeds.delicious.com/v2/json/userinfo/dolmen
Fans: 9

Show lines starting with 'ftp ' in the document at the given URL:

$ perl -MARGV::URL -nlE 'say if /^ftp /' http://www.iana.org/assignments/port-numbers
ftp              21/tcp    File Transfer [Control]
ftp              21/udp    File Transfer [Control]
ftp              21/sctp   FTP

GitHub feature request: Fast-forward my fork

Note: I'm posting this on my blog because since May 2011 GitHub is gone in submarine mode: the Google group is closed, support requests are not public anymore and users can't keep track of their past support requests. And I got no response to the last feature request I submitted.

When my repo is a fork, I would like to be able to fast-forward all the branches that exist in my repo to the latest commit on that branch in the parent repo.
The user inteface would be a single button at the left of "Pull request".
Of course, the process should fail for branches where fast-forward (as defined by git merge) is not possible. For example if I committed directly in a branch of the parent repo (instead of branching before committing) of if the parent has done dirty things with his repo. This is a merge conflict.
The process should also warn me about branches that have been added/removed in the parent repo compared to mine and give me the option to do the same (a list of branches with checkboxes to select the ones to pull/keep/delete). The default action must be conservative (not remove the local branches if they have not been merged into another branch of the parent repo ; not add branch from the remote repo which are locally missing but have been merged into another branch of the parent repo).

Github should also warn me when I'm submitting a pull request but the local branch from which I branched for my patch is not fast-forwarded. This probably means either I missed recent commits of the parent (so my patch may not be relevant anymore) or that I did the fast-forward (on a working repo not on Github) but did not push it to GitHub.

mercredi 10 août 2011

Les noms de domaine en attente à l'AFNIC

Depuis le 1er juillet les mots réservés ont été débloqués pour la réservation de noms de domaines en .fr. C'est donc la course à la réservation de mots usuels comme nom de domaine. Voici une petite visualisation des noms en attente (examen de la légitimité des demandes) à l'AFNIC :

Wordle: 2011-08-10 Les noms de domaines en attente à l'AFNIC

Et voici le code Perl d'extraction que j'ai utilisé pour transformer les données avant de les soumettre à Wordle :

#!/usr/bin/perl -nl
s/^\d+;([^;]*)\.fr;.*;En attente \/ Pending$/\1/ and print

samedi 27 septembre 2008

Playing with timezones and building my own XSLT test framework

I'm currently playing with XSLT to improve my XMLTV grabbing solution. Much fun playing with a functionnal language available in most web browsers.

I had the need to handle timezone with daylight savings to display date/times in my timezone, Europe/Paris (same as most of western Europe), which has currently the following definition (I'm not interested in history for this project):

  • Winter is UTC + 01:00
  • Summer is UTC + 02:00
  • Transition from winter to summer occurs on the last sunday of March at 01:00 UTC
  • Transition from summer to winter occurs on the last sunday of October at 01:00 UTC

So I wrote the following template date-time-Paris which is very specialized as it handles only one specific timezone, but it is very fast. I'm using only two extensions from EXSLT: date:day-in-week() and date:add().

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:tap="test:tap"
  xmlns:date="http://exslt.org/dates-and-times"
  extension-element-prefixes="date"
  >

  <!-- Transformation de l'heure UTC en heure de Paris -->
  <!-- Copyright (c) 2008 Olivier Mengué -->
  <xsl:template name="date-time-Paris">
    <!--
      Daylight savings transitions:
      - last sunday of March at 1:00 UTC
      - last sunday of October at 1:00 UTC
      Time offsets:
      - winter: +01:00
      - summer: +02:00
    -->
    <xsl:param name="date-time-Z"/><!-- UTC -->
    <xsl:variable name="len" select="string-length($date-time-Z)"/>
    <xsl:variable name="seps" select="translate($date-time-Z, '0123456789', '')"/>
    <xsl:if test="$len &lt; 11 or substring($date-time-Z, $len, 1) != 'Z' or ($seps != '--T::Z' and $seps != '--T::.Z' and $seps != '--Z' and $seps != '--TZ' and $seps != '--T:Z')">
      <xsl:message terminate="yes">date-time-Paris: date '<xsl:value-of select="$date-time-Z"/>' invalide.</xsl:message>
    </xsl:if>
    <xsl:variable name="month" select="number(substring($date-time-Z, 6, 2))"/>
    <xsl:variable name="offset">
      <xsl:choose>
        <xsl:when test="$month &lt; 3 or $month &gt; 10">1</xsl:when>
        <xsl:when test="$month &gt; 3 and $month &lt; 10">2</xsl:when>
        <xsl:otherwise>
          <xsl:variable name="transition-day" select="32 - date:day-in-week(concat(substring($date-time-Z, 1, 7), '-31Z'))"/>
          <xsl:variable name="less" select="translate(substring($date-time-Z, 8), '-T:Z', '') &lt; $transition-day*1000000+10000"/>
          <xsl:value-of select="1+number(($less and $month = 10) or (not($less) and $month = 3))"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:value-of select="concat(translate(date:add($date-time-Z, concat('PT', $offset, 'H')), 'Z', ''), '+0', $offset, ':00')"/>
  </xsl:template>

The implementation is really short, however it is quite complex as XSLT 1.0 requires to uses many tricks to workaround languages limitations (no native date type, no string order, no xor operator). Here are some tips to follow the code:

  • (a xor b) is equivalent to ((a and not(b) ) and (not(a) and b)).
  • number(true()) is 1, number(false()) is 0.
  • March and October have the same number of days: 31.
  • $transition-day*10000000+10000 for october 2008 (26010000) is an number representation of 2008-10-26T01:00:00Z.
  • the second argument to date:add() is a ISO 8601 duration. Ex: PT2H (2 hours).

In fact I present here the fourth version of the code, the first one was much longer (5x) as I was using intermediate templates for computations (last-sunday-of-month, previous-sunday). Of course the key to debug, and then optimise and refactor the implementation was to start with a good test suite. The test suite is of course written in XML and embedded in the stylesheet so it can evolve with the code.



  <tap:suite name="date-time-Paris" xmlns:t="test:date-time-Paris">
    <t:t dt="2008-09-24T21:37:52Z"     r="2008-09-24T23:37:52+02:00"/>
    <t:t dt="2008-09-24T23:37:52Z"     r="2008-09-25T01:37:52+02:00"/>
    <t:t dt="2008-09-24T23:37:52.325Z" r="2008-09-25T01:37:52.325+02:00"/>
    <t:t dt="2008-10-01T00:00:00Z"     r="2008-10-01T02:00:00+02:00"/>
    <t:t dt="2008-10-26T00:59:59Z"     r="2008-10-26T02:59:59+02:00"/>
    <t:t dt="2008-10-26T01:00:00Z"     r="2008-10-26T02:00:00+01:00"/>
    <t:t dt="2008-10-31T12:00:00Z"     r="2008-10-31T13:00:00+01:00"/>
    <t:t dt="2008-11-24T21:37:52Z"     r="2008-11-24T22:37:52+01:00"/>
    <t:t dt="2008-12-31T23:30:00Z"     r="2009-01-01T00:30:00+01:00"/>
    <t:t dt="2009-01-01T00:00:00Z"     r="2009-01-01T01:00:00+01:00"/>
    <t:t dt="2009-03-01T12:00:00Z"     r="2009-03-01T13:00:00+01:00"/>
    <t:t dt="2009-03-29T00:59:59Z"     r="2009-03-29T01:59:59+01:00"/>
    <t:t dt="2009-03-29T01:00:00Z"     r="2009-03-29T03:00:00+02:00"/>
    <t:t dt="2009-03-30T12:00:00Z"     r="2009-03-30T14:00:00+02:00"/>
    <t:t dt="2009-07-01T00:00:00Z"     r="2009-07-01T02:00:00+02:00"/>
  </tap:suite>

A more complete testsuite can be generated from a local dump of the zoneinfo database (directly works on Ubuntu Hardy, however the zdump tool is missing on RHEL/CentOS):

zdump -v Europe/Paris | perl -ne 'm/ (Mar|Oct) (\d{2}) (\d{2}:\d{2}:\d{2}) (20[0-5]\d) UTC = ... (?:Oct|Mar) (\d{2}) (\d{2}:\d{2}:\d{2}) / && do { my $mm = sprintf "%02d", 3+7*($1 eq 'Oct'); print "    <t:t dt=\"$4-$mm-$2T$3\" r=\"$4-$mm-$5T$6\"/>\n"; }'

Now, we need is a way to run the test suite. In XSLT terms, we say: "to apply a template to the test data". This template will just apply a date-time-Paris call to each of the tests.

  <xsl:template match="t:t" xmlns:t="test:date-time-Paris">
    <xsl:call-template name="tap:is">
      <xsl:with-param name="got">
        <xsl:call-template name="date-time-Paris">
          <xsl:with-param name="date-time-Z" select="@dt"/>
        </xsl:call-template>
      </xsl:with-param>
      <xsl:with-param name="expected" select="@r"/>
      <xsl:with-param name="name" select="@dt"/>
    </xsl:call-template>
  </xsl:template>

</xsl:stylesheet>

We now have a complete stylesheet that can be used as a library in other stylesheet. But we have not yet run the tests. Also you probably wonder what are this XML namespace test:tap and the template called tap:is.

test:tap is just a small XSLT test framework that I wrote and the report test results following the Test Anything Protocol that is well known by Perl programmers. tap:is is part of the test:tap API and just check for equality and reports in the TAP format.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:tap="test:tap"
  >

  <!-- Run all the tests in the test suite -->
  <xsl:template match="tap:suite">
    <xsl:message>1..<xsl:value-of select="count(node()[name()!=''])"/></xsl:message>
    <xsl:message># Suite: <xsl:value-of select="@name"/></xsl:message>
    <!-- TODO fix this expression -->
    <xsl:apply-templates select="node()[name()!='']"/>
  </xsl:template>

  <!-- Test Anything Protocol style reporting of tests -->
  <xsl:template name="tap:is">
    <xsl:param name="got"/>
    <xsl:param name="expected"/>
    <xsl:param name="name"/>
    <xsl:choose>
      <xsl:when test="$got = $expected">
        <xsl:message>ok <xsl:value-of select="position()"/> - <xsl:value-of select="concat($name, ' -&gt; ', $got)"/></xsl:message>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message>not ok <xsl:value-of select="position()"/> - <xsl:value-of select="$name"/></xsl:message>
        <xsl:message>#          got: <xsl:value-of select="$got"/></xsl:message>
        <xsl:message>#     expected: <xsl:value-of select="$expected"/></xsl:message>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

We now have two libraries. Let's add a bit more XSLT to glue them together. The main stylesheet applies tap:suite templates to all tap:suite from all imported stylesheets.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  >

  <!-- Test framework -->
  <xsl:import href="tap.xslt"/>

  <!-- All stylesheets to test -->
  <xsl:import href="date-time-Paris.xslt"/>

  <!-- Launch the tests embedded in libraires, whatever the input is -->
  <xsl:template match="/">
    <xsl:for-each select="document('')/xsl:transform/xsl:import/@href">
      <xsl:apply-templates select="document(.)//tap:suite" xmlns:tap="test:tap"/>
    </xsl:for-each>
  </xsl:template>

</xsl:transform>

We now have everything to run the test suite:

$ echo '<x/>' | xsltproc tap-run.xslt -
1..15
# Suite: date-time-Paris
ok 1 - 2008-09-24T21:37:52Z -> 2008-09-24T23:37:52+02:00
ok 2 - 2008-09-24T23:37:52Z -> 2008-09-25T01:37:52+02:00
not ok 3 - 2008-09-24T23:37:52.325Z
#          got: 2008-09-25T01:37:52.3249999999998+02:00
#     expected: 2008-09-25T01:37:52.325+02:00
ok 4 - 2008-10-01T00:00:00Z -> 2008-10-01T02:00:00+02:00
ok 5 - 2008-10-26T00:59:59Z -> 2008-10-26T02:59:59+02:00
ok 6 - 2008-10-26T01:00:00Z -> 2008-10-26T02:00:00+01:00
ok 7 - 2008-10-31T12:00:00Z -> 2008-10-31T13:00:00+01:00
ok 8 - 2008-11-24T21:37:52Z -> 2008-11-24T22:37:52+01:00
ok 9 - 2008-12-31T23:30:00Z -> 2009-01-01T00:30:00+01:00
ok 10 - 2009-01-01T00:00:00Z -> 2009-01-01T01:00:00+01:00
ok 11 - 2009-03-01T12:00:00Z -> 2009-03-01T13:00:00+01:00
ok 12 - 2009-03-29T00:59:59Z -> 2009-03-29T01:59:59+01:00
ok 13 - 2009-03-29T01:00:00Z -> 2009-03-29T03:00:00+02:00
ok 14 - 2009-03-30T12:00:00Z -> 2009-03-30T14:00:00+02:00
ok 15 - 2009-07-01T00:00:00Z -> 2009-07-01T02:00:00+02:00

So now I know that I have a failing test due to rounding occuring. For my XMLTV project it is not important as I will not have to handle decimal seconds.

samedi 6 septembre 2008

Mise à jour du site MétéoFrance, donc de Météo Mobile

Le site de MétéoFrance a été relooké. Pas pour le mieux étant donné que l'on retrouve beaucoup de problèmes du site précédent et d'autres en plus : le changement cosmétique est malheureusement bien suffisant et encore une fois mal réalisé :

  • du Flash partout : les possesseurs d'iPhone (qui n'affiche pas le flash) seront à la fête ;
  • l'activation des cookies est nécessaire pour passer la page d'accueil : inadmissible !
  • ce cookie est défini par du code JavaScript sur la page d'accueil, donc JavaScript est nécessaire ;
  • le code HTML est changé, mais toujours aussi bogué puisque ce n'est pas du XML valide (9 erreurs relevées sur le W3C Validator sur la page d'accueil) ;
  • il y a toujours autant de pubs intrusives (popups...) ;
  • les URLs sont toujours aussi abominables. Exemple : http://marine.meteofrance.com/marine/accueil?17865.path=marinecote

Cette réorganisation impacte ma passerelle de consultation des bulletins sur téléphone mobile, Météo Mobile, qui ne fonctionnait donc plus :

  • l'URL des bulletins a changé. Exemple d'un bulletin côtier : http://marine.meteofrance.com/marine/accueil?MARINE_PORTLET.path=marinecotebulletinCOTE_RAPH_MENT ;
  • il faut maintenant envoyer le cookie IntersticielPerso=done pour ne pas être redirigé vers la page d'accueil (voir le code source de la page d'accueil sans cookie) ;
  • il y a encore des problèmes de XML, différents de la version précédente, donc on ne peut toujours pas utiliser un analyseur XML pour extraire le texte du bulletin.

Voici comment j'ai testé le requêtage HTTP pour récupérer la page d'un bulletin :

C:\>curl -i --header "Cookie: IntersticielPerso=done" http://marine.meteofrance.com/marine/accueil?MARINE_PORTLET.path=marinecotebulletinCOTE_RAPH_MENT

J'ai adapté le code de récupération. L'ancien code était très simple, grâce à file_get_contents() :

$url = 'http://www.meteofrance.com/FR/mer/bulCote.jsp?LIEUID=COTE_' . $lieu;
$data = file_get_contents($url);

Pour passer le cookie, j'ai dû utiliser la libcurl ainsi :

$url = 'http://marine.meteofrance.com/marine/accueil?MARINE_PORTLET.path=marinecotebulletin/COTE_' . $lieu;

$ch = curl_init($url);

curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
// C'est ici qu'est la partie spéciale du site de MétéoFrance
curl_setopt($ch, CURLOPT_HTTPHEADER, array("Cookie: IntersticielPerso=done"));

curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_TIMEOUT, 4); // 4 secondes
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$page = curl_exec($ch);

if (curl_errno($ch)) {
    // Faire quelque chose de curl_error($ch)
    $page = '';
}
curl_close($ch);

Comme vous l'avez vu j'utilise pour l'instant les bulletins publiés sur le site web public. Mais je préfèrerais avoir un accès direct au bulletin texte brut. Je n'ai rien trouvé sur le site FTP de MétéoFrance. Alors, si vous connaissez l'URL d'une telle source, je suis intéressé.

mercredi 9 juillet 2008

Utiliser une base Access dans OpenOffice.org

Il est possible d'utiliser et de modifier sous Windows les données d'une base de données Microsoft Access sans le logiciel Microsoft Access.

Le pilote d'accès aux bases de données Access, Microsoft Jet Engine, est depuis longtemps un composant intégré à Windows. Voici comment paramétrer OpenOffice.org 2.4 sous Windows pour utiliser une base et ainsi profiter du requêteur graphique (semblable à celui d'Access), réaliser des mailings, etc.

Lire la suite...

mardi 27 mai 2008

My first Perl 6 / rakudo patch

<rakudo_svn> r27835 | pmichaud++ | [rakudo]:
<rakudo_svn> r27835 | pmichaud++ | * Add an implementation of infix:<xx>, from RT#54870 (dolmen++)
<rakudo_svn> r27835 | pmichaud++ | * Patch courtesy Olivier Mengu?\195?\169 <olivier.mengue xxatxx gmail.com>
<rakudo_svn> r27835 | pmichaud++ | * One minor change to handle negative repetition values.

dimanche 18 mai 2008

Journées Perl 2008, à Albi

Les journées Perl 2008 auront lieu à la fin du mois à Albi. Une fois de plus je ne présenterai pas de Perl. Il y a un an et demi j'avais présenté l'ORM de Django aux Journées Perl 2006. Cette fois-ci, ce ne sera pas du Python, mais du shell Unix (ksh, bash...) : ma présentation est intitulée Mieux programmer en shell. Le contenu sera fortement inspiré du guide de style non officiel d'OpenSolaris.

Mise à jour : voici les dispositives de ma présentation « Mieux programmer en shell ».

jeudi 24 janvier 2008

Formule Excel : extraire le nom de fichier d'un chemin

Voici une formule bien utile pour les administrateurs système qui manipulent des listes de noms de fichiers avec Excel. Elle permet de supprimer le répertoire d'un chemin. Par exemple, « /abc/def/ghi/truc.txt » devient « truc.txt ». A1 est la cellule contenant le texte d'origine.

=SI(ESTERREUR(TROUVE("/";A1));A1;STXT(A1;
 TROUVE(CAR(1);SUBSTITUE(A1;"/";CAR(1);
 NBCAR(A1)-NBCAR(SUBSTITUE(A1;"/";""))))+1;
 NBCAR(A1)))

Sur un Excel anglophone, ou sur Google Document et Tableur cela s'écrit (notez que CHAR(1), l'équivalent anglais de CAR(1) ne fonctionne pas sur Google Document et Tableur) :

=IF(ISERROR(FIND("/";A1));A1;MID(A1;
 FIND("¤";SUBSTITUTE(A1;"/";"¤";
 LEN(A1)-LEN(SUBSTITUTE(A1;"/";""))))+1;
 LEN(A1)))

En Perl, cela s'écrirait :

s@.*/@@;

Est-ce que quelqu'un osera dire qu'Excel c'est simple et que Perl, c'est compliqué ?

Une partie de cette formule Excel est bien utile dans d'autres cas : il s'agit de la recherche de la dernière occurence d'une chaîne.

=TROUVE(CAR(1);SUBSTITUE(A1;"/";CAR(1);
 NBCAR(A1)-NBCAR(SUBSTITUE(A1;"/";""))))

Et en anglais :

=FIND("¤";SUBSTITUTE(A1;"/";"¤";
 LEN(A1)-LEN(SUBSTITUTE(A1;"/";""))))

Un peu d'explications :

  • NBCAR(A1)-NBCAR(SUBSTITUE(A1;"/";"")) calcule le nombre d'occurences de "/" ;
  • le SUBSTITUE() qui l'entoure permet de remplacer cette occurence par un caractère spécial (ici CAR(1) est le caractère dont le code est 1) ;
  • le TROUVER() englobant permet de trouver la position de ce caractère spécial.

Et pour vous faire rire un peu, voici la formule que le support Microsoft propose (à valider en technique matricielle avec Ctrl+Maj+Entrée). Cette fois, le texte dans lequel on cherche est dans la cellule A2 pour bien distinguer de la cellule A1 utilisée dans la formule matricielle.

=STXT(A2;MAX(SI(ESTERREUR(
 CHERCHE("/";A2;LIGNE(DECALER(INDIRECT("A1");;;NBCAR(A2)))));0;
 CHERCHE("/";A2;LIGNE(DECALER(INDIRECT("A1");;;NBCAR(A2))))))+NBCAR("/");
 NBCAR(A2))

Laquelle préférez-vous ?

lundi 5 novembre 2007

A simple ksh framework prototype

Nothing is as good as a old Unix shell for running and monitoring batchs. Here is a proposition for a simple framework that will improve readabilty of the code while increasing error reporting in the output.

Lire la suite...

samedi 3 novembre 2007

Une icône pour les liens vers Wikipédia

J'ai juste ajouté une petite touche à la feuille de style de ce blog (dont j'ai déjà longuement parlé en anglais) : une petite icône pour signaler les liens vers Wikipédia.

J'ai récupéré l'icône du site en ligne de commande avec Wget :

$ wget -O wikipedia.ico http://fr.wikipedia.org/favicon.ico
--19:23:35--  http://fr.wikipedia.org/favicon.ico
           => `wikipedia.ico'
Résolution de fr.wikipedia.org... 145.97.39.155
Connexion vers fr.wikipedia.org|145.97.39.155|:80... connecté.
requête HTTP transmise, en attente de la réponse... 200 OK
Longueur: 318 [image/x-icon]

100%[====================================>] 318           --.--K/s             

19:23:35 (30.79 MB/s) - « wikipedia.ico » sauvegardé [318/318]

Je l'ai ensuite convertie à la taille 14x14 au format GIF avec l'outil convert d'ImageMagick :

$ convert wikipedia.ico -resize 14x14 wikipedia.gif

L'image résultante faisait 595 octets. L'utilisation de l'application en ligne Image Optimizer m'a permis de réduire le nombre de couleurs de l'image à 16 et de réduire la taille à 167 octets.

En suivant Megan McDermott, j'ai modifié ma feuille de style pour ajouter la ligne suivante :

Il peut être utile parfois de désactiver ce style localement ou pour les blocs de code :

pre.code a:link, pre.terminal a:link,
.no-link-icon a:link,
a:link.no-icon
{ background: inherit; padding-right: inherit }

Mise à jour 2007-11-05 à 22:25 : mes règles pour les exceptions ne fonctionnent pas ! Je me suis fait avoir par les règles de précédence des règles CSS ! Il va falloir que je corrige cela...

Mise à jour 2007-11-05 à 22:46 : voici, j'espère, la correction. Il faut que j'augmente le poids des règles d'exception en augmentant le nombre d'attributs ou de pseudo-classes dans le sélecteur.

pre.code a:link[href], pre.terminal a:link[href],
.no-link-icon a:link[href],
a:link.no-icon[href]
{ background: inherit; padding-right: inherit }

Rotation JPEG sans perte, suite

Suite à mon précédent post, j'ai profité qu'exiftool est un module Perl pour re-développer en Perl mon outil de rotation des images JPEG vraiment sans perte. Voici ci-dessous le code et les astuces pour l'intégration dans Nautilus.

Lire la suite...

dimanche 21 octobre 2007

Rotation JPEG vraiment sans perte

Avez-vous déjà examiné en détail les méta-données EXIF d'une photo numérique après une rotation soit-disant « sans perte » ? Si vous l'avez fait avec un outil tel que exiftool, vous avez sûrement du constaté la catastrophe. Certains outils de rotation perdent toutes les données EXIF, d'autres une partie des paramètres de prise de vue ou le champ Software... Et aucun ne fait la rotation des imagettes de prévisualisation, ni ne préserve la date de création originelle du fichier. J'ai essayé sous Ubuntu 7.10, F-Spot, jpegtran (paquet libjpeg-progs) et exiftran. Chacun a ses défauts.

Je vous propose donc ici la solution personnalisée que j'ai développée ce week-end en utilisant jpegtran et exiftool. Ce n'est pas forcément une solution universelle, mais elle est satisfaisante pour les photos issues de mon APN (sous réserve que mon outil d'observation

Lire la suite...

mardi 28 août 2007

TiddlyWiki and favicon

TiddlyWiki is a standalone wiki that is fully interactive and fully contained in a single HTML file. Here is a tip to add a favicon to your wiki.

To enable saving you have to give to the browser some special permissions allowing the JavaScript code to write to your disk. When using it with Microsoft Internet Explorer, the easy way to enable saving is to just rename your wiki as .hta to transform it into an "HTML Application", as Microsoft calls it.

But you can do better with HTML Applications than just decreasing the security level. I was particularly interested in a better integration with Windows. I wanted in particular to change the icon of the window that appears in the taskbar (I commonly have about 30-40 windows opened on my desktop). This is quite easy: you just have to add an <HTA:APPLICATION ICON="myfile.ico"> tag the <head> section of the HTML file.

Here is how to do it the TiddlyWiki way:

  1. Get an icon and save it as tiddlywiki.ico in the same directory as your wiki.hta file. For example: http://www.tiddlywiki.org/favicon.ico. You can also create you own icon here or here.
  2. Edit the built-in tiddler MarkupPreHead
  3. Insert the following text just after <!--{{{-->:
    <!--[if IE]>
    <HTA:APPLICATION ID="oHTA" APPLICATIONNAME="My wiki" ICON='tiddlywiki.ico'/>
    <![endif]-->
    <link rel='shortcut icon' href='tiddlywiki.ico' type='image/vnd.microsoft.icon '/>
    
  4. Reload your wiki.hta. That's it!

I now have to explore how to convert this to a plugin...

samedi 25 août 2007

Syntax highlighting on this blog using semantic tags and Vim

The regular readers of the HTML view of this blog (if they exist) may have noticed some changes in the syntax highlighting of the source code in my posts. I've tweaked the CSS style sheet to use the RubyBlue Vim theme instead of the Blue Vim theme. This is the occasion to explain you my process to format source code samples in HTML.

I'm a Vim addict, using it on Linux, AIX and Windows. Vim has a powerful and extensible syntax highlighting engine that can format almost any existing text file format. And most importantly, it has a plugin that can export the highlighted code as HTML.

There is many advantages in using Vim and storing statically highlighted code:

  • I can use the huge set of languages supported by Vim highlighting ;
  • I can use the huge set of themes built for Vim and easily convert to a CSS for the web ;
  • if a language is not supported, I can define highlighting myself (I already did it for 4 languages) and it will be done once for use both in Vim and on my blog ;
  • as I store only pure HTML as data in the blog engine (no special wiki code, no plugin), I am not dependent on the engine I'm currently using ;
  • no charge on the server (as with PHP formatting engines such as GeSHi) or on the client (such as with syntaxhighlighter) ;
  • as we are using simple <pre> tags, there is no characters/tags pollution: the reader can simply select and copy the text to the clipboard ;
  • last but not least, I can tweak the output to improve it and fix the highlighting bugs (some languages are very hard to parse).

In Vim you can invoke the conversion to HTML from the "Syntax" menu or that way:

:runtime syntax/2html.vim

To get the best XHTML code, I'm using the following settings in $HOME/.vimrc:

syntax on
" Conversion HTML (:help 2html.vim)
let g:html_use_css = 1
let g:html_use_encoding = "utf8"
let g:use_xhtml = 1

For example, here is the HTML I extract (I remove anything around the <pre> tag) from what is generated by 2html.vim from the code above:

<pre>
<span class="Comment">&quot; Conversion HTML (:help 2html.vim)</span>
<span class="Statement">let</span> g:html_use_css <span class="Operator">=</span> <span class="Constant">1</span>
<span class="Statement">let</span> g:html_use_encoding <span class="Operator">=</span> <span class="Constant">&quot;utf8&quot;</span>
<span class="Statement">let</span> g:use_xhtml <span class="Operator">=</span> <span class="Constant">1</span>
</pre>

I just have to add my own set of classes to enable highlighting:

<pre class="code vim vimft-vim">...</pre>

Here is the semantic associated to the classes:

  • code is my generic class for source code blocks 
  • vim is for source code formatted using the Vim classes for highlighting 
  • vimft-html is the class for the specific kind of source code: Vim's filetype option, displayed with ":set ft?".

For terminal output samples, I'm using my own highlighting using semantic XHTML tags :

  • <pre class="terminal">, the enclosing tag, with an optional class :
    • unix for Unix/Linux samples ;
    • cmd for Windows cmd.exe shell code.
  • <kbd> for what is typed in the terminal, with the following optional classes:
    • shell for any Unix shell samples ;
    • bash or ksh (in addition to shell) for Unix shell samples that uses features which are not in the standard POSIX shell ;
    • cmd for Windows cmd.exe shell code.
  • <samp> for programs output:
    • prompt for shell or interactive programs prompts ;
    • shell, bash, ksh or cmd for shell prompts (in addition to prompt) ;
    • sqlite, sqlite3 for SQLite client samples...
  • <var> for variable input/output. Everything except the <var> content should exactly match if you repproduce it yourself. The title can indicate what the variable represent, and on which data it depends. The tag is always a direct child of either <kbd> or <samp>.

Here is an example:

<pre class="terminal unix">
<samp class="prompt shell">$ </samp><kbd class="shell">echo Hello, world!</kbd>
<samp>Hello, world!</samp>
<samp class="prompt shell">$ </samp><kbd class="shell">date</kbd>
<samp><var>samedi 25 août 2007, 18:51:00 (UTC+0200)</var></samp>
</pre>

And the final result:

$ echo Hello, world!
Hello, world!
$ date
samedi 25 août 2007, 18:51:00 (UTC+0200)

This semantic tags will allow me to provide later additional feature using JavaScript code. I'm thinking to a button that would hide any <samp> tags and keep only <kbd> tags to ease copy of the commands to a terminal to run the commands.

With these tags in place, the CSS stylesheet is quite short and simple. More importantly it is easily replaceable in case I change the theme of the blog.

pre.code.vim,
pre.terminal { margin-left: 1pt; padding: 5pt; }

/* Text not embedded in samp or kbd will be in red, to easily detect errors */
pre.terminal { background: #000; color: #f00; }
pre.terminal samp.prompt { color: #888; }
pre.terminal samp { color: #eee; }
pre.terminal kbd { color: #fff; font-weight: bold; }
pre.terminal var { color: #55f; font-style: italic; }

/* colorscheme rubyblue */
pre.code.vim { color: #c7d4e2; background-color: #162433; }
pre.code.vim a[href] { color: #0f0; }
pre.code.vim .Constant { color: #0c0; }
pre.code.vim .Comment { color: #428bdd; }
pre.code.vim .Identifier { color: #fff; }
pre.code.vim .Label { color: #ff0; }
pre.code.vim .Operator { color: #ff0; font-weight: bold; }
pre.code.vim .PreProc { color: #f9bb00; }
pre.code.vim .Special { color: #0c0; }
pre.code.vim .Statement { color: #f9bb00; }
pre.code.vim .Title { color: #fff; font-weight: bold; }
pre.code.vim .Type { color: #fff; text-decoration: underline; }
pre.code.vim .Underlined { color: #208aff; text-decoration: underline; }

I had to tweak a bit the blog engine I use (DotClear) to add this style sheet: modifying the template.php in the theme directory is not enough because the theme is used only on the public part of the blog. So I added an @import rule in ecrire/style/default.css to enable the CSS in the private area of the blog.

 

I would be glad to read your experiences about source code formatting on your own blog or CMS.

Update 2007-09-06: added missing information about the usage of <var> tags.

mercredi 15 août 2007

Vélib’ et Yahoo! Pipes : vive XML et les services Web !

Le site Vélib’ utilise des services web pour afficher les informations sur les stations :

  • la liste statique des stations : numéro, nom, adresse, latitude, longitude ;
  • les informations dynamiques sur une station : vélos disponibles, emplacements libres, impression de tickets.

Voilà donc une occasion d'expérimenter Yahoo! Pipes, l'application web de développement et d'hébergement de scripts de traitement de données du web.

J'ai réalisé ces quelques « pipes » utilisant les services web Vélib’ :

J'ai voulu créer un pipe recherchant toutes les stations autour d'une adresse donnée. Récupérer une adresse est facile avec le tuyau Location Builder. Mais j'ai rencontré un obstacle insurmontable lorsque j'ai voulu comparer les coordonnées latitude/longitude obtenues avec celles des stations : il impossible de manipuler des nombres flottants et même de convertir du texte en nombre.

Bilan de cette utilisation de Yahoo! Pipes dans Firefox globalement positif :

  • Le concept est génial : c'est la programmation fonctionnelle appliquée aux données structurées du web !
  • L'interface d'enchaînement des tuyaux est très intuitive. Le drag'n drop est parfait : on oublie que l'on est dans une appli web et on est vraiment dans un outil de développement.
  • Le debogueur est génial ! On visualise facilement le résultat de chaque étape de la plomberie.
  • C'est bien plus facile à développer et déboguer que XSLT !

Néanmoins l'étiquette « beta » reste de rigueur :

  • l'API, c.à.d. la liste des tuyaux disponibles, n'est pas très intuitive : je n'ai pas compris la logique du classement
  • la documentation des tuyaux est inexistante : Yahoo! ne propose qu'un exemple comme forme de documentation.
  • la documentation de l'affichage de la sortie des tuyaux est aussi inexistante : les éléments title ou y:location.lat/y:location/lon sont particuliers dans l'affichage final, mais il n'existe nulle part de liste complète de ces éléments.
  • l'API est plutôt incomplète : j'aimerais pouvoir traiter des nombres flottants tels que des coordonnées (latitude, longitude)
  • les regex soit-disant « Perl-like » ne le sont pas. Par exemple \L fonctionne pour mettre en minuscules, mais pas \E. Et comme rien n'est documenté...
  • les transformations de types sont laborieuses : le concept d'« item » est nouveau par rapport à d'autres langages de programmation, mais n'est expliqué nulle part. C'est un Item builder qu'il faut utiliser pour convertir du texte en item, mais il est rangé dans la catégorie des Sources. La réorganisation de l'arbre d'un item se fait en utilisant un tuyau Rename. Et il est impossible de convertir du texte en nombre...

dimanche 12 août 2007

Stations Vélib’ : v2.0

Voici une nouvelle version de mon script de conversion des stations Vélib’ en base POI pour Maemo Mapper sur Nokia N800 (voir mon post précédent).

Parmi les améliorations :

  • un script pour automatiser le processus de transformation 
  • le renseignement du champ poi_id, ce qui corrige le problème d'édition des POI 
  • un index sur la table POI sur les champs latitude, longitude en espérant que cela améliore les temps d'accès. En pratique, je n'ai constaté aucune amélioration ;
  • 4 catégories de POI au lieu d'une seule, pour créer votre propre base des stations : Station Vélib’, Station Vélib’ corrigée, Station Vélib’ validée, Station Vélib’ erronée ;
  • des modifications d'implémentaiton XSLT : l'utilisation de xsl:foreach et un traitement exhaustif des caractères de l'adresse de la station.

Lisez la suite pour le nouveau code...

Lire la suite...

samedi 4 août 2007

Le « full utf-8 » est un mythe !

Suite à un message sur la liste des Mongueurs de Perl à propos d'encodage de caractères, j'ai fait un petit historique des jeux de caractères que j'ai utilisés depuis 1989 :

J'étais en full cp437 sous MS-DOS.

Je suis passé au cp437/cp1252 avec Windows. J'ai résisté au passage à cp850 pour garder le jeu complet de caractères semi-graphiques de cp437.

Avec Unix/Linux j'ai ajouté iso-8859-1 vers 1993.

Avec Windows NT, j'ai abandonné finalement cp437 au profit de cp850, toujours utilisé dans les fenêtres DOS.

L'euro est apparu et je suis passé à iso-8859-15 sous Linux.

Depuis Windows 2000, RegEdit 5 enregistre ses fichiers en « Unicode » (comme dit Microsoft), c'est à dire ucs-2le. C'est donc un encodage de plus que j'utilise régulièrement.

Coté Linux, j'ai abandonné iso-8859-{1,15} pour utf-8 (merci à RedHat d'avoir initié ce changement sur l'ensemble d'une distrib). Mon Nokia N800 est aussi en UTF-8. Malheureusement je bosse encore aussi sous Solaris et AIX qui ne connaissent pas UTF-8 pour les locales de base.

Les encodages de caractères que j'utilise actuellement régulièrement sont donc ASCII, utf-8, cp1252, cp850, ucs-2le et iso-8859-1.

Bref, le « full utf-8 », c'est à dire un environnement où toutes les données texte sont uniquement encodées en UTF-8, est pour moi un mythe. La compatibilité avec les anciennes applications, les anciens formats de fichiers ou les systèmes d'exploitation où UTF-8 n'est pas l'encodage primaire nécessite la manipulation de multiples encodages.

Ma solution : avoir la boîte à outils qui me permette d'éditer les fichiers de la façon la plus transparente possible (détection au chargement d'un fichier texte) et de faire facilement les transformations nécessaires. Et si on travaille sur plusieurs plate-formes, il faut que la boîte à outils soit également portable.

Ma boîte à outil, c'est Vim, avec ces paramètres :

" List of encodings for autodetect
if has('win32') || has('win64')
  set fileencodings=ucs-bom,utf-8,cp1252
else
  set fileencodings=ucs-bom,utf-8,iso88591
endif

jeudi 2 août 2007

Une carte des stations Vélib’ sur Nokia N800

Vélib’, c'est génial ! Abonné depuis deux jours et déjà accro ! Vive le vélo Plug'n Play ! Vive le vélo jetable !

Néanmoins, un outils devient vite indispensable : la carte des stations.

Je suis l'heureux propriétaire d'un Nokia N800 : c'est une « tablette Internet », avec connexions wifi, Bluetooth et un écran 800x480. Le tout fonctionne avec un système ouvert : Linux. Et ça tient dans la poche.

Le logiciel Maemo Mapper pour le N800 permet de télécharger des fonds de carte (Google Maps notamment) et de les afficher. Ces cartes peuvent aussi être récupérées dans un cache pour consultation hors-ligne. Ainsi on peut avoir tous les plans de Paris de Google Maps dans la poche, sans connexion Internet. Maemo Mapper peut aussi afficher les informations d'un GPS Bluetooth.

J'ai donc créé une base de points d'intérêt (POI) pour afficher les stations Vélib’ dans Maemo Mapper.

Mais ma source des données pour les emplacements des stations n'est pas libre : il s'agit du fichier XML publié sur le site officiel (http://www.velib.paris.fr/service/carto, voir ce forum). Je préfère donc éviter de distribuer le fichier terminé. Je vais par contre vous expliquer comment recréer vous-même ce fichier.

Photo de mon N800 avec Maemo Mapper affichant un plan de Paris avec les stations Vélib'

Lisez la suite de ce post pour voir le code...

Lire la suite...

vendredi 18 mai 2007

Sécuriser XAMPP

Quelques instructions pour sécuriser XAMPP pour un environnement de développement personnel.

Lire la suite...

- page 1 de 2