Ancient

 view release on metacpan or  search on metacpan

xs/file/file.c  view on Meta::CPAN


static int file_copy_internal(pTHX_ const char *src, const char *dst) {
    int fd_src, fd_dst;
    char *buffer;
    ssize_t n_read, n_written, written;
    struct stat st;
    int result = 0;
#ifdef _WIN32
    int open_flags_r = O_RDONLY | O_BINARY;
    int open_flags_w = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
#else
    int open_flags_r = O_RDONLY;
    int open_flags_w = O_WRONLY | O_CREAT | O_TRUNC;
#endif

    fd_src = open(src, open_flags_r);
    if (fd_src < 0) return 0;

    if (fstat(fd_src, &st) < 0) {
        close(fd_src);
        return 0;
    }

    fd_dst = open(dst, open_flags_w, st.st_mode & 07777);
    if (fd_dst < 0) {
        close(fd_src);
        return 0;
    }

    Newx(buffer, FILE_BULK_BUFFER_SIZE, char);

    while (1) {
        n_read = read(fd_src, buffer, FILE_BULK_BUFFER_SIZE);
        if (n_read < 0) {
            if (errno == EINTR) continue;
            goto cleanup;
        }
        if (n_read == 0) break;

        written = 0;
        while (written < n_read) {
            n_written = write(fd_dst, buffer + written, n_read - written);
            if (n_written < 0) {
                if (errno == EINTR) continue;
                goto cleanup;
            }
            written += n_written;
        }
    }

    result = 1;

cleanup:
    Safefree(buffer);
    close(fd_src);
    close(fd_dst);
    return result;
}

static int file_move_internal(pTHX_ const char *src, const char *dst) {
    /* Try rename first (fast path for same filesystem) */
    if (rename(src, dst) == 0) {
        return 1;
    }

    /* If EXDEV, copy then delete (cross-device move) */
    if (errno == EXDEV) {
        if (file_copy_internal(aTHX_ src, dst)) {
            return file_unlink_internal(src);
        }
    }

    return 0;
}

static int file_touch_internal(const char *path) {
#ifdef _WIN32
    HANDLE h;
    FILETIME ft;
    SYSTEMTIME st;
    int result = 0;

    /* Try to open existing file */
    h = CreateFileA(path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
                    NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (h == INVALID_HANDLE_VALUE) {
        return 0;
    }

    GetSystemTime(&st);
    SystemTimeToFileTime(&st, &ft);
    result = SetFileTime(h, NULL, &ft, &ft) != 0;
    CloseHandle(h);
    return result;
#else
    int fd;
    /* Try to update times on existing file - utime(path, NULL) sets to current time */
    if (utime(path, NULL) == 0) {
        return 1;
    }

    /* File doesn't exist, create it */
    fd = open(path, O_WRONLY | O_CREAT, 0644);
    if (fd < 0) return 0;
    close(fd);
    return 1;
#endif
}

static int file_chmod_internal(const char *path, int mode) {
#ifdef _WIN32
    return _chmod(path, mode) == 0;
#else
    return chmod(path, mode) == 0;
#endif
}

static int file_mkdir_internal(const char *path, int mode) {
#ifdef _WIN32
    PERL_UNUSED_VAR(mode);
    return _mkdir(path) == 0;
#else

xs/file/file.c  view on Meta::CPAN

static AV* file_head_internal(pTHX_ const char *path, IV n) {
    AV *result = newAV();
    IV idx;
    SV *line;
    IV count = 0;

    if (n <= 0) return result;

    idx = file_lines_open(aTHX_ path);
    if (idx < 0) return result;

    while (count < n && (line = file_lines_next(aTHX_ idx)) != &PL_sv_undef) {
        av_push(result, line);
        count++;
    }

    file_lines_close(idx);
    return result;
}

static AV* file_tail_internal(pTHX_ const char *path, IV n) {
    AV *result = newAV();
    AV *buffer;
    SV *line;
    IV idx;
    SSize_t i, buf_len;

    if (n <= 0) return result;

    idx = file_lines_open(aTHX_ path);
    if (idx < 0) return result;

    /* Use circular buffer to keep last N lines */
    buffer = newAV();
    av_extend(buffer, n - 1);

    while ((line = file_lines_next(aTHX_ idx)) != &PL_sv_undef) {
        if (av_len(buffer) + 1 >= n) {
            SV *old = av_shift(buffer);
            SvREFCNT_dec(old);
        }
        av_push(buffer, line);
    }

    file_lines_close(idx);

    /* Copy buffer to result */
    buf_len = av_len(buffer) + 1;
    for (i = 0; i < buf_len; i++) {
        SV **sv = av_fetch(buffer, i, 0);
        if (sv) {
            av_push(result, newSVsv(*sv));
        }
    }

    SvREFCNT_dec((SV*)buffer);
    return result;
}

/* ============================================
   Atomic spew - write to temp file then rename
   ============================================ */

static int file_atomic_spew_internal(pTHX_ const char *path, SV *data) {
    char temp_path[4096];
    int fd;
    const char *buf;
    STRLEN len;
    ssize_t written = 0, n;
    static int counter = 0;
#ifdef _WIN32
    int open_flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
    int pid = (int)GetCurrentProcessId();
#else
    int open_flags = O_WRONLY | O_CREAT | O_TRUNC;
    int pid = (int)getpid();
#endif

    /* Create temp file name in same directory */
    snprintf(temp_path, sizeof(temp_path), "%s.tmp.%d.%d", path, pid, counter++);

    buf = SvPV(data, len);

    fd = open(temp_path, open_flags, 0644);
    if (fd < 0) {
        return 0;
    }

    while ((size_t)written < len) {
        n = write(fd, buf + written, len - written);
        if (n < 0) {
            if (errno == EINTR) continue;
            close(fd);
            file_unlink_internal(temp_path);
            return 0;
        }
        written += n;
    }

#ifdef _WIN32
    /* Sync to disk on Windows */
    _commit(fd);
#else
    /* Sync to disk on POSIX */
    fsync(fd);
#endif

    close(fd);

    /* Atomic rename */
    if (rename(temp_path, path) != 0) {
        file_unlink_internal(temp_path);
        return 0;
    }

    return 1;
}

/* ============================================
   Split lines utility
   ============================================ */

static AV* file_split_lines(pTHX_ SV *content) {
    AV *lines;
    const char *start, *end, *p;
    STRLEN len;

    start = SvPV(content, len);
    end = start + len;
    lines = newAV();

    while (start < end) {
        p = memchr(start, '\n', end - start);
        if (p) {
            av_push(lines, newSVpvn(start, p - start));
            start = p + 1;
        } else {
            if (start < end) {
                av_push(lines, newSVpvn(start, end - start));
            }
            break;
        }
    }

    return lines;
}

/* ============================================
   XS Functions
   ============================================ */

static XS(xs_slurp) {
    dXSARGS;
    const char *path;

    if (items != 1) croak("Usage: file::slurp(path)");

    path = SvPV_nolen(ST(0));
    ST(0) = sv_2mortal(file_slurp_internal(aTHX_ path));
    XSRETURN(1);
}

static XS(xs_slurp_raw) {
    dXSARGS;
    const char *path;

    if (items != 1) croak("Usage: file::slurp_raw(path)");

    path = SvPV_nolen(ST(0));
    ST(0) = sv_2mortal(file_slurp_raw(aTHX_ path));
    XSRETURN(1);



( run in 0.965 second using v1.01-cache-2.11-cpan-0b5f733616e )