J'ai compliqué un peu la chose en faisant de l'allocation dynamique pour
une entrée dont je ne connais pas la taille par avance et que je ne peux
pas calculer.
Ci-dessous le code, tous commentaires bien venus.
-------------------------------------------------------------------------
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum bat_states { UNKNOW, CHARGED, CHARGING, DISCHARGING };
int get_battery_state(int);
#define MALLOC_SIZE 16
int
get_battery_state(int n)
{
int ret = -1;
char *basedir = "/sys/class/power_supply";
char *type = "BAT";
char *basename = "status";
char *fn;
size_t fn_size;
FILE *fp;
int s,
i;
char *nlp; /* newline position */
char *buf; /* buffer for fgets */
size_t buf_size;
char *line; /* line read */
size_t line_size;
char *r; /* return of fgets */
/*
* generate filename
*/
s = snprintf(NULL, 0, "%s/%s%d/%s", basedir, type, n, basename) + 1;
if (s == -1) {
fprintf(stderr, "snprintf(NULL, 0, ...) failed\n");
return -1;
}
fn_size = s;
fn = malloc(fn_size);
if (!fn) {
perror("fn = malloc()");
return -1;
}
s = snprintf(fn, fn_size, "%s/%s%d/%s", basedir, type, n, basename);
if ((s == -1) || ((size_t) s > (fn_size - 1))) {
fprintf(stderr, "snprintf(fn, %d, ...) return %d\n",
(int) fn_size, s);
if (fn)
free(fn);
return -1;
}
/*
* open filename
*/
fp = fopen(fn, "r");
if (!fp) {
perror("fopen() failed");
free(fn);
return -1;
}
free(fn);
/*
* allocate space for buf
*/
buf = malloc(MALLOC_SIZE);
if (!buf) {
perror("buf = malloc()");
fclose(fp);
return -1;
}
/*
* initialisation
*/
line = NULL;
line_size = 0;
i = 0;
if (MALLOC_SIZE < 2) {
fprintf(stderr, "MALLOC_SIZE must be at least 2\n");
fclose(fp);
free(buf);
return -1;
}
/*
* read data
*/
while ((r = fgets(buf, MALLOC_SIZE, fp))) {
nlp = strchr(buf, '\n');
if (nlp)
*nlp = '\0';
/*
* add one byte for the first allocation to include '\0'
*/
line_size += (i == 0 ? 1 : 0);
buf_size = (nlp ? strlen(buf) : (MALLOC_SIZE - 1));
line_size += buf_size;
line = realloc(line, line_size);
if (!line) {
perror("realloc(line)");
fclose(fp);
free(buf);
return -1;
}
if (i == 0) /* line MUST have a '\0' */
*line = '\0';
i++;
strncat(line, buf, buf_size);
if (nlp)
break;
}
free(buf);
if (!r) { /* fgets return NULL */
if (feof(fp))
fprintf(stderr, "fgets receive EOF\n");
else if (ferror(fp))
perror("fgets()");
else
fprintf(stderr, "you should never see this message!");
if (line)
free(line);
fclose(fp);
return -1;
}
fclose(fp);
/*
* compare
*/
if (strcmp(line, "Full") == 0)
ret = CHARGED;
else if (strcmp(line, "Discharging") == 0)
ret = DISCHARGING;
else if (strcmp(line, "Charging") == 0)
ret = CHARGING;
else {
fprintf(stderr, "unknow battery state: %s\n", line);
ret = UNKNOW;
}
free(line);
Petite série de questions au passage :
- comment faites vous pour être sûr que les allocations soient bien
libérées ?
- peut-on remplacer la série if, else if, etc. de strcmp() par un
switch ?
- ce code me parrait bien compliqué pour faire peu de chose, peut-on
le simplifier (tout en gardant la gestion des erreurs) ?
J'ai bien essayé de faire un point de sortie unique de la fonction mais
le code se retrouve inexorablement collé à droite de l'écran (et
j'utilise des indentations de quatre espaces).
mais c'est surtout en respectant les principes de la programmation structurée (un seul return par fonction, par exemple), que le code est 'naturellement correct'...
Je l'ai bien compris, disons que j'ai juste du mal à le mettre en place.
- peut-on remplacer la série if, else if, etc. de strcmp() par un switch ?
Non. L'usage général est if-else if. switch-case est limité aux entiers.
Je sais bien mais je me demandais s'il n'y avait pas une méthode pour faire renvoyer un entier en fonction de ce qui est contenu dans « line ». Je trouve un switch plus lisible que des if-else.
- ce code me parrait bien compliqué pour faire peu de chose, peut-on le simplifier (tout en gardant la gestion des erreurs) ?
On peut surtout mieux le structurer. Il faut aussi séparer le lecture des traitements...
Je ne comprends pas ces deux réponses, pourrais-tu donner un exemple ?
J'ai bien essayé de faire un point de sortie unique de la fonction mais le code se retrouve inexorablement collé à droite de l'écran (et j'utilise des indentations de quatre espaces).
Utilise 3 espaces. Si ça déborde encore, c'est qu'il n'y a pas assez de découpage en fonctions...
J'aime bien mes quatre espaces.
-- Benoit Izac
Bonjour,
le 15/03/2009 à 12:26, -ed- <emmanuel.delahaye@gmail.com> a écrit dans
le message
<cd6aa4c8-acd8-4256-9c64-85d9fae55171@g38g2000yqd.googlegroups.com> :
- comment faites vous pour être sûr que les allocations soient bien
libérées ?
mais c'est surtout en respectant les principes de la programmation
structurée (un seul return par fonction, par exemple), que le code est
'naturellement correct'...
Je l'ai bien compris, disons que j'ai juste du mal à le mettre en place.
- peut-on remplacer la série if, else if, etc. de strcmp() par un
switch ?
Non. L'usage général est if-else if. switch-case est limité aux
entiers.
Je sais bien mais je me demandais s'il n'y avait pas une méthode pour
faire renvoyer un entier en fonction de ce qui est contenu dans
« line ». Je trouve un switch plus lisible que des if-else.
- ce code me parrait bien compliqué pour faire peu de chose, peut-on
le simplifier (tout en gardant la gestion des erreurs) ?
On peut surtout mieux le structurer. Il faut aussi séparer le lecture
des traitements...
Je ne comprends pas ces deux réponses, pourrais-tu donner un exemple ?
J'ai bien essayé de faire un point de sortie unique de la fonction mais
le code se retrouve inexorablement collé à droite de l'écran (et
j'utilise des indentations de quatre espaces).
Utilise 3 espaces. Si ça déborde encore, c'est qu'il n'y a pas assez
de découpage en fonctions...
mais c'est surtout en respectant les principes de la programmation structurée (un seul return par fonction, par exemple), que le code est 'naturellement correct'...
Je l'ai bien compris, disons que j'ai juste du mal à le mettre en place.
- peut-on remplacer la série if, else if, etc. de strcmp() par un switch ?
Non. L'usage général est if-else if. switch-case est limité aux entiers.
Je sais bien mais je me demandais s'il n'y avait pas une méthode pour faire renvoyer un entier en fonction de ce qui est contenu dans « line ». Je trouve un switch plus lisible que des if-else.
- ce code me parrait bien compliqué pour faire peu de chose, peut-on le simplifier (tout en gardant la gestion des erreurs) ?
On peut surtout mieux le structurer. Il faut aussi séparer le lecture des traitements...
Je ne comprends pas ces deux réponses, pourrais-tu donner un exemple ?
J'ai bien essayé de faire un point de sortie unique de la fonction mais le code se retrouve inexorablement collé à droite de l'écran (et j'utilise des indentations de quatre espaces).
Utilise 3 espaces. Si ça déborde encore, c'est qu'il n'y a pas assez de découpage en fonctions...
J'aime bien mes quatre espaces.
-- Benoit Izac
Antoine Leca
Le 15/03/2009 12:13Z, Xavier Roche écrivit :
Xavier Roche a écrit :
En effet, les chaînes sont en général constantes (*)
J'ai oublié le (*): de mémoire, le standard impose que les chaînes constantes soient.. constantes.
Je ne sais pas comment interpréter cela.
Cependant, à propos de la norme, celle-ci explicite que les littéraux chaînes peuvent être (au choix de l'implémentation) traités comme const (=readonly) ou traités comme modifiables.
Pour celui qui écrit des programmes portables, cela signifie donc qu'il faut considérer les littéraux chaînes comme const, donc interdit d'y écrire. Pour celui qui fait de l'adaptation, celui signifie que si le programme plante quand il écrit dans une chaîne (ou si on est averti de cette éventualité), on peut chercher si le compilateur possède une option qui autorise ce genre de manipulation (avec en contre-partie un moindre niveau d'optimisation); sinon il faut réécrire, en général cela représente une copie de chaîne supplémentaire.
Antoine
Le 15/03/2009 12:13Z, Xavier Roche écrivit :
Xavier Roche a écrit :
En effet, les chaînes sont en général constantes (*)
J'ai oublié le (*): de mémoire, le standard impose que les chaînes
constantes soient.. constantes.
Je ne sais pas comment interpréter cela.
Cependant, à propos de la norme, celle-ci explicite que les littéraux
chaînes peuvent être (au choix de l'implémentation) traités comme const
(=readonly) ou traités comme modifiables.
Pour celui qui écrit des programmes portables, cela signifie donc qu'il
faut considérer les littéraux chaînes comme const, donc interdit d'y écrire.
Pour celui qui fait de l'adaptation, celui signifie que si le programme
plante quand il écrit dans une chaîne (ou si on est averti de cette
éventualité), on peut chercher si le compilateur possède une option qui
autorise ce genre de manipulation (avec en contre-partie un moindre
niveau d'optimisation); sinon il faut réécrire, en général cela
représente une copie de chaîne supplémentaire.
En effet, les chaînes sont en général constantes (*)
J'ai oublié le (*): de mémoire, le standard impose que les chaînes constantes soient.. constantes.
Je ne sais pas comment interpréter cela.
Cependant, à propos de la norme, celle-ci explicite que les littéraux chaînes peuvent être (au choix de l'implémentation) traités comme const (=readonly) ou traités comme modifiables.
Pour celui qui écrit des programmes portables, cela signifie donc qu'il faut considérer les littéraux chaînes comme const, donc interdit d'y écrire. Pour celui qui fait de l'adaptation, celui signifie que si le programme plante quand il écrit dans une chaîne (ou si on est averti de cette éventualité), on peut chercher si le compilateur possède une option qui autorise ce genre de manipulation (avec en contre-partie un moindre niveau d'optimisation); sinon il faut réécrire, en général cela représente une copie de chaîne supplémentaire.
De memoire, gcc propose un -fwrite-strings jusqu'a 2.95, et cette option a ensuite disparu...
L'immense majorite des programmes qui avaient besoin de cette option ont ete modifies pour ne plus en avoir besoin. Ca n'est generalement pas trop complique, sauf tentative d'IOCCC manifeste.
In article <gpiui1$378$1@shakotay.alphanet.ch>,
Antoine Leca <root@localhost.invalid> wrote:
De memoire, gcc propose un -fwrite-strings jusqu'a 2.95, et cette option a
ensuite disparu...
L'immense majorite des programmes qui avaient besoin de cette option ont
ete modifies pour ne plus en avoir besoin. Ca n'est generalement pas trop
complique, sauf tentative d'IOCCC manifeste.
De memoire, gcc propose un -fwrite-strings jusqu'a 2.95, et cette option a ensuite disparu...
L'immense majorite des programmes qui avaient besoin de cette option ont ete modifies pour ne plus en avoir besoin. Ca n'est generalement pas trop complique, sauf tentative d'IOCCC manifeste.
Xavier Roche
Antoine Leca a écrit :
Je ne sais pas comment interpréter cela. Cependant, à propos de la norme, celle-ci explicite que les littéraux chaînes peuvent être (au choix de l'implémentation) traités comme const (=readonly) ou traités comme modifiables.
Ah, damned - ca serait en C++ que la restriction aurait été introduite ?
Antoine Leca a écrit :
Je ne sais pas comment interpréter cela.
Cependant, à propos de la norme, celle-ci explicite que les littéraux
chaînes peuvent être (au choix de l'implémentation) traités comme const
(=readonly) ou traités comme modifiables.
Ah, damned - ca serait en C++ que la restriction aurait été introduite ?
Je ne sais pas comment interpréter cela. Cependant, à propos de la norme, celle-ci explicite que les littéraux chaînes peuvent être (au choix de l'implémentation) traités comme const (=readonly) ou traités comme modifiables.
Ah, damned - ca serait en C++ que la restriction aurait été introduite ?
Benoit Izac
Bonjour,
le 15/03/2009 à 12:37, Xavier Roche a écrit dans le message <gpip9j$nne$ :
Cela pourrait être encapsulé de manière plus zen: [...]
Hum, je ne vois pas d'erreur évidente dans le code. Ah, si: vsnprintf() peut laisser "args" dans un état totalement incohérent.
<http://www.opengroup.org/onlinepubs/000095399/functions/vprintf.html> "(..) As these functions invoke the va_arg macro, the value of ap after the return is unspecified."
Je ne vois pas d'autre coquille pour le reste.
char *str; va_start(args, format); len = vsnprintf(NULL, 0, format, args); + va_end(args); if (len < 0) { return NULL; } @@ -25,7 +26,9 @@ if (str == NULL) { return NULL; } + va_start(args, format); if (vsnprintf(str, len + 1, format, args) > len) { + va_end(args); free(str); return NULL; }
Benoit Izac a écrit :
J'ai testé mais ça coredump sévère :
Hum, je ne vois pas d'erreur évidente dans le code. Ah, si: vsnprintf()
peut laisser "args" dans un état totalement incohérent.
<http://www.opengroup.org/onlinepubs/000095399/functions/vprintf.html>
"(..) As these functions invoke the va_arg macro, the value of ap after
the return is unspecified."
Je ne vois pas d'autre coquille pour le reste.
char *str;
va_start(args, format);
len = vsnprintf(NULL, 0, format, args);
+ va_end(args);
if (len < 0) {
return NULL;
}
@@ -25,7 +26,9 @@
if (str == NULL) {
return NULL;
}
+ va_start(args, format);
if (vsnprintf(str, len + 1, format, args) > len) {
+ va_end(args);
free(str);
return NULL;
}
Hum, je ne vois pas d'erreur évidente dans le code. Ah, si: vsnprintf() peut laisser "args" dans un état totalement incohérent.
<http://www.opengroup.org/onlinepubs/000095399/functions/vprintf.html> "(..) As these functions invoke the va_arg macro, the value of ap after the return is unspecified."
Je ne vois pas d'autre coquille pour le reste.
char *str; va_start(args, format); len = vsnprintf(NULL, 0, format, args); + va_end(args); if (len < 0) { return NULL; } @@ -25,7 +26,9 @@ if (str == NULL) { return NULL; } + va_start(args, format); if (vsnprintf(str, len + 1, format, args) > len) { + va_end(args); free(str); return NULL; }
Benoit Izac
Bonjour,
le 15/03/2009 à 21:26, Xavier Roche a écrit dans le message <gpjoa9$nne$ :
Hum, je ne vois pas d'erreur évidente dans le code. Ah, si: vsnprintf() peut laisser "args" dans un état totalement incohérent.
<http://www.opengroup.org/onlinepubs/000095399/functions/vprintf.html> "(..) As these functions invoke the va_arg macro, the value of ap after the return is unspecified."
Bravo ! Tout s'explique, ça marche au poil maintenant.
-- Benoit Izac
Bonjour,
le 15/03/2009 à 21:26, Xavier Roche a écrit dans le message
<gpjoa9$nne$10@news.httrack.net> :
Hum, je ne vois pas d'erreur évidente dans le code. Ah, si:
vsnprintf() peut laisser "args" dans un état totalement incohérent.
<http://www.opengroup.org/onlinepubs/000095399/functions/vprintf.html>
"(..) As these functions invoke the va_arg macro, the value of ap
after the return is unspecified."
Bravo ! Tout s'explique, ça marche au poil maintenant.
le 15/03/2009 à 21:26, Xavier Roche a écrit dans le message <gpjoa9$nne$ :
Hum, je ne vois pas d'erreur évidente dans le code. Ah, si: vsnprintf() peut laisser "args" dans un état totalement incohérent.
<http://www.opengroup.org/onlinepubs/000095399/functions/vprintf.html> "(..) As these functions invoke the va_arg macro, the value of ap after the return is unspecified."
Bravo ! Tout s'explique, ça marche au poil maintenant.