-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
1094 lines (754 loc) · 95.3 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[mind.dump()]]></title>
<link href="http://blog.mymind.fr/atom.xml" rel="self"/>
<link href="http://blog.mymind.fr/"/>
<updated>2014-01-12T14:10:23+01:00</updated>
<id>http://blog.mymind.fr/</id>
<author>
<name><![CDATA[Florent Bruneau]]></name>
</author>
<generator uri="http://octopress.org/">Octopress</generator>
<entry>
<title type="html"><![CDATA[From Dotclear to Octopress]]></title>
<link href="http://blog.mymind.fr/blog/2013/12/29/from-dotclear-to-octopress/"/>
<updated>2013-12-29T22:07:24+01:00</updated>
<id>http://blog.mymind.fr/blog/2013/12/29/from-dotclear-to-octopress</id>
<content type="html"><![CDATA[<p>After more than four years of inactivity on my personal blog
(I’ve been busy <a href="https://techtalk.intersec.com">blogging professionally</a>
in the meantime), I’ve decided to migrate my old blog to
<a href="http://octopress.org">Octopress</a>.</p>
<p>Several reasons for this. The first one is mostly that I don’t believe
<a href="http://dotclear.org">Dotclear</a> has a future anymore. At the time I chose
Dotclear over Wordpress, it was in very active development, going from 1.x
to 2.x with regular release and a very active community: it was a
promising software that could have fought side by side with wordpress.
Morevoer, it was a french software.</p>
<p>However, Dotclear is probably to much of a carbon copy of wordpress: same
technology (PHP), same admin architecture, … to make the difference.
Wordpress is backed by a dedicated compagny, and Dotclear which is made as a
hobby project just can’t catch up. As a consequence, there is no technical
reason to use dotclear over wordpress anymore: the later has a much more
active development pace, a wide ecosystem and a very active community…
Dotclear has been completely forgotten: only its own community care about
it. Tools such as Disqus don’t provide any integrated solution for dotclear,
leaving users to hack the code of their theme if they wish to integrate
that kind of service. Even tools that aims at providing migration utility
to other blogging platform don’t list Dotclear as a source platform.</p>
<p>The second reason is that I find it completely overkill to run some PHP
to serve a near-static content. My blog has not been update for 4 years, but
every time someone reads a page, it goes through some PHP. PHP may be (have
been) great for dynamic sites, but not for delivering content: it consumes
resources and may contain security hole. The sole dynamic part of the site
that is user-facing is the comment system… however nowadays services like
Disqus, Google or Facebook can be integrated in your site for this in just
a few lines of HTML/javascript (and by selling your soul to the devil):
you may have an interactive site with only static pages.</p>
<p>So, its done, my site has been migrated to a new static platform and is
hosted by github (which wasn’t mandatory but frees me from a bit of system
administration). I couldn’t find any packaged tool to perform the migration
so I wrote one <a href="https://gist.github.com/Fruneau/8174826">small script</a> by
myself. If the script is of interest for you here is how to proceed:</p>
<ul>
<li>export the database and the media library of your blog from Dotclear maintenance
page</li>
<li>unpack the media library in the <code>source/assets</code> directory of Octopress</li>
<li>run the Python script on the database export (which should be named
<code>python dotclear-octopress.py <date>-<blogname>-<backup>.txt</code> (the scripts
runs in Python and requires <code>pip install phpserialize</code>)</li>
<li>the script should have generated a new directory that contains a series of
<code>.csv</code> file and a subdirectory called <code>_posts</code> that contains the markdown
pages for your old blog posts.</li>
<li>copy the content of <code>_posts</code> to <code>source/_posts</code> of Octopress.</li>
</ul>
<p>At this point, the result is not perfect (but I’m sure the script can be improved),
you’ll still have to go through your posts and check that they were properly
<em>markdownified</em> (Dotclear source text is in a Dotclear-specific wiki syntax,
the script tries to do the conversion, but it is not 100% accurate and will
miss some corner cases).</p>
<p>The script also dump a file <code>rss.xml</code> that contains the extended RSS format that
can be used to import discussions on <a href="http://disqus.com">Disqus</a>.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Yet Another Policy Daemon for Postfix]]></title>
<link href="http://blog.mymind.fr/blog/2009/04/07/yet-another-policy-daemon-for-postfix2/"/>
<updated>2009-04-07T00:04:00+02:00</updated>
<id>http://blog.mymind.fr/blog/2009/04/07/yet-another-policy-daemon-for-postfix2</id>
<content type="html"><![CDATA[<p><a href="http://blog.madism.org/index.php/2007/08/29/136-postfix-and-srs">MadCoder announced it months ago</a>, he has been working on the pfixtools. The second tool of the <a href="http://www.postfix.com">postfix</a>-related toolsuite is named <strong>postlicyd</strong>.</p>
<p>postlicyd is a versatile <a href="http://www.postfix.org/SMTPD_POLICY_README.html">policy daemon</a> written in C. It does greylisting (far faster than postgrey), it performs R(H)BL access (both locally directly from rbldns zone files and remotely by using DNS), … So, it can be used as a replacement for <a href="http://packages.qa.debian.org/w/whitelister.html">whitelister</a> and <a href="http://postgrey.schweikert.ch/">postgrey</a> with a significant improvement of the performances.</p>
<p>On the same server (with the same email trafic), postlicyd is more than 20 times faster than postgrey:</p>
<ul>
<li>Process load: postgrey ~20% CPU, postlicyd less than 1% CPU</li>
<li>Data base cleanup for 1M entries: postgrey 20 minutes, postlicyd 40 seconds</li>
</ul>
<p>Moreover, it is aware of the ‘session’ feature of the POLICY protocol. Thus, you can write complex configurations and define policies that do not depend on a single SMTP command (like RCPT) but on the whole SMTP transaction…</p>
<p>More informations: [<a href="http://pfixtools.mymind.fr">http://pfixtools.mymind.fr</a>]</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Polytechnique.org Lance Son Blog]]></title>
<link href="http://blog.mymind.fr/blog/2008/06/04/polytechniqueorg-lance-son-blog/"/>
<updated>2008-06-04T01:33:00+02:00</updated>
<id>http://blog.mymind.fr/blog/2008/06/04/polytechniqueorg-lance-son-blog</id>
<content type="html"><![CDATA[<p>Voilà… cela fait assez longtemps que nous recevons des demandes d’utilisateurs pour la mise en place de blog via Polytechnique.org. Afin de préparer la mise en place de ce service, Polytechnique.org lance son blog. Cela permettra à la fois :</p>
<ul>
<li>de tester l’outil d’intégration de l’authentification de Polytechnique.org dans Dotclear.</li>
<li>d’offrir une nouvelle plateforme souple et conviviale pour informer nos utilisateurs.</li>
</ul>
<p>Je ne vais pas m’étendre davantage vu que ce ne serait que recopier <a href="http://blog.polytechnique.org/post/2008/06/01/Creation-du-blog-de-lequipe-Polytechniqueorg">le post d’Aymeric</a></p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Vieux Trucs]]></title>
<link href="http://blog.mymind.fr/blog/2008/05/19/vieux-trucs/"/>
<updated>2008-05-19T00:50:00+02:00</updated>
<id>http://blog.mymind.fr/blog/2008/05/19/vieux-trucs</id>
<content type="html"><![CDATA[<p>Un peu de nostalgie. Je suis retombé sur de vieux programmes que j’avais fait pour ma calculatrice (TI-92) quand j’étais en 1<sup>ère</sup>/Terminale. A mon époque (pas si lointaine… j’ai passé mon bac en 2001), ces outils couvraient le programme de Terminal S (spécialité maths) avec :</p>
<!-- more -->
<ul>
<li>En analyse et algèbre :
<ul>
<li>tableaux de signe et de variation (avec détection de la périodicité des fonctions en cas de besoin)</li>
<li>équation différentielles (de Terminal, donc <code>y' = a.y + w</code> ou <code>y" + w<sup>2</sup>.y = 0</code>)</li>
<li>division polynomiale</li>
<li>linéarisation trigonométrique</li>
<li>équations du 1<sup>er</sup> et 2<sup>nd</sup> degré</li>
<li>résolution de systèmes d’équations</li>
<li><a href="http://fr.wikipedia.org/wiki/Triangle_de_Pascal|fr">triangle de Pascal</a></li>
</ul>
</li>
<li>En géométrie :
<ul>
<li>barycentres</li>
<li>produits scalaires</li>
<li>produits vectoriels</li>
<li>calcul d’équations de droites, plans ou sphères…</li>
<li>mesure principale</li>
</ul>
</li>
<li>En arithmétique :
<ul>
<li>division euclidienne</li>
<li><a href="http://fr.wikipedia.org/wiki/%C3%89quation_diophantienne|fr">équation diophantienne</a>, algorithme d’Euclide et coefficient de Bezout…</li>
<li>factorisation et diviseurs des entiers</li>
</ul>
</li>
<li>…</li>
</ul>
<p>Le tout avec une interface permettant de passer d’un programme à l’autre simplement. Histoire que tout ceci ne se perde pas, je les met à disposition (ils sont également trouvables sur diverses banques de programmes pour TI) :</p>
<p><a href="http://blog.mymind.fr/public/misc/maths3.zip|fr">Télécharger le programme de maths niveau Terminal pour TI-92</a></p>
<p>Evidemment, ces programmes ne dispensent pas d’apprendre le cours pour passer le Bac.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Encodage Et Terminal]]></title>
<link href="http://blog.mymind.fr/blog/2008/03/02/encodage-et-terminal/"/>
<updated>2008-03-02T12:57:00+01:00</updated>
<id>http://blog.mymind.fr/blog/2008/03/02/encodage-et-terminal</id>
<content type="html"><![CDATA[<p>Beaucoup de personnes avec qui je discute sur IRC ont des problèmes avec l’encodage de leur terminal, de leur shell, de leur irssi, ou de tout autre logiciel en “ligne de commande”. Comme j’en ai un peu marre d’expliquer la même chose toutes les semaines, voici une petite mise au point sur les réglages à faire pour travailler efficacement en ligne de commande.</p>
<!-- more -->
<h2>Introduction</h2>
<p>Le point le plus important à mémoriser est que pour avoir une console avec un encodage spécifique il faut que plusieurs couches de logiciels utilisent le même encodage. Prenons le cas simple où nous avons un shell dans un terminal.</p>
<p>Le terminal est un logiciel qui ne fait que convertir les entrées de l’utilisateur vers des entrées compréhensibles par le logiciel qu’il fait tourner, et qui interprète la sortie de ce logiciel pour qu’elle soit lisible par l’utilisateur. Donc pour que le terminal affiche correctement ce qu’indique le shell, il faut qu’il utilise le même encodage que celui-ci. Si par exemple mon shell écrit de l’UTF-8 alors que mon terminal attend du latin1, les caractères multi-octets de l’UTF-8 seront mal interprétés et afficheront un caractère par octet. Ainsi un “é” en utf8 s’affiche comme un “é” en latin1.</p>
<p>Pour l’entrée de l’utilisateur, c’est la même chose. Si mon terminal est en latin1 et mon shell en utf8, si j’entre un “é” dans mon terminal, celui-ci sera passé à mon terminal comme un seul octet (de valeur 0xE9). Or mon shell attend de l’utf8 et lorsqu’il reçoit un 0xE9, il considère que je viens d’entrer un octet d’un caractère multi-octets… les prochains caractères que j’entrerais seront donc ajoutés à mon 0xE9 jusqu’à ce que mon shell considère que j’ai entré un caractère… assez embêtant.</p>
<p>Il faut donc faire extrêmement attention à choisir un encodage unique compatible avec son shell et son terminal (et les logiciels qu’on compte utiliser).</p>
<h2>Terminal</h2>
<p>Le réglage du terminal dépend exclusivement du logiciel en question. Certains terminaux permettent de choisir son encodage, d’autres non. Cela peut donc être un bon critère pour choisir son logiciel. Pour utiliser l’UTF-8, vous pouvez prendre Konsole, urxvt, le Terminal de Mac OS X…</p>
<h2>Shell</h2>
<p>Le réglage du shell se fait par des variables d’environnement qu’on appelle couramment les “locales”. Le réglage courant est accessible en tapant “locale” dans le shell. Un certain nombre de variables sont concernées :</p>
<ul>
<li>gestion de la langue du shell (et des programmes liés)</li>
<li>gestion des formats (dates, monnaies, virgules…)</li>
<li>LC_ALL est un “fallback” (l’encodage par défaut si aucun autre n’a été défini pour une variable donnée).</li>
</ul>
<p>Chaque variable est de la forme xx_YY.ZZ. xx_YY défini la zone géographique et la langue (fr_FR pour le français, en_US pour l’anglais-US). ZZ défini l’encodage. Il est important que toutes les variables utilisent le même encodage (mais elles peuvent avoir des langues différentes). Pour faire de l’UTF-8 pur, un bon choix est d’exporter LC_ALL=“en_US.UTF-8” dans la configuration du shell (ou fr_FR.UTF-8 pour ceux qui veulent un shell en français).</p>
<p>Il faut par contre faire attention, l’UTF-8 n’est pas supporté par tous les shells (à partir de 4.3 pour zsh par exemple).</p>
<h2>Irssi</h2>
<p>Pour irssi, il y a un point supplémentaire à prendre en compte : l’encodage du réseau. Ainsi si j’ai un channel en latin1 à afficher en utf8 (et sur lequel je veux poster). Il faut donc que irssi fasse de transcodage à la volée… et bien sûr il faut lui dire ce qu’il faut faire.</p>
<p>Tout d’abord il faut lui indiquer l’encodage du terminal. Pour ceci il y a la variable term_charset :</p>
<pre><code>settings = {
"fe-common/core" = {
term_charset = "UTF-8";
};
};
</code></pre>
<p>Ensuite, il faut indiquer pour chaque réseau (ou channel) l’encodage du réseau. Ce n’est en fait nécessaire que si l’encodage est différent de celui du terminal, mais ça ne coûte rien de le spécifier pour chaque réseau. Ainsi je suis sur RezoSup (qui est en latin1) et sur FreeNode (qui est en utf8), il me suffit d’ajouter :</p>
<pre><code>settings = {
core = {
recode = "yes";
};
};
conversions = {
FreeNode = "UTF-8";
Rezosup = "ISO-8859-1";
};
</code></pre>
<p>Les entrées “FreeNode” et “Rezosup” doivent reprendre le nom du chatnet donné dans la section “servers” de la configuration. Pour spécifier l’encodage spécifique à un channel, il suffit de mettre <code>"Chatnet/#channel" = "encoding";</code></p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Afficher Des Discussions]]></title>
<link href="http://blog.mymind.fr/blog/2007/11/01/afficher-des-discussions/"/>
<updated>2007-11-01T18:24:00+01:00</updated>
<id>http://blog.mymind.fr/blog/2007/11/01/afficher-des-discussions</id>
<content type="html"><![CDATA[<p>Lorsqu’un logiciel a pour vocation d’afficher des discussions, on attend de sa part qu’il nous permette de voir simplement qui répond à qui, dans quel contexte… Ce n’est pas toujours ce qui est le mieux fait. Par exemple, les programmes de fora en ligne à la mode (phpBB par exemple) affiche les discussion comme une succession de rectangles juxtaposés et seul le contenu du message permet de voir qu’il en cite un autre. D’autres logiciels comme Mail.app ont ce défaut et parfois la fâcheuse manie de ne pas vouloir corriger ce problème.</p>
<p>L’affichage de l’arborescence dans <a href="http://opensource.polytechnique.org/banana">Banana</a> est une des fonctionnalités clés… et elle va beaucoup changer dans la prochaine version.</p>
<!-- more -->
<h1> Une ligne par entrée</h1>
<p>La solution habituelle pour afficher l’arborescence est d’utiliser un message par ligne de telle, des + ou – pour ouvrir ou fermer les noeuds. C’est la solution actuelle de banana.</p>
<p><img src="http://blog.mymind.fr/assets/screenshots/old-thread.png" alt="Banana Thread up to 1.7" /></p>
<p>Avec cette solution, on perd très rapidement en lisibilité : dès que la discussion dépasse une vingtaine de messages, l’arborescence devient très haute et plus ça va, plus le titre dérive vers la droite rendant parfois le lien inaccessible. Lorsqu’il y a un troll, il est de fait très courant que certains nouveaux messages se trouvent perdus plusieurs pages en arrière dans l’arborescence, ou que sur certains navigateurs, il soit difficile d’y accéder. De plus l’interface se trouve souvent surchargée, à la limite de la lisibilité : c’est dur de faire tenir un maximum d’informations en un minimum de place en gardant la lisibilité de l’ensemble.</p>
<h1> Une solution plus visuelle</h1>
<p>Je ne sais pas combien de personnes connaissent <a href="http://home.snafu.de/stk/macsoup/">MacSoup</a>. Il s’agit d’un petit client NNTP pour MacOS, qui en soit n’a pas beaucoup d’intérêt (il est payant et est relativement limité). Le principal atout de MacSoup est son interface de visualisation des threads (les utilisateurs diront qu’il y a bien plus que l’interface graphique, mais également l’interface clavier etc…). On trouve sur internet quelques captures d’écran en cherchant dans les <a href="http://www.exalead.com/image/results?q=macsoup%20screenshot">moteurs de recherche d’image</a> :</p>
<p>((<a href="http://www.fen-net.de/~xx511/bilder/macsoup/Thread.gif">http://www.fen-net.de/~xx511/bilder/macsoup/Thread.gif</a>))</p>
<p>Cette interface est compacte, visuelle et permet d’accéder rapidement à n’importe quel message du thread. Pour la prochaine version de Banana, je me suis fortement inspiré de cette interface pour réécrire de 0 l’affichage de l’arborescence. Ceci donne :</p>
<p><img src="http://blog.mymind.fr/assets/screenshots/new-thread.png" alt="Thread view in Banana" /></p>
<p>Il s’agit de la même discussion que précédemment. On voit donc ici facilement l’arborescence. Lorsqu’un message est non lu, la branche à laquelle il est attaché est noire au lieu de grise ce qui permet de l’identifier du premier coup d’oeil. Les couleurs de fond des noeuds (une idée de <a href="http://www.falco.bz">Falco</a>) sont obtenue à partir d’un hash quelconque sur l’émetteur et permettent donc d’identifier les messages envoyés par la même personne. Lorsqu’on laisse la souris sur un noeud, le nom de l’expéditeur et l’heure du post s’affichent (malheureusement le pointeur de la souris n’apparaît pas sur la capture d’écran)… et bien sûr quand on clique sur un noeud, on va sur le message correspondant.</p>
<p>Lorsqu’on est sur un message, on garde également la vue du thread ce qui permet de toujours savoir où on est dans la discussion :</p>
<p><img src="http://blog.mymind.fr/assets/screenshots/new-thread-nav.png" alt="Thread view with selected message" /></p>
<p>Il y a encore un peu de travail à faire pour améliorer les performances de la génération des arbres et pour augmenter sa compacité (éviter les branches qui descendent très bas alors qu’elles auraient pu trouver leur place dans le l’espace vide disponible).</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[15 000]]></title>
<link href="http://blog.mymind.fr/blog/2007/10/08/15-000/"/>
<updated>2007-10-08T00:32:00+02:00</updated>
<id>http://blog.mymind.fr/blog/2007/10/08/15-000</id>
<content type="html"><![CDATA[<p>Ca y est… 15 000 inscrits à <a href="https://www.polytechnique.org">Polytechnique.org</a>.</p>
<p><img src="http://blog.mymind.fr/assets/screenshots/15.000.png" alt="15.000 inscrits" /></p>
<p>Si l’ascension continue, nous devrions atteindre les 16 000 l’année prochaine.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Compter Les Fichiers]]></title>
<link href="http://blog.mymind.fr/blog/2007/08/31/compter-les-fichiers/"/>
<updated>2007-08-31T01:12:00+02:00</updated>
<id>http://blog.mymind.fr/blog/2007/08/31/compter-les-fichiers</id>
<content type="html"><![CDATA[<p>C’est un peu la suite de mon post “Outils pratiques” où je donnais deux scripts permettant de rendre les commandes SVN plus conviviales. Encore une fois, je réinvente sans doute la roue (des outils équivalents doivent déjà exister… sans doute en mieux), mais je pense que chercher ce genre d’outils sur internet m’aurait pris plus de temps que ce qu’il m’a fallu pour le développer.</p>
<p>En ce moment je manipule des fichiers, beaucoup de fichiers (et même, beaucoup de gros fichiers), que j’ouvre, rouvre, et ferme et puis referme. Et à force d’ouvrir, on oublie parfois de refermer, et là, c’est comme une fuite de mémoire, sauf que le nombre limite de fichiers ouverts est beaucoup plus rapidement atteinte que la limite de mémoire… dans la configuration de base sur un linux, un programme n’a le droit qu’à 1024 descripteurs de fichiers. D’où mon problème : comment traquer les “file-handle leaks” ?</p>
<!-- more -->
<p>Pour le faire, je me suis fait rapidement un petit script qui permet d’analyser les données issues d’un <code>strace</code>. <code>strace</code> est, pour ceux qui ne le savent pas, un programme très pratique qui permet de lister les appels systèmes. Dans mon cas présents, surveiller les ouvertures fermetures de fichiers revient à traquer les commandes <code>open</code> (ouverture d’un fichier), et les commandes <code>close</code>. Donc, je récupère simplement la liste des appels à <code>open</code> et <code>close</code> :</p>
<div class="highlight"><pre><code class="bash">strace -f -e <span class="nv">trace</span><span class="o">=</span>open,close mon programme 2> /quelque/part
</code></pre></div>
<p>Et ainsi, le fichier <code>/quelque/part</code> contient la liste complète des appels à <code>open</code> et <code>close</code> effectués par mon programme (et ses processus fils). Il ne reste plus alors qu’à analyser le contenu de <code>/quelque/part</code>. Pour ceci, il suffit de peu de lignes de code (en perl pour ma part, mais d’autres auraient fait la même chose en shell, python… ou n’importe quel langage de scripting) :</p>
<div class="highlight"><pre><code class="perl"><span class="c1">#!/usr/bin/perl </span>
<span class="k">my</span> <span class="nv">%files</span><span class="p">;</span>
<span class="k">my</span> <span class="nv">%modes</span><span class="p">;</span>
<span class="k">my</span> <span class="nv">%lines</span><span class="p">;</span>
<span class="k">my</span> <span class="nv">$lineNb</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">my</span> <span class="nv">$maxOpened</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">my</span> <span class="nv">$currentOpened</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">my</span> <span class="nv">$totalOpened</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">for</span> <span class="nv">$line</span> <span class="p">(</span><span class="sr"><STDIN></span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nv">$line</span> <span class="o">=~</span><span class="sr"> /open\\("([^""]+)", ([^\\)]+)\\)\\s*=\\s*(\\d+)/</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$files</span><span class="p">{</span><span class="nv">$3</span><span class="p">}</span> <span class="o">=</span> <span class="nv">$1</span><span class="p">;</span>
<span class="nv">$modes</span><span class="p">{</span><span class="nv">$3</span><span class="p">}</span> <span class="o">=</span> <span class="nv">$2</span><span class="p">;</span>
<span class="nv">$lines</span><span class="p">{</span><span class="nv">$3</span><span class="p">}</span> <span class="o">=</span> <span class="nv">$lineNb</span><span class="p">;</span>
<span class="nv">$totalOpened</span><span class="o">++</span><span class="p">;</span>
<span class="nv">$currentOpened</span><span class="o">++</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nv">$currentOpened</span> <span class="o">></span> <span class="nv">$maxOpened</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$maxOpened</span> <span class="o">=</span> <span class="nv">$currentOpened</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nv">$line</span> <span class="o">=~</span><span class="sr"> /close\\((\\d+)\\)/</span> <span class="o">&&</span> <span class="nv">$files</span><span class="p">{</span><span class="nv">$1</span><span class="p">}</span> <span class="ow">ne</span> <span class="s">''</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$files</span><span class="p">{</span><span class="nv">$1</span><span class="p">}</span> <span class="o">=</span> <span class="s">''</span><span class="p">;</span>
<span class="nv">$currentOpened</span><span class="o">--</span><span class="p">;</span>
<span class="p">}</span>
<span class="nv">$lineNb</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">print</span> <span class="s">"$totalOpened files opened, max. $maxOpened at the same time</span>
<span class="s">"</span><span class="p">;</span>
<span class="k">print</span> <span class="s">"$currentOpened files not closed</span>
<span class="s">"</span><span class="p">;</span>
<span class="k">for</span> <span class="nv">$id</span> <span class="p">(</span><span class="nb">keys</span> <span class="nv">%files</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">local</span> <span class="nv">$file</span> <span class="o">=</span> <span class="nv">$files</span><span class="p">{</span><span class="nv">$id</span><span class="p">};</span>
<span class="nb">local</span> <span class="nv">$mode</span> <span class="o">=</span> <span class="nv">$modes</span><span class="p">{</span><span class="nv">$id</span><span class="p">};</span>
<span class="nb">local</span> <span class="nv">$line</span> <span class="o">=</span> <span class="nv">$lines</span><span class="p">{</span><span class="nv">$id</span><span class="p">};</span>
<span class="k">if</span> <span class="p">(</span><span class="nv">$file</span> <span class="ow">ne</span> <span class="s">''</span><span class="p">)</span> <span class="p">{</span>
<span class="k">print</span> <span class="s">"[line $line] id=$id, open $file with mode $mode</span>
<span class="s">"</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>Pour simplifier le tout, on rajoute une fonction dans le <code>zshrc</code> pour wrapper tout ça, et ça donne (attention, ceci ne fonctionne que sous linux, <code>mktemp</code> n’a pas la même syntaxe sur MacOS, et surtout, <code>strace</code> n’est pas disponible sur Mac</p>
<div class="highlight"><pre><code class="bash"><span class="k">function </span>checkFiles<span class="o">()</span> <span class="o">{</span>
<span class="nv">TEMPFILE</span><span class="o">=</span><span class="sb">`</span>mktemp<span class="sb">`</span>
strace -f -e <span class="nv">trace</span><span class="o">=</span>open,close <span class="nv">$*</span> 2> <span class="nv">$TEMPFILE</span>
cat <span class="nv">$TEMPFILE</span> | ~/.zsh/trackFiles.pl
rm <span class="nv">$TEMPFILE</span>
<span class="o">}</span>
</code></pre></div>
<p>Avec, ça, il ne me répond :</p>
<pre><code>% checkFiles cp test test2
12 files opened, max. 2 at the same time
0 files not closed
</code></pre>
<p>Si maintenant, je fais un programme minimaliste qui oublie de fermer un fichier :</p>
<div class="highlight"><pre><code class="c"><span class="cp">#include <stdio.h> </span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="kt">FILE</span><span class="o">*</span> <span class="n">file</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="s">"test"</span><span class="p">,</span> <span class="s">"r"</span><span class="p">);</span>
<span class="k">return</span> <span class="n">file</span> <span class="o">!=</span> <span class="nb">NULL</span> <span class="o">?</span> <span class="mi">0</span> <span class="o">:</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<pre><code>% gcc test.c -o tester
% checkFiles ./tester
3 files opened, max. 1 at the same time
1 files not closed
[line 4] id=3, open test with mode O_RDONLY
</code></pre>
<p>Voilà, maintenant je sais que mon programme oublie de fermer un fichier, que ce fichier s’appelle “test”, et qu’il est ouvert en read-only. La ligne “4” est la ligne dans la sortie de <code>strace</code>, et n’a aucun rapport avec la ligne 4 du fichier source (contrairement aux apparences).</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Templates en C]]></title>
<link href="http://blog.mymind.fr/blog/2007/08/17/templates-en-c/"/>
<updated>2007-08-17T00:53:00+02:00</updated>
<id>http://blog.mymind.fr/blog/2007/08/17/templates-en-c</id>
<content type="html"><![CDATA[<p>En C++, il existe un mécanisme extrêmement pratique pour généré du code générique : les templates. Une fonction templatée est une fonction dont le code comporte un trou qui sera remplacé à la compilation par le nom d’un type, ou une valeur… Par exemple :</p>
<pre><code>template <class T>
T read(const char *buffer)
{
T val;
memcpy(&val, buffer, sizeof(T));
return val;
}
</code></pre>
<p>Cette fonction lit un objet de type T sur un buffer. L’intérêt de cette fonction est très compréhensible : quel que soit le type qu’on fournit à la fonction, elle va fonctionner, en adaptant la taille à lire au type. C’est donc beaucoup plus rapide que d’écrire une fonction pour chaque type… et l’utilisation est également très simple :</p>
<pre><code>read<int>(const char* buffer) // lit un entier sur le buffer
read<double>(const char* buffer) // lit un double sur le buffer
read<MaClass>(const char* buffer) // lit un objet de type "MaClass"
</code></pre>
<p>Mais cette syntaxe n’est qu’un sucre syntaxique, car en fait, on peut également faire des templates en C…</p>
<!-- more -->
<h2> Comment fonctionne les templates ?</h2>
<p>En fait un template est, comme son nom l’indique, qu’un modèle de fonction. Lors de la compilation d’un programme qui utilise des templates, le compilateur regarde la liste des instances de cette fonction qui sont utilisées et les génère. Plus explicitement, si j’appelle read<double>(), le compilateur va générer cette fonction :</p>
<div class="highlight"><pre><code class="cpp"><span class="kt">double</span> <span class="n">read</span><span class="o"><</span><span class="kt">double</span><span class="o">></span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">buffer</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">double</span> <span class="n">val</span><span class="p">;</span>
<span class="n">memcpy</span><span class="p">(</span><span class="o">&</span><span class="n">val</span><span class="p">,</span> <span class="n">buffer</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">double</span><span class="p">));</span>
<span class="k">return</span> <span class="n">val</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>Ainsi, l’appel à read<double> devient un appel de fonction classique.</p>
<h2> Jeu de préprocesseur</h2>
<p>Le préprocesseur en C possède des outils sympathique… Nous nous attarderons particulièrement sur le ##. Il s’agit tout simplement d’un opérateur de concaténation. Donc :</p>
<div class="highlight"><pre><code class="c"><span class="cp">#define Truc(Machin) Truc ## Machin</span>
<span class="n">Truc</span><span class="p">(</span><span class="n">Bidule</span><span class="p">)</span> <span class="cm">/* génère TrucBidule */</span>
</code></pre></div>
<p>C’est donc très pratique. On peut facilement par exemple, imaginer une petite macro du genre :</p>
<div class="highlight"><pre><code class="c"><span class="cp">#define maFonction(Type) maFonction_ ## Type</span>
</code></pre></div>
<p>Si dans ce cas, on défini par exemple maFonction_int, maFonction_double, maFonction_MaStruct (oui, les classes n’existent pas en C), on pourra faire</p>
<div class="highlight"><pre><code class="c"><span class="n">maFonction</span><span class="p">(</span><span class="kt">int</span><span class="p">)(</span><span class="n">args</span><span class="p">);</span> <span class="cm">/* appelle maFonction_int */</span>
<span class="n">maFonction</span><span class="p">(</span><span class="kt">double</span><span class="p">)(</span><span class="n">args</span><span class="p">);</span> <span class="cm">/* appelle maFonction_double */</span>
<span class="n">maFonction</span><span class="p">(</span><span class="n">MaStruct</span><span class="p">)(</span><span class="n">args</span><span class="p">);</span> <span class="cm">/* appelle maFonction_MaStruct */</span>
</code></pre></div>
<p>Maintenant, il ne reste plus qu’à générer les fonctions… sans avoir à toutes les écrire une à une. Pour ceci, nous allons encore une fois profiter de la présence du préprocesseur.</p>
<div class="highlight"><pre><code class="c"><span class="cp">#define maFonctionBuild(Type) \\</span>
<span class="cp"> Type maFonction_ ## Type(const char* buffer) { \\</span>
<span class="cp"> Type val; \\</span>
<span class="cp"> memcpy(&val, buffer, sizeof(Type)); \\</span>
<span class="cp"> return val; \\</span>
<span class="cp"> } </span>
<span class="n">maFonctionBuild</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span> <span class="cm">/* génère maFonction_int */</span>
<span class="n">maFonctionBuild</span><span class="p">(</span><span class="kt">double</span><span class="p">)</span> <span class="cm">/* génère maFonction_double */</span>
<span class="n">maFonctionBuild</span><span class="p">(</span><span class="n">MaStruct</span><span class="p">)</span> <span class="cm">/* génère maFonction_MaStruct */</span>
</code></pre></div>
<p>Il ne reste donc qu’à appeler maFonctionBuild(Type) sur chacun des types pour lesquels nous avons besoin d’instancier la fonction, et d’appeler maFonction(Type) chaque fois qu’on veut appeler maFonction pour le type en question. D’une certaine manière on peut dès lors dire que les fonctions sont :</p>
<div class="highlight"><pre><code class="c"><span class="n">Type</span> <span class="nf">maFonction</span><span class="p">(</span><span class="n">Type</span><span class="p">)(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">buffer</span><span class="p">);</span>
</code></pre></div>
<h2> Et voilà !</h2>
<p>Nous avons donc une implémentation générique grâce au préprocesseur associé à un appel typé. Ce n’est évidemment qu’une astuce de préprocesseur, mais cela donne une souplesse syntaxique agréalble : “J’appelle maFonction appliquée sur les entiers avec les arguments args”.</p>
<p>Tout ceci est certes, moins souple que les templates C++ :</p>
<ul>
<li>il n’y a pas d’auto-instanciation par le compilateur : il faut “manuellement” instancier la fonction pour tous les types pour lesquels ont l’appelle</li>
<li>il n’y a pas de “type check” : les seules limites sont celle de la compilation</li>
<li>le correction des erreurs de compilation est fastidieuse car toutes les erreurs apparaissent comme étant à la ligne d’appel à maFonctionBuild</li>
<li>il est rapidement fastidieux de rajouter l’antislash à la fin de chaque ligne de la définition de la fonction</li>
</ul>
<p>Comme on le voit dans les choix de l’exemple, pour les entrées/sorties, ça permet d’avoir une seule fonction à écrire tout en gardant la flexibilité d’une fonction par type de données.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Comprendre Du Code]]></title>
<link href="http://blog.mymind.fr/blog/2007/08/15/comprendre-du-code/"/>
<updated>2007-08-15T03:28:00+02:00</updated>
<id>http://blog.mymind.fr/blog/2007/08/15/comprendre-du-code</id>
<content type="html"><![CDATA[<p>J’ai tenté cet après-midi une petite expérience sur IRC. J’ai posté 3 lignes de code en demandant “que font ces trois lignes”. Après quelques minutes (sans doute trop peu), j’ai donné la solution car personne n’avait vraiment trouvé. En effet, même si on réussi facilement à lire le code et à reconstituer la suite d’opération qu’il génère, il est très difficile de vraiment comprendre ce qu’il fait, d’imaginer son application dans un contexte et d’en déduire son usage.</p>
<p>Juste pour continuer à m’amuser, et pour amuser ceux qui se perdraient ici, voici le code en question.</p>
<!-- more -->
<div class="highlight"><pre><code class="c"><span class="k">do</span> <span class="p">{</span>
<span class="o">++</span><span class="p">(</span><span class="o">*</span><span class="n">pos</span><span class="p">);</span>
<span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="n">pos</span><span class="o">++</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
</code></pre></div>
<p>Vous pouvez remarquer dès à présent que ce code n’est pas volontairement rendu illisible (il serait facile de supprimer des parenthèses et de changer le test de nullité en une négation. Le but est donc de comprendre la suite d’instruction, de dire ce qu’elle fait et ensuite de comprendre dans quel contexte elle est utilisable. La question est aussi de savoir en combien de temps vous allez trouver la solution.</p>
<script type="text/javascript" src="http://blog.mymind.fr/jquery.js"></script>
<script type="text/javascript">//<![CDATA[
function show_answer() {
$("#show_answer").hide();
$("#answer").show("slow");
return false;
}
//]]></script>
<p>
<a href="" onclick="return show_answer()" id="show_answer">Cliquer ici pour afficher la répondre</a>
</p>
<div id="answer" style="display: none">
Fonctionnement
—————
Si on lit le code, on voit qu’il fait la suite d’opération suivante :
1 on incrémente la valeur pointée par pos
1 on regarde si la valeur obtenue est nulle
1 on incrémente le pointeur pos
1 si le test 2 est vrai, on retourne en 1, sinon, on quitte la boucle
Il s’agit donc d’une petite boucle qui incrémente des objets successifs jusqu’à ce qu’elle en trouve un qui, une fois incrémenté, n’est pas nul.
Voilà donc la solution du pauvre au problème, celle qu’on peut trouver en lisant le code, sans chercher à le comprendre.
Interprétation
—————-
Mais en fait, ce code va plus loin. Imaginons maintenant la zone mémoire sur laquelle pos pointe initialement :
oct1 oct2 oct3 oct4
^
pos
L’octet 1 à une certaine valeur. Je l’incrémente et je m’aperçois que sa nouvelle valeur est 0. Quand un octet incrémenté peut-il retomber à 0 ? Quand il fait un overflow. On a donc fait déborder le premier octet… et quand cet octet déborde on incrémente le suivant :
_+1__
| |
oct1 oct2 oct3 oct4
^
pos
Il s’agit donc ni plus ni moins que d’une retenue (comme quand on apprend à faire les additions ou les multiplications) : quand je fais déborder un octet, je déplace le surplus vers le suivant. Finalement, ces 3 lignes de code ne sont que l’implémentation d’une incrémentation, mais sur plusieurs octets… et même sur un nombre non défini d’octets (puisqu’il n’y a pas de limite explicitée sur le nombre d’étapes autorisées).
Pour être plus précis, il s’agit d’un incrémenteur d’entier stocké en Little Endian sur un nombre illimité d’octets.
Encore ?
———
Bon, pour ceux qui tiennent vraiment à s’assurer que ce n’est pas du code illisible, voici une version vraiment illisible de la même machine :
<div class="highlight"><pre><code class="c"><span class="k">for</span><span class="p">(</span><span class="o">++*</span><span class="n">pos</span><span class="p">;</span><span class="o">!*</span><span class="n">pos</span><span class="o">++</span><span class="p">;</span><span class="o">++*</span><span class="n">pos</span><span class="p">);</span>
</code></pre></div>
(Merci Falco)
Tout ceci n’était bien sûr qu’un exemple. Il est toujours très dur de comprendre du code, alors pour éviter de passer dix minutes sur 3 lignes lors de la prochaine relecture il suffit de documenter ce qu’on écrit.
</div>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[BlueScreen]]></title>
<link href="http://blog.mymind.fr/blog/2007/07/28/bluescreen/"/>
<updated>2007-07-28T23:15:00+02:00</updated>
<id>http://blog.mymind.fr/blog/2007/07/28/bluescreen</id>
<content type="html"><![CDATA[<p>Juste parce que je trouve ça amusant : lorsque Vista plante en raison d’une erreur sérieuse, il fait un écran bleu (exactement le même que sous Windows XP). Ce qui est amusant, c’est le message affiché lors du démarrage suivant de Windows.</p>
<p><img src="http://blog.mymind.fr/assets/screenshots/BlueScreen.png" alt="Vista-bluescreen" /></p>
<p>Windows récupère donc d’une erreur de type “BlueScreen”… cela a l’intérêt d’être précis. En temps normal, on identifie une erreur à sa cause, et non à sa conséquence.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Mail.app, IMAP Et Signatures...]]></title>
<link href="http://blog.mymind.fr/blog/2007/06/12/mailapp-imap-et-signatures/"/>
<updated>2007-06-12T01:24:00+02:00</updated>
<id>http://blog.mymind.fr/blog/2007/06/12/mailapp-imap-et-signatures</id>
<content type="html"><![CDATA[<p>Les temps sont durs pour les utilisateurs de IMAP… jusqu’à présent je n’ai pas trouvé de client idéal pour travailler avec ce protocole :</p>
<ul>
<li>Kmail plante lamentablement</li>
<li>Mail.app se synchronise mal</li>
<li>Outlook Express (et le tout nouveau Windows Mail) n’arrive pas à lister tous mes répertoires</li>
<li>Thunderbird rame comme un malheureux et me crée des répertoires que je ne veux pas avant de m’avoir demandé quoi que ce soit</li>
<li>mutt est trop casse-pied à configurer à mon goût (même si pratique parfois)</li>
</ul>
<p>De fait, j’utilise le moins pire que j’ai trouvé, et qui en plus a le mérite de ne pas être trop lourd, bien intégré avec MacOS X, et qui, jusqu’à présent, n’a pas trop fait de bêtises sur mon IMAP. Ce client, pourtant pas réputé pour sa qualité, c’est évidemment Mail.app.</p>
<!-- more -->
<p>Un gros défauts de Mail.app (une fois configuré pour éviter toutes les fonctions décoratives qui ne servent à rien), est qu’il ne gère pas nativement PGP. Il faut donc, pour profiter de la vérification des mails mais également pour signer ses mails, installer GPGMail, un petit plug-in qui fonctionne, on va dire, pas trop mal.</p>
<p>En tout cas, GPGMail a toujours fonctionné correctement sur mon portable, mais depuis que celui-ci est en réparation (hum, d’ailleurs, j’aimerais bien savoir ce qu’il devient !), je suis obligé de travailler sur le fixe, et donc d’utiliser entre autre mon client mail sur le fixe. Malheureusement, sur mon fixe, GPGMail n’a <strong>jamais</strong> réussi à vérifier une signature. Bizarre… les mêmes messages étaient pourtant correctement vérifiés par mon portable.</p>
<p>Comme, ça fait quelques temps que j’envisage d’ajouter le support de PGP (via GPG) à <a href="http://opensource.polytechnique.org/banana">Banana</a>, j’ai voulu regarder plus en détail comment fonctionne la signature d’un message. J’ai donc commencé à prendre des mails signés dans Mail.app, à en enregistrer les sources sur mon disque dur et à essayer de vérifier les signatures à la main, puis avec une version légèrement hackée de Banana. Malheureusement, ça n’a jamais fonctionner.</p>
<p>J’ai demandé à NC s’il pouvait me donner les sources d’un mail signé qui a le mérite d’apparaître comme correctement signé chez lui, et mal signé chez moi. J’ai comparé les 2 versions du mail… et le <code>diff</code> a été on ne peut plus troublant :</p>
<div class="highlight"><pre><code class="diff"> --nextPart1631992.9B81mC3O8N
<span class="gd">-Content-Type: text/plain;</span>
<span class="gd">- charset="iso-8859-1"</span>
Content-Transfer-Encoding: quoted-printable
<span class="gi">+Content-Type: text/plain;</span>
<span class="gi">+ charset=iso-8859-1</span>
Content-Disposition: inline
///
Le contenu de la partie signée n'est pas identique dans les deux mails. Pour ceux qui ne connaissent pas la structure d'un mail signé, je les invite à jeter un coup d'oeil à la [RFC1847|http://www.faqs.org/rfcs/rfc1847.html] qui se résume au schéma suivant :
</code></pre></div>
<p>Content-Type: multipart/signed; protocol=“TYPE/STYPE”;</p>
<pre><code> micalg="MICALG"; boundary="Signed Boundary"
</code></pre>
<p>—Signed Boundary
Stuff to sign</p>
<p>—Signed Boundary
Signature</p>
<p>—Signed Boundary—</p>
<pre><code>La partie différente entre chez NC et chez moi est la partie @@stuff to sign@@ (en fait ce sont les en-têtes de la partie signée du message).
Donc, je me suis replongé dans les options de Mail.app : pourquoi modifie-t-il les sources de mon mail ? quelle est l'option qui est différente entre mon portable et mon fixe ? J'ai recherché dans les paramètres d'affichage et d'édition sans succès... et puis je me suis dit que j'allais regarder la configuration de mon compte IMAP.
Une différence majeure entre mon fixe et mon portable est que mon portable est susceptible de ne pas être constamment connecté au serveur IMAP, donc il est configuré pour conserver une copie de tous les mails alors que sur mon fixe, Mail.app est sur la même machine que le serveur IMAP, pourquoi irais-je faire une copie supplémentaire des mails (ce qui se concrétiserait par des accès disques concurrents et quelques centaines de méga-octets de données dupliquées) ?
Pourquoi ? et bien maintenant j'ai ma réponse... en demandant à Mail.app de garder une copie des mails, GPGMail arrive à vérifier les signatures, donc, dans ces conditions, il a accès aux sources non modifiées. Il y a donc quelque part dans Mail.app un outil qui transforme les sources des mails... assez hallucinant ! Il faut donc que je considère que si je demande à Mail.app de m'afficher les sources d'un message qui se trouve sur un serveur IMAP sans faire de copie locale, il est possible (probable ?) que les sources en question ne soient pas les vraies sources du message mais une version ''remasterisée'' par Apple.
Même si je ne me fais pas trop d'illusion sur le sujet, j'espère que ce genre de détails (tout comme également la gestion des URLs dans le @@format=flowed@@, ou encore l'affichage de l'arborescence des message...) sera corrigé dans Leopard.
</code></pre>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Khtml2png 2.6.5 Est Sorti]]></title>
<link href="http://blog.mymind.fr/blog/2007/06/06/khtml2png-265-est-sorti/"/>
<updated>2007-06-06T00:31:00+02:00</updated>
<id>http://blog.mymind.fr/blog/2007/06/06/khtml2png-265-est-sorti</id>
<content type="html"><![CDATA[<p>Après 3 mois d’un travail pas très intensif, une nouvelle version de <a href="http://khtml2png.sf.net">khtml2png</a> est disponible. Cette version est partie du fait que les versions précédentes du programme ont parfois du mal à gérer les grandes captures d’écran (en fait, des bugs peuvent apparaître dès que la taille de la zone à capturer est plus grande que la taille affichable).</p>
<p>Donc, rapidement après la sortie de la 2.6.0, j’avais envoyé un correctif (en fait une réécriture du moteur de rendu) au développeur de <code>khtml2png</code>. Malheureusement, ce correctif ne fonctionnait pas correctement chez lui. Donc, pendant 3 mois, j’ai fait du débuggage à distance : j’envoie une version modifiée (1 ou 2 lignes à chaque fois), j’attends 2 ou 3 semaine une réponse, etc… Du développement efficace !</p>
<p>Bon, toujours est-il que maintenant, la version 2.6.5 fonctionne à la fois chez moi (à la fois Debian + xvnc et sur MacOS X), et chez Hauke (sur Debian également, mais avec des réglages différents).</p>
<p>Le Changelog annonce :</p>
<blockquote><p> fix: Now produces screenshots on my Debian Etch system under KDE 3.5.5 without glitches. %%%
fix: Maybe better working on other systems too. Please test.</p></blockquote>
<p>J’aimerais y rajouter quelques points :</p>
<ul>
<li>meilleur moteur de rendu (qui ne scroll plus, mais déplace la fenêtre pour s’affranchir de certains bugs de KDE et/ou Qt)</li>
<li>meilleure détection de la taille de la capture à réaliser</li>
<li>possibilité de choisir le comportement de <code>khtml2png</code> face aux redirections, au javascript, au java, au flash… via la ligne de commande</li>
<li>la détection automatique de la dimension par un <code>id</code> devient compatible avec le format utilisé dans les version <= 2.5</li>
</ul>
<p>Comme d’habitude, vous pouvez télécharger <a href="http://blog.mymind.fr/mind/public/khtml2png/khtml2png-fru-last.tar.bz2">ma dernière version du programme</a>.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Spaces]]></title>
<link href="http://blog.mymind.fr/blog/2007/06/02/spaces/"/>
<updated>2007-06-02T16:44:00+02:00</updated>
<id>http://blog.mymind.fr/blog/2007/06/02/spaces</id>
<content type="html"><![CDATA[<p>Comme un certain nombre de personnes le sait déjà, j’ai repris le développement de <a href="http://virtuedesktops.info">VirtueDesktops</a> depuis quelques semaines. Dans un premier temps je n’avais pas regardé le fonctionnement bas niveau du programme, mais la semaine dernière j’ai commencé à rechercher dans les sources “Comment Virtue fait-il pour cacher les fenêtres, faire les transitions etc… ?”.</p>
<!-- more -->
<p>A ma grande surprise, tout ceci est en fait géré nativement par MacOS X… la bibliothèque CoreGraphics contient en effet quelques fonctions cachées qui gèrent l’attribution d’une fenêtre à un Workspace et les transitions entre ces Workspaces. On peut trouver la liste complète de ces fonctions sur la <a href="http://trac.virtuedesktops.info/browser/trunk/Shared/Headers/CGSPrivate.h">SVN de VirtueDesktops</a>.</p>
<div class="highlight"><pre><code class="objective-c"><span class="k">extern</span> <span class="n">OSStatus</span> <span class="nf">CGSMoveWorkspaceWindowList</span><span class="p">(</span><span class="k">const</span> <span class="n">CGSConnection</span> <span class="n">connection</span><span class="p">,</span> <span class="n">CGSWindow</span> <span class="o">*</span><span class="n">wids</span><span class="p">,</span> <span class="kt">int</span> <span class="n">count</span><span class="p">,</span> <span class="kt">int</span> <span class="n">toWorkspace</span><span class="p">);</span>
<span class="k">extern</span> <span class="n">OSStatus</span> <span class="nf">CGSGetWorkspace</span><span class="p">(</span><span class="k">const</span> <span class="n">CGSConnection</span> <span class="n">cid</span><span class="p">,</span> <span class="kt">int</span> <span class="o">*</span><span class="n">workspace</span><span class="p">);</span>
<span class="k">extern</span> <span class="n">OSStatus</span> <span class="nf">CGSGetWindowWorkspace</span><span class="p">(</span><span class="k">const</span> <span class="n">CGSConnection</span> <span class="n">cid</span><span class="p">,</span> <span class="k">const</span> <span class="n">CGSWindow</span> <span class="n">wid</span><span class="p">,</span> <span class="kt">int</span> <span class="o">*</span><span class="n">workspace</span><span class="p">);</span>
<span class="k">extern</span> <span class="n">OSStatus</span> <span class="nf">CGSSetWorkspace</span><span class="p">(</span><span class="k">const</span> <span class="n">CGSConnection</span> <span class="n">cid</span><span class="p">,</span> <span class="kt">int</span> <span class="n">workspace</span><span class="p">);</span>
</code></pre></div>
<p>VirtueDesktops (et également DesktopsManager) n’est qu’une interface graphique qui tente d’utiliser ces fonctions. Et là <a href="http://www.apple.com/fr/macosx/leopard/spaces.html">Spaces</a> part avec une longueur d’avance car contrairement à VirtueDesktops, il sera facile pour Apple de gérer les ouvertures, fermetures et masquage d’application ce qui est à mon avis le plus gros point faible de Virtue. J’ai beaucoup travaillé là-dessus et je pense arriver actuellement aux limites de ce qu’il est possible de faire avec un gestionnaire de bureaux virtuels sur Tiger.</p>
<p>Un autre problème de VirtueDesktops est l’accès à l’ordonnancement des fenêtres. Comment être sûr que lorsqu’on arrive sur un bureau les fenêtres sont dans le bon ordre ? Idéalement il faudrait enregistrer l’ordre en temps réel et le restaurer lors du retour sur le bureau. Ceci permettrait également de toujours savoir quelle application activer lorsqu’on arrive sur un bureau, simplement en utilisant la fenêtre qui se trouve au premier plan.</p>
<p>Voilà, je me suis certes lancé dans une cause perdue d’avance, mais je suis tout de même content du résultat obtenu lors de ces dernières semaines. Virtue a désormais un comportement qui correspond à ce que j’attends d’un gestionnaire de bureaux virtuels : quand je change d’application il va sur le bureau adéquat, et quand je change de bureau, il active bien la dernière application que j’ai utilisée sur ce bureau.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Et Si on Oubliait Les Bases ?]]></title>
<link href="http://blog.mymind.fr/blog/2007/05/26/et-si-on-oubliait-les-bases/"/>
<updated>2007-05-26T13:40:00+02:00</updated>
<id>http://blog.mymind.fr/blog/2007/05/26/et-si-on-oubliait-les-bases</id>
<content type="html"><![CDATA[<p>MacOS X est un Unix… compatible POSIX. Voilà ce qu’un certain nombre de personnes semblent oublier assez fréquemment. C’est assez dommage quand ces personnes programment pour MacOS, on se retrouve parfois avec du code complexe pour réécrire des fonctions POSIX (en moins bien ?).</p>
<!-- more -->
<p>En fait si je parle de ça, c’est parce que je viens de rencontrer le problème dans le cadre du développement de <a href="http://blog.mymind.fr/post/2007/05/18/VirtueDesktops-revient">VirtueDesktops</a>. Il se trouve qu’en parcourant le tracker du projet je suis tombé sur le <a href="http://trac.virtuedesktops.info/ticket/115">ticket 115</a> qui s’intitule :</p>
<blockquote><p> Patch to check group id of ‘procmod’ group</p></blockquote>
<p>C’est intéressant… jusqu’à maintenant Virtue suppose que le gid du groupe procmod est celui par défaut de MacOS (c’est à dire qu’il vaut 9)… et le problème est donc que si un utilisateur a un procmod différent (ou si Apple décide un jour de changer le gid du groupe), le code actuel peut avoir des résultats inattendus, il faut donc faire un code plus portable qui recherche le gid de procmod au lieu de le stocker en dur. Ce n’est clairement pas une mauvaise idée ! Seul problème, le patch soumis est le suivant (au cas où certain voudrait réutiliser ce code, je tiens à signaler que c’est une mauvaise idée !) :</p>
<div class="highlight"><pre><code class="c"><span class="cp">#define NI_DOMAIN "."</span>
<span class="cp">#define NI_PATH "/name=groups/name=procmod"</span>
<span class="cp">#define NI_KEY "gid"</span>
<span class="c1">// Sucked from netinfo-369.5/tools/niutil/niutil.c</span>
<span class="n">ni_status</span> <span class="nf">ni_read_single_prop</span><span class="p">(</span><span class="kt">char</span> <span class="o">**</span><span class="n">property</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">args</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="n">NI_DOMAIN</span><span class="p">,</span> <span class="n">NI_PATH</span><span class="p">,</span> <span class="n">NI_KEY</span><span class="p">};</span>
<span class="kt">char</span> <span class="n">myname</span><span class="p">[]</span> <span class="o">=</span> <span class="s">"ni_read_single_prop"</span><span class="p">;</span>
<span class="k">const</span> <span class="n">bool</span> <span class="n">opt_tag</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
<span class="k">const</span> <span class="kt">int</span> <span class="n">timeout</span> <span class="o">=</span> <span class="mi">30</span><span class="p">;</span>
<span class="n">ni_namelist</span> <span class="n">nl</span><span class="p">;</span>
<span class="kt">void</span> <span class="o">*</span><span class="n">domain</span><span class="p">;</span>
<span class="n">ni_id</span> <span class="n">dir</span><span class="p">;</span>
<span class="n">ni_status</span> <span class="n">ret</span><span class="p">;</span>
<span class="k">if</span> <span class="p">((</span><span class="n">ret</span> <span class="o">=</span> <span class="n">do_open</span><span class="p">(</span><span class="n">myname</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="o">&</span><span class="n">domain</span><span class="p">,</span> <span class="n">opt_tag</span><span class="p">,</span> <span class="n">timeout</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">))</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="n">ret</span><span class="p">;</span>
<span class="cm">/* args[1] should be a directory specification */</span>
<span class="n">ret</span> <span class="o">=</span> <span class="n">ni2_pathsearch</span><span class="p">(</span><span class="n">domain</span><span class="p">,</span> <span class="o">&</span><span class="n">dir</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">!=</span> <span class="n">NI_OK</span><span class="p">)</span> <span class="p">{</span>
<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"%s: can't open directory %s: %s</span>
<span class="s">", myname, args[1], ni_error(ret));</span>
<span class="n">ni_free</span><span class="p">(</span><span class="n">domain</span><span class="p">);</span>
<span class="k">return</span> <span class="n">ret</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/* get the property values for args[2] */</span>
<span class="n">NI_INIT</span><span class="p">(</span><span class="o">&</span><span class="n">nl</span><span class="p">);</span>
<span class="n">ret</span> <span class="o">=</span> <span class="n">ni_lookupprop</span><span class="p">(</span><span class="n">domain</span><span class="p">,</span> <span class="o">&</span><span class="n">dir</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="o">&</span><span class="n">nl</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">!=</span> <span class="n">NI_OK</span><span class="p">)</span> <span class="p">{</span>
<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"%s: can't get property %s in directory %s: %s</span>
<span class="s">", myname, args[2], args[1], ni_error(ret));</span>
<span class="n">ni_free</span><span class="p">(</span><span class="n">domain</span><span class="p">);</span>
<span class="k">return</span> <span class="n">ret</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">nl</span><span class="p">.</span><span class="n">ni_namelist_len</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
<span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"%s: expected length = 1, found length = %d</span>
<span class="s">", myname, nl.ni_namelist_len);</span>
<span class="k">return</span> <span class="n">NI_FAILED</span><span class="p">;</span>
<span class="p">}</span>
<span class="o">*</span><span class="n">property</span> <span class="o">=</span> <span class="p">(</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="n">calloc</span><span class="p">(</span><span class="n">strlen</span><span class="p">(</span><span class="n">nl</span><span class="p">.</span><span class="n">ni_namelist_val</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">char</span><span class="p">));</span>
<span class="n">strcpy</span><span class="p">(</span><span class="o">*</span><span class="n">property</span><span class="p">,</span> <span class="n">nl</span><span class="p">.</span><span class="n">ni_namelist_val</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
<span class="n">ni_namelist_free</span><span class="p">(</span><span class="o">&</span><span class="n">nl</span><span class="p">);</span>
<span class="n">ni_free</span><span class="p">(</span><span class="n">domain</span><span class="p">);</span>
<span class="k">return</span> <span class="n">NI_OK</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>Donc que fait cette fonction ? C’est assez simple : elle utilise l’utilitaire NetInfo d’Apple pour lire les informations relatives au chemin <code>/groups/procmod</code>. Une fois là dedans elle copie la valeur stockée à la clé <code>gid</code> et renvoie cette chaîne dans le pointeur passé en argument.</p>
<p>Il n’y a pas à dire… c’est une solution qui devrait fonctionner (enfin j’ai même un doute, car je ne vois pas le groupe procmod apparaître quand je regarde dans NetInfo). Seulement cette solution montre clairement que la personne qui l’a écrite savait que MacOS utilise un système de groupes, mais avait oublié qu’en fait c’est avant tout un système POSIX. Et là, ce qui est intéressant c’est que sous MacOS X on a accès à l’API POSIX… en particulier à la fonction <code>getgrnam</code> qui retourne les informations sur un groupe à partir de son nom.</p>
<p>Donc, pas besoin de dépendance vers NetInfo, pas besoin d’une recherche dans une arborescence abstraite : NetInfo n’est qu’une abstraction de la couche Unix pour simplifier l’accès aux données par les utilisateurs… mais ça ne doit pas être un framework d’abstraction de l’API POSIX ce qui entraîne nécessairement du code moins portable (et surtout, moins lisible dans le cas présent), moins rapide et plus lourd.</p>
<p>Ici, le code est utilisé dans un programme en C qui est chargé de mettre un objet dans le groupe procmod pour autoriser Virtue à effectuer certaines actions qui nécessitent un accès privilégié au serveur graphique… ce programme ne fait <em>que</em> changer les permissions. Pourquoi utiliser une API lourde et spécifique à MacOS alors que ce type de programme pourrait clairement être utilisé sur n’importe quel Unix ?</p>
<p>Voici donc une solution qui fait la même chose que la fonction ci-dessus, en mieux et surtout en beaucoup moins de lignes :</p>
<div class="highlight"><pre><code class="c"><span class="cp">#include <grp.h></span>
<span class="k">static</span> <span class="kt">int</span> <span class="nf">getProcmodGid</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">struct</span> <span class="n">group</span><span class="o">*</span> <span class="n">groupDesc</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">gid</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
<span class="n">groupDesc</span> <span class="o">=</span> <span class="n">getgrnam</span><span class="p">(</span><span class="s">"procmod"</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">groupDesc</span><span class="p">)</span> <span class="p">{</span>
<span class="n">gid</span> <span class="o">=</span> <span class="n">groupDesc</span><span class="o">-></span><span class="n">gr_gid</span><span class="p">;</span>
<span class="n">free</span><span class="p">(</span><span class="n">groupDesc</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">gid</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>Pourquoi cette solution est-elle meilleure ?</p>
<ul>
<li>elle retourne un entier… on a donc pas besoin de faire une conversion a posteriori</li>
<li>elle utilise l’API POSIX et est donc utilisable sur n’importe quel OS POSIX (donc quasiment tous… à l’exception de Windows)</li>
<li>elle évite toute manipulation inutile de chaîne de caractères</li>
<li>elle est ne nécessite pas de charger une bibliothèque supplémentaire : <code>getgrnam</code> est dans la <code>libc</code></li>
<li>elle est drôlement plus courte non ?</li>
</ul>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Mac C'est Bien... Mais Pas Top]]></title>
<link href="http://blog.mymind.fr/blog/2007/05/22/mac-cest-bien-mais-pas-top/"/>
<updated>2007-05-22T23:44:00+02:00</updated>
<id>http://blog.mymind.fr/blog/2007/05/22/mac-cest-bien-mais-pas-top</id>
<content type="html"><![CDATA[<p>Bon, la suite des mes problèmes avec les machines Apple !</p>
<p>Alors que la carte mère de mon portable a été changée il y a un peu plus d’un mois, mon portable a décidé aujourd’hui d’arrêter de vouloir fonctionner. Symptômes ? Il s’allume (des fois), fonctionne (rarement) quelques secondes puis s’éteint brutalement… et ce qu’il démarre sur secteur, sur batterie ou les deux ensemble, que ce soit sur le disque dur, sur le DVD d’installation ou encore en mode <em>target</em> (en tant que disque dur externe).</p>
<p>En plus ça lui arrive de s’allumer tout seul alors que ça fait plusieurs minutes que je ne l’ai plus touché. Un autre symptôme étrange, la diode de la prise magsafe clignote bizarrement lorsque la machine n’est pas sous-tension (pas un clignotement vert/rouge, mais une lumière verte continue qui scintille un peu). Tout ça me laisse penser que c’est l’alimentation qui est en cause.</p>
<p>Voilà… les joies des machines de qualité, d’autant plus que la garantie de la machine a expirée il y a <strong>2</strong> jours (le 19 mai).</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[VirtueDesktops Revient...]]></title>
<link href="http://blog.mymind.fr/blog/2007/05/18/virtuedesktops-revient/"/>
<updated>2007-05-18T14:25:00+02:00</updated>
<id>http://blog.mymind.fr/blog/2007/05/18/virtuedesktops-revient</id>
<content type="html"><![CDATA[<p>Voici plus ou moins trois mois que j’ai découvert <a href="http://synergy2.sf.net">synergy</a>, c’est vraiment très agréable de pouvoir contrôler les deux ordinateurs sans changer de clavier/souris continuellement. Seul problème, c’est que sur la machine qui héberge le serveur <code>synergy</code>, <a href="http://virtuedesktops.info">VirtueDesktops</a>, un <em>excellent</em> gestionnaire de bureaux virtuels pour MacOS, n’arrête pas de crasher. J’avais donc posté un bugreport sur le trac de Virtue… malheureusement pour diverses raisons, Tony Arnold a décidé peu après de stopper le développement de Virtue.</p>
<!-- more -->
<p>J’ai donc pris mon courage à 2 mains, et je me suis doucement plongé dans le code de VirtueDesktops (c’est vraiment bien les applications OpenSource). Il y a maintenant deux semaines, j’ai soumis le patch permettant de corriger le crash dont je souffrais. Depuis, ce patch a été publié ce qui a conduit la <a href="http://virtuedesktops.info/index.php/2007/05/14/virtuedesktops-054-beta-3/">release de la bêta 3 de Virtue 0.54</a>.</p>
<p>Je me suis relancé dans le développement cette semaine en me fixant comme objectif de corriger plusieurs comportements énervants de Virtue. Par exemple, si je dis que <code>Mail.app</code> doit être sur le bureau Mail, je veux que <strong>toutes</strong> les fenêtres de <code>Mail.app</code> aillent forcément sur ce bureau. Seul problème, c’est que lorsque je reçois une notification de l’arrivée d’un nouveau mail et que je clique sur cette notification, une fenêtre avec le message s’ouvre sur le bureau courant, et Virtue tourne pour atteindre le bureau de Mail.app… résultat : j’ai changé de bureau mais je n’ai pas accès à la fenêtre que je voulais voir. J’ai corrigé ce comportement, désormais, la fenêtre est automatiquement déplacée sur le bureau de <code>Mail.app</code> lors de son ouverture.</p>
<p>Autres corrections diverses :</p>
<ul>
<li>lorsqu’on ferme une application Virtue reste sur le bureau courant (sauf si il n’y a plus rien sur le bureau courant)</li>
<li>lorsqu’on choisi de changer de bureau, Virtue active désormais la dernière application active connue pour le nouveau bureau</li>
<li>lorsqu’on restaure une fenêtre qui était dans le Dock, soit l’application a le droit d’être sur le bureau courant et la fenêtre est affichée sur ce bureau sans chercher à changer de bureau, soit l’application est attribuée à un bureau et dans ce cas la fenêtre est restaurée sur le bureau de l’application et Virtue change de bureau</li>
<li>…</li>
</ul>
<p>Ces changements ont abouti à la <a href="http://virtuedesktops.info/index.php/2007/05/18/virtuedesktops-054-beta-4/">bêta 4</a> et se résument à :</p>
<ul>
<li>Changing to desktops when you launch, activate, deactivate, minimise, restore or quit an application should be much more consistent;</li>
<li>Window ordering when changing desktops should be much more consistent;</li>
</ul>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[La Banane Et L'escargot]]></title>
<link href="http://blog.mymind.fr/blog/2007/05/07/la-banane-et-lescargot/"/>
<updated>2007-05-07T19:44:00+02:00</updated>
<id>http://blog.mymind.fr/blog/2007/05/07/la-banane-et-lescargot</id>
<content type="html"><![CDATA[<p>La release de <a href="http://opensource.polytechnique.org/banana/">Banana</a> 1.6 en même temps que celle de plat/al 0.9.14 a mis en évidence un certain nombre de faiblesses dans Banana. En particuliers la génération du spool (mise en cache de l’arborescence des messages) et des flux RSS s’est révélé extrêmement lourde pour plusieurs raisons :</p>
<ul>
<li>l’accès aux mbox des Mailing-Lists nécessite d’appel du <code>mbox-helper</code>, et donc un <code>fork</code>… opération lourde, qui répétée plusieurs fois par mbox devient rapidement très lourde lorsqu’on a plusieurs dizaines de Mailing-Lists.</li>
<li>le traitement des données par PHP est loin d’être immédiats… et il y a clairement des goulots d’étranglement dans le code.</li>
</ul>
<p>C’est pour ces raisons que j’ai passé Banana au <code>profiler</code>, c’est à dire que j’ai analysé l’exécution de Banana à l’aide d’un outil qui permet de tracer l’exécution du programme et mettant un accent particuliers sur le temps d’exécution de chaque fonction. L’outil que j’ai trouvé pour faire ça est <a href="http://www.xdebug.org">xdebug</a>, utilisé conjointement à <a href="http://kcachegrind.sourceforge.net/">KCacheGrind</a>.</p>
<!-- more -->
<h1>Forks</h1>
<p>Je dois avouer que lorsque j’avais fait mes tests, je n’avais pas plusieurs centaines de Mailing-Lists à traiter… et je ne m’attendais pas à ce qu’une fois passée en production le script de mise à jour des flux RSS puisse prendre toutes les ressources de la machine pendant plusieurs minutes. Il a donc fallu sérieusement restructurer la gestion des accès aux Mailing-Lists pour restreindre au maximum le nombre d’accès au <code>mbox-helper</code> (un petit programme écrit en C qui se charge de tous les accès aux mbox), et, en cas d’accès, limiter au maximum le temps passer sur le <code>mbox-helper</code>.</p>
<p>Donc désormais le <code>mbox-helper</code> n’est plus appelé que si la mbox a changé depuis le dernier passage (le changement étant détecté par la taille du fichier). Ce qui permet donc de supprimer le lancement d’environ 500 <code>mbox-helper</code> lors des rafraîchissements des spools (en effet, un nombre négligeable de Mailing-List aura des nouveaux messages lors du passage du script toutes les 5, 10 ou 20 minutes). Ajouté à cela la correction d’un bug qui faisait que l’appel au <code>mbox-helper</code> oubliait de spécifier l’offset où chercher le message à traiter et qui forçait donc le <code>mbox-helper</code> à relire la totalité de la mbox, on peut se permettre de supposer que la prochaine version de Banana sera plus efficace pour la gestion des mbox.</p>
<h1>Array_shift</h1>
<h2>Piles</h2>
<p>Il est souvent extrêment pratique d’utiliser une pile de données. Cela permet de traiter les informations dans l’ordre de la pile sans excès de mémoire puisque chaque élément est dépilé avant d’être traité. C’est une technique que j’aime particulièrement lorsque j’ai une suite de lignes à traiter : je prend un tableau contenant une ligne par entrée et je le parcours avec <code>array_shift</code> qui permet de dépiler le premier élément du tableau. On obtient ainsi un code de la forme</p>
<div class="highlight"><pre><code class="php"><span class="x">while (!is_null($line = array_shift($lines))) {</span>
<span class="x"> do_something($line);</span>
<span class="x">}</span>
<span class="x">do_something($lines);</span>
</code></pre></div>
<p>Un <code>foreach</code> peut très bien faire la même chose, mais on perd les avantages de la piles. Avec cette structure, pour avoir le même comportement que la boucle précédente, il faut ajouter un <code>unset()</code> :</p>
<div class="highlight"><pre><code class="php"><span class="x">foreach ($lines as $key=>&$value) {</span>
<span class="x"> do_something($line);</span>
<span class="x"> unset($lines[$key]);</span>
<span class="x">}</span>
<span class="x">do_something($lines);</span>
</code></pre></div>
<p>Certes le <code>foreach</code> sera sensiblement plus rapide que le <code>while</code>/<code>array_shift()</code> car il comprend un appel de fonction à chaque itération, mais on peut s’attendre raisonnablement à ce que cet appel soit en O(1), et ait donc un coût négligeable.</p>
<h2>Profiler</h2>
<p>Là où il y a un problème c’est que le profiler m’indique que <code>array_shift</code> prend 57% du temps d’exécution de Banana. Ces 57% sont partagés entre 80000 appels à la fonction, mais le plus marquant c’est que parmi ces 80000 appels, ce ne sont que 24000 d’entre eux qui prennent la quasi-totalité du temps. Pourquoi ces appels particuliers sont-ils si lourd alors que les 60000 autres ont un coût parfaitement négligeable.</p>
<p>La seule différence entre ces deux cas d’appels c’est que les <em>lourds</em> traitent un énorme tableau de 24000 lignes, alors que les <em>légers</em> traitent un grand nombre de petits tableaux de quelques dizaines de lignes chacun. Il est donc extrêmement clair que <code>array_shift</code> <strong>n’est pas</strong> une fonction en O(1)… J’ai donc changé la structure de code qui reflétait la structure de données par une simple boucle qui perd en lisibilité dans <a href="http://opensource.polytechnique.org/viewsvn/diff.php?path=/trunk/banana/mbox.inc.php&rev=248&repname=Banana">ce patch</a>. Après ce changement, les 60000 <code>array_shift</code> restant ne prennent que 0.16% du temps d’exécution de Banana…</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Vista ?]]></title>
<link href="http://blog.mymind.fr/blog/2007/05/03/vista/"/>
<updated>2007-05-03T18:07:00+02:00</updated>
<id>http://blog.mymind.fr/blog/2007/05/03/vista</id>
<content type="html"><![CDATA[<p>Étant curieux, en tout cas pour ce qui est informatique, je n’ai pas pu m’empêcher de mettre un Windows Vista en double boot sur mon MacBook Pro… Grâce à BootCamp, l’installation ne pose pas de problème. Par contre une fois l’OS installé, je n’ai pas arrêtez d’enchaîner les petits problèmes qui ont été plus une perte de temps qu’autre chose…</p>
<!-- more -->
<h2>Accueil réussi</h2>
<p>Les problèmes commencent dès l’arrivée sous Vista. Déjà un premier coup d’oeil : c’est loin d’être laid à regarder… même si les ombres sont moins agréables que celles de MacOS X et que les effets de flouté sous les fenêtres me fatiguent un peu. Quand on arrive sous Vista, on tombe sur la fenêtre <em>Accueil de Windows</em> ci-dessous :</p>
<p><img src="http://blog.mymind.fr/assets/screenshots/Vista/Vista-accueil1.PNG" alt="Fenêtre d'accueil de Windows Vista" /></p>
<p>Cet écran est sympathique : un navigateur pratique pour circuler entre les fonctionnalités de Windows… le problème provient lorsque justement je clique sur une de ces fonctionnalités, par exemple “Transfert de fichiers”… rien ne se passe. Enfin, je m’attendais à voir surgir un assistant, mais rien n’apparaît. Après quelques secondes, je me rends compte qu’en fait l’en-tête de la fenêtre à changé :</p>
<p><img src="http://blog.mymind.fr/assets/screenshots/Vista/Vista-accueil2.PNG" alt="Fenêtre accueil après sélection" /></p>
<p>Il faut cliquer sur le texte en bas de l’en-tête pour enfin avoir le logiciel de Transfert de fichier… qui se lance dans un environnement bizarre en plein écran avec une petite fenêtre au milieu.</p>
<h2>Personnalisation</h2>
<p>Le premier réflexe après avoir installé un nouveau système, c’est de le personnaliser. Ceci passe donc par le changement du fond d’écran, de l’écran de veille, etc… Sous Vista, ceci se fait via une page du panneau de configuration :</p>
<p><img src="http://blog.mymind.fr/assets/screenshots/Vista/Vista-apparence.PNG" alt="Réglages de l'apparence de Windows" /></p>
<p>C’est très bien, ça marche… sauf que lorsqu’on arrive à la catégorie <em>Thèmes</em>, changer le thème change <strong>tous</strong> les réglages qu’on avait faits précédemment pour les remplacer par ceux du thème sélectionné… L’art et la manière de perdre 10 minutes à faire 2 fois la même configuration, et à refaire tout ce que j’avais déjà fait alors que si <em>Thèmes</em> se trouvait en première place de cette liste, j’aurais d’abord choisi un thème avant de le personnaliser.</p>
<h2>Inquiétude</h2>
<p>Un autre problème se manifeste sous Windows Vista : les ventilateurs de mon MacBook Pro tournent quasiment à fond… alors qu’en temps normal c’est une machine silencieuse, sous Vista mon portable génère un fond sonore désagréable. Comme après avoir cherché un peu, je trouve qu’il y a un <em>gadget</em> (ce n’est pas du tout la même chose que les <em>widgets</em> de Dashboard) pour le panneau latéral de Windows qui permet de (plus ou moins) monitorer la charge CPU et l’occupation mémoire, je l’active. Et là, quelle bonne surprise ! Vista tourne à 15% proc comme occupation de croisière (étant donné que j’ai un dual-core, dois-je considérer que c’est 30% d’un des coeurs ?)… Quand je bouge la souris (si si, ça fait varier la charge), une fenêtre, ou que je travail sur une application, la charge proc augmente. Par exemple, les deux captures suivantes montre la charge de la machine lorsque j’utilise le logiciel de capture d’écran :</p>
<ul>
<li>la première lorsque je capture par sélection (une fois la sélection terminée, il est monté aussi à 30% CPU pendant quelques secondes)</li>
<li>la deuxième lorsque je capture par fenêtre (dans ce cas, il prend du processeur pour faire de joli effets graphiques pour mettre en valeur la fenêtre sélectionnée)</li>
</ul>