Wie kann ich inkonsistentes Verhalten von snprintf in verschiedenen UNIX-ähnlichen Betriebssystemen überwinden?

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

Frage

Per Manpages, snprintf kehrt Anzahl von Bytes von glibc Version geschrieben 2.2 ab. Aber auf niedrigere Versionen von libc2.2 und HP-UX, es gibt eine positive ganze Zahl, die zu einem Pufferüberlauf führen kann.

Wie kann man diese überwinden und portablen Code schreiben?

Edit: Aus Mangel an mehr Klarheit

Dieser Code funktioniert perfekt in 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;
    }

Es gibt die Länge der Saite (10) auf Linux. Aber der gleiche Code zurückkehrt eine positive Zahl, die auf HP-UX-Maschine gedruckt größer ist als die Anzahl der Zeichen ist. Hoffen, dass diese Erklärung ist in Ordnung.

War es hilfreich?

Lösung

Sie könnten eine snprintf Wrapper erstellen, die -1 für jeden Fall zurückgibt, wenn es nicht genug Platz im Puffer ist.

Sehen Sie die Mann rel="nofollow. Es hat auch ein Beispiel, die Bedrohung alle Fälle.

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

Andere Tipps

Haben Sie eine portable Implementierung von printf in Betracht gezogen? Ich suchte nach einer vor einer Weile und ließ sich auf Trio.

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

Ihre Frage ist noch unklar. Das Manpage verbunden spricht also:

  

Die Funktionen snprintf () und vsnprintf () nicht schreiben mehr als size Bytes (einschließlich   der nacheilende '\ 0'). Wenn der Ausgang aufgrund dieser Grenze abgeschnitten wurde, dann ist der Rückgabewert die Anzahl der Zeichen (ohne die Hinter ‚\ 0‘), die würde wurden auf die endgültige Zeichenfolge geschrieben, wenn genügend Platz vorhanden war . Somit ein Rückgabewert der Größe oder mehr bedeutet, dass der Ausgang abgeschnitten wurde.

Also, wenn Sie wollen wissen, ob die Ausgabe abgeschnitten wurde:

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

Es gibt eine ganze Reihe von Problemen mit * printf Portabilität und realistisch möchten Sie wahrscheinlich eine von drei Wege beschreiten:

  1. Erfordern eine c99-konform * printf, weil 9 Jahre für jeden genug sein sollten, und nur sagen, die Plattform anders gebrochen wird.

  2. Haben Sie einen my_snprintf () mit einem Bündel von # ifdef die für die spezifischen Plattformen wollen Sie alle Aufruf der vsnprintf () unter unterstützen (das Verständnis der kleinste gemeinsame Nenner ist das, was Sie haben).

  3. tragen nur um eine Kopie von vsnprintf () mit Ihrem Code, für einfache usecases es ist eigentlich vstr und Sie Kunde Formatierer kostenlos.

... wie andere Leute haben vorgeschlagen, dass Sie einen Hack tun # 1 und # 2, nur für den Fall -1 Zusammenführung, aber das ist riskant aufgrund der Tatsache, dass c99 * printf kann / wird -1 zurück in bestimmten Bedingungen.

Persönlich würde ich empfehlen, geht nur mit eine String-Bibliothek wie ustr , die die einfache tut Abhilfen für Sie und geben Sie Saiten kostenlos geführt werden. Wenn Sie wirklich können Sie kombinieren mit vstr .

Ich habe einen tragbaren Weg gefunden, um vorherzusagen und / oder die Anzahl der Zeichen von sprintf und verwandte Funktionen zurück begrenzen, aber es ist ineffizient und viele halten es für unelegant.

Was Sie tun, ist eine temporäre Datei mit tmpfile create (), fprintf (), um das (die zuverlässig die Anzahl der geschriebenen Bytes zurückgibt), dann zurückspulen und alle oder einen Teil des Textes in einen Puffer lesen.

Beispiel:

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

Mit dem weit überlegen asprintf () statt.

Es ist eine GNU-Erweiterung, aber es lohnt sich auf die Zielplattform für den Fall, Kopieren, dass es nicht nativ verfügbar ist.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top