Solution de remplacement ou solution de contournement pour asprintf sous AIX

J’essaie de construire python-kerberos sous AIX. kerberospw.c utilise un appel à asprintf, mais d’après ce que me dit Google, asprintf n’existe pas sous AIX.

J’ai vu http://www.koders.com/c/fidAA9B130D588302673A28B568430A83131B7734C0.aspx?s=windows.h , qui semble pouvoir créer un remplaçant asprintf, mais je ne sais pas où cela irait ou comment je l’aurais #include dans kerberospw.c.

Est-il possible d’utiliser l’exemple de koders.com ou un autre code pour “simuler” asprintf? Puis-je simplement inclure la fonction asprintf comme indiqué dans kerberospw.c? Je ne suis pas un codeur C, mais

asprintf (char ** resultp, const char * format, …)

ne ressemble pas à une signature valide pour moi avec les points à la fin. La ligne pertinente de kerberospw.c est en dessous

asprintf (& message, “%. * s:%. * s”, (int) result_code_ssortingng.length,
(char *) result_code_ssortingng.data,
(int) result_ssortingng.length,
(char *) result_ssortingng.data);

Je me rends compte que je pourrais contacter l’auteur de python-kerberos, mais a) je pense qu’il serait utile d’avoir un correctif potentiel si je le faisais, et b) il est possible que je rencontre un autre logiciel utilisant asprintf, et ce serait bien d’avoir une solution de contournement.

asprintf est une variante de la famille de fonctions printf qui alloue un tampon pour contenir la mémoire de la chaîne mise en forme et la renvoyer. C’est une fonction avec un nombre variable d’arguments (d’où le ... dans la déclaration qui est un code C valide). Vous pouvez trouver une description ici .

Il peut être réimplémenté relativement facilement si le vsnprintf fonctionne correctement (c’est-à-dire, renvoyer une erreur si le tampon est trop petit pour contenir la chaîne formatée).

Voici une telle implémentation:

 #include  int asprintf(char **ret, const char *format, ...) { va_list ap; *ret = NULL; /* Ensure value can be passed to free() */ va_start(ap, format); int count = vsnprintf(NULL, 0, format, ap); va_end(ap); if (count >= 0) { char* buffer = malloc(count + 1); if (buffer == NULL) return -1; va_start(ap, format); count = vsnprintf(buffer, count + 1, format, ap); va_end(ap); if (count < 0) { free(buffer); return count; } *ret = buffer; } return count; } 

S’appuyant sur la réponse de Sylvain , voici une implémentation simple avec asprintf() et vasprintf() car lorsque vous en avez besoin, vous avez généralement besoin de l’autre également. Et, étant donné la macro va_copy() de C99, il est facile d’implémenter asprintf() en termes de vasprintf() . En effet, lors de l’écriture de fonctions varargs, il est très souvent utile de les va_list par paires, l’une avec la notation ellipsis et l’autre avec l’argument va_list à la place des points de suspension, et vous implémentez sortingvialement la première par rapport à la dernière.

Cela conduit au code:

 int vasprintf(char **ret, const char *format, va_list args) { va_list copy; va_copy(copy, args); /* Make sure it is determinate, despite manuals indicating otherwise */ *ret = NULL; int count = vsnprintf(NULL, 0, format, args); if (count >= 0) { char *buffer = malloc(count + 1); if (buffer == NULL) count = -1; else if ((count = vsnprintf(buffer, count + 1, format, copy)) < 0) free(buffer); else *ret = buffer; } va_end(copy); // Each va_start() or va_copy() needs a va_end() return count; } int asprintf(char **ret, const char *format, ...) { va_list args; va_start(args, format); int count = vasprintf(ret, format, args); va_end(args); return(count); } 

La difficulté de l’utilisation de ces fonctions dans un système où elles ne sont pas fournies consiste à décider où les fonctions doivent être déclarées. Idéalement, ils seraient dans , mais vous n'auriez pas besoin de les écrire. Donc, vous devez avoir un autre en-tête qui inclut mais déclare ces fonctions si elles ne sont pas déclarées dans . Et, idéalement, le code devrait le détecter de manière semi-automatique. Peut-être que l'en-tête est "missing.h" et contient (en partie):

 #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include  #include  #ifndef HAVE_ASPRINTF extern int asprintf(char **ret, const char *format, ...); extern int vasprintf(char **ret, const char *format, va_list args); #endif /* HAVE_ASPRINTF */ 

Notez également que cette page de manuel de asprintf () indique que la valeur de retour dans le pointeur est indéterminée en cas d'erreur. D'autres pages de manuel , y compris celle référencée dans la question, indiquent qu'elle est explicitement définie sur NULL en cas d'erreur. Le document du comité C Standard ( n1337.pdf ) ne spécifie pas le comportement d'erreur en cas de manque de mémoire.

  • Si vous utilisez asprintf (), ne supposez pas que le pointeur est initialisé si la fonction échoue.
  • Si vous implémentez asprintf (), assurez-vous que le pointeur est défini sur null en cas d'erreur pour donner un comportement déterministe.

Je suis venu ici à la recherche d’une implémentation rapide pour Windows et Linux qui définit le pointeur de retour sur NULL en cas d’échec.

La réponse de Jonathan Leffler semblait être la meilleure, mais j’ai alors remarqué qu’elle ne se fixait pas à -1 lorsque malloc échoue.

J’ai fait plus de recherches et je suis tombé sur cette discussion concernant la mise en œuvre d’asprintf , ce qui m’a ensuite éclairé sur le fait que Jonathan et Sylvain ne géraient pas les débordements correctement.

Je recommande maintenant cette solution fournie avec la discussion susmentionnée, qui semble couvrir toutes les plates-formes importantes et semble gérer correctement chaque scénario de défaillance.

Voici une implémentation qui n’appelle pas snprintf() deux fois dans la plupart des cas. J’ai omis les inclus et définit comme indiqué dans les autres réponses.

Comme il se doit, définissez asprintf() comme un appel à vasprintf()

 int asprintf(char **dst, const char * pcFormat, ...) { va_list ap; va_start(ap, pcFormat); int len = vasprintf(dst, pcFormat, ap); va_end(ap); return len; } 

Nous pré-allouons un tampon à une taille appropriée prédéfinie et en cas de débordement, appelez vsnprintf() une seconde fois. La raison étant que la fonction s*printf() est considérée comme très lourde et que la mémoire globale est acceptable.

 int vasprintf(char **dst, const char * pcFormat, va_list ap) { int len = 512; /* Worked quite well on our project */ int allocated = 0; va_list ap_copy; char *buff = NULL; while(len >= allocated) { free(buff); buff = malloc(len+1); if(buff) { allocated = len+1; va_copy(ap_copy, ap); len = vsnprintf(buff, len+1, pcFormat, ap_copy); va_end(ap_copy); } else /* malloc() failed */ return -1; } *dst = buff; return len; } 

EDIT : J’ai remplacé l’appel realloc() par un simple malloc() car il coûte moins cher. En cas de dépassement de capacité, une paire free()/malloc() coûte moins que realloc() raison de sa memcpy() interne cachée memcpy() . Comme nous remplaçons de toute façon le tampon entier avec l’appel suivant à vsnprintf() , cette copie ne sert à rien.