Существуют ли альтернативные реализации интерфейса getline GNU? - PullRequest
11 голосов
/ 09 апреля 2009

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

Он также предназначен для запуска случайной unixish платформы, и только некоторые из библиотек, которые мы поддерживаем, имеют GNU getline, но сейчас код ожидает этого.

Кто-нибудь знает о повторной реализации семантики GNU getline, доступной по менее ограничительной лицензии?

Редактировать :: Я спрашиваю, потому что Google не помог, и я хотел бы, если возможно, не написать его (это может быть забавное упражнение, но не может быть лучшим мое время.)

Чтобы быть более конкретным, рассматриваемый интерфейс:

ssize_t getline (char **lineptr, size_t *n, FILE *stream);

Ответы [ 4 ]

16 голосов
/ 09 апреля 2009

Я озадачен.

Я посмотрел ссылку, прочитал описание, и это хорошая утилита.

Но вы говорите, что просто не можете переписать эту функцию в спецификации? Спецификация кажется вполне понятной,

Здесь:

/* This code is public domain -- Will Hartung 4/9/09 */
#include <stdio.h>
#include <stdlib.h>

size_t getline(char **lineptr, size_t *n, FILE *stream) {
    char *bufptr = NULL;
    char *p = bufptr;
    size_t size;
    int c;

    if (lineptr == NULL) {
        return -1;
    }
    if (stream == NULL) {
        return -1;
    }
    if (n == NULL) {
        return -1;
    }
    bufptr = *lineptr;
    size = *n;

    c = fgetc(stream);
    if (c == EOF) {
        return -1;
    }
    if (bufptr == NULL) {
        bufptr = malloc(128);
        if (bufptr == NULL) {
            return -1;
        }
        size = 128;
    }
    p = bufptr;
    while(c != EOF) {
        if ((p - bufptr) > (size - 1)) {
            size = size + 128;
            bufptr = realloc(bufptr, size);
            if (bufptr == NULL) {
                return -1;
            }
        }
        *p++ = c;
        if (c == '\n') {
            break;
        }
        c = fgetc(stream);
    }

    *p++ = '\0';
    *lineptr = bufptr;
    *n = size;

    return p - bufptr - 1;
}

int main(int argc, char** args) {
    char *buf = NULL; /*malloc(10);*/
    int bufSize = 0; /*10;*/

    printf("%d\n", bufSize);
    int charsRead =  getline(&buf, &bufSize, stdin);

    printf("'%s'", buf);
    printf("%d\n", bufSize);
    return 0;
}

15 минут, и я не писал C в течение 10 лет. Он незначительно нарушает контракт getline в том смысле, что он только проверяет, равен ли lineptr NULL, а не NULL и n == 0. Вы можете исправить это, если хотите. (Другой случай не имел большого смысла для меня, я думаю, вы могли бы вернуть -1 в этом случае.)

Заменить '\ n' на переменную для реализации "getdelim".

Люди все еще пишут код?

6 голосов
/ 10 ноября 2017

Код Уилла Хартунга имеет очень серьезную проблему. realloc, скорее всего, освободит старый блок и выделит новый, но указатель p в коде будет продолжать указывать на оригинал. Этот пытается исправить это, используя вместо этого индексацию массива. Он также пытается более точно воспроизвести стандартную логику POSIX.

/* The original code is public domain -- Will Hartung 4/9/09 */
/* Modifications, public domain as well, by Antti Haapala, 11/10/17
   - Switched to getc on 5/23/19 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <stdint.h>

// if typedef doesn't exist (msvc, blah)
typedef intptr_t ssize_t;

ssize_t getline(char **lineptr, size_t *n, FILE *stream) {
    size_t pos;
    int c;

    if (lineptr == NULL || stream == NULL || n == NULL) {
        errno = EINVAL;
        return -1;
    }

    c = getc(stream);
    if (c == EOF) {
        return -1;
    }

    if (*lineptr == NULL) {
        *lineptr = malloc(128);
        if (*lineptr == NULL) {
            return -1;
        }
        *n = 128;
    }

    pos = 0;
    while(c != EOF) {
        if (pos + 1 >= *n) {
            size_t new_size = *n + (*n >> 2);
            if (new_size < 128) {
                new_size = 128;
            }
            char *new_ptr = realloc(*lineptr, new_size);
            if (new_ptr == NULL) {
                return -1;
            }
            *n = new_size;
            *lineptr = new_ptr;
        }

        ((unsigned char *)(*lineptr))[pos ++] = c;
        if (c == '\n') {
            break;
        }
        c = getc(stream);
    }

    (*lineptr)[pos] = '\0';
    return pos;
}

Производительность может быть увеличена для платформы путем однократной блокировки потока и использования эквивалента getc_unlocked(3) - но они не стандартизированы в C; и если вы используете версию POSIX, то, вероятно, у вас уже будет getline(3).

3 голосов
/ 02 ноября 2017

Используйте эти портативные версии из NetBSD: getdelim () и getline ()

Они приходят из libnbcompat в pkgsrc и имеют BSD-лицензию вверху каждого файла. Вы должны оба, потому что getline () вызывает getdelim (). Получить последние версии обоих файлов. См. Лицензию BSD вверху каждого файла. Измените файлы так, чтобы они подходили для вашей программы: вам может потребоваться объявить getline () и getdelim () в одном из ваших файлов заголовков и изменить оба файла, чтобы они включали ваш заголовок вместо заголовков nbcompat.

Эта версия getdelim () является переносимой, потому что она вызывает fgetc (). Для сравнения, getdelim () из libc (например, BSD libc или musl libc), вероятно, будет использовать частные функции этого libc, поэтому он не будет работать на разных платформах.

За годы, прошедшие с POSIX 2008, заданного getline () , все больше платформ Unixish добавили функцию getline (). Редко бывает, что getline () отсутствует, но все же может происходить на старых платформах. Несколько человек пытаются загрузить NetBSD pkgsrc на старых платформах (например, PowerPC Mac OS X), поэтому они хотят, чтобы libnbcompat предоставлял недостающие функции POSIX, такие как getline ().

3 голосов
/ 09 июля 2011

Если вы компилируете для BSD, используйте fgetln вместо

...