/*
  Module name: pcp.c
  Created by: Ljubomir Buturovic
  Created: 10/27/2002
  Purpose: main source file for PCP (Pattern Classification Program).
*/

/*
  Copyright 2004 Ljubomir J. Buturovic

  Permission is hereby granted, free of charge, to any person
  obtaining a copy of this software and associated documentation files
  (the "Software"), to deal in the Software without restriction,
  including without limitation the rights to use, copy, modify, merge,
  publish, distribute, sublicense, and/or sell copies of the Software,
  and to permit persons to whom the Software is furnished to do so,
  subject to the following conditions:

  The above copyright notice and this permission notice shall be
  included in all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
*/

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#include "pau.h"
#include "pcp.h"
#include "cda.h"
#include "ddset.h"
#include "bagging.h"
#include "hash.h"
#include "hash_util.h"

#define PCP_VERSION          "2.2"

int   debug = 0;

/*
  Training and test data sets.
*/
struct dataset *tds  = (struct dataset *) 0;
struct dataset *teds = (struct dataset *) 0;

/*
  SIGINT and SIGQUIT signal handler. SIGINT is generated by Ctrl-C,
  SIGQUIT by Ctrl-\. The handler restores the cursor.
*/
static void int_handler(int signo)
{
  cursor_on();
  reset_video();
  _exit(0);
}

/*
  SIGSEGV signal handler.
*/
static void segv_handler(int signo)
{
  cursor_on();
  reset_video();
  remove(PCP_DAT);
  fprintf(stderr, "Segmentation fault\n");
  _exit(0);
}

static void sig(void)
{
  struct sigaction act;

/*
  Catch SIGINT and SIGQUIT (the signals generated by Ctrl-C and
  Ctrl-\). Use sigaction(), for portability and reliability.
*/
  act.sa_handler = int_handler;
  sigemptyset(&act.sa_mask);
  act.sa_flags = 0;
#ifdef SA_RESTART
  act.sa_flags |= SA_RESTART;
#endif
  sigaction(SIGINT, &act, (struct sigaction *) 0);
  sigaction(SIGQUIT, &act, (struct sigaction *) 0);
  act.sa_handler = segv_handler;
  sigaction(SIGSEGV, &act, (struct sigaction *) 0);
}

/*
  Remove PCP_DAT if it is empty. Return -1 in case of error and set
  errno.
*/
static int cleanup(void)
{
  int    status;
  FILE   *fptr;
  struct stat buf;

  cursor_on();
  reset_video();
  fptr = fopen(PCP_DAT, "r");
  if (fptr != (FILE *) 0)
    {
      status = fflush(fptr);
      if (status != 0)
	status = -1;
      else
	{
	  status = stat(PCP_DAT, &buf);
	  if ((status == 0) && (buf.st_size == 0))
	    status = unlink(PCP_DAT);
	}
    }
  else
    status = -1;
  return status;
}

static void pcp_init_menu(int *dm, int *errc, int *key, char *xname)
{
  if (errc)
    {
      errmsg(2, *errc, xname);
      *errc = 0;
    }
  *dm = 1;
  read_keyboard(key);
}

/*
  PCP menus.
*/

static void p_linear_menu(int *errc)
{
  int  size;
  int  key;
  int  dm;
  int  rtn;
  char *xname;

  size = 3;
  dm = 1;
  rtn = 0;
  *errc = 0;
  xname = (char *) 0;
  while (!rtn)
    {
      if (dm)
	{
	  clear_screen();
	  p_disp(PCP_MENU_LPAR, size);
	  cursor_off();
	}
      pcp_init_menu(&dm, errc, &key, xname);
      if (key == 1)
	*errc = p_lin_learn(&xname);
      else if (key == 2)
	pcp_ensemble(PALG_BAG_PLC, errc, &xname, debug);
      else if (key == 3)
	{
	  if (teds)
	    *errc = p_lin_predict(PALG_PLC, &xname);
	  else
	    *errc = PERR_UNDEFINED_TEDS;
	}
      else if (key == 4)
	p_xlearn(PALG_PLC, errc, &xname, debug);
      else if (key == 5)
	p_xlearn(PALG_BAG_PLC, errc, &xname, debug);
      else if (key == 6) 
	xlearn_disp(errc, &xname, PCP_XPL);
      else if (key >= 7 && key <= 11)
	{
	  dm = 0;
	  errmsg(2, 0, PCP_MMSG_F);
	}
      else if (key == 12) 
	rtn = 1;
      else
	dm = 0;
    }
  free(xname);
}

static void p_quadratic_menu(int *errc)
{
  int  size;
  int  key;
  int  dm;
  int  rtn;
  char *xname;

  size = 3;
  dm = 1;
  rtn = 0;
  *errc = 0;
  xname = (char *) 0;
  while (!rtn)
    {
      if (dm)
	{
	  clear_screen();
	  p_disp(PCP_MENU_QPAR, size);
	  cursor_off();
	}
      pcp_init_menu(&dm, errc, &key, xname);
      if (key == 1)
	*errc = p_pqc_learn(&xname);
      else if (key == 2)
	pcp_ensemble(PALG_BAG_PQC, errc, &xname, debug);
      else if (key == 3)
	{
	  if (teds)
	    *errc = p_pqc_predict(&xname);
	  else
	    *errc = PERR_UNDEFINED_TEDS;
	}
      else if (key == 4)
	p_xlearn(PALG_PQC, errc, &xname, debug);
      else if (key == 5)
	p_xlearn(PALG_BAG_PQC, errc, &xname, debug);
      else if (key == 6) 
	xlearn_disp(errc, &xname, PCP_XPL);
      else if (key >= 7 && key <= 11)
	{
	  dm = 0;
	  errmsg(2, 0, PCP_MMSG_F);
	}
      else if (key == 12) 
	rtn = 1;
      else
	dm = 0;
    }
  free(xname);
}

/*
  TBD: postponed.
*/
#if 0
static void p_pairwise_menu(void)
{
  

}
#endif

static void p_pac_menu(int *errc)
{
  int  size;
  int  key;
  int  dm;
  int  rtn;

  size = 2;
  dm = 1;
  rtn = 0;
  while (!rtn)
    {
      if (dm)
	{
	  clear_screen();
	  p_disp(PCP_MENU_PAC, size);
	  cursor_off();
	}
      pcp_init_menu(&dm, errc, &key, (char *) 0);
      if (key == 1)
	p_linear_menu(errc);
      else if (key == 2)
	p_quadratic_menu(errc);
      else if (key == 12) 
	rtn = 1;
      else if (key >= 3 && key <= 11)
	{
	  dm = 0;
	  errmsg(2, 0, PCP_MMSG_B);
	}
    }
}

/*
  Linear discriminant classifier menu.
*/
static void p_lind_menu(int *errc)
{
  int   size;
  int   key;
  int   dm;
  int   rtn;
  char  *xname;

  size = 3;
  dm = 1;
  rtn = 0;
  *errc = 0;
  xname = (char *) 0;
  while (!rtn)
    {
      if (dm)
	{
	  clear_screen();
	  p_disp(PCP_MENU_LD, size);
	  cursor_off();
	}
      pcp_init_menu(&dm, errc, &key, xname);
      if (key == 1)
	p_lind_learn(errc, &xname, &debug);
      else if (key == 2)
	pcp_ensemble(PALG_BAG_LIN, errc, &xname, debug);
      else if (key == 3)
	{
	  if (teds)
	    *errc = p_lin_predict(PALG_LIN, &xname);
	  else
	    *errc = PERR_UNDEFINED_TEDS;
	}
      else if (key == 4)
	p_xlearn(PALG_LIN, errc, &xname, debug);
      else if (key == 5)
	p_xlearn(PALG_BAG_LIN, errc, &xname, debug);
      else if (key == 6) 
	xlearn_disp(errc, &xname, PCP_XLD);
      else if (key == 12) 
	rtn = 1;
      else if (key >= 7 && key <= 11)
	dm = 0;
      else
	dm = 0;
    }
  free(xname);
}

static void p_knn_menu(int *errc)
{
  int  size;
  int  key;
  int  dm;
  int  rtn;
  char *xname;

  size = 3;
  dm = 1;
  rtn = 0;
  *errc = 0;
  xname = (char *) 0;
  while (!rtn)
    {
      if (dm)
	{
	  clear_screen();
	  p_disp(PCP_MENU_KNN, size);
	  cursor_off();
	}
      errmsg(2, *errc, xname);
      dm = 1;
      read_keyboard(&key);
      if (key == 1)
	{
	  if (tds && teds)
	    p_knn(errc, &xname, &debug);
	  else
	    *errc = PERR_UNDEFINED;
	}
      else if (key == 2)
	{
	  if (tds && teds)
	    p_knn_bagging(errc, &xname, &debug);
	  else
	    *errc = PERR_UNDEFINED;
	}
      else if (key == 3)
	p_xlearn(PALG_KNN, errc, &xname, debug);
      else if (key == 4)
	p_xlearn(PALG_BAG_KNN, errc, &xname, debug);
      else if (key == 5) 
	xlearn_disp(errc, &xname, PCP_XNN);
      else if (key == 6)
	{
	  if (tds)
	    pcp_knn_xpar(errc, debug, &xname);
	  else
	    *errc = PERR_UNDEFINED_TDS;
	  }
      else if (key >= 7 && key < 11)
	{
	  dm = 0;
	  errmsg(2, 0, PCP_MMSG_F);
	}
      else if (key == 12) 
	rtn = 1;
      else
	dm = 0;
    }
  free(xname);
}

static void p_mlp_menu(int *errc)
{
  int  size;
  int  key;
  int  dm;
  int  rtn;
  char *xname;

  size = 3;
  dm = 1;
  rtn = 0;
  *errc = 0;
  xname = (char *) 0;
  while (!rtn)
    {
      if (dm)
	{
	  clear_screen();
	  p_disp(PCP_MENU_MLP, size);
	  cursor_off();
	}
      errmsg(2, *errc, xname);
      dm = 1;
      read_keyboard(&key);
      if (key == 1)
	p_mlp_learn(errc, &xname, &debug);
      else if (key == 2)
	pcp_ensemble(PALG_BAG_MLP, errc, &xname, debug);
      else if (key == 3)
	{
	  if (teds)
	    p_mlp_predict(errc, &xname);
	  else
	    *errc = PERR_UNDEFINED_TEDS;
	}
      else if (key == 4)
	p_xlearn(PALG_MLP, errc, &xname, debug);
      else if (key == 5)
	p_xlearn(PALG_BAG_MLP, errc, &xname, debug);
      else if (key == 6)
	xlearn_disp(errc, &xname, PCP_XMP);
      else if (key == 7)
	{
	  if (tds)
	    pcp_mlp_xpar(errc, debug, &xname);
	  else
	    *errc = PERR_UNDEFINED_TDS;
	  }
      else if (key >= 8 && key <= 11)
	{
	  dm = 0;
	  errmsg(2, 0, PCP_MMSG_G);
	}
      else if (key == 12) 
	rtn = 1;
      else
	dm = 0;
    }
  free(xname);
}

static void pcp_menu_xpar(int *errc)
{
  int  size;
  int  key;
  int  dm;
  int  rtn;
  char *xname;

  size = 2;
  dm = 1;
  rtn = 0;
  *errc = 0;
  xname = (char *) 0;
  while (!rtn)
    {
      if (dm)
	{
	  clear_screen();
	  p_disp(PCP_MENU_XPAR, size);
	  cursor_off();
	}
      pcp_init_menu(&dm, errc, &key, xname);
      if ((key == 1) || (key == 2))
	{
	  if (tds)
	    {
	      if (key == 1)
		pcp_svm_simplex(errc, debug, &xname);
	      else 
		pcp_svm_grid(errc, debug, &xname);
	    }
	  else
	    *errc = PERR_UNDEFINED_TDS;
	}
      else if (key >= 3 && key <= 11)
	{
	  dm = 0;
	  errmsg(2, 0, PCP_MMSG_B);
	}
      else if (key == 12) 
	rtn = 1;
    }
}

static void p_svm_menu(int *errc)
{
  int  size;
  int  key;
  int  dm;
  int  rtn;
  int  acode;
  char *xname;

  size = 3;
  dm = 1;
  rtn = 0;
  *errc = 0;
  xname = (char *) 0;
  while (!rtn)
    {
      if (dm)
	{
	  clear_screen();
	  p_disp(PCP_MENU_SVM, size);
	  cursor_off();
	}
      pcp_init_menu(&dm, errc, &key, xname);
      if (key == 1)
	{
	  if (!tds)
	    *errc = PERR_UNDEFINED_TDS;
	  else if (tds->c == 1)
	    *errc = PERR_ONE_CLASS;
	  else
	    p_svm_learn(errc, &xname, debug);
	}
      else if (key == 2)
	{
	  if (!tds)
	    *errc = PERR_UNDEFINED_TDS;
	  else if (tds->c == 1)
	    *errc = PERR_ONE_CLASS;
	  else
	    pcp_ensemble(PALG_SVM, errc, &xname, debug);
	}
      else if (key == 3)
	{
	  if (teds)
	    p_svm_predict(errc, &xname);
	  else
	    *errc = PERR_UNDEFINED_TEDS;
	}
      else if ((key == 4) || (key == 5))
	{
	  if (!tds)
	    *errc = PERR_UNDEFINED_TDS;
	  else if (tds->c == 1)
	    *errc = PERR_ONE_CLASS;
	  else
	    {
	      if (key == 4)
		acode = PALG_SVM;
	      else 
		acode = PALG_BAG_SVM;
	      p_xlearn(acode, errc, &xname, debug);
	    }
	}
      else if (key == 6) 
	xlearn_disp(errc, &xname, PCP_XSV);
      else if (key == 7) 
	pcp_menu_xpar(errc);
      else if (key == 8) 
	{
	  if (tds)
	    p_svm_save(errc, &xname);
	  else
	    *errc = PERR_UNDEFINED_TDS;
	}
      else if (key == 12) 
	rtn = 1;
      else if (key >= 9 && key <= 11)
	{
	  dm = 0;
	  errmsg(2, 0, PCP_MMSG_H);
	}
      else
	dm = 0;
    }
  free(xname);
}

static void p_bayes_menu(int *errc)
{
  int  size;
  int  key;
  int  dm;
  int  rtn;
  char *xname;

  size = 2;
  dm = 1;
  rtn = 0;
  *errc = 0;
  xname = (char *) 0;
  while (!rtn)
    {
      if (dm)
	{
	  clear_screen();
	  p_disp(PCP_MENU_BAYES, size);
	  cursor_off();
	}
      pcp_init_menu(&dm, errc, &key, xname);
      if ((key == 1) || (key == 2))
	{
	  if (!tds)
	    *errc = PERR_UNDEFINED_TDS;
	  else if (key == 1)
	    p_bayes(errc, 0, debug);
	  else if (key == 2)
	    p_bayes(errc, 1, debug);
	}
      else if (key == 3)
	p_disp_bayes(errc, &xname);
      else if (key == 12) 
	rtn = 1;
      else if (key >= 4 && key <= 11)
	{
	  dm = 0;
	  errmsg(2, 0, PCP_MMSG_C);
	}
    }
}

/*
  Pattern recognition menu.
*/
static int pr_menu()
{
  int  size;
  int  key;
  int  dm;
  int  rtn;
  int  errc;
  char *xname;

  size = 3;
  dm = 1;
  rtn = 0;
  errc = 0;
  xname = (char *) 0;
  while (!rtn)
    {
      if (dm)
	{
	  clear_screen();
	  p_disp(PCP_MENU_PC, size);
	  cursor_off();
	}
      pcp_init_menu(&dm, &errc, &key, xname);
      if (key == 1)
	p_bayes_menu(&errc);
      else if (key == 2)
	p_pac_menu(&errc); /* parametric classifiers */
      else if (key == 3)
	p_lind_menu(&errc); /* linear discriminant classifiers */
      else if (key == 4)
	p_knn_menu(&errc); /* k-NN */
      else if (key == 5)
	p_mlp_menu(&errc); /* MLP */
      else if (key == 6)
	p_svm_menu(&errc); /* SVM */
      else if (key == 12) 
	rtn = 1;
      else if (key >= 7 && key <= 11)
	{
	  dm = 0;
	  errmsg(2, 0, PCP_MMSG_F);
	}
      else
	dm = 0;
    }
  return errc;
}

/*
  Menu for principal components analysis.
*/
static int pca_menu(void)
{
  int  size;
  int  key;
  int  dm;
  int  rtn;
  int  menu_status;
  int  errc;
  char *xname;

  size = 2;
  dm = 1;
  rtn = 0;
  menu_status = 0;
  xname = malloc(PCP_FLEN+1);
  *xname = '\0';
  errc = 0;
  while (!rtn)
    {
      if (dm)
	{
	  clear_screen();
	  p_disp(PCP_MENU_PCA, size);
	  cursor_off();
	}
      pcp_init_menu(&dm, &errc, &key, xname);
      if (key == 1)
	{
	  p_cda(PDR_PCA, &errc, &xname);
	  if (errc)
	    menu_status = errc;
	}
      else if (key == 2)
	{
	  /*
	    Singular value decomposition. To be used when the number
	    of vectors is less than the dimension of the input
	    space. See source file svd.c for additional comments.
	  */
	  p_svd(&errc, &xname, debug);
	  if (errc)
	    menu_status = errc;
	}
      else if (key == 12) 
	rtn = 1;
      else if (key >= 3 && key <= 11)
	{
	  dm = 0;
	  errmsg(2, 0, "Press a..b.");
	}
      else
	dm = 0;
    }
  free(xname);
  return menu_status;
}

/*
  Feature extraction menu.
*/
static int fe_menu(void)
{
  int  size;
  int  key;
  int  dm;
  int  rtn;
  int  errc;
  int  menu_status;
  char *xname;

  size = 2;
  dm = 1;
  rtn = 0;
  errc = 0;
  menu_status = 0;
  xname = (char *) 0;
  while (!rtn)
    {
      if (dm)
	{
	  clear_screen();
	  p_disp(PCP_MENU_FEXTR, size);
	  cursor_off();
	}
      pcp_init_menu(&dm, &errc, &key, xname);
      if (key == 1)
	{
	  p_cda(PDR_FISHER, &errc, &xname);
	  menu_status = errc;
	}
      else if (key == 2)
	menu_status = pca_menu();
      else if (key == 3)
	{
	  if (tds)
	    p_emap(&errc, &xname, debug);
	  else
	    errc = PERR_UNDEFINED_TDS;
	  menu_status = errc;
	}
      else if (key == 4)
	{
	  if (tds && teds)
	    p_transform(P_MAP, &errc, &xname);
	  else
	    errc = PERR_UNDEFINED;
	  menu_status = errc;
	}
      else if (key == 12) 
	rtn = 1;
      else if (key >= 5 && key <= 7)
	{
	  dm = 0;
	  errmsg(2, 0, PCP_MMSG_D);
	}
      else
	dm = 0;
    }
  free(xname);
  return menu_status;
}

/*
  Feature subset selection menu.
*/
static int fsel_menu(void)
{
  int  size;
  int  key;
  int  dm;
  int  rtn;
  int  errc;
  char *xname;

  size = 2;
  dm = 1;
  rtn = 0;
  errc = 0;
  xname = (char *) 0;
  while (!rtn)
    {
      if (dm)
	{
	  clear_screen();
	  p_disp(PCP_MENU_FSEL, size);
	  cursor_off();
	}
      pcp_init_menu(&dm, &errc, &key, xname);
      if (key == 1)
	p_fselect(&errc, &xname, debug);
      else if (key == 2)
	p_fdisp(&errc, &xname, debug);
      else if (key == 3)
	p_f_subset(&errc, &xname, debug);
      else if (key == 12) 
	rtn = 1;
      else if (key >= 3 && key <= 11)
	{
	  dm = 0;
	  errmsg(2, 0, PCP_MMSG_C);
	}
      else
	dm = 0;
    }
  free(xname);
  return errc;
}

static int data_load_menu(void)
{
  int  size;
  int  key;
  int  dm;
  int  rtn;
  int  errc;
  int  menu_status;
  int  imin;
  char *xname;

  size = 3;
  dm = 1;
  rtn = 0;
  errc = 0;
  menu_status = 0;
  xname = malloc(PCP_FLEN+1);
  *xname = '\0';
  while (!rtn)
    {
      if (dm)
	{
	  clear_screen();
	  p_disp(PCP_MENU_LOAD, size);
	  cursor_off();
	}
      pcp_init_menu(&dm, &errc, &key, xname);
      if (key == 1)
	{
	  p_load(&errc, &xname);
	  menu_status = errc;
	}
      else if (key == 3)
	/*
	  Display data sets.
	*/
	ddset();
      else if (key == 2)
	dstatus();
      else if (key == 5)
	{
	  if (!tds && !teds)
	    {
	      dm = 0;
	      errc = PERR_UNDEFINED;
	      menu_status = errc;
	    }
	  else
	    {
	      p_copy(&errc, &xname);
	      if (!errc)
		errc = PSTS_DONE;
	      else
		menu_status = errc;
	    }
	}
      else if (key == 6)
	{
	  if (!tds && !teds)
	    {
	      dm = 0;
	      errc = PERR_UNDEFINED;
	      menu_status = errc;
	    }
	  else if (!tds)
	    {
	      dm = 0;
	      errc = PERR_UNDEFINED_TDS;
	      menu_status = errc;
	    }
	  else
	    {
	      p_transform(P_NORMALIZE, &errc, &xname);
	      if (!errc)
		errc = PSTS_DONE;
	      else
		menu_status = errc;
	    }
	}
      else if (key == 7)
	{
	  if (tds)
	    {
	      imin = ivec_min(tds->nd, tds->c);
	      if (imin < 2)
		{
		  errc = PERR_ONE_SAMPLE;
		  menu_status = errc;
		}
	      else
		{
		  errmsg(2, 0, PMSG_EIGEN_ANALYSIS);
		  p_eigen(&errc);
		  menu_status = errc;
		}
	    }
	  else
	    {
	      dm = 0;
	      errc = PERR_UNDEFINED_TDS;
	      menu_status = errc;
	    }
	}
      else if (key == 8)
	{
	  if (tds)
	    {
	      clear_screen();
	      covar(tds->x, tds->d, tds->nv, &errc, debug);
	      menu_status = errc;
	      pwait();
	    }
	  else
	    {
	      dm = 0;
	      errc = PERR_UNDEFINED_TDS;
	      menu_status = errc;
	    }
	}
      else if (key == 12) 
	rtn = 1;
      else if (key >= 9 && key <= 11)
	{
	  dm = 0;
	  errmsg(2, 0, "Press a..h.");
	}
      else
	dm = 0;
    }
  free(xname);
  return menu_status;
}

static void usage(char *program, hash_t *status_table)
{
  int     internal_code;
  int     exit_code;
  hscan_t *scan;
  hnode_t *node;
  struct pcp_stat *pcp_status;

  printf("%s (Pattern Classification Program) version %s\n\n", program, PCP_VERSION);
  printf("Usage: %s [-h] [-b batch_file] [-q] [-d]\n", program);
  printf("       -h              display this message\n");
  printf("       -b batch_file   run in batch mode, reading commands from batch_file\n");
  printf("       -q              quiet mode (no screen output)\n");
  printf("       -d              enable debug mode (produces %s file)\n\n", PCP_DBG);
  printf("To kill the program, use SIGINT (Ctrl-C) or SIGQUIT (Ctrl-\\)\n");
  
  if (status_table)
    {
      printf("\ninternal and exit error codes:\n");
      scan = calloc(1, sizeof(hscan_t));
      hash_scan_begin(scan, status_table);
      while ((node = hash_scan_next(scan)))
	{
	  internal_code = atoi((char *) hnode_getkey(node));
	  pcp_status = (struct pcp_stat *) hnode_get(node);
	  exit_code = pcp_status->exit_code;
	  if (exit_code > PMAX_ERRNO)
	    {
	      printf("%6d\t%6d", internal_code, exit_code);
	      if (pcp_status->msg)
		printf("\t%s\n", pcp_status->msg);
	      else
		printf("\n");
	    }
	}
    }
}

/*
  Redirect stdin so that the commands are read from 'batch_file'
  instead of the keyboard.
*/
static int redirect_stdin(char *batch_file)
{
  int  status;
  FILE *fptr;
  
  status = 0;
  fptr = freopen(batch_file, "r", stdin);
  if (!fptr)
    {
      status = -1;
      perror(batch_file);
    }
  return status;
}

/*
  Redirect stdout so that the output is sent to /dev/null. 
*/
static int redirect_stdout()
{
  int  status;
  FILE *fptr;
  
  status = 0;
  fptr = freopen("/dev/null", "w", stdout);
  if (!fptr)
    {
      status = -1;
      perror("/dev/null");
    }
  return status;
}

/*
  Main program for pattern classification. Returns -1 in case of error.
*/
int main(int argc, char **argv)
{
  int  i;
  int  dm;
  int  rtn;
  int  errc;
  int  key;
  int  size;
  int  status;
  int  menu_status;
  int  exit_code;
  char *xname;
  hash_t *status_table;
  struct pcp_stat *pcp_status;

  sig();
  status_table = create_status_table();
  status = 0;
  menu_status = 0;
  errc = 0;
  /*
    Read command-line arguments. 
  */
  if (argc > 1)
    {
      i = 1;
      while ((i < argc) && !status)
	{
	  if (!strcmp(argv[i], "-d"))
	    {
	      debug = 1;
	      status = unlink(PCP_DBG);
	      if (status == -1)
		{
		  if (errno != ENOENT)
		    errc = errno;
		  else
		    status = 0;
		}
	    }
	  else if (!strcmp(argv[i], "-h"))
	    {
	      usage(argv[0], (hash_t *) 0);
	      status = 1;
	    }
	  else if (!strcmp(argv[i], "-H"))
	    {
	      usage(argv[0], status_table);
	      status = 1;
	    }
	  else if (!strcmp(argv[i], "-b"))
	    {
	      if (!argv[i+1])
		{
		  usage(argv[0], (hash_t *) 0);
		  status = -1;
		  errc = EINVAL;
		}
	      else
		{
		  status = redirect_stdin(argv[i+1]);
		  if (status == -1)
		    errc = errno;
		  i++;
		}
	    }
	  else if (!strcmp(argv[i], "-q"))
	    {
	      status = redirect_stdout();
	      if (status == -1)
		errc = errno;
	    }
	  else
	    {
	      /*
		Unrecognized option.
	      */
	      usage(argv[0], (hash_t *) 0);
	      status = -1;
	      errc = EINVAL;
	    }
	  i++;
	}
    }
  if (!status)
    {
      unlink(PCP_DAT);
      size = 2;
      dm = 1;
      rtn = 0;
      errc = 0;
      xname = (char *) 0;
      init_load(&errc, &xname);
      while (!rtn)
	{
	  if (dm)
	    {
	      clear_screen();
	      p_disp(PCP_MENU_MAIN, size);
	      cursor_off();
	    }
	  errmsg(2, errc, xname);
	  dm = 1;
	  status = read_keyboard(&key);
	  if (!status)
	    {
	      if (key == 1)
		menu_status = data_load_menu();
	      else if (key == 2)
		menu_status = pr_menu();
	      else if (key == 3)
		menu_status = fe_menu();
	      else if (key == 4)
		menu_status = fsel_menu();
	      else if (key == 12) 
		{
		  rtn = 1;
		  putc('\n', stdout);
		}
	      else if (key >= 5 && key <= 11)
		{
		  dm = 0;
		  errmsg(2, 0, PCP_MMSG_D);
		}
	      else
		dm = 0;
	    }
	  else
	    {
	      errc = errno;
	      rtn = 1;
	      errmsg(2, errc, xname);
	    }
	}
      /*
	Cleanup.
      */
      cleanup();
      free(xname);
    }
  if (menu_status)
    errc = menu_status;
  pcp_status = hashGetStat(status_table, errc);
  if (pcp_status)
    exit_code = pcp_status->exit_code;
  else
    exit_code = 1;
  return exit_code;
}
  


syntax highlighted by Code2HTML, v. 0.9.1