Are there alternate implementations of GNU getline interface?

心不动则不痛 提交于 2019-11-27 12:55:20

I'm puzzled.

I looked at the link, read the description, and this is a fine utility.

But, are you saying you simply can't rewrite this function to spec? The spec seems quite clear,

Here:

/* 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 minutes, and I haven't written C in 10 years. It minorly breaks the getline contract in that it only checks if the lineptr is NULL, rather than NULL and n == 0. You can fix that if you like. (The other case didn't make a whole lot of sense to me, I guess you could return -1 in that case.)

Replace the '\n' with a variable to implement "getdelim".

Do people still write code any more?

The code by Will Hartung suffers from a very serious problem. realloc will most probably free the old block and allocate a new one, but the p pointer within the code will continue to point to the original. This one tries to fix that by using array indexing instead. It also tries to more closely replicate the standard POSIX logic.

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

The performance can be increased for a platform by locking the stream once and using the equivalent of getc_unlocked(3) - but these are not standardized in C; and if you're using the POSIX version, then you probably will have getline(3) already.

If you are compiling for BSD use fgetln instead

Use these portable versions from NetBSD: getdelim() and getline()

These come from libnbcompat in pkgsrc, and have a BSD license at the top of each file. You need both because getline() calls getdelim(). Fetch the latest versions of both files. See the BSD license at the top of each file. Modify the files to fit into your program: you might need to declare getline() and getdelim() in one of your header files, and modify both files to include your header instead of the nbcompat headers.

This version of getdelim() is portable because it calls fgetc(). For contrast, a getdelim() from a libc (like BSD libc or musl libc) would probably use private features of that libc, so it would not work across platforms.

In the years since POSIX 2008 specified getline(), more Unixish platforms have added the getline() function. It is rare that getline() is missing, but it can still happen on old platforms. A few people try to bootstrap NetBSD pkgsrc on old platforms (like PowerPC Mac OS X), so they want libnbcompat to provide missing POSIX functions like getline().

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!