1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have 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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?