Comment puis-je surmonter le comportement incohérent de snprintf dans différents systèmes d'exploitation de type UNIX?

StackOverflow https://stackoverflow.com/questions/100904

Question

Par pages de manuel, snprintf renvoie le nombre d'octets écrits à partir de la version 2.2 de glibc. Mais sur les versions inférieures de libc2.2 et de HP-UX, il renvoie un entier positif, ce qui pourrait entraîner un débordement de la mémoire tampon.

Comment peut-on surmonter cela et écrire du code portable?

Modifier: faute de plus de clarté

Ce code fonctionne parfaitement dans la lib 2.3

    if ( snprintf( cmd, cmdLen + 1, ". %s%s", myVar1, myVar2 )  != cmdLen )
    {
        fprintf( stderr, "\nError: Unable to  copy bmake command!!!");
        returnCode = ERR_COPY_FILENAME_FAILED;
    }

Il retourne la longueur de la chaîne (10) sous Linux. Mais le même code renvoie un nombre positif supérieur au nombre de caractères imprimés sur un ordinateur HP-UX. J'espère que cette explication va bien.

Était-ce utile?

La solution

vous pouvez créer un wrapper snprintf qui renvoie -1 pour chaque cas où l'espace disponible dans la mémoire tampon est insuffisant.

Consultez la page de manuel pour obtenir plus de documents. Il contient également un exemple qui menace tous les cas.

  while (1) {
      /* Try to print in the allocated space. */
      va_start(ap, fmt);
      n = vsnprintf (p, size, fmt, ap);
      va_end(ap);
      /* If that worked, return the string. */
      if (n > -1 && n < size)
         return p;
      /* Else try again with more space. */
      if (n > -1)    /* glibc 2.1 */
         size = n+1; /* precisely what is needed */
      else           /* glibc 2.0 */
         size *= 2;  /* twice the old size */
      if ((np = realloc (p, size)) == NULL) {
         free(p);
         return NULL;
      } else {
         p = np;
      }
   }

Autres conseils

Avez-vous envisagé une implémentation portable de printf? J'en ai cherché un il y a quelques instants et j'ai opté pour le trio.

http://daniel.haxx.se/projects/trio/

Votre question n’est toujours pas claire. La page de manuel associée à parle ainsi:

  

Les fonctions snprintf () et vsnprintf () n'écrivent pas plus que des octets de taille (y compris   le '\ 0' suivant). Si la sortie a été tronquée en raison de cette limite, la valeur renvoyée est le nombre de caractères ( aurait écrit dans la chaîne finale si l'espace disponible était suffisant. . Ainsi, une valeur de retour de taille ou plus signifie que la sortie a été tronquée.

Donc, si vous voulez savoir si votre sortie a été tronquée:

int ret = snprintf(cmd, cmdLen + 1, ". %s%s", myVar1, myVar2 ) == -1)
if(ret == -1 || ret > cmdLen)
{
    //output was truncated
}
else
{
    //everything is groovy
}

La portabilité * printf soulève une foule de problèmes et, de manière réaliste, vous souhaiterez probablement suivre l'un des trois chemins suivants:

  1. Nécessite une * printf conforme à c99, car toute personne sur 9 ans devrait suffire, et il suffit de dire que la plate-forme est cassée autrement.

  2. Avoir un my_snprintf () avec un tas de # ifdef pour les plates-formes spécifiques que vous voulez supporter tout en appelant le vsnprintf () dessous (comprendre le plus petit commun dénominateur est ce que vous avez).

  3. Munissez-vous simplement d'une copie de vsnprintf () avec votre code. Pour les cas simples, il s'agit en fait d'un assez simple et pour les autres, vous voudrez probablement regarder vstr et vous obtiendrez gratuitement des formateurs clients.

... comme d'autres personnes l'ont suggéré, il est possible de fusionner les codes 1 et 2, juste pour le cas -1, mais c'est risqué car c99 * printf peut renvoyer -1 dans certains cas. conditions.

Personnellement, je vous conseillerais simplement d'utiliser une bibliothèque de chaînes telle que ustr , qui se contente de solutions de contournement pour vous et vous donne des chaînes gérées gratuitement. Si vous y tenez, vous pouvez combiner avec vstr .

J'ai trouvé un moyen portable de prédire et / ou de limiter le nombre de caractères renvoyés par sprintf et les fonctions associées, mais c'est inefficace et beaucoup le considèrent comme inélégant.

Ce que vous faites est de créer un fichier temporaire avec tmpfile (), fprintf () à cela (qui renvoie de manière fiable le nombre d'octets écrits), puis de revenir en arrière et de lire tout ou partie du texte dans un tampon.

Exemple:

int my_snprintf(char *buf, size_t n, const char *fmt, ...)
{
    va_list va;
    int nchars;
    FILE *tf = tmpfile();

    va_start(va, n);
    nchars = vfprintf(tf, fmt, va);
    if (nchars >= (int) n)
        nchars = (int) n - 1;
    va_end(va);
    memset(buf, 0, 1 + (size_t) nchars);

    if (nchars > 0)
    {
        rewind(tf);
        fread(buf, 1, (size_t) nchars, tf);
    }

    fclose(tf);

    return nchars;   
}

Utilisez plutôt la très supérieure asprintf ().

Il s’agit d’une extension GNU, mais il est utile de la copier sur la plate-forme cible si elle n’est pas disponible en mode natif.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top