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

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

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

Есть ли способ использовать пример koders.com или какой-либо другой код для "подделки" asprintf? Могу ли я просто включить функцию asprintf, как показано в kerberospw.c? Я не кодер 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, но a) Я думаю, что было бы полезно иметь потенциальный патч, если бы я это сделал, и б) может быть другое программное обеспечение, с которым я сталкиваюсь, используя asprintf, и было бы неплохо иметь обходное решение.

Ответ 1

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

Ответ 2

Основываясь на 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 Standard Committee (n1337.pdf) не указывает на ошибку при отсутствии памяти.

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

Ответ 3

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

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

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

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

Ответ 4

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

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