<commit_before>﻿import fs = require("fs");
import api = require("./fs-api");
import misc = require("./fs-misc");

import IFilesystem = api.IFilesystem;
import IItem = api.IItem;
import IStats = api.IStats;
import RenameFlags = api.RenameFlags;
import Path = misc.Path;
import FileUtil = misc.FileUtil;

export class LocalFilesystem implements IFilesystem {

    private isWindows: boolean;

    constructor() {
        this.isWindows = (process.platform === 'win32');
    }

    private checkPath(path: string, name: string): string {
        var localPath = Path.create(path, this, name);
        var path = localPath.path;

        if (path[0] == '~') {
            var home = <string>(process.env.HOME || process.env.USERPROFILE || ".");
            if (path.length == 1) return home;
            if (path[1] === '/' || (path[1] === '\\' && this.isWindows)) {
                path = localPath.join(home, path.substr(2)).path;
            }
        }

        return path;
    }

    private checkCallback(callback: any): void {
        if (typeof callback !== "function") throw new Error("Callback must be a function");
    }

    open(path: string, flags: string, attrs: IStats, callback: (err: Error, handle: any) => any): void {
        this.checkCallback(callback);
        path = this.checkPath(path, 'path');

        var mode = (attrs && typeof attrs === 'object') ? attrs.mode : undefined;
        fs.open(path, flags, mode, (err, fd) => callback(err, fd));
        //LATER: pay attemtion to attrs other than mode (low priority - many SFTP servers ignore these as well)
    }

    close(handle: any, callback: (err: Error) => any): void {
        this.checkCallback(callback);

        if (Array.isArray(handle)) {
            if (handle.closed) return FileUtil.fail("Already closed", callback);
            handle.closed = true;
            process.nextTick(() => callback(null));
            return;
        }

        if (isNaN(handle)) return FileUtil.fail("Invalid handle", callback);

        fs.close(handle, callback);
    }

    read(handle: any, buffer: Buffer, offset: number, length: number, position: number, callback: (err: Error, buffer: Buffer, bytesRead: number) => any): void {
        this.checkCallback(callback);

        var initialOffset = offset;
        var totalBytes = 0;

        if (!buffer) {
            buffer = new Buffer(length);
            offset = 0;
        }

        var read = () => {
            fs.read(handle, buffer, offset, length, position, (err, bytesRead, b) => {
                if (typeof err === 'undefined' || err == null) {
                    length -= bytesRead;
                    totalBytes += bytesRead;

                    if (length > 0 && bytesRead > 0) {
                        offset += bytesRead;
                        position += bytesRead;
                        read();
                        return;
                    }
                }<commit_msg>Fixed a bug in handling of last file block (before EOF)<commit_after>﻿import fs = require("fs");
import api = require("./fs-api");
import misc = require("./fs-misc");

import IFilesystem = api.IFilesystem;
import IItem = api.IItem;
import IStats = api.IStats;
import RenameFlags = api.RenameFlags;
import Path = misc.Path;
import FileUtil = misc.FileUtil;

export class LocalFilesystem implements IFilesystem {

    private isWindows: boolean;

    constructor() {
        this.isWindows = (process.platform === 'win32');
    }

    private checkPath(path: string, name: string): string {
        var localPath = Path.create(path, this, name);
        var path = localPath.path;

        if (path[0] == '~') {
            var home = <string>(process.env.HOME || process.env.USERPROFILE || ".");
            if (path.length == 1) return home;
            if (path[1] === '/' || (path[1] === '\\' && this.isWindows)) {
                path = localPath.join(home, path.substr(2)).path;
            }
        }

        return path;
    }

    private checkCallback(callback: any): void {
        if (typeof callback !== "function") throw new Error("Callback must be a function");
    }

    open(path: string, flags: string, attrs: IStats, callback: (err: Error, handle: any) => any): void {
        this.checkCallback(callback);
        path = this.checkPath(path, 'path');

        var mode = (attrs && typeof attrs === 'object') ? attrs.mode : undefined;
        fs.open(path, flags, mode, (err, fd) => callback(err, fd));
        //LATER: pay attemtion to attrs other than mode (low priority - many SFTP servers ignore these as well)
    }

    close(handle: any, callback: (err: Error) => any): void {
        this.checkCallback(callback);

        if (Array.isArray(handle)) {
            if (handle.closed) return FileUtil.fail("Already closed", callback);
            handle.closed = true;
            process.nextTick(() => callback(null));
            return;
        }

        if (isNaN(handle)) return FileUtil.fail("Invalid handle", callback);

        fs.close(handle, callback);
    }

    read(handle: any, buffer: Buffer, offset: number, length: number, position: number, callback: (err: Error, buffer: Buffer, bytesRead: number) => any): void {
        this.checkCallback(callback);

        var initialOffset = offset;
        var totalBytes = 0;

        if (!buffer) {
            buffer = new Buffer(length);
            offset = 0;
        }

        var offset2 = offset;

        var read = () => {
            fs.read(handle, buffer, offset2, length, position, (err, bytesRead, b) => {
                if (typeof err === 'undefined' || err == null) {
                    length -= bytesRead;
                    totalBytes += bytesRead;

                    if (length > 0 && bytesRead > 0) {
                        offset2 += bytesRead;
                        position += bytesRead;
                        read();
                        return;
                    }
                }
{'fraction_non_alphanumeric': 0.13611157788606956, 'fraction_numerical': 0.004200974626113258, 'mean_word_length': 2.036734693877551, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5068940', 'n_tokens_mistral': 1679, 'n_tokens_neox': 1605, 'n_words': 473}