Replace.cs revision 305003
1/*
2** 2016 February 26
3**
4** The author disclaims copyright to this source code.  In place of
5** a legal notice, here is a blessing:
6**
7**    May you do good and not evil.
8**    May you find forgiveness for yourself and forgive others.
9**    May you share freely, never taking more than you give.
10**
11*************************************************************************
12** This file contains C# code to perform regular expression replacements
13** using the standard input and output channels.
14*/
15
16using System;
17using System.Diagnostics;
18using System.IO;
19using System.Reflection;
20using System.Runtime.InteropServices;
21using System.Text.RegularExpressions;
22
23///////////////////////////////////////////////////////////////////////////////
24
25#region Assembly Metadata
26[assembly: AssemblyTitle("Replace Tool")]
27[assembly: AssemblyDescription("Replace text using standard input/output.")]
28[assembly: AssemblyCompany("SQLite Development Team")]
29[assembly: AssemblyProduct("SQLite")]
30[assembly: AssemblyCopyright("Public Domain")]
31[assembly: ComVisible(false)]
32[assembly: Guid("95a0513f-8863-48cd-a76f-cb80868cb578")]
33[assembly: AssemblyVersion("1.0.*")]
34
35#if DEBUG
36[assembly: AssemblyConfiguration("Debug")]
37#else
38[assembly: AssemblyConfiguration("Release")]
39#endif
40#endregion
41
42///////////////////////////////////////////////////////////////////////////////
43
44namespace Replace
45{
46    /// <summary>
47    /// This enumeration is used to represent all the possible exit codes from
48    /// this tool.
49    /// </summary>
50    internal enum ExitCode
51    {
52        /// <summary>
53        /// The file download was a success.
54        /// </summary>
55        Success = 0,
56
57        /// <summary>
58        /// The command line arguments are missing (i.e. null).  Generally,
59        /// this should not happen.
60        /// </summary>
61        MissingArgs = 1,
62
63        /// <summary>
64        /// The wrong number of command line arguments was supplied.
65        /// </summary>
66        WrongNumArgs = 2,
67
68        /// <summary>
69        /// The "matchingOnly" flag could not be converted to a value of the
70        /// <see cref="Boolean"/> type.
71        /// </summary>
72        BadMatchingOnlyFlag = 3,
73
74        /// <summary>
75        /// An exception was caught in <see cref="Main" />.  Generally, this
76        /// should not happen.
77        /// </summary>
78        Exception = 4
79    }
80
81    ///////////////////////////////////////////////////////////////////////////
82
83    internal static class Replace
84    {
85        #region Private Support Methods
86        /// <summary>
87        /// This method displays an error message to the console and/or
88        /// displays the command line usage information for this tool.
89        /// </summary>
90        /// <param name="message">
91        /// The error message to display, if any.
92        /// </param>
93        /// <param name="usage">
94        /// Non-zero to display the command line usage information.
95        /// </param>
96        private static void Error(
97            string message,
98            bool usage
99            )
100        {
101            if (message != null)
102                Console.WriteLine(message);
103
104            string fileName = Path.GetFileName(
105                Process.GetCurrentProcess().MainModule.FileName);
106
107            Console.WriteLine(String.Format(
108                "usage: {0} <regExPattern> <regExSubSpec> <matchingOnly>",
109                fileName));
110        }
111        #endregion
112
113        ///////////////////////////////////////////////////////////////////////
114
115        #region Program Entry Point
116        /// <summary>
117        /// This is the entry-point for this tool.  It handles processing the
118        /// command line arguments, reading from the standard input channel,
119        /// replacing any matching lines of text, and writing to the standard
120        /// output channel.
121        /// </summary>
122        /// <param name="args">
123        /// The command line arguments.
124        /// </param>
125        /// <returns>
126        /// Zero upon success; non-zero on failure.  This will be one of the
127        /// values from the <see cref="ExitCode" /> enumeration.
128        /// </returns>
129        private static int Main(
130            string[] args
131            )
132        {
133            //
134            // NOTE: Sanity check the command line arguments.
135            //
136            if (args == null)
137            {
138                Error(null, true);
139                return (int)ExitCode.MissingArgs;
140            }
141
142            if (args.Length != 3)
143            {
144                Error(null, true);
145                return (int)ExitCode.WrongNumArgs;
146            }
147
148            try
149            {
150                //
151                // NOTE: Create a regular expression from the first command
152                //       line argument.  Then, grab the replacement string,
153                //       which is the second argument.
154                //
155                Regex regEx = new Regex(args[0]);
156                string replacement = args[1];
157
158                //
159                // NOTE: Attempt to convert the third argument to a boolean.
160                //
161                bool matchingOnly;
162
163                if (!bool.TryParse(args[2], out matchingOnly))
164                {
165                    Error(null, true);
166                    return (int)ExitCode.BadMatchingOnlyFlag;
167                }
168
169                //
170                // NOTE: Grab the standard input and output channels from the
171                //       console.
172                //
173                TextReader inputTextReader = Console.In;
174                TextWriter outputTextWriter = Console.Out;
175
176                //
177                // NOTE: Loop until end-of-file is hit on the standard input
178                //       stream.
179                //
180                while (true)
181                {
182                    //
183                    // NOTE: Read a line from the standard input channel.  If
184                    //       null is returned here, there is no more input and
185                    //       we are done.
186                    //
187                    string inputLine = inputTextReader.ReadLine();
188
189                    if (inputLine == null)
190                        break;
191
192                    //
193                    // NOTE: Perform regular expression replacements on this
194                    //       line, if any.  Then, write the modified line to
195                    //       the standard output channel.
196                    //
197                    string outputLine = regEx.Replace(inputLine, replacement);
198
199                    if (!matchingOnly || !String.Equals(
200                            inputLine, outputLine, StringComparison.Ordinal))
201                    {
202                        outputTextWriter.WriteLine(outputLine);
203                    }
204                }
205
206                //
207                // NOTE: At this point, everything has succeeded.
208                //
209                return (int)ExitCode.Success;
210            }
211            catch (Exception e)
212            {
213                //
214                // NOTE: An exception was caught.  Report it via the console
215                //       and return failure.
216                //
217                Error(e.ToString(), false);
218                return (int)ExitCode.Exception;
219            }
220        }
221        #endregion
222    }
223}
224