1 #title = "bcc"
  2 #tooltip = "bcc32"
  3 #include "lib\sgc4jsee.jsee"
  4 
  5 /**
  6  * @fileOverview
  7  * bcc32でコンパイルし、確認ダイアログで確認後、実行します。<br />
  8  * <strong>ドキュメントを上書き保存してから実行します。</strong><br />
  9  * <p>
 10  * rcファイルがあればbrc32でバインドします。<br />
 11  * 作業時に作成されるtdsファイル、objファイル、RESファイル、TEMP1.$$$ファイルは削除します。
 12  * </p>
 13  * <p>
 14  * INIファイルでcfgファイルを使用する設定にしていて、かつcfgファイルが存在しない場合、cfgファイルを自動生成します。
 15  * </p>
 16  * <p>
 17  * ドキュメントがcまたはcppファイルでない場合、以下のルールで同じディレクトリからコンパイル対象ファイルを探します。<br />
 18  * <ul>
 19  * <li>ドキュメントがhファイルであれば、同名のcファイル、なければcppファイル</li>
 20  * <li>同じディレクトリにcファイルまたはcppファイルがあれば、その中で更新日時が最新のもの</li>
 21  * </ul>
 22  * 探し出したファイルを処理する前には、確認ダイアログを表示します。
 23  * </p>
 24  * <p>
 25  * 上記のコンパイル対象ファイルと同名のrcファイルがない場合、以下のルールで同じディレクトリからrcファイルを探します。<br />
 26  * <ul>
 27  * <li>同じディレクトリにrcファイルがあれば、その中で更新日時が最新のもの</li>
 28  * </ul>
 29  * 探し出したファイルを処理する前には、確認ダイアログを表示します。
 30  * rcファイルのバインド先は、INIファイルで設定した追加オプションに"-WD"が存在しなければexeファイル、存在すればdllファイルです。
 31  * </p>
 32  * <p>
 33  * BCPadを参考にしました。
 34  * </p>
 35  * 
 36  * @author gecca from 雪月花 (http://setsugecca.org/)
 37  * @version 1.00 for EmEditor v10.0
 38  */
 39 
 40 (function() {
 41 	if(!document.FullName) {
 42 		alert("存在しないドキュメントです。");
 43 		return;
 44 	}
 45 	
 46 	// 準備
 47 	var ini = JseeUtil.getSnipIniObj("bcc32");
 48 	
 49 	var binDirPath = ini.binDirPath.replace(/(\\)?$/, "");
 50 	var useCfg = ini.useCfg == "true";
 51 	var additionalOption = ini.additionalOption;
 52 	
 53 	var shell = JseeUtil.shell();
 54 	var fso = JseeUtil.fso();
 55 	
 56 	var logic = new Logic();
 57 	
 58 	// exeファイルパスなどを保持 (brc32の存在チェックもここでする)
 59 	var relativeExePath_bccRootPath_fromPATH = (function(binDirPath) {
 60 		// INIファイルに設定がある場合
 61 		if(binDirPath) {
 62 			// INIファイルのBinフォルダをチェック
 63 			if(!fso.FolderExists(binDirPath)) {
 64 				if(confirm("INIファイルのbinDirPathは存在しないパスです。\n\n" + JseeUtil.getSnipIniName() + "を開きますか?")) {
 65 					JseeUtil.selectSnipIniValue("bcc32", "binDirPath");
 66 				}
 67 				return;
 68 			}
 69 			if(!fso.FileExists(binDirPath + "\\bcc32.exe")) {
 70 				OutputBar.clear();
 71 				OutputBar.Visible = true;
 72 				OutputBar.writeln(binDirPath + "\\bcc32.exe");
 73 				if(confirm("INIファイルのbinDirPath内にbcc32.exeが存在しませんでした。\n\n" + JseeUtil.getSnipIniName() + "を開きますか?")) {
 74 					JseeUtil.selectSnipIniValue("bcc32", "binDirPath");
 75 				}
 76 				return;
 77 			}
 78 			if(!fso.FileExists(binDirPath + "\\brc32.exe")) {
 79 				if(confirm("INIファイルのbinDirPath内にbrc32.exeが存在しませんでした。\n\n" + JseeUtil.getSnipIniName() + "を開きますか?")) {
 80 					JseeUtil.selectSnipIniValue("bcc32", "binDirPath");
 81 				}
 82 				return;
 83 			}
 84 			// INIファイルの情報を返す
 85 			return [binDirPath + "\\bcc32.exe", binDirPath.replace(/bin$/i, ""), false];
 86 		
 87 		// INIファイルに設定がある場合
 88 		} else {
 89 			// 環境変数"PATH"から取得
 90 			var paths = shell.Environment("System").Item("PATH").split(";");
 91 			//var paths = JseeUtil.exec("cmd /c path").out.substring("PATH=".length).split(";");
 92 			for(var i=0;i<paths.length;i++) {
 93 				var path = paths[i];
 94 				if(/\\bin$/i.test(path) && fso.FolderExists(path) && fso.FileExists(path + "\\bcc32.exe") && fso.FileExists(path + "\\brc32.exe")) {
 95 					// 環境変数の情報を返す
 96 					return ["bcc32", path.replace(/bin$/i, ""), true];
 97 				}
 98 			}
 99 			
100 			// Binフォルダを特定できなければダイアログでサポート
101 			if(logic.helpConfirmEnviornment()) {
102 				// 環境変数を設定して処理を続行する
103 				arguments.callee();
104 			}
105 			return;
106 		}
107 	})(binDirPath);
108 	if(!relativeExePath_bccRootPath_fromPATH) {
109 		return;
110 	}
111 	var relativeExePath = relativeExePath_bccRootPath_fromPATH[0];
112 	var bccRootPath = relativeExePath_bccRootPath_fromPATH[1];
113 	var fromPATH = relativeExePath_bccRootPath_fromPATH[2];
114 	
115 	// プロセスの環境変数に設定
116 	shell.Environment("Process").Item("PATH") += ";" + bccRootPath + "\\Bin";
117 	
118 	// cfgの自動設定、またはオプション生成
119 	if(useCfg) {
120 		// cfgファイルが存在しない場合はcfg自動設定
121 		if(!fso.FileExists(bccRootPath + "bin\\ilink32.cfg") || !fso.FileExists(bccRootPath + "bin\\bcc32.cfg")) {
122 			if(confirm("INIファイルのuseCfgがtrueですが、\nilink32.cfgまたはbcc32.cfgが存在しません。\n\n自動生成しますか?")) {
123 				logic.autoSetCfg(bccRootPath);
124 				alert("ilink32.cfgとbcc32.cfgを作成しました。");
125 			} else {
126 				if(confirm("INIファイルのuseCfgを設定するため、\n" + JseeUtil.getSnipIniName() + "を開きますか?")) {
127 					JseeUtil.selectSnipIniValue("bcc32", "useCfg");
128 				}
129 				return;
130 			}
131 		}
132 	} else {
133 		// cfgファイルを使わない場合はコンパイラオプションを自動生成
134 		var cfgOption = (function() {
135 			var libPath = bccRootPath + "Lib";
136 			if(!fso.FolderExists(libPath)) {
137 				alert((fromPATH ? "環境変数\"PATH\"にbcc32.exeを含むbccのBinフォルダ" : "INIファイルのbinDirPath") + "から、以下のLibフォルダへ辿ろうとして失敗しました。\n処理を中断します。パスを見直してください。\n\n" + libPath);
138 				if(logic.helpConfirmEnviornment(fromPATH)) {
139 					// 環境変数を設定して処理を続行する
140 					arguments.callee();
141 				}
142 				return;
143 			}
144 			
145 			var incPath = bccRootPath + "Include";
146 			if(!fso.FolderExists(libPath)) {
147 				alert((fromPATH ? "環境変数\"PATH\"にbcc32.exeを含むbccのBinフォルダ" : "INIファイルのbinDirPath") + "から、以下のIncludeフォルダへ辿ろうとして失敗しました。\n処理を中断します。パスを見直してください。\n\n" + libPath);
148 				if(logic.helpConfirmEnviornment(fromPATH)) {
149 					// 環境変数を設定して処理を続行する
150 					arguments.callee();
151 				}
152 				return;
153 			}
154 			
155 			return "-L\"" + libPath + "\" -I\"" + incPath + "\"";
156 		})();
157 		if(!cfgOption) {
158 			return;
159 		}
160 	}
161 	
162 	// コンパイル対象のファイルを決定し、コンパイル時の実行文を生成
163 	var compileTargetFileName = logic.getCompileTargetFileName();
164 	if(!compileTargetFileName) {
165 		return;
166 	}
167 	
168 	var execSentence = relativeExePath + (cfgOption ? " " + cfgOption : "") + (additionalOption ? " " + additionalOption : "")+ " -w- " + compileTargetFileName;
169 	
170 	// コンパイル後のファイル名 (拡張子無し、ピリオド付き)
171 	var compiledFileNameByPeriod = compileTargetFileName.replace(/([^.]*$)/, "");
172 	// コンパイル後のファイルの拡張子 (追加オプションに"-WD"が存在しなければexe、存在すればdll)
173 	var compiledFileExt = (!/(^|\s)-WD(\s|$)/.test(additionalOption)) ? "exe" : "dll";
174 	// コンパイル後のファイル名
175 	var compiledFileName = compiledFileNameByPeriod + compiledFileExt;
176 	
177 	// リソースバインド対象ファイルを決定し、リソースバインド時の実行文を生成
178 	// リソースファイルが無ければundefined
179 	var execRcSentence;
180 	var rcFileName = logic.getRcFileName(compileTargetFileName);
181 	if(rcFileName) {
182 		// ここでは既に存在チェック済みなのでチェックしない
183 		var brcExePath = relativeExePath.replace(/(b)c(c32(\.exe)?)$/i, "$1r$2");
184 		execRcSentence = brcExePath + " " + rcFileName + " " + compiledFileName;
185 	}
186 	
187 	// カレントディレクトリ変更
188 	shell.CurrentDirectory = document.Path;
189 	
190 	// ドキュメント保存
191 	editor.ExecuteCommandByID(4099);
192 	
193 	// 実行文を実行してコンパイル
194 	var logger = JseeUtil.getLogger(true, "");
195 	logger.log(shell.CurrentDirectory + ">" + execSentence);
196 	var std = JseeUtil.exec(execSentence, true);
197 	logger.log(std.out);
198 	if(/[\r\n](エラー|Error)/.test(std.out)) {
199 		if(/インクルードファイル '(stdio|iostream|windows)' をオープンできない[\r\n]/.test(std.out)) {
200 			alert("cfgファイルの設定が間違っている可能性があります。\n\nilink32.cfgとbcc32.cfgを削除して再実行すると、\nエラーが直る可能性があります。");
201 		}
202 		// エラーがあれば終了
203 		return;
204 	}
205 	// コンパイル時の一時ファイルを削除
206 	logic.deleteFile(compiledFileNameByPeriod + "tds");
207 	logic.deleteFile(compiledFileNameByPeriod + "obj");
208 	
209 	// 実行文を実行してリソースバインド
210 	if(execRcSentence) {
211 		logger.log(shell.CurrentDirectory + ">" + execRcSentence);
212 		var std = JseeUtil.exec(execRcSentence, true);
213 		logger.log(std.out);
214 		
215 		// リソースバインド時の一時ファイルを削除
216 		logic.deleteFile("TEMP1.$$$");
217 		logic.deleteFile(rcFileName.replace(/([^.]*$)/, "RES"));
218 	}
219 	
220 	// 作成されたexeを実行
221 	if(compiledFileExt == "exe" && confirm("コンパイルが完了しました。実行しますか?")) {
222 		logger.log(shell.CurrentDirectory + ">" + compiledFileName);
223 		var std = JseeUtil.exec(compiledFileName);
224 		if(std.err) {
225 			logger.log(std.err);
226 		} else {
227 			logger.log(std.out);
228 		}
229 	}
230 })();
231 
232 /**
233  * @class
234  * ロジッククラス
235  */
236 function Logic() {
237 	var shell = JseeUtil.shell();
238 	var fso = JseeUtil.fso();
239 	
240 	/**
241 	 * @function
242 	 * ダイアログで環境変数またはINIファイルの設定を促すダイアログを表示します。
243 	 * 
244 	 * @param {Boolean} fromPATH 環境変数の設定を促す場合は<code>true</code>、両方の場合は<code>undefined</code>
245 	 * @returns {Boolean} 環境変数を設定して処理を続行する場合は<code>true</code>
246 	 */
247 	Logic.prototype.helpConfirmEnviornment = function(fromPATH) {
248 		if(JseeUtil.isUndef(fromPATH)) {
249 			// 環境変数の設定を促すダイアログ
250 			if(confirm("環境変数\"PATH\"にbcc32.exe・brc32.exeを含むbccのBinフォルダが見つからず、\nINIファイルのbinDirPathにも設定がありませんでした。\n\nシステムのプロパティを開いて環境変数を追加しますか?")) {
251 				shell.Run("control.exe sysdm.cpl", 0, true);
252 				return confirm("新たに設定した情報で処理を続けますか?");
253 			}
254 			// INIファイルの設定を促すダイアログ
255 			if(confirm("INIファイルのbinDirPathを設定するため、\n" + JseeUtil.getSnipIniName() + "を開きますか?")) {
256 				JseeUtil.selectSnipIniValue("bcc32", "binDirPath");
257 			}
258 		} else {
259 			if(fromPATH) {
260 				// 環境変数の設定を促すダイアログ
261 				if(confirm("システムのプロパティを開いて環境変数を変更しますか?")) {
262 					shell.Run("control.exe sysdm.cpl", 0, true);
263 					return confirm("新たに設定した情報で処理を続けますか?");
264 				}
265 			} else {
266 				// INIファイルの設定を促すダイアログ
267 				if(confirm(JseeUtil.getSnipIniName() + "を開きますか?")) {
268 					JseeUtil.selectSnipIniValue("bcc32", "binDirPath");
269 				}
270 			}
271 		}
272 	};
273 	
274 	/**
275 	 * @function
276 	 * cfgファイルを自動設定します。
277 	 */
278 	Logic.prototype.autoSetCfg = function(bccRootPath) {
279 		JseeUtil.writeFile(bccRootPath + "\\bin\\bcc32.cfg", "-I\"" + bccRootPath + "Include\"\r\n-L\"" + bccRootPath + "Lib\"\r\n");
280 		JseeUtil.writeFile(bccRootPath + "\\bin\\ilink32.cfg", "-L\"" + bccRootPath + "Lib\"\r\n");
281 	};
282 	
283 	/**
284 	 * @function
285 	 * コンパイル対象となるファイル名を取得します。<br />
286 	 * 処理を中止する場合は<code>undefined</code>を返します。
287 	 * 
288 	 * @returns {String} コンパイル対象となるファイル名、処理中止の場合は<code>undefined</code>
289 	 */
290 	Logic.prototype.getCompileTargetFileName = function() {
291 		var path = document.FullName;
292 		var cOrCppMatcher = /\.c$|\.cpp$/i;
293 		var cMatcher = /\.c$/i;
294 		var cppMatcher = /\.cpp$/i;
295 		var hMatcher = /\.h$/i;
296 		
297 		// ドキュメントがhoge.cまたはhoge.cppの場合はそのまま返却
298 		if(cOrCppMatcher.test(path)) {
299 			return document.Name;
300 			
301 		// ドキュメントがhoge.cでもhoge.cppでもない場合は探す
302 		} else {
303 			// hoge.hならhoge.cかhoge.cppを優先的に探す
304 			if(hMatcher.test(path)) {
305 				var c = path.replace(hMatcher, ".c");
306 				if(fso.FileExists(c)) {
307 					if(confirm("このドキュメントはヘッダファイルです。\n同名のcファイルをコンパイルしてよろしいですか?")) {
308 						return c;
309 					}
310 					return;
311 				}
312 				var cpp = path.replace(hMatcher, ".cpp");
313 				if(fso.FileExists(cpp)) {
314 					if(confirm("このドキュメントはヘッダファイルです。\n同名のcppファイルをコンパイルしてよろしいですか?")) {
315 						return cpp;
316 					}
317 					return;
318 				}
319 			}
320 			// 同じディレクトリ内のhoge.cかhoge.cppを探して配列に詰める
321 			var cOrCppFiles = [];
322 			var files = JseeUtil.col2arr(fso.GetFolder(document.Path).Files);
323 			for(var i=0;i<files.length;i++) {
324 				var file = files[i];
325 				if(cOrCppMatcher.test(file.Name)) {
326 					cOrCppFiles.push(file);
327 				}
328 			}
329 			if(cOrCppFiles.length == 0) {
330 				alert("このドキュメントと同じディレクトリにcファイル、またはcppファイルが見つかりませんでした。");
331 				return;
332 			}
333 			
334 			// 更新日時で降順ソート
335 			cOrCppFiles.sort(function(a, b) { return b.DateLastModified - a.DateLastModified; });
336 			var latestUpdatedFileName = cOrCppFiles[0].Name;
337 			if(confirm("このドキュメントと同じディレクトリの\n以下のファイルをコンパイルしようとしています。\n\n" + latestUpdatedFileName + "\n\nよろしいですか?")) {
338 				return latestUpdatedFileName;
339 			}
340 			return;
341 		}
342 	};
343 	
344 	/**
345 	 * @function
346 	 * rcファイルを取得します。<br />
347 	 * リソースをバインドしない場合は<code>undefined</code>を返します。
348 	 * 
349 	 * @returns {String} rcファイル
350 	 */
351 	Logic.prototype.getRcFileName = function(compileTargetFileName) {
352 		var tempRcPath = compileTargetFileName.replace(/[^.]*$/, "rc");
353 		var rcMatcher = /\.rc$/i;
354 		
355 		if(fso.FileExists(tempRcPath)) {
356 			return tempRcPath;
357 		}
358 		
359 		// 同じディレクトリ内のhoge.rcを探して配列に詰める
360 		var rcFiles = [];
361 		var files = JseeUtil.col2arr(fso.GetFolder(document.Path).Files);
362 		for(var i=0;i<files.length;i++) {
363 			var file = files[i];
364 			if(rcMatcher.test(file.Name)) {
365 				rcFiles.push(file);
366 			}
367 		}
368 		if(rcFiles.length == 0) {
369 			// rcファイルが無ければ何もしない
370 			return;
371 		}
372 		
373 		rcFiles.sort(function(a, b) { return b.DateLastModified - a.DateLastModified; });
374 		var latestUpdatedFileName = rcFiles[0].Name;
375 		if(confirm("このドキュメントと同じディレクトリの\n以下のファイルをリソースとしてバインドしようとしています。\n\n" + latestUpdatedFileName + "\n\nよろしいですか?")) {
376 			return latestUpdatedFileName;
377 		}
378 		return;
379 	};
380 	
381 	/**
382 	 * @function
383 	 * ファイルが存在すれば削除します。
384 	 * 
385 	 * @param {String} fileName ファイル名
386 	 */
387 	Logic.prototype.deleteFile = function(fileName) {
388 		if(fso.FileExists(fileName)) {
389 			fso.DeleteFile(fileName);
390 		}
391 	}
392 }
393