Замена или обходной путь для asprintf в AIX - PullRequest
12 голосов
/ 04 февраля 2011

Я пытаюсь собрать python-kerberos в AIX. kerberospw.c использует вызов asprintf, но, как говорит мне Google, asprintf не существует в AIX.

Я видел http://www.koders.com/c/fidAA9B130D588302673A28B568430A83131B7734C0.aspx?s=windows.h,, который выглядит так, как будто я мог создать автономный asprintf, но я не знаю, куда это пойдет или как я #include его в kerberospw.c.

Есть ли способ, которым я могу использовать пример koders.com или какой-то другой код, чтобы "подделать" asprintf? Могу ли я просто включить функцию asprintf, как показано в kerberospw.c? Я не программист, но

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

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

asprintf (& message, "%. * S:%. * S", (int) result_code_string.length,
(char *) result_code_string.data,
(int) result_string.length,
(char *) result_string.data);

Я понимаю, что могу связаться с автором python-kerberos, но а) я думаю, что было бы полезно иметь потенциальный патч, если бы я это сделал, и б) могло быть другое программное обеспечение, которое я запускаю, которое использует asprintf, и было бы неплохо иметь обходной путь.

Ответы [ 4 ]

21 голосов
/ 04 февраля 2011

asprintf - это разновидность семейства функций printf, которые выделяют буфер для хранения памяти для отформатированной строки и возвращают ее.Это функция с переменным номером аргумента (следовательно, ... в объявлении, который является допустимым кодом C).Вы можете найти описание здесь .

. Это может быть относительно легко переопределено, если vsnprintf работает правильно (то есть, возвращает ошибку, если буфер слишком мал, чтобы содержать отформатированную строку).

Вот такая реализация:

#include <stdarg.h>

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;
}
14 голосов
/ 04 февраля 2011

Опираясь на Sylvain * answer , здесь есть простая реализация с asprintf() и vasprintf(), потому что там, где вам нужен один, вам обычно тоже нужен другой,И, учитывая макрос va_copy() из C99, легко реализовать asprintf() в терминах vasprintf().Действительно, при написании функций varargs очень часто полезно иметь их в парах, один с многоточием и один с аргументом va_list вместо многоточия, и вы тривиально реализуете первый в терминах последнего.

Это приводит к коду:

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

Сложной частью использования этих функций в системе, где они не предусмотрены, является решение, где функции должны быть объявлены.В идеале они были бы в <stdio.h>, но тогда вам не нужно было бы их писать.Итак, у вас должен быть какой-то другой заголовок, который включает <stdio.h>, но объявляет эти функции, если они не объявлены в <stdio.h>.И, в идеале, код должен полуавтоматически обнаруживать это.Может быть, заголовок "missing.h" и содержит (частично):

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#include <stdio.h>
#include <stdarg.h>

#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 */

Также обратите внимание, что эта справочная страница для asprintf () говорит, что возвращаемое значение в указателенеопределенный в случае ошибки.Другие справочные страницы , включая ту, на которую есть ссылка в вопросе, указывают, что в случае ошибки для нее явно установлено значение NULL.В документе комитета по стандарту C ( n1337.pdf ) не указано поведение ошибки при нехватке памяти.

  • При использовании asprintf () не предполагайте, что указатель инициализированесли функция завершается ошибкой.
  • Если реализуется asprintf (), убедитесь, что для указателя установлено значение null при ошибке, чтобы дать детерминированное поведение.
1 голос
/ 03 июля 2014

Я пришел сюда в поисках быстрой реализации для Windows и Linux, которая в случае сбоя установит указатель возврата на NULL.

Ответ Джонатана Леффлера выглядел лучше, но потом я заметил, что он не устанавливает -1, когда malloc не удается.

Я сделал больше поиска и натолкнулся на это обсуждение реализации asprintf , которое затем убедило меня, что Джонатан и Сильвен оба не справились с переполнением правильно.

Теперь я рекомендую это решение , предоставленное вышеупомянутым обсуждением, которое, кажется, охватывает все важные платформы и, по-видимому, правильно обрабатывает каждый сценарий сбоя.

0 голосов
/ 30 сентября 2014

Здесь реализация, которая не вызывает snprintf() дважды в большинстве случаев.Я опустил включения и определяет, как показано в других ответах.

Как и должно быть, определим asprintf() как вызов 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;
}

Мы предварительно выделим буферпредопределенный соответствующий размер и только в случае переполнения вызовите vsnprintf() во второй раз.Обоснование состоит в том, что функция s*printf() считается очень тяжелой, а перераспределение памяти приемлемым.

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 : я заменил вызов realloc() на простой malloc(), поскольку он дешевле.В случае переполнения пара free()/malloc() стоит меньше realloc() из-за ее внутреннего скрытого memcpy().Так как мы перезаписываем весь буфер с последующим вызовом vsnprintf(), в этой копии нет смысла.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...