Как я могу преодолеть несогласованное поведение snprintf в разных UNIX-подобных операционных системах?

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

Вопрос

Для каждой страницы руководства snprintf возвращает количество записанных байтов, начиная с glibc версии 2.2.Но в более ранних версиях libc2.2 и HP-UX он возвращает положительное целое число, что может привести к переполнению буфера.

Как можно преодолеть это и написать переносимый код?

Редактировать :За неимением большей ясности

Этот код отлично работает в 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;
    }

Он возвращает длину строки (10) в Linux.Но тот же код возвращает положительное число, большее, чем количество символов, напечатанных на машине HP-UX.Надеюсь, это объяснение подойдет.

Это было полезно?

Решение

вы можете создать оболочку snprintf, которая возвращает -1 для каждого случая, когда в буфере недостаточно места.

См. справочная страница дополнительные документы.Есть также пример, который ставит под угрозу все случаи.

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

Другие советы

Рассматривали ли вы портативную реализацию printf?Некоторое время назад я искал один и остановился на трио.

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

Ваш вопрос пока неясен.А справочная страница связанный с говорит так:

Функции snprintf() и vsnprintf(). не записывайте больше, чем размер байтов (в том числе след ' 0').Если выходные данные были усечены из-за этого ограничения, возвращаемое значение представляет собой количество символов (не включая завершающий '\0'), которые бы были записаны в последнюю строку, если было доступно достаточно места.Таким образом, возвращаемое значение размера или больше означает, что выходные данные были усечены.

Итак, если вы хотите знать, был ли усечен ваш вывод:

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

Существует целый ряд проблем с переносимостью *printf, и на самом деле вы, вероятно, захотите пойти по одному из трех путей:

  1. Требуется *printf, совместимый с c99, потому что 9 лет должно хватить любому, а в противном случае просто скажите, что платформа сломана.

  2. Имейте my_snprintf() с кучей #ifdef для конкретных платформ, которые вы хотите поддерживать, все вызывающие vsnprintf() внизу (понимание того, что у вас есть наименьший общий знаменатель).

  3. Просто носите с собой копию vsnprintf() вместе с вашим кодом, для простых случаев использования это действительно так. довольно просто и для других, на которые вы, вероятно, захотите посмотреть встр и вы получите клиентские форматировщики бесплатно.

... как предлагали другие люди, вы можете сделать хак, объединив # 1 и # 2, только для случая -1, но это рискованно из-за того, что c99 *printf может / действительно возвращает -1 в определенных условиях.

Лично я бы рекомендовал просто использовать библиотека строк, такая как ustr, который выполняет простые обходные пути и бесплатно предоставляет управляемые строки.Если вам действительно интересно, вы можете объединить с встр.

Я нашел один портативный способ предсказать и/или ограничить количество символов, возвращаемых sprintf и связанными с ним функциями, но он неэффективен, и многие считают его неэлегантным.

Что вы делаете, так это создаете временный файл с помощью tmpfile(), fprintf() (который надежно возвращает количество записанных байтов), затем перематываете назад и читаете весь или часть текста в буфер.

Пример:

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

Вместо этого используйте гораздо более совершенный метод asprintf().

Это расширение GNU, но его стоит скопировать на целевую платформу, если оно недоступно изначально.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top