arana-main / arana / file.c

/*
 * file.c
 *
 * (c) Copyright 2009 by Armin Ronacher.
 */

#include "arana.h"

#define CHUNK_SIZE 1024

/* invoked by AR_FILE_OPEN */
AR
_arana_api_file_open(AR_SIG, AR filename, AR mode)
{
    ArFile *file = (ArFile *)AR_ALLOCATE_OBJECT(ArFile, ArFileType);
    const char *open_mode = "r";
    if (mode != AR_NULL)
        open_mode = AR_AS_CHARP(mode);
    FILE *fp = fopen(AR_AS_CHARP(filename), open_mode);
    if (!fp) {
        char errorbuf[300];
        snprintf(errorbuf, 300, "Could not open %s",
             AR_AS_CHARP(filename));
        AR_RAISE(AR_IO_ERROR(errorbuf));
    }
    file->filename = filename;
    file->mode = mode;
    file->fp = fp;
    return (AR)file;
}

/* invoked by AR_FILE_READ */
AR
_arana_api_file_read(AR_SIG, AR file, Ar_size_t bytes)
{
    Ar_size_t read;
    char *result;
    FILE *fp = ((ArFile *)file)->fp;
    if (!(result = AR_ALLOCA(bytes + 1)))
        AR_RAISE_OOM();
    if ((read = fread(result, 1, bytes, fp)) != bytes) {
        if (!feof(fp))
            /* XXX: better error message */
            AR_RAISE(AR_IO_ERROR("could not read from file"));
    }
    result = AR_REALLOC(result, read + 1);
    if (!result)
        AR_RAISE_OOM();
    return AR_STRING2_NOCOPY(result, read);
}

/* invoked by AR_FILE_READ_TO_END */
AR
_arana_api_file_read_to_end(AR_SIG, AR file)
{
    Ar_size_t read, read_total = 0;
    char *result = NULL;
    FILE *fp = ((ArFile *)file)->fp;

    while (!feof(fp)) {
        if (!(result = AR_REALLOC(result, read_total + CHUNK_SIZE + 1)))
            AR_RAISE_OOM();
        read = fread(result + read_total, 1, CHUNK_SIZE, fp);
        if (read < CHUNK_SIZE)
            if (!feof(fp))
                /* XXX: better error message */
                AR_RAISE(AR_IO_ERROR("could not read from file"));
        read_total += read;
    }
    result = AR_REALLOC(result, read_total + 1);
    if (!result)
        AR_RAISE_OOM();
    return AR_STRING2_NOCOPY(result, read_total);
}

static AR
arana_file_open(AR_SIG, AR self, AR args)
{
    AR filename, mode = AR_NULL;
    AR_PARSE(args, "S|S:open", &filename, &mode);
    if (AR_STRING_HAS_NULL(filename))
        AR_RAISE(AR_ARGUMENT_ERROR(NULL, 0, "filename contains NULL byte."));
    return _arana_api_file_open(AR_ISIG, filename, mode);
}

static AR
arana_file_read(AR_SIG, AR self, AR args)
{
    AR size = AR_INTEGER(-1);
    AR_PARSE(args, "|I:read", &size);
    if (AR_AS_LONG(size) < 0)
        return AR_FILE_READ_TO_END(self);
    return AR_FILE_READ(self, AR_AS_LONG(size));
}

ArObjectType ArFileType = {
    .name = "File",
    .parent = &ArTypeType
};

AR
arana_create_file_type(AR_SIG)
{
    AR type = AR_CREATE_TYPE(ArFileType);
    AR_BIND_CLASS_METHOD(type, "open", arana_file_open);
    AR_BIND_METHOD(type, "read", arana_file_read);
    return type;
}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.