1

bon voila j'ai fait un readline trivial et un truc qui coupe un buffer terminé par zéro en tokens genre argc argv
je donne en vrac, pour le fun. Certainement incomplet et buggé, mais ça marchotte pour ce que ça me sert, je corrigerai plus tard ou selon vos suggestions.

ça gère correctement des trucs genre abc "de' f" (2 tokens ici)
/*released under WTFPL*/ #include <stdio.h> #ifdef __MINGW32__ #include <conio.h> #else #error dont know how to disable line buffering #endif // MINGW32 int readline(const char *prompt, char *buf, int len) { int n=0; int c; printf("%s",prompt); while(n<(len-1)) { c = getch(); //printf(">> %02X\n",c); if(c=='\n' || c=='\r') { //end of line printf("\n"); break; } else if(c==0x08) { //backspace if(n>0) { n--; printf("\x08 \x08"); } } else if(c==0x03) { //break buf[n++] = 0; return E_BREAK; } else if(c=='\t') { //tab //manage completion later } else { buf[n++]=c; printf("%c",c); } } //append final zero buf[n++] = 0; //done return 0; }
/*released under WTFPL*/ #define DEBUG_PARSER static char ** shell_parse(char *buf, int *out_total) { char ** toks = NULL; char *start; int index, count=0; int len = strlen(buf); int quotes = 0; //quoting state index = 0; start = buf; while(buf[index]) { #ifdef DEBUG_PARSER printf("-> [%02X] ", buf[index]); #endif if(buf[index]=='\'') { if(quotes!=2) { if(quotes == 1) { quotes = 0; } else if(quotes==0) { quotes = 1; } memmove(buf+index, buf+index+1, len-index); } #ifdef DEBUG_PARSER printf("quote->%d\n",quotes); #endif } else if(buf[index]=='"') { if(quotes!=1) { if(quotes == 2) { quotes = 0; } else if(quotes==0) { quotes = 2; } memmove(buf+index, buf+index+1, len-index); } #ifdef DEBUG_PARSER printf("quote->%d\n",quotes); #endif } else if(buf[index]==' ' && quotes==0) { //unquoted space, split token count++; toks = realloc(toks, count*sizeof(char*)); toks[count-1] = start; //save old token start buf[index] = 0; //mark end of token start = buf+index+1; //mark new token start #ifdef DEBUG_PARSER printf("split [%s]\n",toks[count-1]); #endif } else { //normal char #ifdef DEBUG_PARSER printf("normal\n"); #endif } index++; } if(strlen(start)>0) { //last characters form the last token count++; toks = realloc(toks, count*sizeof(char*)); toks[count-1] = start; //save old token start buf[index] = 0; //mark end of token #ifdef DEBUG_PARSER printf("last-> [%s]\n", toks[count-1]); #endif } *out_total = count; return toks; }

2

C'est un appât à Folco ? tongue
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

3

4

J'ai fait un truc assez similaire pour PedroM ya quelques semaines tongue

On part du principe que pour un fichier X, le fichier system\X est son fichier de conf. Il contient les switches qu'on a la flemme de taper à chaque fois. Il supporte les commentaires (#blabla). Le rapport avec ton code, c'est que ça découpe le fichier en un tableau de strings, de la forme (int argc, const char** argv), puis ça le passe à une autre fonction de la lib qui sait traiter (argc, argv), en appelant les fonctions qui correspondent à chaque switch. Par contre, je gère pas les quotes... Pas bête ^^
pdtlib::ParseConfigFile
;=============================================================================== ; ; pdtlib::ParseConfigFile ; ; in a1 char** argv ; d0.b char comment char ; ; 4(sp) CMDLINE* cl ; 8(sp) void* Data ; 12(sp) char* SwitchesList ; 16(sp) ushort (*Error) (void* Data asm (a0), ; char* ErrorToken (a1)) ; 20(sp) ushort (*ArgSwitch1) (void* Data asm (a0), ; short Sign asm (d1.w) ; ) ; 24(sp) (*) ... ; ; out ; ; destroy std ; ; Parse the config file of the calling program ; ;=============================================================================== DEFINE pdtlib@0011 movem.l d3-d6/a2-a3,-(sp) ;----------------------------------------------------------------------- ; The reject string of strcspn is built dynamically, ; to include comment char ;----------------------------------------------------------------------- move.l #$20090D00,d4 ; Space, htab, EOL, NULL move.b d0,d4 ; Comment char ;----------------------------------------------------------------------- ; Write the config file name in a stack frame, stripping folder name ;----------------------------------------------------------------------- lea -18(sp),sp movea.l sp,a2 RAMT RAM_kernel::SystemDir \Dir: move.b (a0)+,(a2)+ bne.s \Dir subq.l #1,a2 movea.l (a1),a0 \Strip: move.b (a0)+,d0 bne.s \Bs movea.l (a1),a0 bra.s \File \Bs: cmpi.b #'\',d0 bne.s \Strip \File: move.b (a0)+,(a2)+ bne.s \File ;----------------------------------------------------------------------- ; Check the existence and the type of the file ;----------------------------------------------------------------------- movea.l sp,a0 suba.l a1,a1 moveq.l #$FFFFFFE0,d1 ; TEXT_TAG bsr CheckFileType tst.w d0 beq \NoConfigFile bmi \NotAText ;----------------------------------------------------------------------- ; Alloc a handle (same size than the file) ;----------------------------------------------------------------------- movea.l sp,a0 bsr GetFilePtr move.w (a0),-(sp) clr.w -(sp) ROMT HeapAlloc addq.l #4,sp move.w d0,d3 ; d3 = handle beq \Memory movea.w d3,a0 trap #3 movea.l a0,a2 ; a2 = handle ptr movea.l sp,a0 bsr GetFilePtr ; a0 = file ptr addq.l #4,a0 ;----------------------------------------------------------------------- ; Parse the file to write data in the buffer ;----------------------------------------------------------------------- moveq.l #1,d5 ; d5 = argc (1, beacause it contains the program name) \Loop: suba.l a1,a1 move.b d4,d0 bsr SkipDummyLines pea (a0) clr.w -(sp) ; Reject string termination move.l d4,-(sp) pea (sp) pea (a0) ROMT strcspn lea 14(sp),sp movea.l (sp)+,a0 subq.w #1,d0 bmi.s \EOF \Copy: move.b (a0)+,(a2)+ dbf.w d0,\Copy addq.w #1,d5 ; argc ++ clr.b (a0)+ bra.s \Loop \EOF: ;----------------------------------------------------------------------- ; Get size used in the handle, even it and add the size of argv table ;----------------------------------------------------------------------- move.l a2,d6 movea.w d3,a0 trap #3 sub.l a0,d6 ; used size addq.l #2,d6 ; size must be even bclr.b #0,d6 ; d6 = size of the handle, before argv table move.w d5,d0 add.w d0,d0 add.w d0,d0 add.l d6,d0 move.l d0,-(sp) move.w d3,-(sp) ROMT HeapRealloc addq.l #6,sp tst.w d0 beq.s \MemoryAndFree ;----------------------------------------------------------------------- ; Fill argv table. First ptr is ignored ;----------------------------------------------------------------------- movea.w d0,a0 trap #3 ; a0 = base handle ptr lea 0(a0,d6.l),a2 ; a2 = argv lea 4(a2),a1 move.w d5,d0 beq.s \Exec subq.w #1,d0 \ArgvLoop: move.l a0,(a1)+ \Skip: tst.b (a0)+ bne.s \Skip dbf.w d0,\ArgvLoop \Exec: ;----------------------------------------------------------------------- ; Prepare stack for ManageCmdline, and fill CMDLINE struct ;----------------------------------------------------------------------- lea 18(sp),sp movea.l (sp)+,a3 movea.l (sp),a0 move.w d5,ARGC(a0) move.l a2,ARGV(a0) clr.w CURARG(a0) bsr ManageCmdline move.w d3,(sp) ROMT HeapFree pea (a3) bra.s \Final ;----------------------------------------------------------------------- ; Error handling ;----------------------------------------------------------------------- \MemoryAndFree: move.w d3,(sp) ROMT HeapFree \Memory: moveq.l #PDTLIB_MEMORY_ERROR,d0 bra.s \End \NotAText: moveq.l #PDTLIB_WRONG_CONFIG_FILE,d0 bra.s \End \NoConfigFile: moveq.l #PDTLIB_NO_CONFIG_FILE,d0 \End: lea 18(sp),sp \Final: movem.l (sp)+,d3-d6/a2-a3 rts
pdtlib::ManageCmdline
;=============================================================================== ; ; pdtlib::InitCmdline ; ; in a0 CMDLINE* cl ; a1 char** argv ; d0.w int argc ; ; out nothing ; ; destroy nothing ; ; Initialize command line parsing ; ;=============================================================================== DEFINE pdtlib@0000 move.l a1,ARGV(a0) move.w d0,ARGC(a0) ;=============================================================================== ; ; pdtlib::ResetCmdline ; ; in a0 CMDLINE* cl ; ; out nothing ; ; destroy nothing ; ; Reset command line parsing ; ;=============================================================================== DEFINE pdtlib@0010 clr.w CURARG(a0) rts ;=============================================================================== ; ; pdtlib::GetNextArg ; ; in a0 CMDLINE* cl ; ; out a0 char* NextArg ; ; destroy d0.w ; ; Return a pointer to the next arg while parsing with ManageCmdline, or ; NULL if no arg remain ; ;=============================================================================== DEFINE pdtlib@0003 GetNextArg: addq.w #1,CURARG(a0) ;=============================================================================== ; ; pdtlib::GetCurrentArg ; ; in a0 CMDLINE* cl ; ; out a0 char* CurrentArg ; ; destroy d0.w ; ; Return a pointer to the current arg while parsing with ManageCmdline, ; or NULL if parsing is finished ; ;=============================================================================== DEFINE pdtlib@0002 move.w CURARG(a0),d0 cmp.w ARGC(a0),d0 bls.s \Fail movea.l ARGV(a0),a0 movea.l 0(a0,d0.w),a0 rts \Fail: suba.l a0,a0 rts ;=============================================================================== ; ; pdtlib::ManageCmdline ; ; in 4(sp) CMDLINE* cl ; 8(sp) void* Data ; 12(sp) char* SwitchesList ; 16(sp) ushort (*ArgNotSwitch) (void* Data asm (a0)) ; 20(sp) ushort (*ArgSwitch1) (void* Data asm (a0), ; short Sign asm (d1.w) ; ) ; 24(sp) (*) ... ; ; out d0.w ushort ; ; destroy std ; ; Look at pdtlib.h for return values ; ; SwitchesList format: ; ; dc.b 'ShortSwitch1', "Long Switch 1", 0 ; dc.b 'ShortSwitch2', "Long Switch 2", 0 ; dc.b ... ; dc.b 0, 0 ; ; 'ShortSwitchX' or "Long Switch X" may be 0, if no such switch exists ; When both are 0, this is the end of the table ; ;=============================================================================== DEFINE pdtlib@0001 ManageCmdline: movem.l a2-a5,-(sp) movem.l 5*4(sp),a2-a5 \Loop: movea.l a2,a0 bsr.s GetNextArg move.l a0,d0 beq.s \EndOfParsing movea.l a4,a1 moveq.l #0,d2 move.w #'+',d1 ; Clear upper byte for (*ArgSwitch) cmp.b (a0)+,d1 beq.s \CheckSecondSign addq.w #2,d1 ; - cmp.b -1(a0),d1 bne.s \ArgNotSwitch \CheckSecondSign: cmp.b (a0)+,d1 beq.s \LongSwitch ; This is a short switch moveq.l #PDTLIB_INVALID_SWITCH,d0 tst.b (a0) bne.s \EndOfParsing tst.b -(a0) beq.s \EndOfParsing moveq.l #PDTLIB_SWITCH_NOT_FOUND,d0 \ShortSwitchLoop: tst.b (a1) bne.s \TestShortSwitch tst.b 1(a1) beq.s \EndOfParsing \TestShortSwitch: cmpm.b (a0)+,(a1)+ beq.s \ShortSwitchFound \SkipShortSwitchLoop: tst.b (a1)+ bne.s \SkipShortSwitchLoop addq.w #4,d2 subq.l #1,a0 bra.s \ShortSwitchLoop ; This is a long switch \LongSwitch: moveq.l #PDTLIB_INVALID_SWITCH,d0 tst.b (a0) beq.s \EndOfParsing moveq.l #PDTLIB_SWITCH_NOT_FOUND,d0 pea (a0) \LongSwitchLoop: movea.l (sp),a0 addq.l #1,a1 \TestLongSwitch: cmpm.b (a0)+,(a1)+ beq.s \TestEndOfLongSwitch subq.l #1,a1 \SkipLongSwitchLoop: tst.b (a1)+ bne.s \SkipLongSwitchLoop addq.w #4,d2 tst.b (a1) bne.s \LongSwitchLoop tst.b 1(a1) bne.s \LongSwitchLoop bra.s \EndOfParsing ; This is not a switch \ArgNotSwitch: movea.l a3,a0 jsr (a5) ; Check return value of callbacks \CheckReturnValue: subq.w #1,d0 beq.s \Loop subq.w #1,d0 bne.s \WrongReturnValue moveq.l #PDTLIB_STOP_PARSING,d0 bra.s \EndOfParsing \WrongReturnValue: moveq.l #PDTLIB_WRONG_RETURN_VALUE,d0 \EndOfParsing: movem.l (sp)+,a2-a5 rts \TestEndOfLongSwitch: tst.b -1(a0) bne.s \TestLongSwitch ; LongSwitchFound addq.l #4,sp \ShortSwitchFound: movea.l a3,a0 movea.l 9*4(sp,d2.w),a1 jsr (a1) bra.s \CheckReturnValue

Pour la petite histoire, un programme au standard PedroM à mon standard exporte sa table de switches en monprog@0000. Comme ça, un programme en appelant plusieurs autres (au pif, gcc), peut lire les arguments, savoir à quel softs ils appartiennent, et les passer au bon programme au bon moment.
La table des switches est simple, c'est une table de string, sachant que le premier caractère de chaque string représente la version courte du switch (-x ou +x), la suite la version longue (--zyx ou ++ xyz). Evidemment, une seule des deux versions peut être présente.
avatar
<<< Kernel Extremist©®™ >>>
Feel the power of (int16) !

5

[zeph] post effacé par erreur

6

[zeph] post effacé par erreur

7

./1 : la question des quotes, des backslashes et des espaces est assez étrange j'ai l'impression. Je n'ai pas testé mais quelques remarques en vrac :

- Si on utilise un \ on passe en mode "1" (vive les constantes magiques grin) et on y reste jusqu'à utiliser à nouveau un autre \. Par exemple \""plop" va me donner "plop" au lieu de "plop ;
- De même, un \ fait passer dans un mode étrange où tout ce qui suit est ignoré, \a b c donne a b c au lieu de 3 paramètres séparés ;
- Les backslashes ne sont pas gérés à l'intérieur d'une paire de quotes (ils sont ignorés).

Sinon la fonction modifie le tableau passé en entrée, c'est assez dommage puisque ça limite considérablement les cas d'utilisation (impossible de lui donner un tableau qu'on souhaite conserver intact, ou bien n'importe quelle chaîne constante).

[edit] arg, à cause d'un bug introduit ce soir sur l'edit inline je viens d'effacer tes deux posts PpHd. Désolé, le bug est corrigé mais je n'ai aucun moyen de récupérer le contenu des posts sorry
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

8

squalyl (./1) :
#ifdef __MINGW32__ #include <conio.h> #else #error dont know how to disable line buffering #endif // MINGW32

Il faut inclure <curses.h> pour avoir getch sous *nix.
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

9

Zeph (./7) :
./1 : la question des quotes, des backslashes et des espaces est assez étrange j'ai l'impression. Je n'ai pas testé mais quelques remarques en vrac :

- Si on utilise un \ on passe en mode "1" (vive les constantes magiques grin) et on y reste jusqu'à utiliser à nouveau un autre \. Par exemple \""plop" va me donner "plop" au lieu de "plop ;
- De même, un \ fait passer dans un mode étrange où tout ce qui suit est ignoré, \a b c donne a b c au lieu de 3 paramètres séparés ;- Les backslashes ne sont pas gérés à l'intérieur d'une paire de quotes (ils sont ignorés).

Euh, à ce que je vois, son code ne gère pas du tout les backslashes, seulement les ' et ".
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

10

Zeph (./7) :
[edit] arg, à cause d'un bug introduit ce soir sur l'edit inline je viens d'effacer tes deux posts PpHd. Désolé, le bug est corrigé mais je n'ai aucun moyen de récupérer le contenu des posts


Tant pis. Il n'y avait rien d'important.

11

effectivement je ne gère pas les backslashes, seulement les guillemets simples et doubles, et abc "de"f donnera deux arguments abc et def.

merci kevin pour l'idée, mais je ne vais pas faire une dépendance sur curses pour un truc aussi simple, donc je passerai par getchar et un tc[s|g]etattr pour désactiver le buffering temporairement. (sinon je me serais pas fait chier et j'aurais déja utilisé gnu readline, mais mon code ne sera pas gpl contrairement à cette lib).

zeph: oui en effet je modifie le paramètre sur place, je m'étais accordé ça parce que ce paramètre est en fait une variable locale char[] qui est écrasé à chaque commande par les nouvelles données de readline.
Dans une autre version je faisais 2 passes, une pour compter les paramètres et l'autre pour les stocker dans un truc malloc()é une fois pour toutes avec assez de place pour les arguments et les pointeurs. Là j'avais un peu la flemme tongue

(pour les quotes c'est pas des constantes magiques, c'est le nombre de quotes embarrassed 0=QUOTE_NONE, 1=QUOTE_SINGLE, 2=QUOTE_DOUBLE, 012 ça allait plus vite embarrassed)

si vous avez des règles de gestion plus intelligentes pour les quotes je veux bien les lire, mais je veux que ça reste simple, c'est pas le code principal de mon programme grin

et d'ailleurs vous suxez, vous avez pas vu que count n'était pas initialisée et que ça partait dans le décor au 2e appel embarrassed

12

./9 & ./10 : ah oui en effet j'ai lu trop vite et pris le '\' pour un backslash ^^
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

13

Pourquoi ne pas avoir utilisé strtok ?
avatar
Proud to be CAKE©®™


GCC4TI importe qui a problème en Autriche, pour l'UE plus et une encore de correspours nucléaire, ce n'est pas ytre d'instérier. L'état très même contraire, toujours reconstruire un pouvoir une choyer d'aucrée de compris le plus mite de genre, ce n'est pas moins)
Stalin est l'élection de la langie.

14

parce que j'aime bien réinventer la roue.

ok strtok va couper aux espaces, mais comment va t il gérer les espaces inclus dans des chaines quotées?

15

Il ne le fait pas c'est vrai
avatar
Proud to be CAKE©®™


GCC4TI importe qui a problème en Autriche, pour l'UE plus et une encore de correspours nucléaire, ce n'est pas ytre d'instérier. L'état très même contraire, toujours reconstruire un pouvoir une choyer d'aucrée de compris le plus mite de genre, ce n'est pas moins)
Stalin est l'élection de la langie.

16

17

Sinon pour la lecture, il existe aussi libedit, qui est équivalente à readline, mais sous licence Berkeley.
Ceci étant dit, libedit dépend de libcurses donc pas sûr que ça te fasse tripper.

18

./12 : Zeph et les backslashes, une grande histoire d'amour grin
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

19

ouep grin
avatar
<<< Kernel Extremist©®™ >>>
Feel the power of (int16) !

20

Au passage, pour le readline(), je déclarerais prompt en const char *.
avatar
Maintenant j'ai la flemme de garder une signature à jour sur ce site. Je n'ai même plus ma chaîne Exec sous la main.

21

si tu veux grin

chuis vraiment pas au point sur ces conneries grin

edit: fait

j'ai aussi des merdouilles quand deux espaces se suivent, on verra après.
Et aussi des merdouilles avec le backspace de linux, z'auront qu'à faire ^H ces bandes de moules.

edit pour kevin : pas besoin de curses #include <stdio.h> #if defined( __MINGW32__ ) #define GETC_MINGW #include <conio.h> #elif defined(__linux__) #include <unistd.h> #include <termios.h> #define GETC_LINUX #else #error do something about unbuffer char reading #endif #include "studis.h" int readline(const char *prompt, char *buf, int len) { int n=0; int c; int ret = 0; #ifdef GETC_LINUX struct termios tio; #endif printf("%s",prompt); #ifdef GETC_LINUX tcgetattr(STDIN_FILENO,&tio); tio.c_lflag &=(~ICANON & ~ECHO); tcsetattr(STDIN_FILENO,TCSANOW,&tio); #endif while(n<(len-1)) { #ifdef GETC_MINGW c = getch(); #endif #ifdef GETC_LINUX c = getchar(); #endif

[edit] [zeph] correction de la balise [code]

22

squalyl (./21) :
#ifdef GETC_MINGW
c = getch();
#endif
#ifdef GETC_LINUX
c = getchar();#endif



#if defined(GETC_MINGW)
#elif defined(GETC_LINUX)
#elif defined(...)
#else
# error "paf"
#endif

ne serait pas mieux ?

23

ton patch est mal formaté embarrassed

nan, comme ça, ça fait paf tout seul, l'avait qu'à à utiliser les plateformes que je supporte, eh embarrassed

24

grin

25

squalyl (./21) :
edit pour kevin : pas besoin de curses

Mais tu as des tonnes de #ifdefs au lieu d'un seul pour le nom du header. (Et sinon, il y a aussi un portage W32 de pdcurses, donc tu pourrais même utiliser curses partout.)
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

26

c'est hors de question.

toi qui aimes le nostub, tu devrais comprendre ma démarche.

27

Oui et non… Sous GNU/Linux, les dépendances sont gérées automatiquement, donc ce n'est pas comparable, mais il est vrai que sur les systèmes d'exploitation inférieurs (propriétaires), c'est le bordel. Et il est vrai aussi que si c'est pour getch seulement, curses fait un peu overkill. smile
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

28

Même pas en rêve.

Ceci dit c'est publié en WTFPL, alors si tu penses que tu dois l'améliorer pour ton projet, vas y.