¿Cómo puedo superar los comportamientos incoherentes de snprintf en diferentes sistemas operativos UNIX-like?

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

Pregunta

Por las páginas man, snprintf está volviendo número de bytes escritos de glibc la versión 2.2 en adelante.Pero en versiones inferiores de libc2.2 y HP-UX, devuelve un entero positivo, lo que podría provocar un desbordamiento de búfer.

¿Cómo se puede superar esto y escribe el código portable?

Editar :Para más claridad

Este código funciona perfectamente en 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;
    }

Devuelve la longitud de la cadena (10) en Linux.Pero el mismo código devuelve un número positivo es mayor que el número de caracteres impresos en la HP-UX de la máquina.Espero que esta explicación está muy bien.

¿Fue útil?

Solución

se podría crear una snprintf contenedor que devuelve -1 para cada caso, cuando no hay suficiente espacio en el buffer.

Ver el el hombre de la página para obtener más docs.Tiene también un ejemplo de que las amenazas de todos los casos.

  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;
      }
   }

Otros consejos

Han considerado que un portátil implementación de printf?Busqué uno hace un rato, y se establecieron en trío.

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

Su pregunta no está claro aún.El el hombre de la página vinculado al habla así:

Las funciones snprintf() y vsnprintf() no escriba más de tamaño bytes (incluyendo al final el '\0').Si la salida se vio truncado debido a este límite, el valor de retorno es el número de caracteres (no incluyendo al final el '\0') que sería se han escrito para el final de la cadena si hay suficiente espacio había estado disponible.Por lo tanto, un valor de retorno de tamaño o más significa que el resultado se trunca.

Por lo tanto, si usted quiere saber si su salida fue truncado:

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

Hay toda una serie de problemas con *printf portabilidad, y de manera realista, usted probablemente querrá seguir uno de tres caminos:

  1. Requieren una c99. *compatible con printf, porque 9 años debería ser suficiente para cualquier persona, y acaba de decir que la plataforma está roto lo contrario.

  2. Tiene un my_snprintf() con un montón de #ifdef para las plataformas específicas que usted desea apoyar a todos llamando a la vsnprintf() debajo de (entender el mínimo común denominador es lo que tiene).

  3. Acaba de llevar alrededor de una copia de vsnprintf() con el código, para los escenarios de uso simple es en realidad bastante simple y para los demás, quizás quieras mirar vstr y obtendrá el cliente formateadores de forma gratuita.

...como otras personas han sugerido que puede hacer un hack de la fusión de #1 y #2, sólo por el -1 caso, pero que es arriesgado debido al hecho de que c99 *printf puede/devuelve -1 en ciertas condiciones.

Personalmente yo recomiendo ir con una cadena de la biblioteca como ustr, que hace las simples soluciones para usted y le da cadenas administradas de forma gratuita.Si realmente te interesa lo puedes combinar con vstr.

Yo he encontrado una manera portátil para predecir y/o limitar el número de caracteres devueltos por sprintf y funciones relacionadas, pero es ineficiente y muchos consideran que es poco elegante.

Lo que tienes que hacer es crear un archivo temporal con tmpfile(), fprintf() para que (los cuales de forma fiable devuelve el número de bytes escritos), luego rebobinar y leer todo o parte del texto en un búfer.

Ejemplo:

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;   
}

El uso de la muy superior asprintf() en su lugar.

Es una extensión de GNU, pero vale la pena copiar a la plataforma de destino en el caso de que no disponibles de forma nativa.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top