// CoreUtil
// 
// Copyright (C) 2012-2014 Daiyuu Nobori. All Rights Reserved.
// Copyright (C) 2012-2014 SoftEther VPN Project at University of Tsukuba. All Rights Reserved.
// 
// License: The Apache License, Version 2.0
// https://www.apache.org/licenses/LICENSE-2.0
// 
// DISCLAIMER
// ==========
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// 
// THIS SOFTWARE IS DEVELOPED IN JAPAN, AND DISTRIBUTED FROM JAPAN, UNDER
// JAPANESE LAWS. YOU MUST AGREE IN ADVANCE TO USE, COPY, MODIFY, MERGE, PUBLISH,
// DISTRIBUTE, SUBLICENSE, AND/OR SELL COPIES OF THIS SOFTWARE, THAT ANY
// JURIDICAL DISPUTES WHICH ARE CONCERNED TO THIS SOFTWARE OR ITS CONTENTS,
// AGAINST US (SOFTETHER PROJECT, SOFTETHER CORPORATION, DAIYUU NOBORI OR OTHER
// SUPPLIERS), OR ANY JURIDICAL DISPUTES AGAINST US WHICH ARE CAUSED BY ANY KIND
// OF USING, COPYING, MODIFYING, MERGING, PUBLISHING, DISTRIBUTING, SUBLICENSING,
// AND/OR SELLING COPIES OF THIS SOFTWARE SHALL BE REGARDED AS BE CONSTRUED AND
// CONTROLLED BY JAPANESE LAWS, AND YOU MUST FURTHER CONSENT TO EXCLUSIVE
// JURISDICTION AND VENUE IN THE COURTS SITTING IN TOKYO, JAPAN. YOU MUST WAIVE
// ALL DEFENSES OF LACK OF PERSONAL JURISDICTION AND FORUM NON CONVENIENS.
// PROCESS MAY BE SERVED ON EITHER PARTY IN THE MANNER AUTHORIZED BY APPLICABLE
// LAW OR COURT RULE.
// 
// USE ONLY IN JAPAN. DO NOT USE THIS SOFTWARE IN ANOTHER COUNTRY UNLESS YOU HAVE
// A CONFIRMATION THAT THIS SOFTWARE DOES NOT VIOLATE ANY CRIMINAL LAWS OR CIVIL
// RIGHTS IN THAT PARTICULAR COUNTRY. USING THIS SOFTWARE IN OTHER COUNTRIES IS
// COMPLETELY AT YOUR OWN RISK. THE SOFTETHER VPN PROJECT HAS DEVELOPED AND
// DISTRIBUTED THIS SOFTWARE TO COMPLY ONLY WITH THE JAPANESE LAWS AND EXISTING
// CIVIL RIGHTS INCLUDING PATENTS WHICH ARE SUBJECTS APPLY IN JAPAN. OTHER
// COUNTRIES' LAWS OR CIVIL RIGHTS ARE NONE OF OUR CONCERNS NOR RESPONSIBILITIES.
// WE HAVE NEVER INVESTIGATED ANY CRIMINAL REGULATIONS, CIVIL LAWS OR
// INTELLECTUAL PROPERTY RIGHTS INCLUDING PATENTS IN ANY OF OTHER 200+ COUNTRIES
// AND TERRITORIES. BY NATURE, THERE ARE 200+ REGIONS IN THE WORLD, WITH
// DIFFERENT LAWS. IT IS IMPOSSIBLE TO VERIFY EVERY COUNTRIES' LAWS, REGULATIONS
// AND CIVIL RIGHTS TO MAKE THE SOFTWARE COMPLY WITH ALL COUNTRIES' LAWS BY THE
// PROJECT. EVEN IF YOU WILL BE SUED BY A PRIVATE ENTITY OR BE DAMAGED BY A
// PUBLIC SERVANT IN YOUR COUNTRY, THE DEVELOPERS OF THIS SOFTWARE WILL NEVER BE
// LIABLE TO RECOVER OR COMPENSATE SUCH DAMAGES, CRIMINAL OR CIVIL
// RESPONSIBILITIES. NOTE THAT THIS LINE IS NOT LICENSE RESTRICTION BUT JUST A
// STATEMENT FOR WARNING AND DISCLAIMER.
// 
// READ AND UNDERSTAND THE 'WARNING.TXT' FILE BEFORE USING THIS SOFTWARE.
// SOME SOFTWARE PROGRAMS FROM THIRD PARTIES ARE INCLUDED ON THIS SOFTWARE WITH
// LICENSE CONDITIONS WHICH ARE DESCRIBED ON THE 'THIRD_PARTY.TXT' FILE.
// 
// 
// SOURCE CODE CONTRIBUTION
// ------------------------
// 
// Your contribution to SoftEther VPN Project is much appreciated.
// Please send patches to us through GitHub.
// Read the SoftEther VPN Patch Acceptance Policy in advance:
// http://www.softether.org/5-download/src/9.patch
// 
// 
// DEAR SECURITY EXPERTS
// ---------------------
// 
// If you find a bug or a security vulnerability please kindly inform us
// about the problem immediately so that we can fix the security problem
// to protect a lot of users around the world as soon as possible.
// 
// Our e-mail address for security reports is:
// softether-vpn-security [at] softether.org
// 
// Please note that the above e-mail address is not a technical support
// inquiry address. If you need technical assistance, please visit
// http://www.softether.org/ and ask your question on the users forum.
// 
// Thank you for your cooperation.
// 
// 
// NO MEMORY OR RESOURCE LEAKS
// ---------------------------
// 
// The memory-leaks and resource-leaks verification under the stress
// test has been passed before release this source code.


using System;
using System.Threading;
using System.Data;
using System.Data.Sql;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Text;
using System.Configuration;
using System.Collections;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Web.Mail;
using System.Reflection;
using System.Runtime.InteropServices;

#if ASPNET
using Resources.BuildUtil.Properties;
#else
using BuildUtil.Properties;
#endif

namespace CoreUtil
{
	public static class Con
	{
		static ConsoleService cs = null;

		public static ConsoleService ConsoleService
		{
			get { return Con.cs; }
		}

		public static void SetConsoleService(ConsoleService svc)
		{
			cs = svc;
		}

		public static void UnsetConsoleService()
		{
			cs = null;
		}

		public static string ReadLine()
		{
			return ReadLine("");
		}
		public static string ReadLine(string prompt)
		{
			return ReadLine(prompt, false);
		}
		public static string ReadLine(string prompt, bool noFile)
		{
			if (cs != null)
			{
				return cs.ReadLine(prompt, noFile);
			}
			else
			{
				Console.Write(prompt);
				return Console.ReadLine();
			}
		}

		public static void WriteLine()
		{
			WriteLine("");
		}

		public static void WriteLine(object arg)
		{
			if (cs != null)
			{
				cs.WriteLine(arg);
			}
			else
			{
				Console.WriteLine(arg);
			}
		}

		public static void WriteLine(string str)
		{
			if (cs != null)
			{
				cs.WriteLine(str);
			}
			else
			{
				Console.WriteLine(str);
			}
		}

		public static void WriteLine(string str, object arg)
		{
			if (cs != null)
			{
				cs.WriteLine(str, arg);
			}
			else
			{
				Console.WriteLine(str, arg);
			}
		}

		public static void WriteLine(string str, params object[] args)
		{
			if (cs != null)
			{
				cs.WriteLine(str, args);
			}
			else
			{
				Console.WriteLine(str, args);
			}
		}
	}

	public class ConsoleUserCancelException : Exception
	{
		public ConsoleUserCancelException(string msg)
			: base(msg)
		{
		}
	}

	public class ConsoleEvalMinMaxParam
	{
		public readonly string ErrorMessageString;
		public readonly int MinValue, MaxValue;

		public ConsoleEvalMinMaxParam(string errorMessageString, int minValue, int maxValue)
		{
			this.ErrorMessageString = errorMessageString;
			this.MinValue = minValue;
			this.MaxValue = maxValue;
		}
	}

	public enum ConsoleType
	{
		Local,
		Csv,
	}

	public class ConsoleParam
	{
		public readonly string Name;
		public readonly ConsolePromptProcDelegate PromptProc;
		public readonly object PromptProcParam;	
		public readonly ConsoleEvalProcDelegate EvalProc;	
		public readonly object EvalProcParam;	
		internal string Tmp = null;			
		public ConsoleParam(string name)
			: this(name, null, null)
		{
		}
		public ConsoleParam(string name,
			ConsolePromptProcDelegate promptProc,
			object promptProcParam)
			: this(name, promptProc, promptProcParam, null, null)
		{
		}
		public ConsoleParam(string name,
			ConsolePromptProcDelegate promptProc,
			object promptProcParam,
			ConsoleEvalProcDelegate evalProc,
			object evalProcParam)
		{
			this.Name = name;
			this.PromptProc = promptProc;
			this.PromptProcParam = promptProcParam;
			this.EvalProc = evalProc;
			this.EvalProcParam = evalProcParam;
		}
	}

	public delegate string ConsolePromptProcDelegate(ConsoleService c, object param);
	public delegate bool ConsoleEvalProcDelegate(ConsoleService c, string str, object param);

	delegate void ConsoleFreeDelegate();
	delegate string ConsoleReadLineDelegate(string prompt, bool nofile);
	delegate string ConsoleReadPasswordDelegate(string prompt);
	delegate bool ConsoleWriteDelegate(string str);
	delegate int ConsoleGetWidthDelegate();

	public class ConsoleParamValueList
	{
		List<ConsoleParamValue> o;

		public ConsoleParamValueList()
		{
			o = new List<ConsoleParamValue>();
		}

		public IEnumerable<ConsoleParamValue> Values
		{
			get
			{
				int i;
				for (i = 0; i < o.Count; i++)
				{
					yield return o[i];
				}
			}
		}

		public void Add(ConsoleParamValue v)
		{
			if (o.Contains(v) == false)
			{
				o.Add(v);
			}
		}

		public ConsoleParamValue this[string name]
		{
			get
			{
				ConsoleParamValue v = new ConsoleParamValue(name, "", 0);

				int i = o.IndexOf(v);
				if (i == -1)
				{
					return new ConsoleParamValue(name, "", 0);
				}

				return o[i];
			}
		}

		public ConsoleParamValue DefaultParam
		{
			get
			{
				foreach (ConsoleParamValue c in o)
				{
					if (c.IsDefaultParam)
					{
						return c;
					}
				}

				return new ConsoleParamValue("", "", 0, true);
			}
		}

		public string GetStr(string name)
		{
			ConsoleParamValue v = this[name];
			if (v == null)
			{
				return null;
			}

			return v.StrValue;
		}

		public int GetInt(string name)
		{
			ConsoleParamValue v = this[name];
			if (v == null)
			{
				return 0;
			}

			return v.IntValue;
		}

		public bool GetYes(string name)
		{
			return Str.StrToBool(name);
		}
	}

	public class ConsoleParamValue : IComparable<ConsoleParamValue>, IEquatable<ConsoleParamValue>
	{
		public readonly string Name;			
		public readonly string StrValue;	
		public readonly int IntValue;			
		public readonly bool BoolValue;			
		public readonly bool IsEmpty;			
		public readonly bool IsDefaultParam;	

		public ConsoleParamValue(string name, string strValue, int intValue)
			: this(name, strValue, intValue, false)
		{
		}
		public ConsoleParamValue(string name, string strValue, int intValue, bool isDefaultParam)
		{
			this.Name = name;
			this.IntValue = intValue;
			this.StrValue = strValue;
			this.BoolValue = Str.StrToBool(strValue);
			this.IsDefaultParam = isDefaultParam;

			this.IsEmpty = Str.IsEmptyStr(strValue);
		}

		public int CompareTo(ConsoleParamValue other)
		{
			return Str.StrCmpiRetInt(this.Name, other.Name);
		}

		public bool Equals(ConsoleParamValue other)
		{
			return Str.StrCmpi(this.Name, other.Name);
		}
	}

	public class ConsoleCommandParam : Attribute
	{
	}

	public class ConsoleCommandMethod : Attribute
	{
		public readonly string Description;
		public readonly string ArgsHelp;
		public readonly string BodyHelp;
		public readonly SortedList<string, string> ParamHelp;

		internal BindingFlags bindingFlag;
		internal MemberInfo memberInfo;
		internal MethodInfo methodInfo;
		internal string name;

		public ConsoleCommandMethod(string description, string argsHelp, string bodyHelp, params string[] paramHelp)
		{
			this.Description = description;
			this.ArgsHelp = argsHelp;
			this.BodyHelp = bodyHelp;
			this.ParamHelp = new SortedList<string, string>(new StrComparer(false));

			foreach (string s in paramHelp)
			{
				int i = s.IndexOf(":");
				if (i == -1)
				{
					throw new ArgumentException(s);
				}

				this.ParamHelp.Add(s.Substring(0, i), s.Substring(i + 1));
			}
		}
	}

	public static class ConsoleErrorCode
	{
		public const int ERR_BAD_COMMAND_OR_PARAM = -100001;
		public const int ERR_INNER_EXCEPTION = -100002;
		public const int ERR_USER_CANCELED = -100003;

		public static string ErrorCodeToString(int code)
		{
			bool b;

			return ErrorCodeToString(code, out b);
		}
		public static string ErrorCodeToString(int code, out bool unknownError)
		{
			unknownError = false;

			switch (code)
			{
				case ERR_BAD_COMMAND_OR_PARAM:
					return "Bad command or parameters.";

				case ERR_USER_CANCELED:
					return "User canceled.";

				case ERR_INNER_EXCEPTION:
				default:
					unknownError = true;
					return string.Format("Unknown Error {0}", code);
			}
		}
	}

	public class ConsoleService
	{
		IO inFile;			
		Buf inBuf;					
		IO outFile;						
		int win32_OldConsoleWidth;		

		public const int MaxPromptStrSize = 65536;
		public const int Win32DefaultConsoleWidth = 100;

		ConsoleType consoleType;
		public ConsoleType ConsoleType
		{
			get { return consoleType; }
		}

		int retCode;
		public int RetCode
		{
			get { return retCode; }
		}

		string retErrorMessage;
		public string RetErrorMessage
		{
			get
			{
				bool b;
				string s = ConsoleErrorCode.ErrorCodeToString(this.RetCode, out b);

				if (b)
				{
					s = this.retErrorMessage;
				}

				Str.NormalizeString(ref s);

				return s;
			}
		}

		ConsoleFreeDelegate free;

		ConsoleReadLineDelegate readLine;

		ConsoleReadPasswordDelegate readPassword;

		ConsoleWriteDelegate write;

		ConsoleGetWidthDelegate getWidth;

		SortedList<string, ConsoleCommandMethod> currentCmdList = null;


		private ConsoleService()
		{
		}

		public static int EntryPoint(string cmdLine, string programName, Type commandClass)
		{
			string s;
			return EntryPoint(cmdLine, programName, commandClass, out s);
		}
		public static int EntryPoint(string cmdLine, string programName, Type commandClass, out string lastErrorMessage)
		{
			int ret = 0;
			string infile, outfile;
			string csvmode;
			ConsoleService c;

			lastErrorMessage = "";

			infile = ParseCommand(cmdLine, "in");
			outfile = ParseCommand(cmdLine, "out");
			if (Str.IsEmptyStr(infile))
			{
				infile = null;
			}
			if (Str.IsEmptyStr(outfile))
			{
				outfile = null;
			}
			c = ConsoleService.NewLocalConsoleService(infile, outfile);

			csvmode = ParseCommand(cmdLine, "csv");
			if (csvmode != null)
			{
				c.consoleType = ConsoleType.Csv;
			}

			if (c.DispatchCommand(cmdLine, ">", commandClass) == false)
			{
				ret = ConsoleErrorCode.ERR_BAD_COMMAND_OR_PARAM;
			}
			else
			{
				ret = c.retCode;
			}

			lastErrorMessage = c.RetErrorMessage;

			return ret;
		}

		public bool WriteLine(object value)
		{
			return WriteLine(value.ToString());
		}
		public bool WriteLine(string str)
		{
			return localWrite(str);
		}
		public bool WriteLine(string format, object arg0)
		{
			return WriteLine(string.Format(format, arg0));
		}
		public bool WriteLine(string format, params object[] arg)
		{
			return WriteLine(string.Format(format, arg));
		}

		public string ReadLine(string prompt)
		{
			return ReadLine(prompt, false);
		}
		public string ReadLine(string prompt, bool noFile)
		{
			return localReadLine(prompt, noFile);
		}

		public string ReadPassword(string prompt)
		{
			return localReadPassword(prompt);
		}

		public static ConsolePromptProcDelegate Prompt
		{
			get { return new ConsolePromptProcDelegate(prompt); }
		}
		static string prompt(ConsoleService c, object param)
		{
			string p = (param == null) ? Resources.CMD_PROMPT : (string)param;

			return c.readLine(p, true);
		}

		public static ConsoleEvalProcDelegate EvalIsFile
		{
			get { return new ConsoleEvalProcDelegate(evalIsFile); }
		}
		static bool evalIsFile(ConsoleService c, string str, object param)
		{
			string tmp;
			if (c == null || str == null)
			{
				return false;
			}

			tmp = str;

			if (Str.IsEmptyStr(tmp))
			{
				c.write(Resources.CMD_FILE_NAME_EMPTY);
				return false;
			}

			if (IO.IsFileExists(tmp) == false)
			{
				c.write(Str.FormatC(Resources.CMD_FILE_NOT_FOUND, tmp));

				return false;
			}

			return true;
		}

		public static ConsoleEvalProcDelegate EvalInt1
		{
			get { return new ConsoleEvalProcDelegate(evalInt1); }
		}
		static bool evalInt1(ConsoleService c, string str, object param)
		{
			string p = (param == null) ? Resources.CMD_EVAL_INT : (string)param;

			if (Str.StrToInt(str) == 0)
			{
				c.write(p);

				return false;
			}

			return true;
		}

		public static ConsoleEvalProcDelegate EvalNotEmpty
		{
			get { return new ConsoleEvalProcDelegate(evalNotEmpty); }
		}
		static bool evalNotEmpty(ConsoleService c, string str, object param)
		{
			string p = (param == null) ? Resources.CMD_EVAL_NOT_EMPTY : (string)param;

			if (Str.IsEmptyStr(str) == false)
			{
				return true;
			}

			c.write(p);

			return false;
		}

		public static ConsoleEvalProcDelegate EvalMinMax
		{
			get { return new ConsoleEvalProcDelegate(evalMinMax); }
		}
		static bool evalMinMax(ConsoleService c, string str, object param)
		{
			string tag;
			int v;
			if (param == null)
			{
				return false;
			}

			ConsoleEvalMinMaxParam e = (ConsoleEvalMinMaxParam)param;

			if (Str.IsEmptyStr(e.ErrorMessageString))
			{
				tag = Resources.CMD_EVAL_MIN_MAX;
			}
			else
			{
				tag = e.ErrorMessageString;
			}

			v = Str.StrToInt(str);

			if (v >= e.MinValue && v <= e.MaxValue)
			{
				return true;
			}
			else
			{
				c.write(Str.FormatC(tag, e.MinValue, e.MaxValue));

				return false;
			}
		}

		public void PrintCmdHelp(string cmdName, List<string> paramList)
		{
			string tmp;
			string buf;
			string description, args, help;
			List<string> t;
			int width;
			int i;
			string space;
			if (cmdName == null || paramList == null)
			{
				return;
			}

			width = GetConsoleWidth() - 2;

			description = this.currentCmdList[cmdName].Description;
			args = this.currentCmdList[cmdName].ArgsHelp;
			help = this.currentCmdList[cmdName].BodyHelp;

			space = Str.MakeCharArray(' ', 2);

			tmp = Str.FormatC(Resources.CMD_HELP_TITLE, cmdName);
			this.write(tmp);
			this.write("");

			this.write(Resources.CMD_HELP_DESCRIPTION);
			t = Str.StrArrayToList(SeparateStringByWidth(description, width - 2));
			for (i = 0; i < t.Count; i++)
			{
				buf = Str.FormatC("%S%s", space, t[i]);
				this.write(buf);
			}
			this.write("");

			this.write(Resources.CMD_HELP_HELP);
			t = Str.StrArrayToList(SeparateStringByWidth(help, width - 2));
			for (i = 0; i < t.Count; i++)
			{
				buf = Str.FormatC("%S%s", space, t[i]);
				this.write(buf);
			}
			this.write("");

			this.write(Resources.CMD_HELP_USAGE);
			t = Str.StrArrayToList(SeparateStringByWidth(args, width - 2));
			for (i = 0; i < t.Count; i++)
			{
				buf = Str.FormatC("%S%s", space, t[i]);
				this.write(buf);
			}

			if (paramList.Count >= 1)
			{
				this.write("");
				this.write(Resources.CMD_HELP_ARGS);
				PrintCandidateHelp(cmdName, paramList.ToArray(), 2, this.currentCmdList);
			}
		}

		public void PrintCandidateHelp(string cmdName, string[] candidateList, int leftSpace, SortedList<string, ConsoleCommandMethod> ccList)
		{
			int console_width;
			int max_keyword_width;
			List<string> o;
			int i;
			string tmpbuf;
			string left_space_array;
			string max_space_array;
			if (candidateList == null)
			{
				return;
			}

			console_width = GetConsoleWidth() - 1;

			left_space_array = Str.MakeCharArray(' ', leftSpace);

			o = new List<string>();

			max_keyword_width = 0;

			for (i = 0; i < candidateList.Length; i++)
			{
				int keyword_width;

				o.Add(candidateList[i]);

				keyword_width = Str.GetStrWidth(candidateList[i]);
				if (cmdName != null)
				{
					if (candidateList[i].StartsWith("[", StringComparison.InvariantCultureIgnoreCase) == false)
					{
						keyword_width += 1;
					}
					else
					{
						keyword_width -= 2;
					}
				}

				max_keyword_width = Math.Max(max_keyword_width, keyword_width);
			}

			max_space_array = Str.MakeCharArray(' ', max_keyword_width);

			for (i = 0; i < o.Count; i++)
			{
				string tmp;
				string name = o[i];
				List<string> t;
				string help;
				int j;
				int keyword_start_width = leftSpace;
				int descript_start_width = leftSpace + max_keyword_width + 1;
				int descript_width;
				string space;

				if (console_width >= (descript_start_width + 5))
				{
					descript_width = console_width - descript_start_width - 3;
				}
				else
				{
					descript_width = 2;
				}

				if (cmdName != null && name.StartsWith("[", StringComparison.InvariantCultureIgnoreCase) == false)
				{
					tmp = Str.FormatC("/%s", name);
				}
				else
				{
					if (cmdName == null)
					{
						tmp = name;
					}
					else
					{
						if (name.Length >= 1)
						{
							tmp = name.Substring(1);
						}
						else
						{
							tmp = "";
						}

						if (tmp.Length >= 1)
						{
							tmp = tmp.Substring(0, tmp.Length - 1);
						}
					}
				}

				if (cmdName == null)
				{
					help = ccList[name].Description;
				}
				else
				{
					if (ccList[cmdName].ParamHelp.ContainsKey(name))
					{
						help = ccList[cmdName].ParamHelp[name];
					}
					else
					{
						help = Resources.CMD_UNKNOWN_PARAM;
					}
				}

				space = Str.MakeCharArray(' ', max_keyword_width - Str.GetStrWidth(name) -
					(cmdName == null ? 0 : (name.StartsWith("[", StringComparison.InvariantCultureIgnoreCase) == false ? 1 : -2)));

				t = Str.StrArrayToList(SeparateStringByWidth(help, descript_width));

				for (j = 0; j < t.Count; j++)
				{
					if (j == 0)
					{
						tmpbuf = Str.FormatC("%S%S%S - %s",
							left_space_array, tmp, space, t[j]);
					}
					else
					{
						tmpbuf = Str.FormatC("%S%S   %s",
							left_space_array, max_space_array, t[j]);
					}

					this.write(tmpbuf);
				}
			}
		}

		public static string[] SeparateStringByWidth(string str, int width)
		{
			if (str == null)
			{
				return new string[0];
			}
			if (width <= 0)
			{
				width = 1;
			}

			StringBuilder tmp = new StringBuilder();
			int len, i;
			List<string> o = new List<string>();

			str += (char)0;
			len = str.Length;

			for (i = 0; i < len; i++)
			{
				char c = str[i];

				switch (c)
				{
					case (char)0:
					case '\r':
					case '\n':
						if (c == '\r')
						{
							if (str[i + 1] == '\n')
							{
								i++;
							}
						}

						o.Add(tmp.ToString());
						tmp = new StringBuilder();
						break;

					default:
						tmp.Append(c);
						if (Str.GetStrWidth(tmp.ToString()) >= width)
						{
							o.Add(tmp.ToString());
							tmp = new StringBuilder();
						}
						break;
				}
			}

			if (o.Count == 0)
			{
				o.Add("");
			}

			return o.ToArray();
		}

		public static bool IsHelpStr(string str)
		{
			if (str == null)
			{
				return false;
			}

			if (Str.IsStrInList(str, true,
				"help", "?", "man", "/man", "-man", "--man",
				"/help", "/?", "-help", "-?",
				"/h", "--help", "--?"))
			{
				return true;
			}

			return false;
		}

		public bool DispatchCommand(string execCommandOrNull, string prompt, Type commandClass)
		{
			return DispatchCommand(execCommandOrNull, prompt, commandClass, null);
		}
		public bool DispatchCommand(string execCommandOrNull, string prompt, Type commandClass, object invokerInstance)
		{
			SortedList<string, ConsoleCommandMethod> cmdList = GetCommandList(commandClass);

			currentCmdList = cmdList;
			try
			{
				string str, tmp, cmd_name;
				bool b_exit = false;
				string cmd_param;
				int ret = 0;
				List<string> t, candidate;
				int i;

				if (Str.IsEmptyStr(execCommandOrNull))
				{
				RETRY:
					tmp = prompt;
					str = this.readLine(tmp, false);

					if (str != null && Str.IsEmptyStr(str))
					{
						goto RETRY;
					}
				}
				else
				{
					if (prompt != null)
					{
						if (this.consoleType != ConsoleType.Csv)
						{
						}
					}
					str = execCommandOrNull;
				}

				if (str == null)
				{
					return false;
				}

				str = Str.TrimCrlf(str).Trim();

				if (Str.IsEmptyStr(str))
				{
					return true;
				}

				if (SeparateCommandAndParam(str, out cmd_name, out cmd_param) == false)
				{
					return true;
				}

				if (cmd_name.Length >= 2 && cmd_name[0] == '?' && cmd_name[1] != '?')
				{
					cmd_name = cmd_name.Substring(1);
					cmd_param = "/?";
				}

				if (cmd_name.Length >= 2 && cmd_name.EndsWith("?") && cmd_name[cmd_name.Length - 2] != '?')
				{
					cmd_name = cmd_name.Substring(0, cmd_name.Length - 1);
					cmd_param = "/?";
				}

				t = new List<string>();
				for (i = 0; i < cmdList.Count; i++)
				{
					t.Add(cmdList.Keys[i]);
				}

				if (IsHelpStr(cmd_name))
				{
					if (Str.IsEmptyStr(cmd_param))
					{
						this.write(Str.FormatC(Resources.CMD_HELP_1, t.Count));

						string[] candidateList = t.ToArray();

						PrintCandidateHelp(null, candidateList, 1, cmdList);

						this.write("");
						this.write(Resources.CMD_HELP_2);
					}
					else
					{
						string tmp2, tmp3;
						if (SeparateCommandAndParam(cmd_param, out tmp2, out tmp3))
						{
							bool b = true;

							if (IsHelpStr(tmp2))
							{
								b = false;
							}

							if (b)
							{
								DispatchCommand(Str.FormatC("%S /help", tmp2), null, commandClass, invokerInstance);
							}
						}
					}
				}
				else if (Str.StrCmpi(cmd_name, "exit") ||
					Str.StrCmpi(cmd_name, "quit"))
				{
					b_exit = true;
				}
				else
				{
					candidate = Str.StrArrayToList(GetRealnameCandidate(cmd_name, t.ToArray()));

					if (candidate == null || candidate.Count == 0)
					{
						this.write(Str.FormatC(Resources.CON_UNKNOWN_CMD, cmd_name));

						this.retCode = ConsoleErrorCode.ERR_BAD_COMMAND_OR_PARAM;
					}
					else if (candidate.Count >= 2)
					{
						this.write(Str.FormatC(Resources.CON_AMBIGIOUS_CMD, cmd_name));
						this.write(Resources.CON_AMBIGIOUS_CMD_1);
						string[] candidateArray = candidate.ToArray();

						PrintCandidateHelp(null, candidateArray, 1, cmdList);
						this.write(Resources.CON_AMBIGIOUS_CMD_2);

						this.retCode = ConsoleErrorCode.ERR_BAD_COMMAND_OR_PARAM;
					}
					else
					{
						string real_cmd_name;
						int j;

						real_cmd_name = candidate[0];

						for (j = 0; j < cmdList.Count; j++)
						{
							if (Str.Equals(cmdList.Values[j].name, real_cmd_name))
							{
								if (this.consoleType != ConsoleType.Csv)
								{
									this.write(Str.FormatC(Resources.CMD_EXEC_MSG_NAME,
										cmdList.Values[j].name,
										cmdList.Values[j].Description));
								}

								object srcObject = null;
								if (cmdList.Values[j].methodInfo.IsStatic == false)
								{
									srcObject = invokerInstance;
								}
								object[] paramList =
								{
									this,
									real_cmd_name,
									cmd_param,
								};

								try
								{
									ret = (int)cmdList.Values[j].methodInfo.Invoke(srcObject, paramList);
								}
								catch (TargetInvocationException ex)
								{
									Exception ex2 = ex.GetBaseException();

									if (ex2 is ConsoleUserCancelException)
									{
										this.write(Resources.CON_USER_CANCELED);
										this.write("");
										this.retCode = ConsoleErrorCode.ERR_USER_CANCELED;
									}
									else
									{
										this.write(ex2.ToString());
										this.write("");

										this.retCode = ConsoleErrorCode.ERR_INNER_EXCEPTION;
										this.retErrorMessage = ex2.Message;
									}

									return true;
								}

								if (ret == -1)
								{
									b_exit = true;
								}
								else
								{
									this.retCode = ret;
								}
							}
						}
					}
				}

				if (b_exit)
				{
					return false;
				}

				return true;
			}
			finally
			{
				currentCmdList = null;
			}
		}

		public static SortedList<string, ConsoleCommandMethod> GetCommandList(Type commandClass)
		{
			SortedList<string, ConsoleCommandMethod> cmdList = new SortedList<string, ConsoleCommandMethod>(new StrComparer(false));

			BindingFlags[] searchFlags =
			{
				BindingFlags.Static | BindingFlags.NonPublic,
				BindingFlags.Static | BindingFlags.Public,
				BindingFlags.Instance | BindingFlags.NonPublic,
				BindingFlags.Instance | BindingFlags.Public,
			};

			foreach (BindingFlags bFlag in searchFlags)
			{
				MemberInfo[] members = commandClass.GetMembers(bFlag);

				foreach (MemberInfo info in members)
				{
					if ((info.MemberType & MemberTypes.Method) != 0)
					{
						MethodInfo mInfo = commandClass.GetMethod(info.Name, bFlag);

						object[] customAtts = mInfo.GetCustomAttributes(true);

						foreach (object att in customAtts)
						{
							if (att is ConsoleCommandMethod)
							{
								ConsoleCommandMethod cc = (ConsoleCommandMethod)att;
								cc.bindingFlag = bFlag;
								cc.memberInfo = info;
								cc.methodInfo = mInfo;
								cc.name = info.Name;

								cmdList.Add(info.Name, cc);

								break;
							}
						}
					}
				}
			}

			return cmdList;
		}

		public int GetConsoleWidth()
		{
			int size = this.getWidth();

			if (size == 0)
			{
				size = 80;
			}

			if (size < 32)
			{
				size = 32;
			}

			if (size > 65535)
			{
				size = 65535;
			}

			return size;
		}

		public static bool SeparateCommandAndParam(string src, out string cmd, out string param)
		{
			int i, len;
			StringBuilder tmp;
			string src_tmp;
			cmd = param = null;
			if (src == null)
			{
				return false;
			}

			src_tmp = Str.TrimCrlf(src).Trim();

			len = src_tmp.Length;
			tmp = new StringBuilder();

			for (i = 0; i < (len + 1); i++)
			{
				char c;

				if (i != len)
				{
					c = src_tmp[i];
				}
				else
				{
					c = (char)0;
				}

				switch (c)
				{
					case (char)0:
					case ' ':
					case '\t':
						if (Str.IsEmptyStr(tmp.ToString()))
						{
							return false;
						}
						cmd = tmp.ToString().Trim();
						goto ESCAPE;

					default:
						tmp.Append(c);
						break;
				}
			}

		ESCAPE:
			param = src_tmp.Substring(tmp.Length).Trim();

			return true;
		}

		public static string[] GetRealnameCandidate(string inputName, string[] realNameList)
		{
			List<string> o = new List<string>();
			if (inputName == null || realNameList == null)
			{
				return new string[0];
			}

			int i;
			bool ok = false;
			for (i = 0; i < realNameList.Length; i++)
			{
				string name = realNameList[i];

				if (Str.StrCmpi(name, inputName))
				{
					o.Add(name);
					ok = true;
					break;
				}
			}

			if (ok == false)
			{
				for (i = 0; i < realNameList.Length; i++)
				{
					string name = realNameList[i];

					if (IsOmissionName(inputName, name) ||
						IsNameInRealName(inputName, name))
					{
						o.Add(name);
						ok = true;
					}
				}
			}

			if (ok)
			{
				return o.ToArray();
			}
			else
			{
				return new string[0];
			}
		}

		public static bool IsOmissionName(string inputName, string realName)
		{
			string oname;
			if (inputName == null || realName == null)
			{
				return false;
			}

			if (Str.IsAllUpperStr(realName))
			{
				return false;
			}

			oname = GetOmissionName(realName);

			if (Str.IsEmptyStr(oname))
			{
				return false;
			}

			if (oname.StartsWith(inputName, StringComparison.InvariantCultureIgnoreCase))
			{
				return true;
			}

			if (inputName.StartsWith(oname, StringComparison.InvariantCultureIgnoreCase))
			{
				if (realName.EndsWith(inputName.Substring(oname.Length), StringComparison.InvariantCultureIgnoreCase))
				{
					return true;
				}
			}

			return false;
		}

		public static string GetOmissionName(string src)
		{
			int i, len;
			if (src == null)
			{
				return null;
			}

			string dst = "";
			len = src.Length;

			for (i = 0; i < len; i++)
			{
				char c = src[i];

				if ((c >= '0' && c <= '9') ||
					(c >= 'A' && c <= 'Z'))
				{
					dst += c;
				}
			}

			return dst;
		}

		public static bool IsNameInRealName(string inputName, string realName)
		{
			if (inputName == null || realName == null)
			{
				return false;
			}

			if (realName.StartsWith(inputName, StringComparison.InvariantCultureIgnoreCase))
			{
				return true;
			}

			return false;
		}

		public ConsoleParamValueList ParseCommandList(string cmdName, string command, ConsoleParam[] param)
		{
			ConsoleParamValueList ret = parseCommandLineMain(cmdName, command, param);

			if (ret == null)
			{
				throw new ConsoleUserCancelException("");
			}

			return ret;
		}
		private ConsoleParamValueList parseCommandLineMain(string cmdName, string command, ConsoleParam[] param)
		{
			int i;
			ConsoleParamValueList o;
			List<string> param_list;
			List<string> real_name_list;
			bool help_mode = false;
			string tmp;
			bool ok = true;
			if (command == null || cmdName == null)
			{
				return null;
			}

			for (i = 0; i < param.Length; i++)
			{
				if (Str.IsEmptyStr(param[i].Name) == false)
				{
					if (param[i].Name.StartsWith("["))
					{
						param[i].Tmp = "";
					}
					else
					{
						param[i].Tmp = null;
					}
				}
				else
				{
					param[i].Tmp = "";
				}
			}

			param_list = Str.StrArrayToList(GetCommandNameList(command));

			real_name_list = new List<string>();

			for (i = 0; i < param.Length; i++)
			{
				real_name_list.Add(param[i].Name);
			}

			for (i = 0; i < param_list.Count; i++)
			{
				string s = param_list[i];

				if (Str.StrCmpi(s, "help") ||
					Str.StrCmpi(s, "?"))
				{
					help_mode = true;
					break;
				}
			}

			tmp = ParseCommand(command, "");
			if (tmp != null)
			{
				if (Str.StrCmpi(tmp, "?"))
				{
					help_mode = true;
				}
			}

			if (help_mode)
			{
				PrintCmdHelp(cmdName, real_name_list);
				return null;
			}

			for (i = 0; i < param_list.Count; i++)
			{
				string[] candidate = GetRealnameCandidate(param_list[i], real_name_list.ToArray());

				if (candidate != null && candidate.Length >= 1)
				{
					if (candidate.Length >= 2)
					{
						this.write(Str.FormatC(Resources.CON_AMBIGIOUS_PARAM,
							param_list[i]));

						this.write(Str.FormatC(Resources.CON_AMBIGIOUS_PARAM_1,
							cmdName));

						PrintCandidateHelp(cmdName, candidate, 1, this.currentCmdList);
						this.write(Resources.CON_AMBIGIOUS_PARAM_2);

						ok = false;
					}
					else
					{
						int j;
						string real_name = candidate[0];

						for (j = 0; j < param.Length; j++)
						{
							if (Str.StrCmpi(param[j].Name, real_name))
							{
								param[j].Tmp = param_list[i];
							}
						}
					}
				}
				else
				{
					this.write(Str.FormatC(Resources.CON_INVALID_PARAM,
						param_list[i],
						cmdName,
						cmdName));

					ok = false;
				}
			}

			if (ok == false)
			{
				return null;
			}

			o = new ConsoleParamValueList();

			for (i = 0; i < param.Length; i++)
			{
				ConsoleParam p = param[i];
				bool is_default_value = false;

				if (p.Tmp == "")
				{
					is_default_value = true;
				}

				if (p.Tmp != null || p.PromptProc != null)
				{
					string name = p.Name;
					string tmp2, str;

					if (p.Tmp != null)
					{
						tmp2 = p.Tmp;
					}
					else
					{
						tmp2 = p.Name;
					}

					str = ParseCommand(command, tmp2);

					if (str != null)
					{
						string unistr;
						bool ret;
					EVAL_VALUE:
						unistr = str;

						if (p.EvalProc != null)
						{
							ret = p.EvalProc(this, unistr, p.EvalProcParam);
						}
						else
						{
							ret = true;
						}

						if (ret == false)
						{
							string tmp3;
							if (p.PromptProc == null)
							{
								ok = false;
								break;
							}
							else
							{
								str = null;
								tmp3 = p.PromptProc(this, p.PromptProcParam);
								if (tmp3 == null)
								{
									ok = false;
									break;
								}
								else
								{
									this.write("");
									str = tmp3;
									goto EVAL_VALUE;
								}
							}
						}
						else
						{
							o.Add(new ConsoleParamValue(p.Name, str, Str.StrToInt(str), is_default_value));
						}
					}
					else
					{
						if (p.PromptProc != null)
						{
							string tmp4;
							tmp4 = p.PromptProc(this, p.PromptProcParam);
							if (tmp4 == null)
							{
								ok = false;
								break;
							}
							else
							{
								this.write("");
								str = tmp4;
								if (true)
								{
									string unistr;
									bool ret;
								EVAL_VALUE:
									unistr = str;

									if (p.EvalProc != null)
									{
										ret = p.EvalProc(this, unistr, p.EvalProcParam);
									}
									else
									{
										ret = true;
									}

									if (ret == false)
									{
										if (p.PromptProc == null)
										{
											ok = false;
											break;
										}
										else
										{
											str = null;
											tmp4 = p.PromptProc(this, p.PromptProcParam);
											if (tmp4 == null)
											{
												ok = false;
												break;
											}
											else
											{
												this.write("");
												str = tmp4;
												goto EVAL_VALUE;
											}
										}
									}
									else
									{
										o.Add(new ConsoleParamValue(p.Name, str, Str.StrToInt(str), is_default_value));
									}
								}
							}
						}
					}
				}
			}

			if (ok)
			{
				return o;
			}
			else
			{
				return null;
			}
		}

		public static string[] GetCommandNameList(string str)
		{
			if (str == null)
			{
				return new string[0];
			}

			string[] pl;
			ParseCommand(str, "dummy_str", out pl);

			return pl;
		}

		public static string ParseCommand(string str, string name)
		{
			string[] pl;
			return ParseCommand(str, name, out pl);
		}
		public static string ParseCommand(string str, string name, out string[] paramList)
		{
			int i;
			string tmp, ret = null;
			SortedList<string, int> o;
			paramList = null;
			if (str == null)
			{
				return null;
			}
			if (Str.IsEmptyStr(name))
			{
				name = null;
			}

			o = new SortedList<string, int>(new StrComparer(false));

			tmp = str.Trim();

			i = Str.SearchStr(tmp, "/CMD", 0, false);

			if (i >= 1 && tmp[i - 1] == '/')
			{
				i = -1;
			}
			if (i == -1)
			{
				i = Str.SearchStr(tmp, "/CMD\t", 0, false);
				if (i >= 1 && tmp[i - 1] == '/')
				{
					i = -1;
				}
			}
			if (i == -1)
			{
				i = Str.SearchStr(tmp, "/CMD:", 0, false);
				if (i >= 1 && tmp[i - 1] == '/')
				{
					i = -1;
				}
			}
			if (i == -1)
			{
				i = Str.SearchStr(tmp, "/CMD=", 0, false);
				if (i >= 1 && tmp[i - 1] == '/')
				{
					i = -1;
				}
			}
			if (i == -1)
			{
				i = Str.SearchStr(tmp, "-CMD ", 0, false);
				if (i >= 1 && tmp[i - 1] == '-')
				{
					i = -1;
				}
			}
			if (i == -1)
			{
				i = Str.SearchStr(tmp, "-CMD\t", 0, false);
				if (i >= 1 && tmp[i - 1] == '-')
				{
					i = -1;
				}
			}
			if (i == -1)
			{
				i = Str.SearchStr(tmp, "-CMD:", 0, false);
				if (i >= 1 && tmp[i - 1] == '-')
				{
					i = -1;
				}
			}
			if (i == -1)
			{
				i = Str.SearchStr(tmp, "-CMD=", 0, false);
				if (i >= 1 && tmp[i - 1] == '-')
				{
					i = -1;
				}
			}

			if (i != -1)
			{
				string s = "CMD";
				if (o != null)
				{
					if (o.ContainsKey(s) == false)
					{
						o.Add(s, 0);
					}
				}
				if (Str.StrCmpi(name, "CMD"))
				{
					ret = str.Substring(i + 5).Trim();
				}
				else
				{
					tmp = tmp.Substring(0, i);
				}
			}

			if (ret == null)
			{
				string[] t = Str.ParseCmdLine(tmp);

				if (t != null)
				{
					for (i = 0; i < t.Length; i++)
					{
						string token = t[i];

						if ((token[0] == '-' && token[1] != '-') ||
							(Str.StrCmpi(token, "--help")) ||
							(token[0] == '/' && token[1] != '/'))
						{
							int j;
							if (Str.StrCmpi(token, "--help"))
							{
								token = token.Substring(1);
							}

							j = Str.SearchStr(token, ":", 0, false);
							if (j == -1)
							{
								j = Str.SearchStr(token, "=", 0, false);
							}
							if (j != -1)
							{
								string tmp2;
								string a;

								tmp2 = token;
								if (tmp2.Length >= j)
								{
									tmp2 = tmp2.Substring(0, j);
								}

								a = tmp2.Substring(1);
								if (o != null)
								{
									if (o.ContainsKey(a) == false)
									{
										o.Add(a, 0);
									}
								}

								if (tmp2.Length >= 1 && Str.StrCmpi(name, tmp2.Substring(1)))
								{
									if (ret == null)
									{
										ret = token.Substring(j + 1);
									}
								}
							}
							else
							{
								string a = token.Substring(1);

								if (o != null)
								{
									if (o.ContainsKey(a) == false)
									{
										o.Add(a, 0);
									}

									if (Str.StrCmpi(name, token.Substring(1)))
									{
										if (ret == null)
										{
											ret = "";
										}
									}
								}
							}
						}
						else
						{
							if (name == null)
							{
								if (ret == null)
								{
									if (token.StartsWith("--"))
									{
										ret = token.Substring(1);
									}
									else if (token.StartsWith("//"))
									{
										ret = token.Substring(1);
									}
									else
									{
										ret = token;
									}
								}
							}
						}
					}
				}
			}

			if (o != null)
			{
				List<string> t = new List<string>();

				int j;
				for (j = 0; j < o.Count; j++)
				{
					t.Add(o.Keys[j]);
				}

				paramList = t.ToArray();
			}

			if (ret != null)
			{
				if (Str.StrCmpi(ret, "none") || Str.StrCmpi(ret, "null"))
				{
					ret = "";
				}
			}

			return ret;
		}

		public static ConsoleService NewLocalConsoleService()
		{
			return NewLocalConsoleService(null, null);
		}
		public static ConsoleService NewLocalConsoleService(string outFileName)
		{
			return NewLocalConsoleService(null, outFileName);
		}
		public static ConsoleService NewLocalConsoleService(string inFileName, string outFileName)
		{
			IO in_io = null, out_io = null;

			ConsoleService c = new ConsoleService();
			int old_size = 0;

			c.consoleType = ConsoleType.Local;
			c.free = new ConsoleFreeDelegate(c.localFree);
			c.readLine = new ConsoleReadLineDelegate(c.localReadLine);
			c.readPassword = new ConsoleReadPasswordDelegate(c.localReadPassword);
			c.write = new ConsoleWriteDelegate(c.localWrite);
			c.getWidth = new ConsoleGetWidthDelegate(c.localGetWidth);

			if (Str.IsEmptyStr(inFileName) == false)
			{
				try
				{
					in_io = IO.FileOpen(inFileName, false);
				}
				catch
				{
					c.write(Str.FormatC(Resources.CON_INFILE_ERROR, inFileName));
					return null;
				}
				c.write(Str.FormatC(Resources.CON_INFILE_START, inFileName));
			}

			if (Str.IsEmptyStr(outFileName) == false)
			{
				try
				{
					out_io = IO.FileCreate(outFileName);
				}
				catch
				{
					c.write(Str.FormatC(Resources.CON_OUTFILE_ERROR, outFileName));
					if (in_io != null)
					{
						in_io.Close();
					}

					return null;
				}
				c.write(Str.FormatC(Resources.CON_OUTFILE_START, outFileName));
			}

			c.inFile = in_io;
			c.outFile = out_io;
			c.win32_OldConsoleWidth = old_size;

			if (in_io != null)
			{
				byte[] data = in_io.ReadAll();

				c.inBuf = new Buf(data);
			}

			Con.SetConsoleService(c);

			return c;
		}

		void localFree()
		{
			if (inFile != null)
			{
				inFile.Close();
				inFile = null;
			}

			if (outFile != null)
			{
				outFile.Close();
				outFile = null;
			}
		}

		int localGetWidth()
		{
			int ret = Console.WindowWidth;

			if (ret <= 0)
			{
				ret = 1;
			}

			return ret;
		}

		string localReadLine(string prompt, bool noFile)
		{
			string ret;
			if (prompt == null)
			{
				prompt = ">";
			}

			writeOutFile(prompt, false);

			if (noFile == false && inBuf != null)
			{
				ret = readNextFromInFile();

				if (ret != null)
				{
					Console.Write(prompt);

					Console.WriteLine(ret);
				}
			}
			else
			{
				Console.Write(prompt);
				ret = Console.ReadLine();

				if (ret != null)
				{
					if (ret.IndexOf((char)0x04) != -1 || ret.IndexOf((char)0x1a) != -1)
					{
						ret = null;
					}
				}
			}

			if (ret != null)
			{
				writeOutFile(ret, true);
			}
			else
			{
				writeOutFile("[EOF]", true);
			}

			return ret;
		}

		string localReadPassword(string prompt)
		{
			if (prompt == null)
			{
				prompt = "Password>";
			}

			Console.Write(prompt);
			writeOutFile(prompt, false);

			string tmp = Str.PasswordPrompt();
			if (tmp != null)
			{
				writeOutFile("********", true);
				return tmp;
			}

			return null;
		}

		bool localWrite(string str)
		{
			Console.Write("{0}{1}",
				str,
				(str.EndsWith("\n") ? "" : "\n"));

			writeOutFile(str, true);

			return true;
		}

		string readNextFromInFile()
		{
			if (inBuf == null)
			{
				return null;
			}

			while (true)
			{
				string str = inBuf.ReadNextLineAsString();
				if (str == null)
				{
					return null;
				}

				str = str.Trim();

				if (Str.IsEmptyStr(str) == false)
				{
					return str;
				}
			}
		}

		void writeOutFile(string str, bool addLastCrlf)
		{
			if (outFile != null)
			{
				string tmp = Str.NormalizeCrlf(str);

				outFile.Write(Str.Utf8Encoding.GetBytes(str));

				if (str.EndsWith("\n") == false && addLastCrlf)
				{
					outFile.Write(Str.Utf8Encoding.GetBytes(Env.NewLine));
				}

				outFile.Flush();
			}
		}
	}
}
