Замена или обходной путь для asprintf в AIX

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

Я видел http://www.koders.com/c/fidAA9B130D588302673A28B568430A83131B7734C0.aspx?s=windows.h, который выглядит так, как будто я мог бы создать замену asprintf, но я не знаю, куда это пойдет или как я мог бы # Включите это в 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 ответа

Решение

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

Основываясь на ответе Sylvain Defresne, вот простая реализация с обоими 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 Standard ( n1337.pdf) не определяет поведение ошибки при нехватке памяти.

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

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

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

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

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

Здесь реализация, которая не вызывает 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;
}

РЕДАКТИРОВАТЬ: я заменил realloc() позвонить по простому malloc() как дешевле. В случае переполнения free()/malloc() пара стоит меньше realloc() из-за его внутренней скрытой memcpy(), Поскольку мы перезаписываем весь буфер в любом случае с последующим вызовом vsnprintf() в этой копии нет смысла.

Другие вопросы по тегам