// ------------------------------- //
// -------- Start of File -------- //
// ------------------------------- //
// ----------------------------------------------------------- // 
// C++ Source Code File Name: txfilter.cpp
// Compiler Used: MSVC40, DJGPP 2.7.2.1, GCC 2.7.2.1, HP CPP 10.24
// Produced By: Doug Gaer 
// File Creation Date: 09/17/1997
// Date Last Modified: 03/31/1999
// ----------------------------------------------------------- // 
// ------------- Program Description and Details ------------- // 
// ----------------------------------------------------------- // 
/*
THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
THE ENTIRE RISK OF THE QUALITY AND PERFORMANCE OF THIS SOFTWARE
IS WITH YOU. SHOULD ANY ELEMENT OF THIS SOFTWARE PROVE DEFECTIVE,
YOU WILL ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR
CORRECTION.

Text file filter program used to filter out unwanted characters
from ASCII text files. This program can also be used to convert
DOS text files to a UNIX format of UNIX text files to a DOS
format.
*/
// ----------------------------------------------------------- // 
#include <iostream.h>
#include <fstream.h>
#include <string.h>
#include <stdlib.h> 
#include <stdio.h>
#include <ctype.h>

// Define this macro for DOS file opens (the default is UNIX)
// #ifndef __MSDOS__
// #define __MSDOS__
// #endif

// Txfilter version number and program name
const double TXFilterVersionNumber = 1031.101;
const char *ProgramName = "txfilter";

// Program functions
void ProcessArgs(int argc, char *argv[]);
void HelpMessage(const char *program_name, const double version_number);
int ConvertTextFile(ifstream &infile, ostream &stream);

// Program globals
const int MAX_LINE = 1024; // Maximum characters per line
int VerboseMode;           // Echo results
char in_file[MAX_LINE];    // Input file to clean
char out_file[MAX_LINE];   // Optional output file name
int UNIX_TEXT_FORMAT = 1;  // Output text file in UNIX format
int DOS_TEXT_FORMAT = 0;   // Output text file in DOS CR/LF format
int filter_text = 1;       // Filter the text
int printable_chars = 1;   // Output all printable characters only
int alpha_chars = 0;       // Output alphabetic characters only
int alpha_num_chars = 0;   // Output alphanumeric characters only
int numeric_chars = 0;     // Output numeric characters only
const int txLF = 0x0A;     // Line feed
const int txCR = 0x0D;     // Carriage return

void HelpMessage(const char *program_name, const double version_number)
{
  char vbuffer[255];
  sprintf(vbuffer, "%.3f", version_number);
  cout << endl;
  cout << "ASCII Text file filter program version "
       << vbuffer  << endl;
  cout << "Usage: " << program_name << " [switches] infile.txt "
       << "outfile.txt (optional)" << endl;
  cout << "Switches:  -?      = Display this help message." << endl;
  cout << "           -d      = Output text file in DOS CR/LF format."
       << endl;
  cout << "           -D      = Do not insert line feeds." << endl;
  cout << "           -n      = No text filtering (defaults to filtered)."
       << endl;
  cout << "           -u      = Output text file in UNIX format (default)."
       << endl;
  cout << "           -P      = Output printable characters only (default)."
       << endl;
  cout << "           -A      = Output alphabetic characters only."
       << endl;
  cout << "           -M      = Output alphanumeric characters only."
       << endl;
  cout << "           -N      = Output numeric characters only."
       << endl;
  cout << endl;
  exit(0);
}

void ProcessArgs(int argc, char *argv[])
// Process the program's argument list
{
  int i;
  for(i = 1; i < argc; i++ ) {
    if(*argv[i] == '-') {
      char sw = *(argv[i] +1);
      switch(sw) {
	case '?' :
	  HelpMessage(ProgramName, TXFilterVersionNumber);
	  break;

	case 'd' :
	  DOS_TEXT_FORMAT = 1;   
	  UNIX_TEXT_FORMAT = 0;  
	  break;

	case 'D':
	  DOS_TEXT_FORMAT = 0;   
	  UNIX_TEXT_FORMAT = 0;  
	  break;
	  
	case 'n' :
	  filter_text = 0;
	  break;

	case 'u' :
	  DOS_TEXT_FORMAT = 0;   
	  UNIX_TEXT_FORMAT = 1;  
	  break;

	case 'v' :
	  VerboseMode = 1;
	  break;

	case 'P' :
	  printable_chars = 1;   // Output all printable characters only
	  alpha_chars = 0;       // Output alphabetic characters only
	  alpha_num_chars = 0;   // Output alphanumeric characters only
	  numeric_chars = 0;     // Output numeric characters only
	  break;

	case 'A' :
	  printable_chars = 0;   // Output all printable characters only
	  alpha_chars = 1;       // Output alphabetic characters only
	  alpha_num_chars = 0;   // Output alphanumeric characters only
	  numeric_chars = 0;     // Output numeric characters only
	  break;

	case 'M' :
	  printable_chars = 0;   // Output all printable characters only
	  alpha_chars = 0;       // Output alphabetic characters only
	  alpha_num_chars = 1;   // Output alphanumeric characters only
	  numeric_chars = 0;     // Output numeric characters only
	  break;

	case 'N' :
	  printable_chars = 0;   // Output all printable characters only
	  alpha_chars = 0;       // Output alphabetic characters only
	  alpha_num_chars = 0;   // Output alphanumeric characters only
	  numeric_chars = 1;     // Output numeric characters only
	  break;

	default:
	  cerr << endl;
	  cerr << "Unknown switch " << argv[i] << endl;
	  cerr << "Exiting..." << endl;
	  cerr << endl;
	  exit(0);
	  break;
      }
    }
    else { 
      strcpy(in_file, argv[i]);      // Input file name
      if(argv[i+1]) {
	strcpy(out_file, argv[i+1]); // Output file name
	break;
      }
      else
	break;
    }
  }
}

int ConvertTextFile(ifstream &infile, ostream &stream)
// Filter all unwanted control characters from the text file
{
  char LineBuffer[MAX_LINE];
  char OutputBuffer[MAX_LINE];
  int i, j;

  // Clear the buffers
  for(i = 0; i < MAX_LINE; i++) LineBuffer[i] = '\0';
  for(i = 0; i < MAX_LINE; i++) OutputBuffer[i] = '\0';
  
  while(!infile.eof()) {
    // Read in the file line by line
    for(i = 0, j = 0; i < MAX_LINE && !infile.eof(); i++) {
      infile.get(LineBuffer[i]);
      if(!filter_text) stream << LineBuffer[i];
      
      // Look for the end of line sequence CR/LF
      if((LineBuffer[i] == '\r') || (LineBuffer[i] == '\n')) {
	// Account for extra '\n' in DOS files
	if(LineBuffer[i] == '\r') continue; 
	break;
      }
      
      if(filter_text) { // Output specified character sets only
	if(printable_chars)
	  if(isgraph(LineBuffer[i]) || LineBuffer[i] == ' ')
	    OutputBuffer[j++] = LineBuffer[i];
	
	if(alpha_chars)
	  if(isalpha(LineBuffer[i]) || LineBuffer[i] == ' ')
	    OutputBuffer[j++] = LineBuffer[i];
	
	if(alpha_num_chars)
	  if(isalnum(LineBuffer[i]) || LineBuffer[i] == ' ')
	    OutputBuffer[j++] = LineBuffer[i];
	
	if(numeric_chars)
	  if(isdigit(LineBuffer[i]) || LineBuffer[i] == ' ')
	    OutputBuffer[j++] = LineBuffer[i];
      }
    }

    if(filter_text == 1) {
      if(OutputBuffer[0] != 0)
	stream.write(OutputBuffer, strlen(OutputBuffer));
      if(!infile.eof()) { // Get rid of EOF marker
	char line_feed = (char)txLF;
	char carriage_return = (char)txCR;
	if(DOS_TEXT_FORMAT) {
	  stream.write(&carriage_return, 1);
	  stream.write(&line_feed, 1);
	}
	if(UNIX_TEXT_FORMAT)
	  stream.write(&line_feed, 1);
      }
    }

    // Clear the buffer
    for(i = 0; i < MAX_LINE; i++) LineBuffer[i] = '\0';
    for(i = 0; i < MAX_LINE; i++) OutputBuffer[i] = '\0';
  }
  return 1;
}

int main(int argc, char *argv[])
// Program's main thread of execution
{
  // If no argument is given print usage message to the screen 1
  if(argc < 2) {
    HelpMessage(ProgramName, TXFilterVersionNumber);
    return(0);
  }

  // Process the programs command line arguments
  ProcessArgs(argc, argv);
  
  if(in_file[0] == 0 ) {
    cout << endl;
    cout << "You must specify a valid input file name." << endl;
    cout << endl;
    exit(0);
  }
    
#if defined (__DOS__)
  // In UNIX there is only one file type
  ifstream infile(in_file, ios::in | ios::binary | ios::nocreate);
#elif defined (__UNIX__) 
  // In MS-DOS/Windows there are two file types, text and binary
  ifstream infile(in_file, ios::in | ios::nocreate);
#else
  #error "You must define a file type: __DOS__ or __UNIX__"
#endif
  
  if(!infile) {
      cerr << endl;
      cerr << "Cannot open file: " << in_file << endl;
      cerr << "Exiting..." << endl;
      cerr << endl;
    return 1;
  }

  if(out_file[0] == 0 ) {
    ConvertTextFile(infile, cout);
  }
  else { // Open specified file and truncate it

#if defined (__DOS__)
    // In MS-DOS/Windows there are two file types, text and binary
    fstream stream(out_file, ios::out|ios::trunc|ios::binary);
#elif defined (__UNIX__) 
    // In UNIX there is only one file type
    fstream stream(out_file, ios::out|ios::trunc);
#else
  #error "You must define a file type: __DOS__ or __UNIX__"
#endif

    if(!stream) {
      cerr << endl;
      cerr << "Cannot open " << out_file << endl;
      cerr << endl;
      return 1;
    }
    ConvertTextFile(infile, stream);
    stream.close();
  }

  infile.close();
  return 0;
}
// ----------------------------------------------------------- //
// ------------------------------- //
// --------- End of File --------- //
// ------------------------------- //
