Précédent   Forum du club des développeurs et IT Pro > Autres langages > Pascal > Free Pascal
Free Pascal Le compilateur Pascal multiplateforme
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse
 
Outils de la discussion
Publicité
'
Vieux 06/03/2012, 18h34   #1
eldoir
Invité régulier
 
Homme Arthur Cousseau
Inscription : octobre 2011
Messages : 23
Détails du profil
Informations personnelles :
Nom : Homme Arthur Cousseau
Localisation : France, Maine et Loire (Pays de la Loire)

Informations forums :
Inscription : octobre 2011
Messages : 23
Points : 7
Points : 7
Par défaut Stocker des sous-parties d'une chaîne : isoler les paramètres d'une commande

Bonjour,
Voilà pour le contexte : je voudrais faire un jeu de hacking.
Le joueur rentrerait des commandes pour trouver une adresse IP, essayer de passer le firewall, etc, etc... je n'y connais rien en hacking mais j'ai toujours eu envie de m'y essayer, malheureusement ce n'est pas simple
Du coup, je me suis dit que j'allais faire un jeu pour ceux qui sont comme moi intéressés par le hacking, bien sûr ce serait pour de faux mais assez réaliste tout de même
Donc voilà, je veux imiter le comportement d'une console UNIX.
Je définirai mes propres commandes, écrirai le mode d'emploi de chaque commande moi-même, etc... Voilà pour la théorie

Malheureusement en pratique ce beau rêve est gâché par un problème ma foi relativement simple : et pourtant, je ne vois pas l'erreur !
Il s'agit de récupérer la commande que va entrer l'utilisateur et d'en repérer les composantes :
[cmd] [-opt] [arg]
Cette syntaxe ne doit pas vous être inconnue
Donc en gros, l'utilisateur saisit une chaîne de caractères, et moi j'identifie les morceaux et je stocke le nom de la commande dans une variable, l'option dans une autre, et l'argument dans une troisième variable.
Et évidemment, si la saisie de l'utilisateur n'est pas de cette forme, eh bien ça plante
Voilà le code que j'ai déjà écrit.

Code :
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
program hack;
 
var cmd, ch, opt, arg : string;
	i, comptEsp : integer;
begin
	comptEsp := 0; cmd := ' '; opt := ' '; arg := ' ';
	readln(ch);
	for i := 1 to length(ch) do
		if ch[i] = ' ' then comptEsp := comptEsp + 1;
	if comptEsp = 0 then cmd := ch // Si il n'y a pas d'espace dans la chaîne alors la chaîne est une commande seule
	else // Sinon, alors on va séparer la chaîne en morceaux
	begin
		i := 1;
		while (ch[i] <> ' ') do // On commence par mettre le premier morceau de la chaîne dans la var. cmd
		begin
			cmd[i] := ch[i];
			i := i + 1
		end;
		i := i + 1; // Au sortir du while, la position i correspond dans la chaîne à l'espace, donc on itère pour passer au premier caractère du morceau suivant
		if comptEsp = 1 then 
			while (ch[i] <> ' ') do // Si il y a un espace alors on met le second morceau de la chaîne dans la var. opt
			begin
				opt := ch[i];
				i := i + 1
			end;
		i := i + 1;
		if comptEsp = 2 then // Si il y a deux espaces alors la chaîne était de la forme [cmd] [-opt] [arg]  ( comme par ex. "ls -a MesDocuments" en code console )
			while (i <= length(ch)) do
			begin
				arg[i] := ch[i];
				i := i + 1
			end
		else // Si finalement il n'y avait qu'un espace alors le deuxième morceau ne peut qu'être un argument et non pas une option et donc on exécute le bloc suivant pour éviter de confondre à l'avenir
		begin
			arg := opt;
			opt := ' ' // On réinitialise opt.
		end
	end;
	writeln(cmd);
	writeln(opt);
	writeln(arg)
end.
Je précise qu'il n'y a pas de problème à la compilation, seulement le résultat obtenu n'est pas satisfaisant.
Exemple : testez mon code avec ls -a Documents. ( bien sûr je ne reprendrai pas les mêmes commandes que celles existantes c'est juste un exemple )
Le programme vous renverra, ligne après ligne :
- La commande
- L'option
- L'argument
Donc il devrait renvoyer :
ls
-a
Documents
Et pourtant il me renvoie :
l
[rien]
[rien]
Consternant !!!
eldoir est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 06/03/2012, 22h37   #2
M.Dlb
Rédacteur/Modérateur
 
Avatar de M.Dlb
 
Inscription : avril 2002
Messages : 2 273
Détails du profil
Informations personnelles :
Âge : 28

Informations forums :
Inscription : avril 2002
Messages : 2 273
Points : 3 377
Points : 3 377
Et non ce n'est pas consternant !

Le problème vient d'une "mauvaise" gestion des chaînes de caractères. Premièrement, il faut savoir que le premier octet d'une chaîne en Pascal contient la longueur de la chaîne, ce premier octet on peut y accéder par l'intermédiaire de l'index 0 (souvenez vous les chaînes de caractères ne sont pas autres choses que des tableaux). Donc, la valeur stockée dans ch[0] doit retourner le nombre de caractères dans la chaîne ch...

Autre chose, l'affectation d'une valeur à une variable String modifie également ce nombre. Le fait d'initialiser cmd à ' ' va donc écrire 1 dans cmd[0] puis l'équivalent du caractère ASCII espace (c'est-à-dire le code 32 en décimal). Cependant, si on travaille comme avec un tableau, case par case (comme tu le fais), aucun mécanisme ne met à jour ce compteur en positon [0].

Sachant ces deux points, tu as affecté à cmd, opt et arg les valeurs ' ', donc la taille de la chaîne que ces variables contiennent sera de 1 caractère (mais attention comme ce sont des variables de type string standard sans spécification de taille, 256 octets sont alloués...). Cela veut dire que si tu fais des ajouts dans la chaîne (sans dépasser 255 caractères !) le compteur en position [0] n'est jamais modifié, donc l'ordinateur croira que ta chaîne fait toujours 1 caractère de long. D'où le fait que ton affichage ne fasse qu'une seule lettre (pour le deuxième et troisième il n'affiche rien du tout ou il affiche un espace te faisant croire qu'il n'affiche rien ?...)

Heureusement y'a une parade. Au lieu de travailler case par case, tu peux utiliser la concaténation de chaîne (opérateur +) qui met à jour le fameux compteur... La ligne

devient

Pareil pour les autres !
__________________
M.Dlb - Modérateur z/OS - Rédacteur et Modérateur Pascal
M.Dlb est déconnecté   Envoyer un message privé Réponse avec citation 10
Vieux 07/03/2012, 01h08   #3
eldoir
Invité régulier
 
Homme Arthur Cousseau
Inscription : octobre 2011
Messages : 23
Détails du profil
Informations personnelles :
Nom : Homme Arthur Cousseau
Localisation : France, Maine et Loire (Pays de la Loire)

Informations forums :
Inscription : octobre 2011
Messages : 23
Points : 7
Points : 7
Merci beaucoup pour ces explications je me suis repenché sur mon code, corrigé et optimisé deux-trois trucs par là et voilà le résultat 100% fonctionnel pour ceux que ça intéresse !!!!

Code :
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
program hack;
 
var cmd, ch, arg : string[50];
	i, comptEsp : integer;
	opt : char;
begin
	comptEsp := 0;
	readln(ch);
	for i := 1 to length(ch) do
		if ch[i] = ' ' then comptEsp := comptEsp + 1;
	if comptEsp = 0 then cmd := ch // Si il n'y a pas d'espace dans la chaîne alors la chaîne est une commande seule
	else // Sinon, alors on va séparer la chaîne en morceaux
	begin
		i := 1;
		while (ch[i] <> ' ') do // On commence par mettre le premier morceau de la chaîne dans la var. cmd
		begin
			cmd := cmd + ch[i];
			i := i + 1
		end;
		i := i + 1; // Au sortir du while, la position i correspond dans la chaîne à l'espace, donc on itère pour passer au premier caractère du morceau suivant
		if comptEsp = 2 then // Si il y a 2 espaces dans la chaine on sait que le deuxième morceau est une option
		begin
			i := i + 1;   // On itère pour passer au-dessus du tiret de l'option qui ne nous intéresse pas
			while (ch[i] <> ' ') do
			begin
				opt := ch[i];
				i := i + 1
			end;
			i := i + 1;
			while (i <= length(ch)) do
			begin
				arg := arg + ch[i];
				i := i + 1
			end
		end
		else if comptEsp = 1 then // Si la chaîne n'a qu'un espace alors le second morceau est un argument
			while (i <= length(ch)) do
			begin
				arg := arg + ch[i];
				i := i +1
			end
		else // Si la chaîne ne comporte pas 0, 1 ou 2 espaces alors ça plante
			writeln('La requête n''a pu être effectuée.')
	end;
	writeln('|',cmd,'|');
	writeln('|',opt,'|');
	writeln('|',arg,'|')
end.
Merci encore à toi
eldoir est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 07/03/2012, 05h23   #4
Paul TOTH
Expert Confirmé Sénior
 
Avatar de Paul TOTH
 
Homme Paul TOTH
Freelance
Inscription : novembre 2002
Messages : 4 393
Détails du profil
Informations personnelles :
Nom : Homme Paul TOTH
Âge : 43
Localisation : Réunion

Informations professionnelles :
Activité : Freelance
Secteur : High Tech - Éditeur de logiciels

Informations forums :
Inscription : novembre 2002
Messages : 4 393
Points : 10 728
Points : 10 728
un petit conseil, tu devrais écrire une fonction qui lit une chaîne et retourne un argument...

Code :
1
2
3
4
5
6
7
 
function GetSubString(const Str: string; var Start: Integer; var SubString: string): Boolean;
begin
  // SubString = la sous-chaine à partir de Start jusque la fin ou un espace
 // au retour, Start pointe sur la nouvelle position de départ
 // retour False si on est en fin de chaîne
end.
Et voici à quoi pourrait ressemble le code...
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 
var
  Ch: string;
  Cmd: string;
  Index: Integer;
begin
  ReadLn(Ch);
  Index := 1;
  if GetSubString(Ch, Index, Cmd) then
  begin
    if Cmd = 'ls' then
     ls(Ch, Index)
    else
     WritELn('Commande inconnue');
  end;
end;
de même, la fonction LS
Code :
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
 
procedure ls(const Ch: string; Index: Integer);
var
  Opt : string;
  OptA: Boolean;
  Arg : string;
begin
  while GetSubString(ch, Index, Opt) do
  begin
    if Opt[1] = '-' then
    begin
      if Opt = '-a' then
         OptA := True
      else begin
         WriteLn('Option inconnue');
         Exit;
      end;
    end else 
      if Arg = '' then
        Arg := Opt
      else begin
        WriteLn('Trop de paramètres');
        Exit;
      end;
  end;
  if Arg = '' then
  begin  
    WriteLn('Paramètre obligatoire');
    Exit;
  end;
  ...
end;
__________________
Developpez.com: Mes articles, forum FlashPascal
Entreprise: Execute SARL
Produits : UPnP, RemoteOffice, FlashPascal
Embarcadero : Ile de la Réunion, Dephi, C++Builder, RADPHP...TVA à 8,5%
Paul TOTH est déconnecté   Envoyer un message privé Réponse avec citation 10
Réponse Cette discussion est résolue.
Outils de la discussion

Navigation rapide


Fuseau horaire GMT +2. Il est actuellement 12h43.


 
 
 
 
Partenaires

Hébergement Web