
/*
   The progrem "cef" is meant to solve a problem with compiling a program in
   a DOS window under Windows 95 or 98:  If the compilation produces more than
   a few error messages, the first messages will scroll off the screen.  In UNIX
   and in Windows NT, there is a way to send the error messages to a file, which
   you can then view at your leisure.   The cef program does something similar.
   "cef" stands for "Compile and send Errors to a File".  (Of course, this
   program will also work under UNIX or Windows NT, though it isn't necessary
   there.)
   
   You will have to start by compiling cef itself, using the command
   "javac cef.java".  (There will be a warning about using a deprecated method,
   but this is not an error.)  The compiler will produce a class file,
   cef.class.  In order to use cef, you should copy the class file, cef.class,
   into the directory where you want to use it.  That is, copy it into the
   directory that contains the java source code file that you want to compile.
   
   Then, to use cef, run it using the "java" command.  You can give the name of
   the Java source code file, or files, as command line parameters.  For example,
   if you want to compile a file named "MyFile.java", say:
   
                   java cef MyFile.java
                   
   Alternatively, you can just say
   
                   java cef
   
   and you will be prompted to enter the name of the file.
                   
   The file will be compiled as usual, as if you had said "javac MyFile.java".
   However, error messages will be put in a file named "errors.txt".  You can
   view this file, for example, by giving the command:  notepad errors.txt
   If the Java file does not contain any errors, then a message will be output
   to the screen saying "Compilation finished with  no errors."  In this case,
   the file errors.txt will just contain the message "No errors".
   
   Please note that if there is an existing file named errors.txt, it will
   be overwritten.
   
   It is possible to specify a different output file for the error messages.
   To do this, use the option "-f <filename>" on the "java cef" command.
   For example, to send the error messages to a file named "messages.dat":
   
                  java cef -f messages.dat MyFile.java
      or
                  java cef -f messages.dat
                  
   if you want to be prompted for the file name.

   (It's really too bad that such a kludge as this program is necessary.)
   
   
   By the way, you might consider making a DOS .bat script containing the
   following lines:
   
                 java cef
                 notepad errors.txt
                 
   Put this in a file named comp.bat, for example, and put that file the
   same directory with cef.class and the program you want to compile.  If you 
   double-click the comp.bat file, a DOS window will open and will prompt
   you to enter the name of the file to be compiled.  Type the name and
   press return.  The errors from the compilation will appear in a
   separate window. 

*/


import java.io.*;

public class cef {


   static final String defaultFile = "errors.txt"; 
             // This is the name of the file that is used for outputting
             // error messages, unless the user specifies a different name with 
             // the "-f" option.
             
   
   static final String compileCommand = "javac";
             // This is the command used to run the compiler.
             
   
   public static void main(String[] args) {
   
      String errorFile = defaultFile;  // The name of the file to which error 
                                       //              messages will be written.
                                       
      String[] command;  // The words in the command line to be executed,
                         //    consising of the "javac" command and the file name(s) from
                         //    the command line or user input.
      
      Process compiler;  // A process object that will run the javac compiler.
      
      InputStream errorInput;   // A stream for reading error output from 
                                //    the compiler process.
      
      PrintStream errorOutput = null;  // A stream for writing error messages to the
                                       //    file.  Lines from the errorInput stream
                                       //    are copied to this stream.
                                       
      boolean foundErrors = false;   // This will be set to true if any error messages
                                     //    are produced by the compiler.
      
      String errorline = null;  // One line of error output from the compiler process.
                                // (The last line of output is written to the screen
                                // as well as to the file.  It should contain a message
                                // such as "17 errors, 1 warning".)
      
      Runtime runtime = Runtime.getRuntime();  // The Runtime object, which is needed to
                                               //    create a process.

      int paramStart = 0;  // Position of (first) file name in the command-line parameter
                           //   array.  This is set to 2 if the parameters begin with
                           //   a "-f" option.
   
   
      if (args.length > 0 && args[0].equalsIgnoreCase("-f")) {
              // Parameters begin with a "-f" option, specifying an alternative name for
              // file that gets the error messages.  The second parameter must be the
              // name of the output file.  If there is no second parameter, abort.
         if (args.length == 1) {
            System.out.println("The \"-f\" option requires a file name.");
            System.out.println("    For example:   java cef -f badstuff.out MyFile.java");
            return;
         }
         errorFile = args[1];
         paramStart = 2;
      }
      
      try {
            // Create a stream for writing the error messages to a file.
            // If an error occurs, errorOutput is null.
         errorOutput = new PrintStream(new FileOutputStream(errorFile));
      }
      catch (IOException e) {
         errorOutput = null;
      }

      if (args.length > paramStart) {
             // Get the name of the file or files to be compiled from the command line,
             // and use it to build the compilation command.
         command = new String[args.length + 1 - paramStart];
         command[0] = compileCommand;
         for (int i = 0; i < args.length; i++)
            command[i + 1] = args[i + paramStart];
      }
      else {
             // Prompt the user to enter the name of a single file to be compiled,
             // and use it to build the compilation command.
         command = new String[2];
         command[0] = compileCommand;
         String file;
         do {
            System.out.print("Name of file to compile: ");
            file = readLine(System.in);
            if (file == null) {
                System.out.println("*** Error: Can't get file name from Standard Input!");
                if (errorOutput != null)
                    System.out.println("*** Error: Can't get file name from Standard input!");
                return;
            }
         } while (file.trim().length() == 0);
         command[1] = file;
      }

      try {
            // Create the compiler process to run the compiler, and get a stream for
            // reading the error messages from the compiler process.
         compiler = runtime.exec(command);
         errorInput = compiler.getErrorStream();
      }
      catch (Exception e) {
         System.out.println("*** Error while trying to start the compiler:");
         System.out.println(e.toString());
         if (errorOutput != null)
            errorOutput.println("*** Error while trying to start the compiler.");
         return;
      }

      try {         
             // Read error messages, if any, from the compiler process, and
             // write them to the file.
         checkLF = false;
         while (true) {
            String line = readLine(errorInput);
            if (line == null)  // Signal that all data has been read from the stream.
               break;
            foundErrors = true;
            errorline = line;  // For saving the LAST line of the error messages.
            if (errorOutput == null) {
                   // errorOutput is null only if the output stream to the file
                   // couldn't be created, AND this is the first error message.
                   // In this case, write the errors to standard output.
               errorOutput = System.out;
               System.out.println("*** Can't open file \"" 
                                + errorFile + "\" -- sending errors to standard out.\n");
               errorFile = "Standard Output";
            }
            errorOutput.println(line.trim());
         } 
      }
      catch (Exception e) {
         System.out.println("*** Error while trying to get error messages from the compiler:");
         System.out.println(e.toString());
         if (foundErrors && errorOutput != null)
            errorOutput.println("*** Error while trying to get error messages from the compiler.");
         return;
      }
      
      try {
            // Wait for the compiler process to finish.  (Actually, it should
            // already be done when the program gets here...)
         compiler.waitFor();
      }
      catch (Exception e) {
         System.out.println("*** Error while waiting for compiler to finish.");
         System.out.println("*** Output might be incorrect or incomplete.");
         return;
      }
      
      if (foundErrors == false) {
         System.out.println("Compilation finished with no errors.");
         errorOutput.println("No errors");
      }
      else {
         System.out.println("Compiliation finished with errors.  Error messages sent to " + errorFile + ".");
         System.out.println(errorline);
      }
      
   } // end main()
   
   
   static boolean checkLF;  // A kludge to take care of the fact that text files
                            // on different files can have different formats.  Lines can end
                            // with either a carriage return, or a line feed, or a carriage
                            // return followed by a line feed.  This variable is used by the
                            // following subroutine so that it can remember to throw away
                            // a line feed that follows a carriage return, rather than
                            // treat it as an empty line.
   
   static String readLine(InputStream in) {
         // This subroutine reads one line from the input stream, in.
         // If the end-of-stream has been reached, null is returned.
         // (Null is also returned if an input error occurs.)
      try {
         int ch = in.read();
         if (checkLF && ch == '\n')
            ch = in.read();
         if (ch == -1)
            return null;
         StringBuffer b = new StringBuffer();
         while (ch != -1 && ch != '\r' && ch != '\n') {
            b.append( (char)ch );
            ch = in.read();
         }
         return b.toString();
      }
      catch (IOException e) {
         return null;
      }
   }
      

}  // end class cef
