LoginSignup
1
2

More than 1 year has passed since last update.

【ネタ】サクラエディタ用の diff.exe を JScript で自作してみる

Last updated at Posted at 2021-03-31

実際にコンパイル、実行可能ですが、動作はとっても遅いです……。
あくまでも 4/1 用のネタとしてご利用ください。

サクラエディタ用の diff.exe を JScript で自作してみる

サクラエディタでは、diff.exeを呼び出すことで、2つのファイルの差分を表示する機能があります。
しかし、Windows用の出回っている diff.exe は古いし、新しめの diff.exe は依存ファイルをいろいろ要求されて動きません。
そこで、以前作った JavaScript のソースを流用して、自前の diff.exe を作成してみました。
内部のアルゴリズムについては、過去の記事で解説しています。

コンパイル手順

まず、.NET Framework に含まれる JScript のコンパイラを探します。
エクスプローラで jsc.exe を検索してみてください。Windows7、Windows10 であれば、C:\Windows\ 以下のどこかにあると思います。
jsc.exe が見つかれば、この記事の末尾にあるソースをコマンドラインからコンパイルして、diff.exeを作成します。

cd /d C:\test
C:\Windows\Microsoft.NET\Framework\v4.0.30319\jsc.exe C:\test\diff.js

パスはそれぞれの環境にあわせて適宜書き換えてください。

出来上がった diff.exe をサクラエディタと同じフォルダに配置します。
既に diff.exe を利用している場合は、いつでも元に戻せるよう、どこか別フォルダに退避しておいて下さい。間違っても上書きしないように!!!

コンパイルした diff.exe は、コマンドラインから起動して使用可能です。
サポートしている出力形式は、ノーマル出力と --context、--unified、--brief です。
引数の仕様は本家 diffutils に合わせていますが、サクラエディタで diff を表示するのに必要な最低限の機能しか実装していません。

diff -u "c:\test\old.txt" "c:\test\new.txt"

ソース

diff.js の名前でローカルに保存して下さい。

diff.js
import System;

/****************************************************************************
  lesser diff.exe for Windows  (NOT compatible with GNU Diff)
  version 0.01
  Copyleft 2020-2021 stonee

  license: GPL3  https://www.gnu.org/licenses/gpl-3.0.html

  compile:
  C:\Windows\Microsoft.NET\Framework\v4.0.30319\jsc.exe diff.js
 ****************************************************************************/

var fso = new ActiveXObject("Scripting.FileSystemObject");

function isBlank(s) {
    for (var i = s.length - 1; i >= 0; i--) {
        var n = s.charCodeAt(i);
        if (!((n === 0x9) || (n === 0x20) || (n === 0xD) || (n === 0xA))) {
            return false;
        }
    }
    return true;
}
//==============================================================================
var Hunks = function() {
    this.vList = [];
}
Hunks.prototype.hunk_size = function(i) { return (this.vList[i].end - this.vList[i].start - 1); }
Hunks.prototype.gap = function(i) { return ((i == 0) ? (this.vList[i].start + 1) : (this.vList[i].start - this.vList[i - 1].end + 1)); }
Hunks.prototype.addEntry = function(start, end, isBlankLines) {
    this.vList.push({start:start, end:end, isBlankLines:isBlankLines});
}
Hunks.prototype.length = function() { return this.vList.length; }
Hunks.prototype.getHunk = function(i) { return this.vList[i]; }
Hunks.prototype.merge = function(lines, o_Hunk_other) {
    var i, n = this.vList.length, nEnd = 0;
    for (i = 1; i < n; i++) {
        if (nEnd + 1 < i) {
            this.vList[nEnd + 1] = this.vList[i];
            o_Hunk_other.vList[nEnd + 1] = o_Hunk_other.vList[i];
        }
        if ((this.vList[i].start - this.vList[nEnd].end + 1) <= lines) {
            this.vList[nEnd].end = this.vList[i].end;
            this.vList[nEnd].isBlankLines = (this.vList[nEnd].isBlankLines && this.vList[i].isBlankLines);
            o_Hunk_other.vList[nEnd].end = o_Hunk_other.vList[i].end;
            o_Hunk_other.vList[nEnd].isBlankLines = (o_Hunk_other.vList[nEnd].isBlankLines && o_Hunk_other.vList[i].isBlankLines);
        } else {
            nEnd++;
        }
    }

    for (i = nEnd + 2; i < n; i++) {
        this.vList.pop();
        o_Hunk_other.vList.pop();
    }
}
//==============================================================================
var DiffEngine = {
    bIgnoreCase: false,
    flgIgnoreBlank: 0,  //0:compare 1:ignore change 2:ignore all
    bIgnoreBlankLines: false,
    bDetectSimilarLine: true,
    strFile_L: "",
    strFile_R: "",
    vInfo_L: [],
    vInfo_R: [],
    oHunks_L: null,
    oHunks_R: null,
    threshold: 0.5,
    BLANK_PATTERN: /[\s\t]+/g,
    lines: 3,

    Compare: function(s1, s2, flgIgnoreBlank) {
        var i = 0, j = 0, n1, n2, len1 = s1.length, len2 = s2.length;
        var _isWhite = function(n) { return ((n == 0x9) || (n == 0x20) || (n == 0xD) || (n == 0xA)); };

        if (flgIgnoreBlank != 0) {
            while ((i < len1) && _isWhite(s1.charCodeAt(i))) { i++; }
            while ((j < len2) && _isWhite(s2.charCodeAt(j))) { j++; }
        } else if (len1 != len2) {
            return false;
        }
        while ((i < len1) && (j < len2)) {
            n1 = s1.charCodeAt(i);
            n2 = s2.charCodeAt(j);
            if ((flgIgnoreBlank != 0) && _isWhite(n1)) {
                if (flgIgnoreBlank == 1) {
                    if (!_isWhite(n2)) { return false; }
                    j++;
                }
                i++;
                while ((i < len1) && _isWhite(s1.charCodeAt(i))) { i++; }
                while ((j < len2) && _isWhite(s2.charCodeAt(j))) { j++; }
            } else if ((flgIgnoreBlank != 0) && _isWhite(n2)) {
                if (flgIgnoreBlank == 1) { return false; }
                j++;
                while ((j < len2) && _isWhite(s2.charCodeAt(j))) { j++; }
            } else if (n1 != n2) {
                return false;
            } else {
                i++;
                j++;
            }
        }
        if (flgIgnoreBlank != 0) {
            while ((i < len1) && _isWhite(s1.charCodeAt(i))) { i++; }
            while ((j < len2) && _isWhite(s2.charCodeAt(j))) { j++; }
        }
        return ((i >= len1) && (j >= len2));
    },
    CompareLine: function(x, y, bStrict) {
        if (this.vInfo_L[x].isBlank != this.vInfo_R[y].isBlank) { return 0; }
        var s1 = this.vLeft[x], s2 = this.vRight[y];

        if (this.vInfo_L[x].isBlank) {
            if ((this.flgIgnoreBlank != 0) || (s1 == s2)) { return 2; }
            return bStrict ? 0 : 1;
        }
        if (this.bIgnoreCase) {
            s1 = s1.toLowerCase();
            s2 = s2.toLowerCase();
        }
        if (this.Compare(s1, s2, this.flgIgnoreBlank)) { return 2; }
        if (bStrict || (this.vInfo_L[x].UniquePair >= 0) || (this.vInfo_R[y].UniquePair >= 0)) {
            return 0;
        }
        s1 = s1.replace(this.BLANK_PATTERN, "");
        s2 = s2.replace(this.BLANK_PATTERN, "");
        var len1 = s1.length, len2 = s2.length;
        return ((len1 > len2) ? this.CalcScore(s2, s1, len2, len1) : this.CalcScore(s1, s2, len1, len2));
    },
    CalcScore: function(s1, s2, len1, len2) {
        var p_max = len1 + 1 - this.threshold * len2;
        if (p_max < 1) { return 0; }    //optimize
        var y, p = 0, k = (len1 + len2 + 2), fp = new Array(k + 1);
        do {
            fp[k] = -1;
        } while (k--);
        for (; (p <= p_max) && (fp[len2 + 1] != len2); p++) {
            for (k = len1 - p; k < len2; k++) {
                for (y = Math.max(fp[k] + 1, fp[k + 2]);
                    (y < k) && (s1.charCodeAt(y - k + len1) == s2.charCodeAt(y));
                    y++) {}
                fp[k + 1] = y;
            }
            for (k = len2 + p; k >= len2; k--) {
                for (y = Math.max(fp[k] + 1, fp[k + 2]);
                    (y < len2) && (s1.charCodeAt(y - k + len1) == s2.charCodeAt(y));
                    y++) {}
                fp[k + 1] = y;
            }
        }
        return (1 - (len2 - len1 + p - 1) / len2);  //distance score
    },
    IsDuplicated: function(v, vInfo, i, j, bIgnoreCase, flgIgnoreBlank) {
        if ((vInfo[i].isBlank != vInfo[j].isBlank) || (vInfo[i].hash != vInfo[j].hash)) { return false; }
        return (bIgnoreCase ? this.Compare(v[i].toLowerCase(), v[j].toLowerCase(), flgIgnoreBlank) : this.Compare(v[i], v[j], flgIgnoreBlank));
    },
    hash_: function(s, bIgnoreCase) {
        s = s.replace(this.BLANK_PATTERN, "");
        var i, len = s.length, result = len;
        if (len == 0) { return 0; }
        if (bIgnoreCase) { s = s.toLowerCase(); }
        for (i = 0; i < len; i++) {
            result = s.charCodeAt(i) ^ (result << 2);
        }
        return result;
    },
    InitInfo: function(len1, len2) {
        var i, j, isUnique_L = new Array(len1), isUnique_R = new Array(len2);
        const bIgnoreCase = this.bIgnoreCase;
        this.vInfo_L = new Array(len1);
        this.vInfo_R = new Array(len2);
        for (i = 0; i < len1; i++) {
            this.vInfo_L[i] = {status:"+", pair:-1, isBlank:isBlank(this.vLeft[i]), hash:this.hash_(this.vLeft[i], bIgnoreCase), UniquePair:-1};
            isUnique_L[i] = !(this.vInfo_L[i].isBlank);
        }
        for (i = 0; i < len2; i++) {
            this.vInfo_R[i] = {status:"+", pair:-1, isBlank:isBlank(this.vRight[i]), hash:this.hash_(this.vRight[i], bIgnoreCase), UniquePair:-1};
            isUnique_R[i] = !(this.vInfo_R[i].isBlank);
        }
        if (!this.bDetectSimilarLine) { return; }
        for (i = 0; i < len1; i++) {
            if (!(isUnique_L[i])) { continue; }
            for (j = len1 - 1; j > i; j--) {
                if (!(this.vInfo_L[j].isBlank) && this.IsDuplicated(this.vLeft, this.vInfo_L, i, j, bIgnoreCase, 2)) {
                    isUnique_L[i] = isUnique_L[j] = false;
                    break;
                }
            }
        }
        for (i = 0; i < len2; i++) {
            if (!(isUnique_R[i])) { continue; }
            for (j = len2 - 1; j > i; j--) {
                if (!(this.vInfo_R[j].isBlank) && this.IsDuplicated(this.vRight, this.vInfo_R, i, j, bIgnoreCase, 2)) {
                    isUnique_R[i] = isUnique_R[j] = false;
                    break;
                }
            }
            if (!(isUnique_R[i])) { continue; }
            for (j = 0; j < len1; j++) {
                if (isUnique_L[j] && (this.vInfo_L[j].UniquePair < 0) && (this.CompareLine(j, i, true) > 1)) {
                    this.vInfo_R[i].UniquePair = j;
                    this.vInfo_L[j].UniquePair = i;
                    break;
                }
            }
        }
    },
    Brief: function(strFile1, strFile2) {
        this.strFile_L = strFile1;
        this.strFile_R = strFile2;
        var s1 = LoadFile(strFile1);
        var s2 = LoadFile(strFile2);
        this.vLeft = s1.replace(/^\n+|\n+$/g, "").split("\n");
        this.vRight = s2.replace(/^\n+|\n+$/g, "").split("\n");

        var len1 = this.vLeft.length, len2 = this.vRight.length;

        //work around - "No newline at end of file"
        if (isBlank(this.vLeft[len1 - 1])) {
            this.vLeft.pop();
            len1--;
        }
        if (isBlank(this.vRight[len2 - 1])) {
            this.vRight.pop();
            len2--;
        }
        this.InitInfo(len1, len2);

        var x, y;
        for (x = 0, y = 0; (x < len1) && (y < len2); x++, y++) {
            if (this.bIgnoreBlankLines) {
                while (this.vInfo_L[x].isBlank && (x < len1 - 1)) { x++; }
                while (this.vInfo_R[y].isBlank && (y < len2 - 1)) { y++; }
            }
            if (this.CompareLine(x, y, true) < 1) { return true; }
        }
        return false;
    },
    Diff: function(strFile1, strFile2) {
        this.strFile_L = strFile1;
        this.strFile_R = strFile2;
        var s1 = LoadFile(strFile1);
        var s2 = LoadFile(strFile2);
        var eol1 = s1.match(/\r\n|\n|\r/);
        var eol2 = s2.match(/\r\n|\n|\r/);
        this.vLeft = ((eol1 != null) ? s1.split(eol1) : [s1]);
        this.vRight = ((eol2 != null) ? s2.split(eol2) : [s2]);

        var len1 = this.vLeft.length, len2 = this.vRight.length;

        //work around - "No newline at end of file"
        if (isBlank(this.vLeft[len1 - 1])) {
            this.vLeft.pop();
            len1--;
        }
        if (isBlank(this.vRight[len2 - 1])) {
            this.vRight.pop();
            len2--;
        }
        this.InitInfo(len1, len2);

        var o = (len1 > len2) ? this.Diff_(len2, len1, true) : this.Diff_(len1, len2, false);

        var vResult = [], vWork = [];
        for (; o; o = o.prev) {
            vResult.unshift(o);
            if ((o.op == "-") || (o.op == "+")) { continue; }
            this.vInfo_L[o.x].status = this.vInfo_R[o.y].status = o.op;
            this.vInfo_L[o.x].pair = o.y;
            this.vInfo_R[o.y].pair = o.x;
        }

        this.CreateHunksList(len1, len2, vResult);

        return vResult;
    },
    //len1 <= len2
    Diff_: function(len1, len2, bReverse) {
        var p, k = len1 + len2 + 2, fp = new Array(k + 1), ed = new Array(k + 1);
        var x, y, x1, y1, y0, score, bIsBlank, org;
        var bStrict = !this.bDetectSimilarLine, threshold = this.threshold;
        do {
            fp[k] = -1;
            ed[k] = null;
        } while (k--);
        for (p = 0; fp[len2 + 1] != len2; p++) {
            for (k = len1 - p; k < len2; k++) {
                y = Math.max(fp[k] + 1, fp[k + 2]);
                x = y - k + len1;
                if (bReverse) {
                    x1 = y; y1 = x;
                    if (y == fp[k + 2]) {
                        ed[k + 1] = {op:"+", x:-1, y:y1 - 1, prev:ed[k + 2]};
                    } else if (y > 0) {
                        ed[k + 1] = {op:"-", x:x1 - 1, y:-1, prev:ed[k]};
                    }
                } else {
                    x1 = x; y1 = y;
                    if ((y != (fp[k] + 1)) || (fp[k + 2] == 0)) {
                        ed[k + 1] = {op:"-", x:x1 - 1, y:-1, prev:ed[k + 2]};
                    } else if (y > 0) {
                        ed[k + 1] = {op:"+", x:-1, y:y1 - 1, prev:ed[k]};
                    }
                }
                for (bIsBlank = true, y0 = y, org = ed[k + 1]; x < len1; x++, y++, x1++, y1++) {
                    if ((score = this.CompareLine(x1, y1, bStrict)) <= threshold) { break; }
                    if (!this.vInfo_L[x1].isBlank) { bIsBlank = false; }
                    ed[k + 1] = {op:((score > 1) ? "=" : "!"), x:x1, y:y1, prev:ed[k + 1]};
                }
                if (bIsBlank && !((x >= (len1 - 1)) && (y >= (len2 - 1)))) { y = y0; ed[k + 1] = org; }
                fp[k + 1] = y;
            }
            for (k = len2 + p; k >= len2; k--) {
                y = Math.max(fp[k] + 1, fp[k + 2]);
                x = y - k + len1;
                if (bReverse) {
                    x1 = y; y1 = x;
                    if (y == fp[k + 2]) {
                        ed[k + 1] = {op:"+", x:-1, y:x - 1, prev:ed[k + 2]};
                    } else if (y > 0) {
                        ed[k + 1] = {op:"-", x:y - 1, y:-1, prev:ed[k]};
                    }
                } else {
                    x1 = x; y1 = y;
                    if ((y != (fp[k] + 1)) || (fp[k + 2] == 0)) {
                        ed[k + 1] = {op:"-", x:x - 1, y:-1, prev:ed[k + 2]};
                    } else if (y > 0) {
                        ed[k + 1] = {op:"+", x:-1, y:y - 1, prev:ed[k]};
                    }
                }
                for (bIsBlank = true, y0 = y, org = ed[k + 1]; y < len2; x++, y++, x1++, y1++) {
                    if ((score = this.CompareLine(x1, y1, bStrict)) <= threshold) { break; }
                    if (!this.vInfo_L[x1].isBlank) { bIsBlank = false; }
                    ed[k + 1] = {op:((score > 1) ? "=" : "!"), x:x1, y:y1, prev:ed[k + 1]};
                }
                if (bIsBlank && !((x >= (len1 - 1)) && (y >= (len2 - 1)))) { y = y0; ed[k + 1] = org; }
                fp[k + 1] = y;
            }
        }
        return ed[len2 + 1];
    },
    CreateHunksList: function(len1, len2, vResult) {
        this.oHunks_L = new Hunks();
        this.oHunks_R = new Hunks();
        var o_L = this.oHunks_L, o_R = this.oHunks_R;
        var o, x = 0, y = 0, x0 = -1, y0 = -1, bIsBlank_L, bIsBlank_R, op_bak = "", i;

        for (i = 0; o = vResult[i]; ) {
            if (o.op == "-") {          //left block
                for (x0 = o.x - 1, bIsBlank_L = true; (o = vResult[i]) && (x < len1) && (o.op == "-"); i++, x++) {
                    if (!this.vInfo_L[x].isBlank) { bIsBlank_L = false; }
                }
                o_L.addEntry(x0, x, bIsBlank_L);
                op_bak = "-";
            } else if (o.op == "+") {   //right block
                if (op_bak != "-") { o_L.addEntry(x - 1, x, true); }
                for (y0 = o.y - 1, bIsBlank_R = true; (o = vResult[i]) && (y < len2) && (o.op == "+"); i++, y++) {
                    if (!this.vInfo_R[o.y].isBlank) { bIsBlank_R = false; }
                }
                o_R.addEntry(y0, y, bIsBlank_R);
                op_bak = "+";
            } else if (o.op == "!") {   //change
                if (op_bak == "-") { o_R.addEntry(o.y - 1, o.y, true); }
                x0 = o.x - 1, bIsBlank_L = true;
                y0 = o.y - 1, bIsBlank_R = true;
                for (op_bak = o.op; (o = vResult[i]) && (o.op == op_bak); i++, x++, y++) {
                    if (!this.vInfo_L[x].isBlank) { bIsBlank_L = false; }
                    if (!this.vInfo_R[o.y].isBlank) { bIsBlank_R = false; }
                }
                o_L.addEntry(x0, x, bIsBlank_L);
                o_R.addEntry(y0, y, bIsBlank_R);
            } else {    //common
                if (op_bak == "-") { o_R.addEntry(o.y - 1, o.y, true); }
                for (op_bak = o.op; (o = vResult[i]) && (o.op == op_bak); i++, x++, y++) {}
            }
        }
        if (op_bak == "-") { o_R.addEntry(len2 - 1, len2, true); }
    }
};//DiffEngine
//==============================================================================
function Int2Str(v) { return ((v == 0) ? "" : (v + "")); }

function do_diff_c(strFile1, strFile2) {
    var objDiff = DiffEngine;
    objDiff.bDetectSimilarLine = true;
    var vResult = objDiff.Diff(strFile1, strFile2);
    var vTable = [];
    var i, x, y, x_min, y_min, x_max, y_max, oHunk_L, oHunk_R, nHunkLen = 0;
    var lines = objDiff.lines;

    var dt1 = new Date(fso.GetFile(strFile1).DateLastModified);
    var dt2 = new Date(fso.GetFile(strFile2).DateLastModified);
    vTable.push("*** " + strFile1 + "   " + formatdate(dt1));
    vTable.push("--- " + strFile2 + "   " + formatdate(dt2));

    nHunkLen = objDiff.oHunks_L.length();
    for (i = 0; i < nHunkLen; i++) {
        oHunk_L = objDiff.oHunks_L.getHunk(i);
        oHunk_R = objDiff.oHunks_R.getHunk(i);

        vTable.push("***************");

        x_min = Math.max(oHunk_L.start - lines + 1, 0);
        x_max = Math.min(objDiff.vLeft.length - 1, oHunk_L.end + lines - 1);
        vTable.push("*** "  + (x_min + 1) + ", "  + (x_max + 1) + " ****");

        for (x = x_min; x <= x_max; x++) {
            switch (objDiff.vInfo_L[x].status) {
                case "+":
                    vTable.push("- " + objDiff.vLeft[x]);
                    break;
                case "!":
                    vTable.push("! " + objDiff.vLeft[x]);
                    break;
                default:
                    vTable.push("  " + objDiff.vLeft[x]);
                    break;
            }
        }

        y_min = Math.max(oHunk_R.start - lines + 1, 0);
        y_max = Math.min(objDiff.vRight.length - 1, oHunk_R.end + lines - 1);
        vTable.push("--- "  + (y_min + 1) + ", "  + (y_max + 1) + " ----");

        for (y = y_min; y <= y_max; y++) {
            switch (objDiff.vInfo_R[y].status) {
                case "+":
                    vTable.push("+ " + objDiff.vRight[y]);
                    break;
                case "!":
                    vTable.push("! " + objDiff.vRight[y]);
                    break;
                default:
                    vTable.push("  " + objDiff.vRight[y]);
                    break;
            }
        }
    }

    for (i = 0; i < vTable.length; i++) {
        print(vTable[i]);
    }
}
function do_diff_u(strFile1, strFile2) {
    var objDiff = DiffEngine;
    objDiff.bDetectSimilarLine = false;
    var vResult = objDiff.Diff(strFile1, strFile2);
    var vTable = [], vWork = [], nCount_L = 0, nCount_R = 0, nCount_M = -1;
    var i, j, o, n1 = 1, n2 = 1, current;
    var lines = objDiff.lines;

    var dt1 = new Date(fso.GetFile(strFile1).DateLastModified);
    var dt2 = new Date(fso.GetFile(strFile2).DateLastModified);
    vTable.push("--- " + strFile1 + "   " + formatdate(dt1));
    vTable.push("+++ " + strFile2 + "   " + formatdate(dt2));

    for (i = 0; current = vResult[i]; i++) {
        if (current.op == "+") {
            if ((i <= 0 || vResult[i - 1].op == "=") && (nCount_M < 0 || nCount_M >= lines)) {
                CountLine(i, lines);
                vTable.push("@@ -" +  + Int2Str(n1 - vWork.length) + "," + nCount_L
                    + " +" + Int2Str(n2 - vWork.length) + "," + nCount_R + " @@");
                vTable = vTable.concat(vWork);
                vWork = [];
            }
            vTable.push("+" + objDiff.vRight[current.y]);
            nCount_M = 0;
            n2++;
        } else if (current.op == "-") {
            if ((i <= 0 || vResult[i - 1].op == "=") && (nCount_M < 0 || nCount_M >= lines)) {
                CountLine(i, lines);
                vTable.push("@@ -" + Int2Str(n1 - vWork.length) + "," + nCount_L
                    + " +" + Int2Str(n2 - vWork.length) + "," + nCount_R + " @@");
                vTable = vTable.concat(vWork);
                vWork = [];
            }
            vTable.push("-" + objDiff.vLeft[current.x]);
            nCount_M = 0;
            n1++;
        } else {
            if (nCount_M >= 0 && nCount_M < lines) {
                vTable.push(" " + objDiff.vLeft[current.x]);
                nCount_M++;
            } else {
                nCount_L = 0;
                nCount_R = 0;
                nCount_M = -1;
                for (j = i + 1; o = vResult[j]; j++) {
                    if (j > (i + lines)) { break; }
                    if (o.op != "=") {
                        vTable.push(" " + objDiff.vLeft[current.x]);
                        break;
                    }
                }
            }
            n1++;
            n2++;
        }
    }

    function CountLine(i_start, lines) {
        nCount_L = vWork.length;
        nCount_R = vWork.length;
        var nCount_M2 = -1, j, o;
        for (j = i_start; o = vResult[j]; j++) {
                switch (o.op) {
                    case "-":
                        nCount_L++;
                        nCount_M2 = 0;
                        break;
                    case "+":
                        nCount_R++;
                        nCount_M2 = 0;
                        break;
                    default:
                        nCount_L++;
                        nCount_R++;
                        if (nCount_M2 >= 0 && nCount_M2 < lines) {
                            nCount_M2++;
                            if (nCount_M2 >= lines) { return; }
                        }
                        break;
                }
        }
    }

    for (i = 0; i < vTable.length; i++) {
        print(vTable[i]);
    }
}

function do_diff_normal(strFile1, strFile2) {
    var objDiff = DiffEngine;
    objDiff.bDetectSimilarLine = true;
    var vResult = objDiff.Diff(strFile1, strFile2);
    var vTable = [], vWork = [], vWork2 = [], i, current, strHeader = "", epos;
    var arrlen = vResult.length - 1, x = -1;

    vTable.push("$diff " + strFile1 + " " + strFile2);
    for (i = 0; current = vResult[i]; i++) {
        if (objDiff.bIgnoreBlankLines && 
            (((current.x >= 0) && objDiff.vInfo_L[current.x].isBlank)
            || ((current.y >= 0) && objDiff.vInfo_R[current.y].isBlank))) {
            if (current.x >= 0) { x = current.x; }
            continue;
        }
        if (current.op == "+") {
            if (i == 0) {
                strHeader = "0a" + (current.y + 1);
            } else {
                strHeader = (x + 1) + "a" + (current.y + 1);
            }

            vWork = [];
            vWork.push("> " + objDiff.vRight[current.y]);
            while ((i < (arrlen - 1)) && vResult[i + 1].op == "+") {
                i++;
                vWork.push("> " + objDiff.vRight[vResult[i].y]);
            }

            if (current.y != vResult[i].y) {
                epos = vResult[i].y;
                if (objDiff.bIgnoreBlankLines) {
                    while (objDiff.vInfo_R[epos].isBlank) {
                        vWork.pop();
                        epos--;
                    }
                }
                if (current.y != epos) {
                    strHeader += "," + (epos + 1);
                }
            }
            vTable.push(strHeader);
            vTable = vTable.concat(vWork);
        } else if (current.op == "-") {
            strHeader = (current.x + 1);

            vWork = [];
            vWork.push("< " + objDiff.vLeft[current.x]);
            while ((i < (arrlen - 1)) && vResult[i + 1].op == "-") {
                i++;
                vWork.push("< " + objDiff.vLeft[vResult[i].x]);
            }

            if (current.x != vResult[i].x) {
                epos = vResult[i].x;
                if (objDiff.bIgnoreBlankLines) {
                    while (objDiff.vInfo_L[epos].isBlank) {
                        vWork.pop();
                        epos--;
                    }
                }
                if (current.x != epos) {
                    strHeader += "," + (epos + 1);
                }
            }
            if (i >= (arrlen - 1)) {
                strHeader += "d " + objDiff.vRight.length;
            } else {
                strHeader += "d " + (vResult[i + 1].y);
            }
            vTable.push(strHeader);
            vTable = vTable.concat(vWork);
            x = vResult[i].x;
        } else if (current.op == "!") {
            vWork = [];
            vWork2 = [];
            var x0 = (current.x + 1), y0 = (current.y + 1);

            vWork.push("< " + objDiff.vLeft[current.x]);
            vWork2.push("> " + objDiff.vRight[current.y]);
            while ((i < (arrlen - 1)) && vResult[i + 1].op == "!") {
                i++;
                vWork.push("< " + objDiff.vLeft[vResult[i].x]);
                vWork2.push("> " + objDiff.vRight[vResult[i].y]);
            }

            if (current.x != vResult[i].x) {
                strHeader = x0 + "," + (vResult[i].x + 1) + "c" + y0 + "," + (vResult[i].y + 1);
            } else {
                strHeader = x0 + "c" + y0;
            }
            vTable.push(strHeader);
            vTable = vTable.concat(vWork)
            vTable.push("---");
            vTable = vTable.concat(vWork2)
            x = vResult[i].x;
        } else {
            x = vResult[i].x;
        }
    }//for

    for (i = 0; i < vTable.length; i++) {
        print(vTable[i]);
    }
}

function do_diff_q(strFile1, strFile2, strOutputFormat) {
    var objDiff = DiffEngine;
    objDiff.bDeectSimilarLine = false;
    if (objDiff.Brief(strFile1, strFile2)) {
        print("Files " + strFile1 + " and " + strFile2 + " differ");
    } else if (strOutputFormat == "s") {
        print("Files " + strFile1 + " and " + strFile2 + " are identical");
    }
}


function pad_zero(n) { return ((n < 10) ? ("0" + n) : n); }
function formatdate(dt) {
    var month = dt.getMonth() + 1;
    return (dt.getFullYear() + "-" + pad_zero(month) + "-" + pad_zero(dt.getDate()) + " "
        + pad_zero(dt.getHours()) + ":" + pad_zero(dt.getMinutes()) + ":" + pad_zero(dt.getSeconds())
        + ".000000000 +0900");
}

function LoadFile(strFile) {
    var o = new ActiveXObject("ADODB.Stream");
    var text = "";
    o.type = 2;
    o.charset = "_autodetect_all";
    o.open();
    o.loadFromFile(strFile);
    text = o.readText(-1);
    o.close();
    return text;
}
//==============================================================================
function Version() {
    print("lesser diff.exe for Windows  (NOT compatible with GNU Diff)");
}

function do_diff_main() {
    try {
        var arg = Environment.GetCommandLineArgs();
        var strFile1 = "", strFile2 = "", strOption;
        var strOutputFormat = "", nLines = 3;

        if (arg.length < 2) {
            Version();
            return 0;
        }

        for (var i = 1; i < arg.length; i++) {
            strOption = arg[i];

            var index = strOption.indexOf("=");
            var strOption2 = "";
            if (index !== -1) {
                strOption2 = strOption.substring(index + 1);
                strOption = strOption.substring(0, index);
            }

            switch (strOption) {
                case "--text":
                case "--minimal":
                case "--label":
                    break;
                case "--context":
                    strOutputFormat = "c";
                    if (strOption2 != "") {
                        nLines = parseInt(strOption2, 10);
                        if (isNaN(nLines)) { nLines = 3; }
                    }
                    break;
                case "--inhibit-hunk-merge":
                    //not implemented
                    break;
                case "--ignore-space-change":
                    if (DiffEngine.flgIgnoreBlank != 2) {
                        DiffEngine.flgIgnoreBlank = 1;
                    }
                    break;
                case "--ignore-all-space":
                    DiffEngine.flgIgnoreBlank = 2;
                    break;
                case "--ignore-blank-lines":
                    DiffEngine.bIgnoreBlankLines = true;
                    break;
                case "--ignore-tab-expansion":
                case "--expand-tabs":
                case "--initial-tab":
                    //not implemented
                    break;
                case "--ignore-case":
                    DiffEngine.bIgnoreCase = true;
                    break;
                case "--brief":
                    if (strOutputFormat != "s") { strOutputFormat = "q"; }
                    break;
                case "--unified":
                    strOutputFormat = "u";
                    if (strOption2 != "") {
                        nLines = parseInt(strOption2, 10);
                        if (isNaN(nLines)) { nLines = 3; }
                    }
                    break;
                case "--report-identical-files":
                    strOutputFormat = "s";
                    break;
                case "--recursive":
                    break;
                case "-v":
                case "--version":
                    Version();
                    return 0;
                    break;
                case "--help":
                    Version();
                    return 0;
                    break;
                default:
                    var strPrefix = strOption.substring(0, 1);
                    if ((strPrefix == "-") || (strPrefix == "/")) {
                        if (strOption.substring(0, 2) == "--") {
                            //do nothing
                        } else {
                            for (var j = 1; j < strOption.length; j++) {
                                switch (strOption.substring(j, j + 1)) {
                                    case "a":   //--text
                                    case "d":   //--minimal
                                    case "e":   //--ed
                                    case "E":   //--ignore-tab-expansion
                                    case "I":   //--ignore-matching-lines
                                    case "L":   //--label
                                    case "n":   //--rcs
                                    case "t":   //--expand-tabs
                                    case "T":   //--initial-tab
                                    case "r":   //--recursive
                                    case "y":   //--side-by-side
                                        //not implemented
                                        break;
                                    case "c":   //--context
                                    case "C":   //--context
                                        strOutputFormat = "c";
                                        if ((i + 1) < arg.length) {
                                            nLines = parseInt(arg[i + 1], 10);
                                            if (isNaN(nLines)) {
                                                nLines = 3;
                                            } else {
                                                i++
                                            }
                                        }
                                        break;
                                    case "q":   //--brief
                                        if (strOutputFormat != "s") { strOutputFormat = "q"; }
                                        break;
                                    case "s":   //--report-identical-files
                                        strOutputFormat = "s";
                                        break;
                                    case "i":   //--ignore-case
                                        break;
                                    case "b":   //--ignore-space-change
                                        if (DiffEngine.flgIgnoreBlank != 2) {
                                            DiffEngine.flgIgnoreBlank = 1;
                                        }
                                        break;
                                    case "w":   //--inore-all-space
                                        DiffEngine.flgIgnoreBlank = 2;
                                        break;
                                    case "B":   //--ignore-blank-lines
                                        DiffEngine.bIgnoreBlankLines = true;
                                        break;
                                    case "u":   //--unified
                                    case "U":   //--unified
                                        strOutputFormat = "u";
                                        if ((i + 1) < arg.length) {
                                            nLines = parseInt(arg[i + 1], 10);
                                            if (isNaN(nLines)) {
                                                nLines = 3;
                                            } else {
                                                i++
                                            }
                                        }
                                        break;
                                    default:
                                        break;
                                }
                            }
                        }
                    } else if (strFile1 == "") {
                        strFile1 = strOption;
                    } else if (strFile2 == "") {
                        strFile2 = strOption;
                    }
                    break;
            }
        }

        if ((strFile1 == "") || (strFile2 == "")) {
            throw "file name is empty";
        }
        if (!fso.FileExists(strFile1) || !fso.FileExists(strFile2)) {
            throw "file not found";
        }

        DiffEngine.lines = nLines;

        if (strOutputFormat == "u") {   //unified
            do_diff_u(strFile1, strFile2);
        } else if ((strOutputFormat == "q") || (strOutputFormat == "s")) {
            do_diff_q(strFile1, strFile2, strOutputFormat);
        } else if (strOutputFormat == "c") {    //context
            do_diff_c(strFile1, strFile2);
        } else {
            do_diff_normal(strFile1, strFile2);
        }

        return 0;
    } catch (e) {
        print (e.message || e.toString())
        return -1;
    } finally {
    }
}

do_diff_main();

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2