      ///////////////////////////////////////
     // EXTRACTING TEXT FROM A BINARY FILE //
    // Written by Andrew Broad            ///
   // © BROADSOFT 2003                   ////
  //                                    /////
 // BinText.java (2003:11:28 14:12)    //////
////////////////////////////////////////////
// Methods of BinText:                /////
// - text                             ////
// - main                             ///
// - processFlags                     //
///////////////////////////////////////

import java.io.*;

/*****************************************************************************
** BinText is an application-class for extracting ASCII text from a binary file.
*****************************************************************************/

public class BinText {
	static byte[] _file;
	static boolean _ignoreMSB= false;
	static boolean _offset= false;
	static boolean _numeric= false;

	/********************************************************************/
	// static BinText.text : int x int -> void
	/*********************************************************************
	** Prints the text from a binary file.
	** @param start The start-address.
	** @param stop The stop-address.
	*********************************************************************/

	private static void text (int start, int stop, int lineLength) {
		int l= 0;
		for (int a= start; a <= stop; a++) {
			if (_numeric) {
				short c= _file[a];
				if (c < 0) c+= 256;
				System.out.print(c);
				if (a != stop) System.out.print(",");
			} else {
				byte c= _file[a];
				if (_ignoreMSB && c < 0) {
					c+= 128;
				}
				if (c >= 0 && c < 32) {
					if (_offset) c+=32;
					else c= 32;
				}
				System.out.print((char)c);
				l++;
				if (l == lineLength) {
					System.out.println();
					l= 0;
				}
			}
		}
		if (l != 0) {
			System.out.println();
		}
	}

	/********************************************************************/
	// static BinText.main : String[] -> void
	/*********************************************************************
	** Application entry-point for extracting ASCII text from a binary file.
	** @param argv The vector of command-line arguments. There should be
	**             up to four of these: a binary file, a start-offset, a
	**             stop-offset, and the line-length.
	*********************************************************************/

	public static void main (String argv[]) {
		////////////////////////////////////
		// Process command-line arguments //
		////////////////////////////////////
		String[] argv2= new String[argv.length];
		int argv2_length= 0;
		for (int i= 0; i < argv.length; i++) {
			if (argv[i].startsWith("-") && ! isInteger(argv[i])) {
				processFlags(argv[i]);
			} else {
				argv2[argv2_length++]= argv[i];
			}
		}
		if (argv2_length < 1 || argv2_length > 4) {
			System.err.println("Usage: java BinText <file>"
			+ " (<start_offset> (<stop_offset> (<line_length>)))");
			System.err.println("where {0 <= offset <= length_of_file}");
			System.err.println("use stop_offset = -1 for length_of_file");
			System.err.println("    Flags:-");
			System.err.println("-m: Knock out MSBs");
			System.err.println("-o: Offset Codes 0-31 by 32");
			System.err.println("-a: Print ASCII codes rather than characters");
			System.exit(1);
		}
		int startAddress= 0;
		if (argv2_length >= 2) {
			try {
				startAddress= Integer.parseInt(argv2[1]);
			} catch (NumberFormatException nfe) {
				System.err.println(argv2[1] + " doesn't parse"
				+ " as an integer!");
				System.exit(1);
			}
		}
		int stopAddress= -1;
		if (argv2_length >= 3) {
			try {
				stopAddress= Integer.parseInt(argv2[2]);
			} catch (NumberFormatException nfe) {
				System.err.println(argv2[2] + " doesn't parse"
				+ " as an integer!");
				System.exit(1);
			}
		}
		int lineLength= 0;
		if (argv2_length >= 4) {
			try {
				lineLength= Integer.parseInt(argv2[3]);
			} catch (NumberFormatException nfe) {
				System.err.println(argv2[3] + " doesn't parse"
				+ " as an integer!");
				System.exit(1);
			}
		}
		///////////////
		// Read file //
		///////////////
		FileInputStream fis= null;
		try {
			fis= new FileInputStream(argv2[0]);
		} catch (FileNotFoundException fnfe) {
			System.err.println("BinText: cannot open \""
			+ argv2[0] + "\"");
			System.exit(1);
		}
		try {
			_file= new byte[fis.available()];
			if (stopAddress < 0 || stopAddress >= fis.available()) {
				stopAddress= fis.available() - 1;
			}
			int offset= 0;
			while (fis.available() > 0) {
				_file[offset++]= (byte)fis.read();
			}
		}
		catch (IOException ioe) {
			System.err.println("IOException attempting to read  \""
			+ argv2[0] + "\"");
			System.exit(1);
		}
		//////////////////
		// Extract text //
		//////////////////
		text(startAddress, stopAddress, lineLength);
	}

	/********************************************************************/
	// static BinText.isInteger : String -> boolean
	/*********************************************************************
	** Returns true if a given string parses as an integer, otherwise
	** false.
	** @param string The string.
	** @return True if string parses as an integer, otherwise false.
	*********************************************************************/

	public static boolean isInteger (String string) {
		try {
			Integer.parseInt(string);
		} catch (NumberFormatException nfe) {
			return false;
		}
		return true;
	}

	/********************************************************************/
	// static BinText.processFlags : String -> void
	/*********************************************************************
	** A subroutine to process the flags in a command-line argument
	** starting with `-'. This method assumes that it is only called with
	** strings starting with `-', and ignores the first character.
	** @param arg A `flags' command-line argument (i.e. `-' followed by
	**            letters).
	**            -m: Knock out MSBs.
	**            -o: Offset Codes 0-31 by 32.
	*********************************************************************/

	private static void processFlags (String arg) {
		char[] flags= arg.toCharArray();

		for (int i= 1; i < flags.length; i++) {
			switch (flags[i]) {
				case 'm':
					_ignoreMSB= true;
					break;
				case 'o':
					_offset= true;
					break;
				case 'a':
					_numeric= true;
					break;
				default:
					System.err.println("Warning: -"
					+ flags[i] + " not recognised.");
			}
		}
	}
}
