[C] Faire un shell basique

Salut les gens,

je travaille actuellement sur un interfaçage de la carte K8055 de Velleman sous Linux. J’ai besoin de faire une interface en ligne de commande qui puisse prendre en entrée une commande et des paramètres internes au programme, du genre :

set digital 1 1 1 1 0 1 1 0
ou
get digital / set analog 128 64 / get analog

et ce genre de joyeusetés.
Donc, je veux inclure une sorte d’entrée type “shell” à mon bout de code. J’avais songé à faire un char[][], mais je ne sais pas l’utiliser (oui, je suis une moule qui apprend toute seule… mais j’vais voir des personnes qualifiées pour demander conseil ;-)). Donc, si quelqu’un avait un bout de code d’exemple à me montrer, ça serait sympa, comme dirait l’autre.

Merci pour vos réponses

scanf ?

un mot quand même sur la console en C avec la lib standard (pas curses & cie), c’est quand même d’un lourdingue… si tu peux switcher au C++, tu te prendras moin la tête à ce niveau là.

[edit : grosse faut d’ortho]

La base d’un mimi-parser en C, c’est cette fonction… http://www.cplusplus.com/ref/cstring/strtok.html

ATTENTION ! Comme pas mal de vieilles fonctions de manipulation de chaînes en C, c’est à peu près aussi dangereux que de jouer au baseball avec une grenade et un fusil de chasse. Mais bon, c’est du C :stuck_out_tongue:

[quote=“Jojosan, post:1, topic: 27455”]Salut les gens,

je travaille actuellement sur un interfaçage de la carte K8055 de Velleman sous Linux. J’ai besoin de faire une interface en ligne de commande qui puisse prendre en entrée une commande et des paramètres internes au programme, du genre :

set digital 1 1 1 1 0 1 1 0
ou
get digital / set analog 128 64 / get analog[/quote]

Si je devais faire un parser dans l’environnement que tu décris j’utiliserais flex pour l’analyse lexicale et bison pour le parser.

Note que si ton shell est très simple tu n’as pas forcément besoin d’utiliser un outil tel que bison, tu peux te contenter de quelque chose de “fait main”, mais bison peut te simplifier la tâche et limiter les erreurs.

En bon fainéant, je me suis tourné vers strtok().

Ça ne marche pas super, le premier élément est correctement parsé, mais pas le reste. Si quelqu’un se sent d’humeur à m’aider à débugger ça (code écrit avec le pied droit), je viens de passer une heure trente dessus, c’est fatigant :stuck_out_tongue:

http://jojosangnu.free.fr/C/nano_shell.c
http://jojosangnu.free.fr/C/ueib.h pour les fonctions qui vont avec.

[EDIT]
Pour ceux intéressés, je met l’utilitaire « set_digital » que j’ai écrit, qui permet d’écrire sur les sorties numériques.
http://jojosangnu.free.fr/C/set_digital.c

j’ai zieuté rapidement et je doit déclarer :

Pas de fonction dans un .h !!

Pour comprendre mes 2 points d’exclamation (oui je me suis emporté), imaginons que ton projet grossisse, et que tu inclus le ueib.h dans un autre .c :

  • tu #inclus ueib.h dans nano_shell.c
  • il copie le contenu ueib.h au début de nano_shell.c
  • il compile (hop fichier objet avec les symboles des fonctions contenues dans ueib.h)
  • tu compile un deuxième .c lambda incluent ueib.h 2eme objet avec les fonctions de ueib.h
  • tu fais l’édition des liens en indiquant tes 2 fichiers objets

Blang ! erreur :
symbole défini 2 fois, il sait pas lequel choisir, ils sont identique, il va pas les mettre au pif, ça sera un peu violent

bon ça compte pas si tes fonctions sont inline (dans ce cas le code est directement injecté dans le code appellant, donc spa rave).

Donc premièrement soulager ton .h en faisant migrer toutes tes fonctions dans un .c annexe et compiler chaque module séparement.

Sinon perso j’aurais plutôt tendance à limiter le plus possible les #include dans un .h, le strict minimum, c’est plutôt au .c d’aller inclure ce dont il a réellement besoin (donc là pifometriquement parlant le #include <string.h> devrait pas forcément être dans le .h [appa vérifié]).

bon jvais continuer à faire mon chieur, les majuscules plutôt pour les constantes

[codebox]

void shell_k8055(ueib_handle * k8055_handle)
{
char entree[20]=" ";
char * parsed;
int counter=0;
int do_we_set=0;
int do_we_get=0;
int output[8];

printf("$ ");
scanf("%s",&entree);
parsed = strtok (entree, " ");

if (strcmp("set",parsed)==0) do_we_set=1;
if (strcmp("get",parsed)==0) do_we_get=1;

for (counter = 0; counter < 8; counter++)
{
	parsed = strtok (NULL, " "); // cf. man strtok for NULL
	printf("parsed : [[%s]]\n",parsed);
	printf("%d\n",counter);

	if ( do_we_set ) output[ counter ] = atoi( &parsed );
	else if ( do_we_get )
	{
		get_k8055(k8055_handle);
		printf("debug get_k8055\n");
	}		
}

    // euh là je suppose totalement
if ( do_we_set ) set_k8055(k8055_handle, output);

}

void set_k8055(ueib_handle * k8055_handle, int coef[])
{
int i;
printf(« DEBUG !\n »);
for (i = 0; i < 8; i++) printf("%d ", coef[i]);

// Bon en zieutant le .h j'ai reperé le truc de feignant :-°
// Les outputs sont des puissances de 2 successive
// bon je pense qu'un cafzonien va surement me taper sur
// les doits à cause de ça
for (i = 0; i < 8; i++)				// 2 lignes \o/
	ueib_set_dig_output(k8055_handle, 1 << i, coef[i]);

ueib_update(k8055_handle);

}
[/codebox]

Bonne chance pour la suite de l’aprentissage :stuck_out_tongue:

Bon il faut que je sois calme et zen… ooooom.

J’suis d’accord sur les variable en majuscule, c’est CONSTANTE only

Ensuite…

AAAIIIEEEE CA PIQUE LES YEUX!
Une allocation memoire + une affectation d’une chaine constante… C’est quoi CE BORDEL?

Bon scanf, c’est une MAUVAISE IDEE. utilise fgets comme je t’ai dit hier soir.

Quand a strtok, je rappelle ce qu’il y a ecrit dans le man:

[quote]BUGS
Never use these functions. If you do, note that:

          These functions modify their first argument.[/quote]

C’est clair non?

mais mais NON! Non et Non.

[quote]RETURN VALUE
The strtok() function returns a pointer to the next token, or NULL if there are no more
tokens.[/quote]
donc

Tu gere en interne dans la boucle le fait qu’il y ait plus de 10 elements (eventuellement). Cette technique te permet aussi de gerer le fait de ne pas se planter comme une merde s’il manque un element.

Franchement jojo, tu veux commencer trop fort (je me repete), prend un bon bouquin de C avec des exercices, je sais que c’est naze mais tu apprendrais bien mieux que de faire un truc que tu comprends qu’a moitie. Surtout qu’il y a plusieurs personnes ici qui seront capable de corriger tes exos (et te mettre des notes yek yek)

LoneWolf
Cours de C en live.

Ecoute je t’encourage à plutôt avoir une structure dans ce style

[code]#include <assert.h>
#include <string.h>
#include <stdio.h>

enum ECommand
{
Command_Unknown = 0,
Command_Get = 1,
Command_Set = 2
};

inline char GetNibbleCommandSeparator()
{
return ’ ';
}

inline bool IsNibbleCommandSeparator(char cChar)
{
return (cChar == GetNibbleCommandSeparator());
}

ECommand ExtractCommandFromNibble(const char *szNibble, size_t lNibbleLength)
{

if (!lNibbleLength || !szNibble)
	return Command_Unknown;

size_t i = 0;

for(; i < lNibbleLength; i++)
{
	if (IsNibbleCommandSeparator(szNibble[i]))
		break;			
}

assert((i == lNibbleLength) || (IsNibbleCommandSeparator(szNibble[i])));

if ((i == lNibbleLength) || !i)
	return Command_Unknown;

assert(i < lNibbleLength);

// perhaps case sensitivity is not required
if (!strncmp(szNibble, "get", i))
	return Command_Get;

if (!strncmp(szNibble, "set", i))
	return Command_Set;

return Command_Unknown;

}

int main(int argc, char **argv)
{

const char *szNibbleGet = "get my ass over here";
assert(ExtractCommandFromNibble(szNibbleGet, strlen(szNibbleGet)) == Command_Get);

const char *szNibbleSet = "set your body free";
assert(ExtractCommandFromNibble(szNibbleSet, strlen(szNibbleSet)) == Command_Set);

const char *szNibbleUnknown = "blah get set";
assert(ExtractCommandFromNibble(szNibbleUnknown, strlen(szNibbleUnknown)) == Command_Unknown);

szNibbleUnknown = "gett";
assert(ExtractCommandFromNibble(szNibbleUnknown, strlen(szNibbleUnknown)) == Command_Unknown);

szNibbleUnknown = "sett";
assert(ExtractCommandFromNibble(szNibbleUnknown, strlen(szNibbleUnknown)) == Command_Unknown);

szNibbleUnknown = "";
assert(ExtractCommandFromNibble(szNibbleUnknown, strlen(szNibbleUnknown)) == Command_Unknown);

szNibbleUnknown = " ";
assert(ExtractCommandFromNibble(szNibbleUnknown, strlen(szNibbleUnknown)) == Command_Unknown);

}[/code]

Tu remarqueras qu’on met dans une fonction à part l’acquisition du premier élément lexical qui te donne un enum, ceci te permet par la suite de facilement faire des switches et de gagner en performances (car tu travailles sur des entiers et plus sur des chaînes).

Rajouter une commande est aussi très facile, mais si la grammaire devient riche tu as vraiment intérêt à passer sous Bison.

Pour les commandes d’après, il faut que tu fasses une fonction par commande, par exemple

[code]CGetParameters *ParseGetParameters(const char *szNibble, size_t lNibbleLength)
{
// …
}

// body
{
switch(ExtractedCommand)
{
case Command_Get:
pParameters = ParseGetParameters(szNibble, blah blah);
break;

}
}[/code]

dis donc moloch, on sent la grande inspiration objet de ton machin la, mais ca m’a pas l’air de pouvoir compiler en C strict… t’es sur que c’est du C?

LoneWolf
C’te code de folie.

:stuck_out_tongue: ah oui faudra p’tet un peu modifier deux trois trucs, mais en C99 ça doit passer non ? :stuck_out_tongue:

En plus il faudrait plutôt stocker les commandes dans un tableau et les parser dans une boucle, comme ça

[code]struct SCommand
{
ECommand CommandToken;
const char *szCommandString;
}

#define COMMAND_COUNT 666
SCommand pCommandList[COMMAND_COUNT] = { Command_Get, « get » // … etc.

// …
{
for(size_t i = 0; i < COMMAND_COUNT; i++)
{
if (!strncmp(szNibble, pCommandList[i].szCommandString, i))
return pCommandList[i].CommandToken;
}
}[/code]

Enfin tu vois quoi, ce sera plus simple pour ajouter des commandes par la suite.

Lony >> Ça fait mal ton commentaire. Les baffes du Loup Solitaire sont incluses dans le packaging avec le bouquin ?

/me rajoute un bouquin “J’apprends le C avec mon ordinateur pour les nuls” sur sa wishlist.

Merci pour les conseils, j’vais tâcher de lire ça.

A ma décharge, je ne parle pas le patois du programmeur, et donc les manpages ne sont pas toujours d’une grande aide :P)

Oui, il faut bien avouer que les manpages, c’est souvent chiant et rébarbatif à lire.