xref: /freebsd/contrib/sqlite3/Replace.cs (revision 8d13bc63c0e1d50bc9e47ac1f26329c999bfecf0)
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 
16 using System;
17 using System.Diagnostics;
18 using System.IO;
19 using System.Reflection;
20 using System.Runtime.InteropServices;
21 using 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 
44 namespace 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