OVH Cloud OVH Cloud

Script num=c3=a9ro 6

21 réponses
Avatar
Olivier Miakinen
Je n'ose plus espérer que ce sera le dernier...


#!/bin/bash
############################################################################
# Name:
# decode_headers
#
# Description:
# Script used for decoding RFC 2047 MIME encoded headers.
#
# Example:
# decode_headers <<POTIRON
# Message-ID: <c5a9-1@part.org>
# From: arvo@part.org (=?ASCII?Q?Arvo?= =?L1?Q?=20?= =?UTF-8?Q?P=C3=A4rt?=)
# To: =?Latin1?Q?Fr=E9d=E9ric_Chopin?= <fred@chopin.org>,
# =?Latin2?Q?Anton=EDn_Dvo=F8=E1k?= <anton@dvorak.org>
# Cc: Arvo =?UTF-8?Q?P=C3=A4rt?= <arvo@part.org>
# References: <A65R-4d@chopin.org> <c5a7-3@part.org>
# <A72Q-5a@chopin.org>
# In-Reply-To: <A72Q-5a@chopin.org>
# Subject: Re: Going to =?Shift-JIS?B?k4yLngo=?= (Tokyo) =?UTF-8?B?zpEK?=
# =?UTF-8?B?zrjOrs69zrEK?= (Athens) and =?ISO-8859-5?Q?=BC=DE=E1=DA?=
# =?ISO-8859-5?Q?=D2=D0?= (Moscow)
# POTIRON
# ->
# Message-ID: <c5a9-1@part.org>
# From: arvo@part.org (Arvo Pärt)
# To: Frédéric Chopin <fred@chopin.org>, Antonín Dvořák <anton@dvorak.org>
# Cc: Arvo Pärt <arvo@part.org>
# References: <A65R-4d@chopin.org> <c5a7-3@part.org> <A72Q-5a@chopin.org>
# In-Reply-To: <A72Q-5a@chopin.org>
# Subject: Re: Going to 東京 (Tokyo) Αθήνα (Athens) and Москва (Moscow)
############################################################################

#
# This script uses the following global variables
# DECODED_LINE
# The current state of a header being currently decoded
# STATUS
# Has three possible values:
# - "none" at the beginning of a new header
# - "decoded-word" after a correctly decoded MIME part
# - "normal" after any other string
#
DECODED_LINE=""
STATUS="none"

#
# Function: usage
#
usage()
{
printf "Usage: %s [OPTION...] [FILE...]\n" $0
printf "Decode headers from given files for RFC 2047 MIME encodings.\n"
printf "\n"
printf "With no FILE, or when FILE is -, read standard input.\n"
printf "\n"
printf " -h, --help Give this help list\n"
exit 1
}

#
# Function: decode_qp
#
# Description:
# Decode a quoted-printable encoded text.
#
# Parameter:
# The third part of an encoded-word of type 'Q'.
#
# Return value:
# If decoding is ok, print the result and return 0
# Otherwise, may print a partial result, but return 1
#
function decode_qp()
{
while IFS= read -n 1 char; do
case $char in
"_")
printf " "
;;
"=")
IFS= read -n 2 hex
if [ $(LANG=C expr "_${hex}" : '_[0-9A-Fa-f]*') != 3 ]; then
return 1
fi
printf "%b" "\\x${hex}"
;;
*)
printf "%s" "$char"
;;
esac
done

return 0
}

#
# Function: b64index
#
# Description:
# Return the index corresponding to an encoding character in base64.
#
# Parameter:
# A character, normally in [A-Z] or [a-z] or [0-9] or + or /
# It may also be the special end character =
#
# Return value:
# If OK, print a number from 0 to 63 corresponding to the character,
# or 64 for the special end character, then return 0.
# Otherwise return 1.
#
function b64index()
{
# A(65)..Z(90) : 0..25 i.e. ascii - 65
# a(97)..z(122) : 26..51 i.e. ascii - 71
# 0(48)..9(57) : 52..61 i.e. ascii + 4
# +(43) : 62
# /(47) : 63
# =(61) : 64
ascii=$(printf "%d" "'$1")
if [ "$ascii" -ge 65 -a "$ascii" -le 90 ]; then
echo $(( "$ascii" - 65 ))
return 0
fi
if [ "$ascii" -ge 97 -a "$ascii" -le 122 ]; then
echo $(( "$ascii" - 71 ))
return 0
fi
if [ "$ascii" -ge 48 -a "$ascii" -le 57 ]; then
echo $(( "$ascii" + 4 ))
return 0
fi
case "$ascii" in
43) echo 62 ;;
47) echo 63 ;;
61) echo 64 ;;
*) return 1 ;;
esac
return 0
}

#
# Function: decode_b64
#
# Description:
# Decode a base64 encoded text.
#
# Parameter:
# The third part of an encoded-word of type 'B'.
#
# Return value:
# If decoding is ok, print the result and return 0
# Otherwise, may print a partial result, but return 1
#
function decode_b64()
{
while IFS= read -n 4 chunk; do
c1="${chunk:0:1}"
c2="${chunk:1:1}"
c3="${chunk:2:1}"
c4="${chunk:3:1}"
if i1=$(b64index "${c1}"); then
n24=$(( i1 << 18 ))
else
return 1
fi
if i2=$(b64index "${c2}"); then
n24=$(( $n24 | ( i2 << 12 ) ))
printf $(printf "\\%03o" $(($n24 >> 16)) )
else
return 1
fi
if [ "${c3}" = "=" ]; then
return 0
elif i3=$(b64index "${c3}"); then
n24=$(( $n24 | ( i3 << 6 ) ))
printf $(printf "\\%03o" $(( ($n24 >> 8) & 255 )) )
else
return 1
fi
if [ "${c4}" = "=" ]; then
return 0
elif i4=$(b64index "${c4}"); then
n24=$(( $n24 | i4 ))
printf $(printf "\\%03o" $(( $n24 & 255 )) )
else
return 1
fi
done

return 0
}

#
# Function: decode_word
#
# Description:
# Check that the parameter is an encoded word, then decode it and
# convert it to UTF-8.
#
# Parameter:
# A single (possibly MIME-encoded) word.
#
# Return value:
# If decoding is ok, print the result and return 0
# Otherwise return 1
#
decode_word()
{
word="$*"
###################################################################
# An encoded word contains only ASCII characters in range from
# '!' (Ascii value 0x21) to '~' (Ascii value 0x7e). This excludes
# in particular the SPACE (Ascii 0x20) and the TAB (Ascii 0x09).
#
# More specifically, it consists of five parts separated by
# question marks '?'
# 1. A character "="
# 2. The charset, e.g. "UTF-8" or "ISO-8859-1"
# 3. The encoding, B or Q in upper or lower case
# 4. The encoded text
# 5. A character "="
###################################################################

# Check that:
# - there is no character outside range from '!' to '~'
# - the 1st part is a "="
# - the 5th part is a "=" and it is the end of the string
if [ $(LANG=C expr "_$word" : '_[!-~]*$') = 0 ]; then return 1; fi
part1=$(printf "$word" | cut -f 1 -d '?')
part5=$(printf "$word" | cut -f 5- -d '?')
if [ "$part1" != "=" -o "$part5" != "=" ]; then return 1; fi

# Extract charset, encoding, and encoded text
charset=$(printf "$word" | cut -f 2 -d '?')
encoding=$(printf "$word" | cut -f 3 -d '?')
encoded=$(printf "$word" | cut -f 4 -d '?')

case $encoding in
B | b)
decoded=$(printf "$encoded" | decode_b64 2>/dev/null)
if [ $? != 0 ]; then return 1; fi
;;
Q | q)
decoded=$(printf "$encoded" | decode_qp 2>/dev/null)
if [ $? != 0 ]; then return 1; fi
;;
*)
return 1
;;
esac

printf "$decoded" | iconv -f $charset -t UTF-8 2>/dev/null
return $?
}

#
# Function: add_word
#
# Description:
# Try to decode a new word, and update DECODED_LINE and STATUS
# depending on the result and the previous STATUS.
#
# Parameter:
# A single (possibly MIME-encoded) word.
#
# Return value:
# None
#
# Side effects:
# Change DECODED_LINE and STATUS
#
add_word()
{
# Manage possible initial and final parentheses
# $p123 = prefix + word + suffix
# $p1 = prefix, $p2 = word, $p3 = suffix
p123="$*"
p1=$(printf "%s" "${p123}" | sed -e 's/^\([()]*\).*/\1/')
p23="${p123:${#p1}}"
p2=$(printf "%s" "${p23}" | sed -e 's/[()]*$//')
p3="${p23:${#p2}}"

if word=$(decode_word "$p2"); then
p123="${p1}${word}${p3}"
if [ "$STATUS" = "normal" ]; then
DECODED_LINE="${DECODED_LINE} "
elif [ "$STATUS" != "none" -a -n "${p1}" ]; then
DECODED_LINE="${DECODED_LINE} "
fi
DECODED_LINE="${DECODED_LINE}${p123}"
if [ -n "${p3}" ]; then
STATUS="normal"
else
STATUS="decoded-word"
fi
else
if [ "$STATUS" != "none" ]; then
DECODED_LINE="${DECODED_LINE} "
fi
DECODED_LINE="${DECODED_LINE}${p123}"
STATUS="normal"
fi
}

#
# Function: flush_line
#
# Description:
# Before beginning to manage a new header, or just before ending
# the script, print the pending DECODED_LINE if any.
#
# Parameter:
# None
#
# Return value:
# None
#
# Side effects:
# Print things to stdout
# Change DECODED_LINE and STATUS
#
flush_line()
{
if [ -n "${DECODED_LINE}" ]; then
printf "%s\n" "${DECODED_LINE}"
DECODED_LINE=""
STATUS="none"
fi
}

#
# Function: manage_line
#
# Description:
# Manage a new line, which can be either the beginning or the
# continuation of a mail/news header.
# This function prints the previous line if this is a new one,
# then it adds successive parts to the new DECODED_LINE, while
# updating STATUS as needed.
#
# Parameter:
# An input line.
#
# Return value:
# None
#
# Side effects:
# Print things to stdout
# Change DECODED_LINE and STATUS
#
manage_line()
{
line="$*"

# Is it a continuation line?
if [ $(LANG=C expr "_$line" : "_[ \t]") = 0 ]; then
# No: new header
flush_line
fi

for word in $line; do
add_word "$word"
done
}

#
# Function: manage_file
#
# Description:
# Call manage_line for each line in a given file
#
# Parameter:
# A file name, or "-" for stdin
#
# Return value:
# None
#
# Side effects:
# Same as manage_line
#
manage_file()
{
file=${1--} # POSIX-compliant; ${1:--} can be used either.
while IFS= read -r line; do
manage_line "$line"
done < <(cat -- "$file")
}

#
# Parse arguments for -h or --help
#
for i in "$@"; do
case $i in
-h | --help)
usage
;;
-)
;;
-*)
printf "Unknown argument '%s'\n" "$i"
usage
;;
esac
done

#
# Main loop.
# Call manage_file for each filename in parameters,
# then print the last pending DECODED_LINE if any.
#
if [ "$*" = "" ]; then
manage_file "-"
else
for file in "$@"; do
manage_file "$file"
done
fi
flush_line

exit 0


--
Olivier Miakinen

10 réponses

1 2 3
Avatar
Korben Dallas
Le 20/10/2019 à 20:37:26, Olivier Miakinen a écrit :
Je n'ose plus espérer que ce sera le dernier...

`
quel gâchis de discuter de ça sur fr.test, alors que ça aurait eu ça
place sur le groupe de programmation mac...
--
/ Croire c'est le contraire de savoir,
-- o -- si j'y crois, je ne sais pas,
/ si je sais, pas la peine d'y croire.
--> Je Croix Pas, car Je Sais que c'est Faux MalgRê TouT...
Avatar
Manfred La Cassagnère
C'est Korben Dallas qui le 20 octobre 2019 a eu le culot d'écrire:
Le 20/10/2019 à 20:37:26, Olivier Miakinen a écrit :
Je n'ose plus espérer que ce sera le dernier...

`
quel gâchis de discuter de ça sur fr.test, alors que ça aurait eu ça
place sur le groupe de programmation mac...

C'est amusant de te voir t'ériger en donneur de leçons sur ce thème,
alors que tu pollues régulièrement Usenet en postant n'importe quoi
n'importe comment et n'importe où... "Fais ce que je dis, mais ne fais
pas ce que je fais"!
--
Manfred
Middle Of Nowhere
iMac Intel Core 2 Duo, early 2009, OS X 10.11.6
"I would trade all my technology for an afternoon with Socrates."(S.J.)
Avatar
josephb
Olivier Miakinen <om+ wrote:
en attendant que j'essaie la nouvelle fonction, un peu de lecture ;-)
et si ça ne marche pas j'aimerais bien avoir le manuel de la
commande test sur Mac

Ça, ce n'est pas le plus compliqué :
TEST(1) BSD General Commands Manual TEST(1)
NAME
test, [ -- condition evaluation utility
SYNOPSIS
test expression
[ expression ]
DESCRIPTION
The test utility evaluates the expression and, if it evaluates to true,
returns a zero (true) exit status; otherwise it returns 1 (false). If
there is no expression, test also returns 1 (false).
All operators and flags are separate arguments to the test utility.
The following primaries are used to construct expressions:
-b file True if file exists and is a block special file.
-c file True if file exists and is a character special file.
-d file True if file exists and is a directory.
-e file True if file exists (regardless of type).
-f file True if file exists and is a regular file.
-g file True if file exists and its set group ID flag is set.
-h file True if file exists and is a symbolic link. This operator
is retained for compatibility with previous versions of
this program. Do not rely on its existence; use -L instead.
-k file True if file exists and its sticky bit is set.
-n string True if the length of string is nonzero.
-p file True if file is a named pipe (FIFO).
-r file True if file exists and is readable.
-s file True if file exists and has a size greater than zero.
-t file_descriptor
True if the file whose file descriptor number is
file_descriptor is open and is associated with a terminal.
-u file True if file exists and its set user ID flag is set.
-w file True if file exists and is writable. True indicates only
that the write flag is on. The file is not writable on a
read-only file system even if this test indicates true.
-x file True if file exists and is executable. True indicates only
that the execute flag is on. If file is a directory, true
indicates that file can be searched.
-z string True if the length of string is zero.
-L file True if file exists and is a symbolic link.
-O file True if file exists and its owner matches the effective
user id of this process.
-G file True if file exists and its group matches the effective
group id of this process.
-S file True if file exists and is a socket.
file1 -nt file2
True if file1 exists and is newer than file2.
file1 -ot file2
True if file1 exists and is older than file2.
file1 -ef file2
True if file1 and file2 exist and refer to the same file.
string True if string is not the null string.
s1 = s2 True if the strings s1 and s2 are identical.
s1 != s2 True if the strings s1 and s2 are not identical.
s1 < s2 True if string s1 comes before s2 based on the ASCII value
of their characters.
s1 > s2 True if string s1 comes after s2 based on the ASCII value
of their characters.
n1 -eq n2 True if the integers n1 and n2 are algebraically equal.
n1 -ne n2 True if the integers n1 and n2 are not algebraically equal.
n1 -gt n2 True if the integer n1 is algebraically greater than the
integer n2.
n1 -ge n2 True if the integer n1 is algebraically greater than or
equal to the integer n2.
n1 -lt n2 True if the integer n1 is algebraically less than the inte-
ger n2.
n1 -le n2 True if the integer n1 is algebraically less than or equal
to the integer n2.
These primaries can be combined with the following operators:
! expression True if expression is false.
expression1 -a expression2
True if both expression1 and expression2 are true.
expression1 -o expression2
True if either expression1 or expression2 are true.
(expression) True if expression is true.
The -a operator has higher precedence than the -o operator.
GRAMMAR AMBIGUITY
The test grammar is inherently ambiguous. In order to assure a degree of
consistency, the cases described in IEEE Std 1003.2 (``POSIX.2'') section
4.62.4, are evaluated consistently according to the rules specified in
the standards document. All other cases are subject to the ambiguity in
the command semantics.
RETURN VALUES
The test utility exits with one of the following values:
0 expression evaluated to true.
1 expression evaluated to false or expression was missing.
1 An error occurred.

STANDARDS
The test utility implements a superset of the IEEE Std 1003.2
(``POSIX.2'') specification.
BSD May 31, 1993 BSD
[Opération terminée]
--
J. B.
Avatar
Olivier Miakinen
Le 21/10/2019 00:45, Joseph-B a écrit :
++ '[' 85 -ge 65 ']'
++ '[' 85 -le 90 ']'
/usr/local/bin/olivier6.sh: line 121: "85" - 65 : syntax error: operand expected (error token is ""85" - 65 ")

Pourtant, la page de man dit que c'est une syntaxe autorisée.
Ou alors c'est que le 'test' du shell n'est pas le 'test' documenté !
Je te propose deux fois trois essais :
[ 85 -ge 65 ] ; echo $?
test 85 -ge 65 ; echo $?
/usr/bin/test 85 -ge 65 ; echo $?
[ 85 -lt 65 ] ; echo $?
test 85 -lt 65 ; echo $?
/usr/bin/test 85 -lt 65 ; echo $?
Si à chaque fois les deux premiers sur les trois plantent et que le troisième
ne plante pas, c'est bien ça le problème.
Encore merci pour tous ces tests que tu fais !
--
Olivier Miakinen
Avatar
josephb
Olivier Miakinen <om+ wrote:
Pourtant, la page de man dit que c'est une syntaxe autorisée.
Ou alors c'est que le 'test' du shell n'est pas le 'test' documenté !

Ça, c'est bien du domaine du possible…
Je te propose deux fois trois essais :

tu veux dire que je copie-colle directement dans le terminal le bloc 1
puis le 2 ?
Si oui, voilà ce que ça donne pour le 1
en BASH
**********
imac:~ iMacJB$ set[ 85 -ge 65 ] ; echo $?
-bash: set[ 85 -ge 65 ]: command not found
127
imac:~ iMacJB$ test 85 -ge 65 ; echo $?
0
imac:~ iMacJB$ /usr/bin/test 85 -ge 65 ; echo $?
-bash: /usr/bin/test: No such file or directory
127
#################
le 2
**********
imac:~ iMacJB$ [ 85 -lt 65 ] ; echo $?
1
imac:~ iMacJB$ test 85 -lt 65 ; echo $?
1
imac:~ iMacJB$ /usr/bin/test 85 -lt 65 ; echo $?
-bash: /usr/bin/test: No such file or directory
127
##################
En ZSH maintenant, on dirait que la syntaxe est acceptée pour le premier
test ?
imac% [ 85 -ge 65 ] ; echo $?
0
imac% test 85 -ge 65 ; echo $?
0
imac% /usr/bin/test 85 -ge 65 ; echo $?
zsh: no such file or directory: /usr/bin/test
127
#################
imac% [ 85 -lt 65 ] ; echo $?
1
imac% test 85 -lt 65 ; echo $?
1
imac% /usr/bin/test 85 -lt 65 ; echo $?
zsh: no such file or directory: /usr/bin/test
127
Apparemment la commande test est dans /bin, pas dans /usr/bin
Cordialement,
--
J. B.
Avatar
Olivier Miakinen
Le 21/10/2019 01:55, Joseph-B a écrit :
Je te propose deux fois trois essais :

tu veux dire que je copie-colle directement dans le terminal le bloc 1
puis le 2 ?

Oui, ou directement l'ensemble de six instructions. J'ai juste fait deux
blocs parce que je testais « supérieur ou égal » dans le premier et
« strictement inférieur » dans le second, mais ça n'avait pas vraiment
d'importance.
Si oui, voilà ce que ça donne pour le 1
en BASH
**********
imac:~ iMacJB$ set[ 85 -ge 65 ] ; echo $?
-bash: set[ 85 -ge 65 ]: command not found

Il y a eu une fausse manip de copier-coller ici, mais peu importe car
le reste a fonctionné.
En gros, j'en déduis :
[ 85 -ge 65 ] : ok, répond vrai
test 85 -ge 65 : ok, répond vrai
[ 85 -lt 65 ] : ok, répond faux
test 85 -lt 65 : ok, répond faux
Du coup, je ne comprends pas du tout l'erreur que tu avais avec mon
script. Peut-être qu'il faut mettre la constante en premier ?
imac:~ iMacJB$ /usr/bin/test 85 -ge 65 ; echo $?
-bash: /usr/bin/test: No such file or directory

Bon, et là je ne m'attendais pas à ce qu'il soit dans /bin, ainsi
que tu le remarques ensuite.
Mais finalement j'ai trouvé un moyen de ne pas utiliser du tout
les tests sur des nombres. Ta trace précédente m'a montré que le
calcul du code ascii fonctionne, et c'était ce dont je craignais
le plus qu'il ne fonctionne pas.
Du coup :
function b64index()
{
ascii=$(printf "%d" "'$1")
if [ $(LANG=C expr "_$1" : '_[A-Z]') == 2 ]; then
# A(65)..Z(90) : 0..25 i.e. ascii - 65
echo $(( "$ascii" - 65 ))
return 0
fi
if [ $(LANG=C expr "_$1" : '_[a-z]') == 2 ]; then
# a(97)..z(122) : 26..51 i.e. ascii - 71
echo $(( "$ascii" - 71 ))
return 0
fi
if [ $(LANG=C expr "_$1" : '_[0-9]') == 2 ]; then
# 0(48)..9(57) : 52..61 i.e. ascii + 4
echo $(( "$ascii" + 4 ))
return 0
fi
case "$1" in
"+") echo 62 ;;
"/") echo 63 ;;
"=") echo 64 ;;
*) return 1 ;;
esac
return 0
}
Cordialement,
--
Olivier Miakinen
Avatar
yamo'
Salut,
Olivier Miakinen a tapoté le 20/10/2019 20:37:
Je n'ose plus espérer que ce sera le dernier...

Tu as déjà pensé à git,svn ou autre?
Sinon, j'ai essayé sans succès une version (je ne sais plus laquelle et
sur raspbian 10). C'est un sujet qui m'intéresse mais que je ne maîtrise
pas du tout.
--
Stéphane
Avatar
Elephant Man
Le 20/10/2019 à 21:45, Manfred La Cassagnère a écrit :
C'est Korben Dallas qui le 20 octobre 2019 a eu le culot d'écrire:
quel gâchis de discuter de ça sur fr.test, alors que ça aurait eu ça
place sur le groupe de programmation mac...

C'est amusant de te voir t'ériger en donneur de leçons sur ce thème,
alors que tu pollues régulièrement Usenet en postant n'importe quoi
n'importe comment et n'importe où... "Fais ce que je dis, mais ne fais
pas ce que je fais"!

C'est pas pour être d'accord avec lui mais il a un peu raison, le but de
fr.test est de vérifier la bonne diffusion de ses articles, et même si
c'est passionnant il me semble que vous vous en êtes tous un peu
éloignés, sans compter que Free nettoie les articles tous les matins, ce
qui ne permet pas un bon suivi. Par contre je pense pas que le bon endroit
soit fr.comp.sys.mac.programmation mais un plutôt fr.comp.lang.* (je sais
pas lequel, c'est pas mon truc).
Avatar
Elephant Man
Le 20/10/2019 à 21:45, Manfred La Cassagnère a écrit :
C'est Korben Dallas qui le 20 octobre 2019 a eu le culot d'écrire:
quel gâchis de discuter de ça sur fr.test, alors que ça aurait eu ça
place sur le groupe de programmation mac...

C'est amusant de te voir t'ériger en donneur de leçons sur ce thème,
alors que tu pollues régulièrement Usenet en postant n'importe quoi
n'importe comment et n'importe où... "Fais ce que je dis, mais ne fais
pas ce que je fais"!

C'est pas pour être d'accord avec lui mais il a un peu raison, le but de
fr.test est de vérifier la bonne diffusion de ses articles, et même si
c'est passionnant il me semble que vous vous en êtes tous un peu
éloignés, sans compter que Free nettoie les articles tous les matins, ce
qui ne permet pas un bon suivi. Par contre je pense pas que le bon endroit
soit fr.comp.sys.mac.programmation mais plutôt un fr.comp.lang.* (je sais
pas lequel, c'est pas mon truc).
Avatar
Olivier Miakinen
Le 21/10/2019 10:00, yamo' a écrit :
Salut,
Olivier Miakinen a tapoté le 20/10/2019 20:37:
Je n'ose plus espérer que ce sera le dernier...

Tu as déjà pensé à git,svn ou autre?

Pour échanger avec deux heureux possesseurs de Mac sur fr.test ?
Je n'en vois pas trop l'intérêt. De mon point de vue, chacune des
versions fonctionnait, et je cherche juste à ce qui fonctionne
chez moi marche aussi sur Mac. Je n'ai en revanche aucune raison
de vouloir garder une trace des versions précédentes.
Sinon, j'ai essayé sans succès une version (je ne sais plus laquelle et
sur raspbian 10). C'est un sujet qui m'intéresse mais que je ne maîtrise
pas du tout.

Je ne le maîtrise pas non plus. Au boulot les sources sont gérés sur CVS
depuis le début (une trentaine d'années), et tant que ça fonctionne on n'a
pas de raison de changer.
--
Olivier Miakinen
1 2 3