30

Cette bibliothèque risque d'être difficile à utiliser avec ces listes d'argument et les valeurs de retour non directes...

Peut-être que tu pourrais ajouter des fonctions plus simples à utiliser, tout en conservant les autres si tu trouves qu'elles ont leur intérêt.
Il pourrait y avoir :
unsigned short par__ArchiveFile(const char *Archive, unsigned short Flags, const char *Folder, const char *File);
unsigned short par__ExtractFile(const char *Archive, unsigned short Flags, const char *Folder, const char *File);
unsigned short par__Remove(const char *Archive, const char *File);

J'ai gardé les mêmes noms, je pense qu'il faudrait renommer les fonctions agissant sur des va_list smile
avatar

31

Yep, pencil ./30 smile
avatar
Membre de la TI-Chess Team.
Co-mainteneur de GCC4TI (documentation en ligne de GCC4TI), TIEmu et TILP.
Co-admin de TI-Planet.

32

Pourquoi pas...
Ca simplifie énormément les choses, côté lib et côté programme d'ailleurs.

En soi, gérer une va_list en assembleur, c'est tout couillon, mais c'est vrai que ça risque de pas être évident de créer la va_list en C : il faut allouer une zone de mémoire de taille indéfinie, puis la faire passer pour une va_list. En asm, c'est 100 fois plus simple, on pousse sur la pile dans une boucle, on restaure sp à la fin comme un bourrin et c'est marre.

C'est donc ok a priori pour cette proposition. smile



Et donc pour par__List, ça se présenterait comment ?

unsigned short par_List (const char* Archive, const char* File, short* Size, short* Tag, char* Filename); ?

Les variables Size, Tag et Filename sont renseignées par la lib.
Si File == NULL, alors on renvoie les caractéristiques de l'archive : taille, nombre de fichiers (faut que Tag soit une union en fait, pour accueillir un tag ou un nombre de fichiers).



Je compte aussi ajouter un export :
const char* par__GetErrorString(int Error);
Ca renvoie un pointeur vers la chaine utilisée par le programme quand il envoie son erreur sur stderr.

Donc un programme appelant pourrait faire ça :
int err = par__Extract ("Archive1", NO_FLAG, NULL, filename);         // Pas de flag particulier, NULL pour ne pas forcer un répertoire)
if (err != PAR_NO_ERROR)
{
    printf (par__GetErrorString (err), filename);         // Il se peut que le second argument ne soit pas utilisé
}                                                         // par la string de Par (genre "Error: Memory"), mais c'est pas grave.

33

Je trouve que ta fonction par_List fait trop de choses et je n'aime pas beaucoup le retour des valeurs au travers de pointeurs.
Peut-être que tu devrais créer une structure de description des fichiers. La fonction par_List pourrait ainsi renvoyer un tableau de ces structures :
short par_List(const char *Archive, PAR_FILE *list_files); // PAR_FILE **list_files si c'est la bibliothèque qui fait l'allocation...Qui retourne le nombre de fichiers présents ou -1 en cas d'erreur, ou ce que tu veux tongue

Suivant qui a la charge de l'allocation du tableau, il peut y avoir besoin d'une fonction donnant le nombre de fichiers présents dans l'archive. En fait, ce serait bien de l'ajouter dans tous les cas, ça peut être utile smile

Ensuite, tu utilises la fonction par_List pour avoir les informations sur un fichier. Pourquoi ne pas créer une fonction dédiée ?
unsigned short par_Info(const char *Archive, char *Filename, PAR_FILE *file);
avatar

34

RHJPP (./33) :
PAR_FILE **list_files si c'est la bibliothèque qui fait l'allocation

Pourquoi pas, bonne idée encore une fois. Je suis partisan que ce soit la bibliothèque qui fasse l'allocation. J'ai pour principe de décharger au maximum les programmes de ce qui est répétitif pour le faire faire à une lib.


Récalipilutatilons :
unsigned short par__ArchiveFile (const char *Archive, unsigned short Flags, const char *Folder, const char *File); 
unsigned short par__ExtractFile (const char *Archive, unsigned short Flags, const char *Folder, const char *File); 
unsigned short par__Remove (const char *Archive, const char *File);
unsigned short par__List (const char *Archive, PAR_FILE **ListFiles);
unsigned short par__Info (const char *Archive, const char *Filename, PAR_FILE *File);
const char* par__GetErrorString(int Error);

typedef struct PAR_FILE
{
    char Filename [18];
    unsigned short Size;
    unsigned short Type;
} PAR_FILE;


Quid ?

Et un grand merci, t'es vraiment Ze Pro du design #itm# (Roh poutée qu'elle était fine celleulà) embarrassed

35

tongue

Je n'avais pas enlevé le unsigned au début de la ligne de par__List sans raison grin Comment tu fais pour savoir s'il y a eu une erreur ? Ou si tu retournes un numéro d'erreur, comment tu sais combien de fichiers ont été listés ?

Généralement, on retourne le nombre de valeurs lues ou -1 en cas d'erreur.

Je trouve qu'il manque une fonction qui donne le nombre de fichiers d'une archive, sinon, ça me semble bien smile
avatar

36

unsigned short par__ArchiveFile (const char *Archive, unsigned short Flags, const char *Folder, const char *File);  
unsigned short par__ExtractFile (const char *Archive, unsigned short Flags, const char *Folder, const char *File);  
unsigned short par__Remove (const char *Archive, const char *File); 
signed short par__List (const char *Archive, PAR_FILE **ListFiles); 
signed short par__ArchiveInfo (const char *Archive);
unsigned short par__FileInfo (const char *Archive, const char *Filename, PAR_FILE *File); 
const char* par__GetErrorString(int Error); 
 
typedef struct PAR_FILE 
{ 
    char Filename [18]; 
    unsigned short Size; 
    unsigned short Type; 
} PAR_FILE;


Les fonctions signées renvoient un nombre positif :
- par__List renvoie le nombre d'éléments de l'archive et crée une liste de PAR_FILE
- par__ArchiveInfo renvoie le nombre d'éléments de l'archive
En cas d'erreur, le nombre retourné est négatif, et est == -(erreur). tongue

Ca va comme ça ?

Je vais détailler plus les fonctions (ce que ça renvoie etc...) après le diner, pour voir si tout est cohérent.

37

désolé mais puisqu'on est dans l'élaboration de la spec, j'ajoute mes remarques:
Folco (./30) :
Le mieux serait peut-être de renvoyer un handle au format documenté, genre :

Folco (./30) :
A charge pour le programme appelant de libérer le handle évidemment.


je trouve très pas bien du tout que la lib alloue des handles comme dit avant. On est en embarqué, la mémoire est "chère", je veux controler comment elle est allouée.
je pense que ce serait 10 fois plus mieux bien de passer un handle de bonne taille à ta lib pour qu'elle le remplisse, puisque la taille du bidule est connue. Au moins l'utilisateur a le controle de l'allocation de A à Z alloc à free.
Folco (./33) :
Et donc pour par__List, ça se présenterait comment ?
Idem pour lister les fichiers, le coup de compter + retourner les infos sur l'entrée N marche est carrément mieux que de laisser la lib allouer des handles.

tu peux aussi faire un truc qui énumère les fichiers.

-soit tu files un callback à une fonction style par__EnumerateFiles(type_fn_callback the_callback, void *contexte), qui a la responsabilité d'appeler ta fonction callback pour chaque fichier en lui passant les infos sur ce fichier et le contexte (pour pouvoir relier tous les appels -- plus de détails dans un prochain post si ça t'intéresse, même si c'est juste pour la khûlture)

-soit une fonction par__listFirst(blabla) qui te renvoie les infos sur le premier fichier de la lib, et un par__listNext(blabla) qui te renvoie à chaque coup les infos sur le fichier suivant. On peut avoir une structure de "contexte" qui se souvient d'ou on en est, ou stocker ça en interne.

tu peux aussi m'envoyer bouler et faire comme tu veux, je m'offusquerai pas tongue

38

squalyl (./37) :
je trouve très pas bien du tout que la lib alloue des handles comme dit avant. On est en embarqué, la mémoire est "chère", je veux controler comment elle est allouée. je pense que ce serait 10 fois plus mieux bien de passer un handle de bonne taille à ta lib pour qu'elle le remplisse, puisque la taille du bidule est connue. Au moins l'utilisateur a le controle de l'allocation de A à Z alloc à free.

Je comprends bien, mais on est quand même en embarqué, donc au final, pourquoi ne pas centraliser ce boulot dans la lib (niveau place) ? Et de toute façon, cette mémoire sera allouée.
Je comprends que ça puisse être plus joli niveau concept, mais je ne crois pas qu'on gagne quelque chose en efficacité réelle.
squalyl (./37) :
soit tu files un callback à une fonction style par__EnumerateFiles(type_fn_callback the_callback, void *contexte), qui a la responsabilité d'appeler ta fonction callback pour chaque fichier en lui passant les infos sur ce fichier et le contexte (pour pouvoir relier tous les appels -- plus de détails dans un prochain post si ça t'intéresse

Oui, ça m'intéresse.
Si je comprends bien, plutôt que le caller dise à la lib "tu fais ça", il dit "lis-moi cette archive" ; "ok, premier fichier c'est ça" ; "ok, tu fais rien" ; "2nd fichier..." ; "ah là, tu désarchives" ; etc...
C'est ça ?

Ca me semble un peu chiant niveau perf, pour chaque occurence de fichier trouvé par la lib, le caller va devoir parcourir sa propre liste de fichiers pour savoir ce qu'il doit faire du fichier en question.

Je ne sais pas pourquoi, mais ça me fait penser à des architectures de langage de beaucoup plus haut niveau. Ton habitude du Java peut-être ?
squalyl (./37) :
-soit une fonction par__listFirst(blabla) qui te renvoie les infos sur le premier fichier de la lib, et un par__listNext(blabla) qui te renvoie à chaque coup les infos sur le fichier suivant. On peut avoir une structure de "contexte" qui se souvient d'ou on en est, ou stocker ça en interne.

Alors dans ce genre de cas, ça sera au programme de stocker ça (lib read-only powa). Comme mes fonctions pour gérer les arguments, les programmes passent une structure CMDLINE* à ma lib.
Puisqu'on est read-only, on ne peut rien retenir. Et c'est pour ça que mes fonctions utilisent beaucoup des callbacks également, pour ne pas perdre le boulot en cours.


Sinon, ça revient en fait à énumérer, mais sans callback, donc avec le problême cité au-dessus (perf).
En fait, ma première version de lib pour parser la ligne de commande faisait comme ton énumérateur. Beaucoup de code à écrire par le programme, c'était absolument pas rentable. La version actuelle ressemble beaucoup plus à ta proposition du dessus (avec callback). C'est beaucoup plus facile à gérer pour la ligne de commande, parce que la lib sait d'elle-même quoi faire avec les switches. Le programme en lui-même ne s'occupe que des arguments qui n'en sont pas (et donc a priori, des fichiers).
Je crains que ce ne soit guère efficace pour cette situation, mais je veux bien que tu détailles si j'ai mal compris.
squalyl (./37) :
tu peux aussi m'envoyer bouler et faire comme tu veux, je m'offusquerai pas

Mais pas du tout, bien au contraire, je suis mauvais en spec, je ne demande pas mieux que des avis et conseils jsutement (bon, évidemment, même si je pourrai pas tout suivre hein grin)

39

Folco (./39) :
Je comprends bien, mais on est quand même en embarqué, donc au final, pourquoi ne pas centraliser ce boulot dans la lib (niveau place) ? Et de toute façon, cette mémoire sera allouée. Je comprends que ça puisse être plus joli niveau concept, mais je ne crois pas qu'on gagne quelque chose en efficacité réelle.
ben l'intéret c'est que ça te permet d'allouer *un seul* handle au début, et d'utiliser celui là tout le temps. Allouer un nouveau handle a chaque fois me semble inutile.
Folco (./39) :
Oui, ça m'intéresse.
Si je comprends bien, plutôt que le caller dise à la lib "tu fais ça", il dit "lis-moi cette archive" ; "ok, premier fichier c'est ça" ; "ok, tu fais rien" ; "2nd fichier..." ; "ah là, tu désarchives" ; etc... C'est ça ?

presque. C'est un appel pour chaque fichier de l'archive. Avec des callbacks différents ça peut
-compter les fichiers
-les désarchiver tous ou non
-listage des noms de fichiers de l'archive

bref, tout ce qui requiert une inspection de l'archive fichier par fichier.

Exemple pour compter:
int count_files(archive a) {
int total=0;
par_enumerate(a, callback_counter, &total);
return total;
}

void callback_counter(char *filename, void *context) {
int * total = (int*)context;
(*total)++;
}

la fonction callback_counter est donc appelée pour chaque fichier et incrémente le compteur dont on lui a passé l'adresse.

idem pour afficher , etc.

40

squalyl (./39) :
ben l'intéret c'est que ça te permet d'allouer *un seul* handle au début, et d'utiliser celui là tout le temps. Allouer un nouveau handle a chaque fois me semble inutile.

Ok. Dans l'idée de ... Thepro !, le but était de renvoyer un seul handle avec tout le listing. Le caller fournit juste un pointeur vers le pointeur de ce tableau. La lib crée le tableau, lui renvoie l'adresse et le nombre d'entrées. Et ensuite, le programme fait ce qu'il veut avec les fichiers de la liste.

41

Oui, mais dans l'idée de mon ex-moi, on aurait aussi pu lui donner un pointeur vers un tableau déjà alloué smile

En fait, la méthode consistant à passer une fonction à appliquer à chaque entrée de la liste est intéressante, mais les performances pourraient en pâtir. Et il faudrait veiller à ne pas l'utiliser à mauvais escient. Par exemple, je suppose que l'archive contient directement le nombre de fichiers archivés, il ne sert à rien de les compter un par un smile

Mais tu peux aussi ajouter cette méthode de parcours de la liste. C'est vrai que ça peut être utile des fois. Lorsque l'on cherche un fichier suivant des critères non prévus par exemple. Mais cette méthode d'accès est très généraliste et ne permet pas de prendre en compte certains éléments. Par exemple, je ne sais pas comment sont organisés tes fichiers, si dans une hypothétique table des matières ils sont triés par nom ou pas, mais si c'était le cas, une recherche dichotomique serait bien plus rapide pour trouver un fichier que de parcourir toute la liste.

Je vois deux accès possibles :
- On cherche des infos sur un fichier dont on connaît le nom => Utilisation de la fonction par__FileInfo
- On veut quelque chose concernant un fichier dont on ne connaît pas le nom => Parcours avec une boucle for du tableau retourné par par__List. Ce sera bien plus rapide que d'utiliser une autre méthode.

Ici, les données sont toutes petites. Il est beaucoup plus efficace de les recopier toutes dans un tableau. Après, si par exemple le contenu du fichier devenait accessible à la fonction à appliquer, ce serait peut-être bien de le faire. Mais bon, il ne s’agirait probablement que d'un pointeur à ajouter à la structure d'information tongue
avatar

42

RHJPP (./41) :
En fait, la méthode consistant à passer une fonction à appliquer à chaque entrée de la liste est intéressante, mais les performances pourraient en pâtir.

- pourraient en pâtir + en patiront certainement.
RHJPP (./41) :
Par exemple, je suppose que l'archive contient directement le nombre de fichiers archivés

Oui, sur deux octets, pourquoi s'en priver. D'autant plus que si on n'utilise pas ça, il faut un marqueur de fin, donc au final on ne gagne pas en place.
RHJPP (./41) :
Par exemple, je ne sais pas comment sont organisés tes fichiers, si dans une hypothétique table des matières ils sont triés par nom ou pas, mais si c'était le cas, une recherche dichotomique serait bien plus rapide pour trouver un fichier que de parcourir toute la liste.

Non, c'est une archive, pas une base de données. Même parcourir 200 fichiers, en asm et optimisé (pas de fonctions strcmp etc..., mais tout à la main (ça prend pas de place), c'est très rapide. Et le passage d'une entrée à l'autre est une simple addition. Donc je ne me fais pas de souci pour trouver vite un fichier, même au bout de l'archive.
RHJPP (./41) :
- On cherche des infos sur un fichier dont on connaît le nom => Utilisation de la fonction par__FileInfo - On veut quelque chose concernant un fichier dont on ne connaît pas le nom => Parcours avec une boucle for du tableau retourné par par__List. Ce sera bien plus rapide que d'utiliser une autre méthode.

Je pense aussi. Et autant laisser le programme faire ça, la lib n'est pas censée savoir ce que le programme recherche, et ne peut prévoir tous les cas. Au final, elle ne fournirait pas une très grande aide. AMHA le programme a déjà tout ce qu'il lui faut dans un listing.
RHJPP (./41) :
Après, si par exemple le contenu du fichier devenait accessible à la fonction à appliquer, ce serait peut-être bien de le faire. Mais bon, il ne s’agirait probablement que d'un pointeur à ajouter à la structure d'information

Et j'ai pas envie de le faire. Ca nécessite de locker l'archive. Hors je la fais bouger si, par exemple, on me dit d'y rajouter un fichier et que l'archive est en flash. C'est automatique, et le caller n'est pas censé savoir que ça va arriver, à part s'il refait le boulot de localiser l'archive et de regarder où elle est ; ça fait double emploi avec la lib qui la décharge de ce boulot : une simple C-string suffit à accéder à une archive. Un de mes buts est de rendre l'archive accessible avec un minimum de travail pour le caller.

J'ai relu il y a quelques jours tous les headers des libs d'archive/compression existantes sur TI, chaque fois une grosse part est laissée à faire au programme, et j'aime pas beaucoup ça. Pour moi, deux programmes utilisant la même lib ne devraient pas dupliquer de code.

43

Bon, je suis en train d'implémenter ça :
;=======================================================================
;       Exported functions
;
;   unsigned short  par__ArchiveFile    (const char* Archive, unsigned short Flags, const char* Folder, const char* File);
;   unsigned short  par__ExtractFile    (const char* Archive, unsigned short Flags, const char* Folder, const char* File);
;   unsigned short  par__Remove         (const char* Archive, const char* File);
;   signed short    par__List           (const char* Archive, PAR_FILE** ListFiles);
;   signed short    par__ArchiveInfo    (const char* Archive asm("%a0"));
;   unsigned short  par__FileInfo       (const char* Archive, const char* Filename, PAR_FILE* File);
;   const char*     par__GetErrorString (int Error asm("%d0"));
;
;=======================================================================

J'ai écrit les fonctions List, ArchiveInfo, GetErrorString. Ce sont les plus simples, mais elles sont très utilises aux fonctions de manipulation (Archive/Extract/Remove).

Finalement, le programme s'articule en trois parties très distinctes :
- Le coeur, qui contient les fonctions réelles de manipulation de fichiers
- Le programme, qui gère les appels en ligne de commande en appellant les fonctions du coeur
- Les exports, qui appellent les fonctions du coeur également.

On pourrait donc concevoir une release en 3 parties : par-core (coeur du programme), par-lib (wrappers pour les dlls), par-app (application en ligne de commande).
Je pense que ça restera une vue de l'esprit, on a pas besoin d'un tel extrémisme sur TI, surtout que la place à gagner n'est pas monumentale.


Maintenant, j'écris parce que j'ai une question : je ne vois en fait pas trop l'intérêt de la fonction FileInfo. A partir du moment où le programme dispose d'une liste de PAR_FILE, il peut se démerder simplement, non ? Trouver une chaine dans un tableau dont on connait la structure c'est pas plus de trois lignes, non ?

Et le plus embêtant, je pense, c'est qu'un programme qui veut savoir si tel fichier est dans telle archive, va devoir créer une liste de PAR_FILE pour toute l'archive, alors qu'au final, un seul fichier servira.

Est-ce qu'il ne serait pas plus simple de pouvoir chercher un fichier dans une archive ?
Je propose ce proto :
// renvoie 0 si le fichier est trouvé, sinon un code d'erreur. Crée et renseigne le Buffer (24 octets) pour donner des infos sur le fichier.

unsigned short par__FileInfo (const char* Archive, const char* Filename, PAR_FILE** Buffer);

RHJPP, je ne sais pas si c'est de ça dont tu parlais pour FileInfo, mais le proto que tu donnes en ./33 ne correspond pas à ça (je crois que tu pensais plus à deux étapes : création de la liste pour toute l'archive, puis recherche du fichier kivabien par la lib dans la liste qu'il a renvoyé).

44

Rechercher un fichier et en retourner les informations, c'est exactement le but du par_Info que je donnais.

Dans ta fonction par__FileInfo, le paramètre File n'est pas en entrée mais en sortie. Et il n'y a pas besoin de double indirection puisque tu connais déjà la taille de la structure. On n'a pas besoin que la librairie alloue de la mémoire pour nous. Et pas de liste de PAR_FILE à connaître...

PAR_FILE bidule;

if (par__FileInfo("Mon_archive", "bidule", &bidule)) {
    // le fichier n'est pas là => erreur...
} else {
    // bidule contient les informations dont j'ai besoin...
}

Peut-être qu'il faudra ajouter un paramètre pour le dossier smile
avatar

45

Le dossier peut-être inclus au nom de l'archive ou au nom du fichier, pas de souci. Sauf si tu parles des informations crées par Par, alors elles sont renvoyées avec.

Et donc on pensait bien à la même chose, sauf que tu as raison, c'est plus simple de stocker ces infos sur la pile que dans le heap en effet.
Merci bien. smile