1304732Scy/*
2304732Scy** 2016 February 26
3304732Scy**
4304732Scy** The author disclaims copyright to this source code.  In place of
5304732Scy** a legal notice, here is a blessing:
6304732Scy**
7304732Scy**    May you do good and not evil.
8304732Scy**    May you find forgiveness for yourself and forgive others.
9304732Scy**    May you share freely, never taking more than you give.
10304732Scy**
11304732Scy*************************************************************************
12304732Scy** This file contains C# code to perform regular expression replacements
13304732Scy** using the standard input and output channels.
14304732Scy*/
15304732Scy
16304732Scyusing System;
17304732Scyusing System.Diagnostics;
18304732Scyusing System.IO;
19304732Scyusing System.Reflection;
20304732Scyusing System.Runtime.InteropServices;
21304732Scyusing System.Text.RegularExpressions;
22304732Scy
23304732Scy///////////////////////////////////////////////////////////////////////////////
24304732Scy
25304732Scy#region Assembly Metadata
26304732Scy[assembly: AssemblyTitle("Replace Tool")]
27304732Scy[assembly: AssemblyDescription("Replace text using standard input/output.")]
28304732Scy[assembly: AssemblyCompany("SQLite Development Team")]
29304732Scy[assembly: AssemblyProduct("SQLite")]
30304732Scy[assembly: AssemblyCopyright("Public Domain")]
31304732Scy[assembly: ComVisible(false)]
32304732Scy[assembly: Guid("95a0513f-8863-48cd-a76f-cb80868cb578")]
33304732Scy[assembly: AssemblyVersion("1.0.*")]
34304732Scy
35304732Scy#if DEBUG
36304732Scy[assembly: AssemblyConfiguration("Debug")]
37304732Scy#else
38304732Scy[assembly: AssemblyConfiguration("Release")]
39304732Scy#endif
40304732Scy#endregion
41304732Scy
42304732Scy///////////////////////////////////////////////////////////////////////////////
43304732Scy
44304732Scynamespace Replace
45304732Scy{
46304732Scy    /// <summary>
47304732Scy    /// This enumeration is used to represent all the possible exit codes from
48304732Scy    /// this tool.
49304732Scy    /// </summary>
50304732Scy    internal enum ExitCode
51304732Scy    {
52304732Scy        /// <summary>
53304732Scy        /// The file download was a success.
54304732Scy        /// </summary>
55304732Scy        Success = 0,
56304732Scy
57304732Scy        /// <summary>
58304732Scy        /// The command line arguments are missing (i.e. null).  Generally,
59304732Scy        /// this should not happen.
60304732Scy        /// </summary>
61304732Scy        MissingArgs = 1,
62304732Scy
63304732Scy        /// <summary>
64304732Scy        /// The wrong number of command line arguments was supplied.
65304732Scy        /// </summary>
66304732Scy        WrongNumArgs = 2,
67304732Scy
68304732Scy        /// <summary>
69304732Scy        /// The "matchingOnly" flag could not be converted to a value of the
70304732Scy        /// <see cref="Boolean"/> type.
71304732Scy        /// </summary>
72304732Scy        BadMatchingOnlyFlag = 3,
73304732Scy
74304732Scy        /// <summary>
75304732Scy        /// An exception was caught in <see cref="Main" />.  Generally, this
76304732Scy        /// should not happen.
77304732Scy        /// </summary>
78304732Scy        Exception = 4
79304732Scy    }
80304732Scy
81304732Scy    ///////////////////////////////////////////////////////////////////////////
82304732Scy
83304732Scy    internal static class Replace
84304732Scy    {
85304732Scy        #region Private Support Methods
86304732Scy        /// <summary>
87304732Scy        /// This method displays an error message to the console and/or
88304732Scy        /// displays the command line usage information for this tool.
89304732Scy        /// </summary>
90304732Scy        /// <param name="message">
91304732Scy        /// The error message to display, if any.
92304732Scy        /// </param>
93304732Scy        /// <param name="usage">
94304732Scy        /// Non-zero to display the command line usage information.
95304732Scy        /// </param>
96304732Scy        private static void Error(
97304732Scy            string message,
98304732Scy            bool usage
99304732Scy            )
100304732Scy        {
101304732Scy            if (message != null)
102304732Scy                Console.WriteLine(message);
103304732Scy
104304732Scy            string fileName = Path.GetFileName(
105304732Scy                Process.GetCurrentProcess().MainModule.FileName);
106304732Scy
107304732Scy            Console.WriteLine(String.Format(
108304732Scy                "usage: {0} <regExPattern> <regExSubSpec> <matchingOnly>",
109304732Scy                fileName));
110304732Scy        }
111304732Scy        #endregion
112304732Scy
113304732Scy        ///////////////////////////////////////////////////////////////////////
114304732Scy
115304732Scy        #region Program Entry Point
116304732Scy        /// <summary>
117304732Scy        /// This is the entry-point for this tool.  It handles processing the
118304732Scy        /// command line arguments, reading from the standard input channel,
119304732Scy        /// replacing any matching lines of text, and writing to the standard
120304732Scy        /// output channel.
121304732Scy        /// </summary>
122304732Scy        /// <param name="args">
123304732Scy        /// The command line arguments.
124304732Scy        /// </param>
125304732Scy        /// <returns>
126304732Scy        /// Zero upon success; non-zero on failure.  This will be one of the
127304732Scy        /// values from the <see cref="ExitCode" /> enumeration.
128304732Scy        /// </returns>
129304732Scy        private static int Main(
130304732Scy            string[] args
131304732Scy            )
132304732Scy        {
133304732Scy            //
134304732Scy            // NOTE: Sanity check the command line arguments.
135304732Scy            //
136304732Scy            if (args == null)
137304732Scy            {
138304732Scy                Error(null, true);
139304732Scy                return (int)ExitCode.MissingArgs;
140304732Scy            }
141304732Scy
142304732Scy            if (args.Length != 3)
143304732Scy            {
144304732Scy                Error(null, true);
145304732Scy                return (int)ExitCode.WrongNumArgs;
146304732Scy            }
147304732Scy
148304732Scy            try
149304732Scy            {
150304732Scy                //
151304732Scy                // NOTE: Create a regular expression from the first command
152304732Scy                //       line argument.  Then, grab the replacement string,
153304732Scy                //       which is the second argument.
154304732Scy                //
155304732Scy                Regex regEx = new Regex(args[0]);
156304732Scy                string replacement = args[1];
157304732Scy
158304732Scy                //
159304732Scy                // NOTE: Attempt to convert the third argument to a boolean.
160304732Scy                //
161304732Scy                bool matchingOnly;
162304732Scy
163304732Scy                if (!bool.TryParse(args[2], out matchingOnly))
164304732Scy                {
165304732Scy                    Error(null, true);
166304732Scy                    return (int)ExitCode.BadMatchingOnlyFlag;
167304732Scy                }
168304732Scy
169304732Scy                //
170304732Scy                // NOTE: Grab the standard input and output channels from the
171304732Scy                //       console.
172304732Scy                //
173304732Scy                TextReader inputTextReader = Console.In;
174304732Scy                TextWriter outputTextWriter = Console.Out;
175304732Scy
176304732Scy                //
177304732Scy                // NOTE: Loop until end-of-file is hit on the standard input
178304732Scy                //       stream.
179304732Scy                //
180304732Scy                while (true)
181304732Scy                {
182304732Scy                    //
183304732Scy                    // NOTE: Read a line from the standard input channel.  If
184304732Scy                    //       null is returned here, there is no more input and
185304732Scy                    //       we are done.
186304732Scy                    //
187304732Scy                    string inputLine = inputTextReader.ReadLine();
188304732Scy
189304732Scy                    if (inputLine == null)
190304732Scy                        break;
191304732Scy
192304732Scy                    //
193304732Scy                    // NOTE: Perform regular expression replacements on this
194304732Scy                    //       line, if any.  Then, write the modified line to
195304732Scy                    //       the standard output channel.
196304732Scy                    //
197304732Scy                    string outputLine = regEx.Replace(inputLine, replacement);
198304732Scy
199304732Scy                    if (!matchingOnly || !String.Equals(
200304732Scy                            inputLine, outputLine, StringComparison.Ordinal))
201304732Scy                    {
202304732Scy                        outputTextWriter.WriteLine(outputLine);
203304732Scy                    }
204304732Scy                }
205304732Scy
206304732Scy                //
207304732Scy                // NOTE: At this point, everything has succeeded.
208304732Scy                //
209304732Scy                return (int)ExitCode.Success;
210304732Scy            }
211304732Scy            catch (Exception e)
212304732Scy            {
213304732Scy                //
214304732Scy                // NOTE: An exception was caught.  Report it via the console
215304732Scy                //       and return failure.
216304732Scy                //
217304732Scy                Error(e.ToString(), false);
218304732Scy                return (int)ExitCode.Exception;
219304732Scy            }
220304732Scy        }
221304732Scy        #endregion
222304732Scy    }
223304732Scy}
224