Autocomplétion pacman-g2 en bash

Ainsi que certains ont pu le remarquer, il n’existe pas d’autocomplétion pour la commande pacman-g2. Cette fonctionnalité est pourtant bien pratique! Nous allons voir dans cet article comment fonctionne l’autocomplétion avec bash (pas de zsh et autres pour l’instant pacman-g2. Nous resterons pour l’instant dans un cadre simple : compléter le nom des paquets.
L’autocomplétion n’est pas une fonctionnalité par défaut de bash. Dans le cas de Frugalware, vous devez installer, si ce n’est déjà fait, le paquet bash-completion :

pacman-g2 -S bash-completion

Le fonctionnement de l’autocomplétion est assez simple : on associe
à une commande quelconque, une «fonction de complétion»
prenant en entrée la liste des paramètres déjà tapés, et en sortie une
liste contenant les complétions possibles.
Ces « scripts » sont situés dans le répertoire « /etc/bash_completion.d/ ».
Dans le cas de pacman-g2, voici le script en question :

# Programmable bash completion for Pacman-g2.
#
# Copyright 2009 Albar Boris "elentir ^ frugalware.org"
#
# Distributed under the terms of the GNU General Public License v2.
#

_pacman-g2 ()
{
	local cur prev first cmd repos

	repos=$(grep '\[' /etc/pacman-g2.conf | grep -v -e 'options' -e '^#' | tr -d '[]' )

	# follows the inclusions
	Includes=$(cat /etc/pacman-g2.conf | grep "^Include[' ']*=" | sed "s/Include[' ']*=[' ']*//")
	for i in $Includes; do
		repos+=" $(grep '\[' $i | grep -v -e 'options' -e '^#' | tr -d '[]' )"
	done

	COMPREPLY=()
	cur=${COMP_WORDS[COMP_CWORD]}
	prev=${COMP_WORDS[COMP_CWORD-1]}
	first=${COMP_WORDS[1]}

	cmd=""
	case "$first" in
	-*S*|-*R*) # we expect at least one package now
		for i in $repos; do
			if [[ "$cur" == " " ]]; then
				fileSync=$(ls /var/lib/pacman-g2/$i)
			else
				fileSync=$(ls /var/lib/pacman-g2/$i | grep ^$cur)
			fi
			for j in $fileSync; do
				cmd+=$(echo "${j%-*-*} ")
			done
		done
		;;
	-Qo)
		;;
	-*Q*)
		if [[ "$cur" == " " ]]; then
			fileSync=$(ls /var/lib/pacman-g2/local/$i)
		else
			fileSync=$(ls /var/lib/pacman-g2/local/$i | grep ^$cur)
		fi
		for i in $fileSync; do
			cmd+=$(echo ${i%-*-*})
			cmd+=' '
		done
		;;
	-*A*)
		cmd=$(ls | grep .fpm$)
		;;
	*)
		;;
	esac

	COMPREPLY=( $( compgen -W "$cmd"  -- $cur ) )
	return 0
}
complete -F _pacman-g2 -o default pacman-g2
complete -F _pacman-g2 -o default pacman

Voyons dans un premier temps les variables qui entrent en jeu :

COMP_WORDS est un tableau contenant les divers paramètres déjà entrés, par exemple si on a tapé :

pacman-g2 -Si alsa

le tableau contiendra alors les éléments « -Si » et « alsa ».

COMP_CWORDS indique l’index du dernier élément du tableau précédent (dans le cas de l’exemple ci-dessus : 2)

De ces deux variables, on en déduit l’élément à compléter (le dernier) que l’on appelle « cur » et qui est définit par :

cur=${COMP_WORDS[COMP_CWORD]}

COMPREPLY qui comme son nom
l’indique contient la liste des possibilité de complétion. On peut
considérer cette variable comme la « valeur de retour » de la
fonction.

Toute la difficulté désormais consiste à renvoyer la bonne liste de possibilités en fonctions des commandes entrées.
Nous allons nous limiter aux fonctions les plus couramment utilisés de pacman-g2 sans nous préoccuper des cas particuliers. Nous considèrerons pour cela qu’une commande de pacman-g2 est toujours de la forme :

pacman-g2 -Opération paquet1 paquet2 ...

sauf pour la commande

pacman -Qo

qui prend en paramètre un fichier et non un nom de paquet.

Un petit rappel sur le fonctionnement de pacman-g2 nous indique que la base de donnée de paquets est situé dans le répertoire « /var/lib/pacman-g2 ». Chaque paquet est identifié par un dossier unique de la forme :

"nom du dépôt"/"nom du paquet"-"version"-"pkgrel"

chaque dossier contenant les informations propres au paquet considéré.
Les paquets déjà installés sur la machine, quant à eux, sont listés dans le dépôt /var/lib/pacman-g2/local ». On utilise uniquement ce dépôt pour les commandes du type pacman-g2 -Q * » puisque ces commandes s’adressent spécifiquement à ces paquets.

La suite n’est que du bash, on récupère le nom des dépôts dans le fichier « /etc/pacman-g2.conf » :

repos=$(grep ‘\[' /etc/pacman-g2.conf | grep -v -e 'options' -e '^#' | tr -d '[]‘ )

Puis on cherche le nom des dossiers commençant par $cur (càd le mot à compléter) dans chaque dépôt avec :

ls | grep ^$cur"

On supprime ensuite le numéro de version et le pkgrel du nom des dossiers avec un commande avec un « echo » approprié :

echo ${i%-*-*}

Les différentes commandes de pacman ( « -S », « -Q » ou autres ) sont traités les différents cas dans une
simple commande « case ».

A partir de cette liste, on utilise la commande compgen qui génère la liste que l’on met dans la variable COMPREPLY.

Enfin les deux dernières commandes du script permettent d’associer aux commandes pacman et pacman-g2 (sous frugalware pacman est un lien vers pacman-g2), la fonction de complétion que l’on a définit ci-dessus.

Voilà, nous avons vu un script qui reste très basique, mais rien
n’empèche maintenant de l’améliorer en rajoutant la complétion de
toutes les options de pacman-g2 et en tenant compte de toutes les possibilités d’options.

A priori la couleur n’est pas supporté dans la complétion bash, il
serait intéressant de voir si on ne peut pas ajouter un support des
couleurs dans bash-completion.
Peut-être le sujet d’un prochain article…

Lien vers le script

Voyage au pays des groupes

Hier soir,  j’ai poussé Gap sur le dépôt current de Frugalware. C’est un logiciel permettant de faire des calculs en algèbre notamment en théorie des groupes et théorie des corps.  C’est une belle occasion, pour ce premier article, d’approcher un peu ses fonctionnalités, et un beau prétexte pour faire un petit détour par la théorie des groupes.
Pour cette introduction à Gap, nous allons prendre un problème classique : Définir le groupe le groupe alterné A_5, et vérifier qu’il est simple et non résoluble.

Le groupe A_5 peut se définir à partir du groupe symétrique S_5 :

Définissons donc S_5 :

S5 := SymmetricGroup(5);
Sym( [ 1 .. 5 ] )

comme le groupe symétrique d’indice 5; C’est le groupe des permutations d’un ensemble de 5 éléments. Il est d’ordre 5!=120 comme on peut le voir avec :

Order(S5);
120

On peut visualiser ses éléments avec la commande :

Elements(S5)

On peut alors définir le groupe alterné A_5 de plusieurs manières :

– soit comme l’ensemble des permutations d’ordre pair (c’est à dire décomposable en un nombre pair de transpositions);
– soit comme le noyau du morphisme signature;
– soit dériver le groupe S_5;

On va utiliser la dernière manière :

A5:=DerivedSubgroup(S5);
Group([ (1,3,2), (2,4,3), (2,3)(4,5) ])

On vérifie rapidement qu’il s’agit bien de A_5 :

C’est d’abord bien un sous-groupe distingué de S_5 :

IsSubgroup(S5,A5);
true
IsNormal(S5,A5);
true

De plus il est composé de permutations paires et d’ordre \frac{(5!)}{2}=60 :

List(Elements(A5),x -> SignPerm(x));
[ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ]
Order(A5);
60

Une autre manière plus rapide aurait-été de définir directement :

A5 := AlternatingGroup(5);
Alt( [ 1 .. 5 ] )

Voyons maintenant les propriétés interessantes qu’il a :

IsSimple(A5);
true
IsCyclic(A5);
false
IsSolvable(A5);
false

Il est donc simple, non cyclique et non résoluble ! Une conséquence importante de ce résultat est qu’il existe des équations de degré 5 qui ne sont pas résoluble par radicaux. Considérons en effet le groupe de Galois du polynôme qui n’est autre que S_5 qui n’est pas résoluble puisque A_5 ne l’est pas.