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