 /************************************************************************
 *			Display.c	Display Current Problems 	*
 *									*
 *	   This program displays the problems in a format useful to	*
 *	the operators.  There is a date and time field for when the	*
 *	problem occured, and an Update command so that a problem	*
 *	status can be carried across operator shifts.  Each Update	*
 * 	appends a timestamped event to the pingky.log file as well.	*
 *									*
 *	   This Display program may be modified to the installations	*
 *	specifications without modifying the background data collection.*
 *									*
 *	   I encourage programmers to make additions to this code and	*
 *	forward them to me for inclusion in the next release.		*
 *									*
 ************************************************************************
 * Author:   WB Norton	4/23/89		Merit Computer Network		*
 *									*
 *	Version 1.0   Author: WB Norton, Merit Computer Network         *
 *									*
 * Modification History:						*
 * Written 5/18/89 Bill Norton, Merit Computer Network                  *
 *									*
 ************************************************************************/
/*
  * Copyright 1989
  * The Regents of the University of Michigan
  * All Rights eserved
  *
  * Permission to use, copy, modify, and distribute this software and its
  * documentation for any purpose and without fee is hereby granted, provided
  * that the above copyright notice and this permission notice appear in
  * all copies of the software and derivative works or modified versions
  * thereof, and that both that copyright notice and this permission
  * notice appear in supporting documentation.
  *
  * The software is provided "as is" and the University of Michigan
  * disclaims all warranties with regard to this software, including
  * all implied warranties of merchantability and fitness.  In no event
  * shall the University of Michigan be liable for any special, direct,
  * indirect, or consequential damages or any damages whatsoever
  * resulting from loss of use, data or profits, whether in an action of
  * contract, negligence or other tortious action, arising out of or in
  * connection with the use or performance of this software.
  */

#include "../ProblemManager/ProblemManager.h"
#include "../Ctools/ctools.h"
#include "version.h"
#include <curses.h>
#ifdef AIX
#include <term.h>
#endif
#include <time.h>
#include <signal.h>
#include <ctype.h>
#include <string.h>
#include <sys/file.h>
#include <unistd.h>

#define HUMANTIME 1
#define ELAPSEDMIN 2
#define ELAPSEDSEC 3
#define LONGTIME 4
static int TimeStampFormat=HUMANTIME;

static int CurrentLine=0;

static char *title="NOC Display";
static char *DefaultPingkyDir=".";
static char hostfile[100];		/* List of nodes we are checking */
static char Checking_File[100];		/* What node are we checking? */
static char ProblemFile[100];		/* Problem File! */
static char cycletime[100];		/* How long did pingky take to cycle */
static char logfile[100];		/* Where do we log things */
static char DisplayType[40];		/* Where do we log things */
static char Filter_File[100];		/* Filter out/in Nodes based on regexp*/

static char *tz;
static char *Defaulttz="GMT";

#define MAXFILTERS 12
struct Filters {
        char *Name;             /* Name on PushButton */
        char *RegExp;           /* NodeName Reg Exp   */
        char *Pattern;          /* Compiled pattern   */
        int  Selected;       	/* Is this net selected*/
        int count;              /* Count of items in net*/
} Filter[MAXFILTERS];

		/* Update Interval : How often do we check to see if the */
		/*                   problem file changed.		 */
#define UPDATEINTERVAL 15 /* Check for PROBLEM.FILE changes every 15 secs*/

static char Updating=0;	/* Don't refresh screen during user update/delete */
static char Updateable=0;	/* Don't Allow updating by default */


SetupFilters()
{
FILE *stream;
int i,line;
static char inbuffer[MAXFILTERS][100];
char buffer[200];

        for( i=0; i<MAXFILTERS; i++ ) Filter[i].Name=NULL;

        if ( ( stream = fopen( Filter_File, "r" )) == NULL ) return;

        for(i=0,line=1; fgets( inbuffer[i], 100, stream ) != NULL; line++) {
                if (inbuffer[i][0] == '#' ) continue;
                if ((Filter[i].Name=strtok(inbuffer[i]," \t\n"))==NULL) {
                        fprintf(stderr,"Blank Line  in filter file: %s line %d \n",Filter_File, line);
                        continue; /*BLANK*/
                }

                if ((Filter[i].RegExp=strtok(NULL," \t\n"))==NULL) {
                        fprintf(stderr,"NULL Regular Expression in filterfile: %s line %d (%s)\n",Filter_File, line,Filter[i].Name );
                        Filter[i].Name=NULL;
                        continue;
                }
#ifdef AIX
                if ( ( Filter[i].Pattern = regcmp( Filter[i].RegExp, NULL ) ) == NULL ) {
#endif
#ifndef AIX
                if ( ( Filter[i].Pattern = re_comp( Filter[i].RegExp ) ) != NULL ) {
#endif
                        fprintf(stderr,"Error compiling %s regexp %s (%s)\n",Filter[i].Name, Filter[i].RegExp,Filter[i].Name);

                        Filter[i].Name=NULL;
                        continue;
                }
/*printf("Line %d: Filter[i].Name=%s Filter[i].RegExp=%s\n",line,Filter[i].Name,Filter[i].RegExp);*/
                Filter[i].Selected=TRUE;
                Filter[i].count=0;
                if ( i++ >= MAXFILTERS ) {
                        fprintf(stderr,"TOO MANY FILTERS in FILTER FILE %s (MAX=%d)\nIgnoring the rest\n",Filter_File, MAXFILTERS);
                        break;
                }
        }
        fclose(stream);
}


/********************************************************
 * PrintTime:  Print the current time on the screen.	*
 ********************************************************/
Print_Time()		/* Print the current time on the screen */
{
long TimeNow;
char buffer[27];
struct tm *tm;

	time( &TimeNow );
	strcpy(buffer,ctime( &TimeNow ));
	buffer[16]='\0';
	strcat(buffer," ");
	strcat(buffer,tz);
	buffer[20]='\0';
    	mvaddstr(0,59,buffer); 
	tm=gmtime( &TimeNow );
	strcpy(buffer,asctime(tm));
	buffer[16]='\0';
	strcat(buffer," GMT");
    	mvaddstr(1,59,buffer); 
}

#define SECSDAY (60*60*24)
#define SECSHR	(60*60)
#define SECSMIN	60

char *ElapsedMins( diff )
long diff;		/* Elapsed Seconds */
{
static char buffer[20];
int 	ElapsedDay,ElapsedHr,ElapsedMin;

	if (ElapsedDay= diff / SECSDAY ) diff %= (ElapsedDay*SECSDAY);
	if (ElapsedHr = diff / SECSHR ) diff %= (ElapsedHr*SECSHR);
	ElapsedMin= diff / SECSMIN;
	if ( ElapsedDay ) sprintf( buffer,"%d:%2.2d:%2.2d",
				ElapsedDay,ElapsedHr,ElapsedMin );
	else
		if ( ElapsedHr ) sprintf( buffer,"%d:%2.2d",
				ElapsedHr,ElapsedMin );
		else
			if ( ElapsedMin ) sprintf( buffer,"%d",
						ElapsedMin );
			else sprintf(buffer,"%d",0);
	return( buffer );
}

char *PrintTimeStamp( TimeStamp )
long TimeStamp;
{
static char buffer[50];
long TimeNow;

	switch( TimeStampFormat ) {
	case LONGTIME:	return( ltoa( TimeStamp, buffer ) );
				/*NOTREACHED*/
	case HUMANTIME:	sprintf(buffer,"%s_%s ",
					_Day( TimeStamp ), _Time( TimeStamp ));
				return( buffer );
				/*NOTREACHED*/
	case ELAPSEDMIN:	time( & TimeNow );
				/*return( ltoa( (TimeNow-TimeStamp)/60, buffer) );*/
				return( ElapsedMins(TimeNow-TimeStamp) );
				/*NOTREACHED*/
	case ELAPSEDSEC:	time( & TimeNow );
				return( ltoa( (TimeNow-TimeStamp), buffer ) );
				/*NOTREACHED*/
	default:		return("BADCALL");
				/*NOTREACHED*/
	}
}
	
/**********************************************************
 *  Message: Put a message to the user on the bottom line * 
 **********************************************************/
void Message(msg)
char msg[];
{
   standout();
   mvaddstr(LINES-1,0,msg);
   standend();
   clrtoeol();
   refresh();
}

/********************************************************
 * DrawFrame: Draw a frame in which problems get logged	*
 ********************************************************/
DrawFrame()
{
char TopLine[80],buffer[80],buf2[20];
int i;

    clear(); 
    sprintf(TopLine,"%s          %s",DisplayType,title);
    mvaddstr(0,0,TopLine);
    strcpy(buffer,"Filters: ");
    for( i=0; i<MAXFILTERS && Filter[i].Name!=NULL; i++) 
	if (Filter[i].Selected==FALSE) {
		strcat(buffer,Filter[i].Name); strcat(buffer," ");
		/*strcat(buffer,"("); strcat(buffer,itoa(Filter[i].count,buf2)); strcat(buffer,") ");*/
	}
    if ( strlen(buffer)>9) mvaddstr(2,10,buffer);
    switch( TimeStampFormat ) {
    case HUMANTIME:
    mvaddstr(3,0,"# Reported                Problem                          StatusLine"); 
	break;
    case LONGTIME:
    mvaddstr(3,0,"# longtime		    Problem                          StatusLine");
		break;
    case ELAPSEDSEC:
    mvaddstr(3,0,"# ElapsedSecs		    Problem                          StatusLine");
		break;
    case ELAPSEDMIN:
    mvaddstr(3,0,"# Elapsed Time            Problem                          StatusLine");
		break;
    default:
		break;
    }	

    mvaddstr(4,0,"  ========= --------------------------------------  --------------------------");
}

/**************************************************************************
 *  PrintCycleTime: Print the current cycle time of the background pingky * 
 **************************************************************************/
PrintCycle_Time()
{
FILE *stream;
char buffer[10];

   mvaddstr(1,0,"Cycle Time:");
   if ((stream=fopen(cycletime,"r"))==NULL) mvaddstr(1,12,"--:--");
   else {
      if (fgets(buffer,10,stream)==NULL) mvaddstr(1,12,"--:--");
      else mvaddstr(1,12,buffer);
      fclose(stream);
   }
}

/******************************************************************
 * ReadChecking:  See what node the background pingky is querying *
 ******************************************************************/
ReadChecking()
{
FILE *stream;
char buffer[100];
char buffer2[100];
long timenow;
char CollectorStatus[200];

   if ((stream=fopen(Checking_File,"r"))==NULL) {
      perror("Checking_File NOT FOUND! Error: Possibly no Data Collectors!");
   }
   else {
      fgets(CollectorStatus,sizeof(CollectorStatus),stream);
      mvaddstr(LINES-1,0,"Collector:  ");
      mvaddstr(LINES-1,12,CollectorStatus); clrtoeol();
      sprintf(buffer,"%3.3s",&CollectorStatus[18]);
      mvaddstr(2,71,buffer);
      fclose(stream);
      time( &timenow );
      if ( timenow - LastMod( Checking_File ) > 60*5  ) {
		mvaddstr(LINES-1,0,"COLLECTOR has NOT BEEN RUNNING FOR AT LEAST 5 MINUTES!!");
		sprintf(buffer2,"** Collector Died %16.16s RIP **",CollectorStatus);
		mvaddstr(1,20,buffer2);
      }
   }
}

/*****************************************************************
 * ReadProblems:  Read Problems and Display on the screen nicely *
 *****************************************************************/
ReadProblems()
{
	Problem_Manager( CHECK, NULL, NULL, NULL, NULL ); /* Reread prob list */
	DrawProblems();
}

/*
 *      Filter - Actual Filter Routine ( called for each problem entry
 */
int FilterProblem( p )
struct ProblemType *p;
{
extern char *regex();
int i;

        for( i=0; Filter[i].Name!=NULL; i++ ) {
#ifndef AIX
                re_comp( Filter[i].RegExp );
                if ( re_exec( p->Name ) == 1 ) {
#endif
#ifdef AIX
                if ( regex( Filter[i].Pattern, p->Name ) != NULL ) {
#endif
                        Filter[i].count++;
                        if ( Filter[i].Selected==FALSE) {
                                /*printf("Filtered out %s Node %s\n", Filter[i].Name, p->Name);*/
                                return(0);
                        }
                        else return(1); /* Matches pattern -user wants to see */
                }
                else;   /* See if we match the next one */
        }
        return(1);      /* No Match - user wants to see */
}


/****************************************************************
 * DrawProblems:  Draw Problems on the screen in a nice format	*
 ****************************************************************/
DrawProblems()
{
int i=0,y=5;
char buffer[100];
extern struct ProblemType ProblemArray[];/*In Coreversion of existing problems*/
extern int NumProblems;
 
  for( i=0; i<MAXFILTERS; i++ ) Filter[i].count=0;
  sprintf(buffer,"[%3d]",NumProblems);
  mvaddstr(2,74,buffer); 
   if ( CurrentLine > NumProblems ) CurrentLine=0;
   if (NumProblems==0) { 
      move(y,0);clrtoeol();
      mvaddstr(y++,30,"**** No Problems ****");
      CurrentLine=0;
   }
   else {
	if ( CurrentLine <= 0 ) {	/* Not scrolling now */
		for(i=0; (i<NumProblems) && (y<LINES-2); i++,y++) {
		   if ( FilterProblem( &ProblemArray[i] ) == 1 ) {
			sprintf(buffer,"%2.2d",i+1);
			mvaddstr(y,0,buffer);
         		sprintf(buffer," %9.9s %s %s %s",
				PrintTimeStamp( ProblemArray[i].TimeStamp ),
                  		ProblemArray[i].Name,ProblemArray[i].UniqueID,
				ProblemArray[i].TestName);
         		mvaddstr(y,3,buffer);
	 		clrtoeol();
			if ( ProblemArray[i].StatusLine[0] != '\0' )
         			mvaddstr(y,STATUSX,ProblemArray[i].StatusLine);
		   }
	    	   else y--;	/* ProblemFiltered*/
		}
	}
	else {
                for(i=CurrentLine; i<NumProblems && y<LINES-2; i++,y++) {
		   if ( FilterProblem( &ProblemArray[i] ) == 1 ) {
			/*if ( ProblemArray[i].StatusLine[0] == '\0' ) standout();*/
			sprintf(buffer,"%2.2d",i+1);
			mvaddstr(y,0,buffer);
			/*if ( ProblemArray[i].StatusLine[0] == '\0' ) standend();*/
                        sprintf(buffer," %9.9s %s %s %s",
                                PrintTimeStamp( ProblemArray[i].TimeStamp ),
                                ProblemArray[i].Name,ProblemArray[i].UniqueID,
                                ProblemArray[i].TestName);
                        mvaddstr(y,3,buffer);
                        clrtoeol();
                        mvaddstr(y,STATUSX,ProblemArray[i].StatusLine);
		   } else y--;
		}
	}
   } 
   if (i<NumProblems) {
	mvaddstr(2,55,"[ MORE ... ]");
   }
   else {
	mvaddstr(2,55,"            ");
      	for(; y<LINES-2; y++) {
	 	move( y, 0 );
	 	clrtoeol();
      	}
   }
}

/****************************************************************
 * GotoCmdLine:  Got the Command Prompt 			*
 ****************************************************************/
GotoCmdLine()
{
   move(LINES-2,0); clrtoeol();
   standout();
   addstr("Command:");
   standend();  
   move(LINES-2,9);
}

/****************************************************************
 * DrawDynamics:  Redraw what does change			*
 *			time of day				*
 *			cycletime				*
 *			PROBLEM.FILE				*
 *			CHECKING				*
 *			Goto the command line 			*
 ****************************************************************/
DrawDynamics()
{
      Print_Time();	/* Update the time of day */
      PrintCycle_Time();/* Update the Cycle Time portion */
      ReadProblems();	/* Update the Problems section  */
      ReadChecking();	/* Update the checking portion */
      GotoCmdLine();	/* Go to the command line and */
      refresh();	/* refresh the screen */
}

/****************************************************************
 * RefreshScreen:  Force Update of the user's screen		*
 *		   DO NOT CALL THIS ROUTINE!!!!			*
 *		   The user callable version is DrawDynamic	*
 *		   RefreshScreen should only be called 		*
 *		   by alarm clock because it restarts the alarm *
 *		   and we don't want multiple alarms to go off. *  
 *		   once every UPDATEINTERVAL seconds.		*
 ****************************************************************/
RefreshScreen()
{
   if (!Updating) {
	DrawDynamics();	
   }
   else {
	/*system("echo `date` BUSY>>debug");*/
   }
   signal(SIGALRM,RefreshScreen);	/*  Invoke RefreshScreen on alarm  */
   alarm(UPDATEINTERVAL);
}

/****************************************************************
 * ClearMessage:  Clear the message line			*
 ****************************************************************/
void ClearMessage()
{
   move(LINES-1,0);
   clrtoeol();
   refresh();
}

SetUpdating( claim )
int claim;
{
	if ( claim )
		mvaddstr(2,40,"*Claiming*"); 
	else
		mvaddstr(2,40,"*Updating*"); 
	refresh();
	Updating=1;
}
ClearUpdating()
{
	mvaddstr(2,40,"          "); refresh();
	Updating=0;
}
	

/***********************************************************************
 * GetUpdateList: get the user's request to update a list of problems. *
 ***********************************************************************/
int GetUpdateList()
{
char problemID[100];

   getstr(problemID);		/* 	get the user's string 	*/
   return(atoi(problemID));
}

/****************************************************************
 *		Update - Update the status of a problem		*
 ****************************************************************/
void Update( claim )
int claim;
{
char ProblemList[100];	/* array of nodes to be updated at once */
int i;
char buffer[200];
extern int NumProblems;
extern struct ProblemType ProblemArray[];
char TestName[100], Name[100], UniqueID[100];

   for(i=0; i<100; i++) ProblemList[i]=0;   
   if (NumProblems==0) {
      Message("  There are no problems to update, Bonehead!");
      sleep(5);
      ClearMessage();
      return;
   }
   if (Updateable) {
      SetUpdating( claim );
      if ( claim ) {
      	Message("   Enter the problem # that you want to Claim");
      	mvaddstr(LINES-2,10,"laim Problem # ");
      }
      else {
      	Message("   Enter the problem # that you want to Update   ");
      	mvaddstr(LINES-2,10,"pdate Problem # ");
      }
      refresh();
      if ((i=GetUpdateList())==0) {
         ClearMessage();
	 ClearUpdating();
         Updating=0;
         return;
      }
      if (i>0 && i<=NumProblems) {
	 strcpy(Name,ProblemArray[i-1].Name);
	 strcpy(TestName,ProblemArray[i-1].TestName);
	 strcpy(UniqueID,ProblemArray[i-1].UniqueID);
	 if ( claim )
         	Message("  Enter your initials and brief note (if any)     ");
	 else
         	Message("  Enter the StatusLine as you want it to appear   ");
         move(4+i,STATUSX);
         clrtoeol(); 
         refresh();
         getstr(buffer);
	 buffer[MAXSTATUSLINE-1]='\0';
	 if ( claim )
	    Problem_Manager( CLAIM_PROBLEM, TestName, Name, UniqueID, buffer );
	 else
	    Problem_Manager( UPDATE_PROBLEM, TestName, Name, UniqueID, buffer );
      }
      else { 
         Message("   Illegal Problem Number   ");
         sleep(5);
         ClearMessage();
      } 
      ClearUpdating();
   }
}

sigcatcher()
{
extern int In_Critical_Section;

	printf("Interrupt. ERR! Don't do that!\n");
	if ( In_Critical_Section ) {
		Log("Display: Interrupt during Fileio",logfile); 
		printf("Display: Interrupt during Fileio - try again",logfile); 
		return;
	}
   	signal(SIGALRM,SIG_DFL);
   	signal(SIGHUP,SIG_DFL);
   	signal(SIGINT,SIG_DFL);
   	signal(SIGQUIT,SIG_DFL);
   	signal(SIGTERM,SIG_DFL);
   	signal(SIGTSTP,SIG_DFL);
	exit( 0 );
}

/****************************************************************
 * Loop:  main loop of program.  				*
 *		Set an alarm to go off in 30 seconds		*
 *			and provide address of alarm routine	*
 *		Read the Problem List, Checking file		*
 *		Update the screen and display the current time	*
 *		Forever						*
 *			Get a key stroke and handle user cmd.	*	
 ****************************************************************/
Loop( )
{
extern int NumProblems;
char ch;

   Log("Display Program Starting\n", logfile);
   signal(SIGALRM,RefreshScreen);	/*  Invoke RefreshScreen on alarm  */
   signal(SIGHUP,sigcatcher);		/*  Catch all signals */
   signal(SIGINT,sigcatcher);		/*  Catch all signals */
   signal(SIGQUIT,sigcatcher);		/*  Catch all signals */
   signal(SIGTERM,sigcatcher);		/*  Catch all signals */
   signal(SIGTSTP,sigcatcher);		/*  Catch all signals */
   alarm(UPDATEINTERVAL);		/* Alarm in UPDATEINTERVAL seconds */
   DrawFrame();				/* Start out with Framework */
   while(1) {
	DrawDynamics();			/*Draw the changing part of the screen*/
	switch( getch() ) {
		case 'u':
		case 'U': 		/* Update a problem */
			if ( Updateable ) {
				Update( 0 );
			}
			else { Message("Permission Denied"); sleep(5); }
			break;
		case 'c':		/* Claim a problem */
		case 'C': 
			if ( Updateable ) {
				Update( 1 );
			}
			else { Message("Permission Denied"); sleep(5); }
			break;
		case 'q':
		case 'Q':
			addstr("uit");
			move(LINES-1,0);
			clrtoeol();
			refresh();
			return;
			/*NOTREACHED*/
		case 'h':
		case 'H':
			if ( Updateable ) {
				GetHelp();		
				DrawFrame();	/* Screen got trashed */
                        }
                        else { Message("Permission Denied"); sleep(5); }
			break;
		case 'd':
		case 'D':
			addstr("elete Problem # ");
                        if ( Updateable ) {
				DeleteProblem();
                        }
                        else { Message("Permission Denied"); sleep(5); }
			break;
		case 'F':
		case 'f':
			addstr("ilter ");
                        if ( Updateable ) {
				GetFilter();
				DrawFrame();	/* Frame will get corrupted */
                        }
                        else { Message("Permission Denied"); sleep(5); }
			break; 
		case '>':	/* ^DOWN */
			if ( (CurrentLine + LINES-6 )> NumProblems )
				CurrentLine=NumProblems-LINES-6;
			else CurrentLine=CurrentLine + LINES-6-1;
			break;
		case '<':	/* ^UP */
			if ( (CurrentLine - LINES-6 ) <=0 )
				CurrentLine=0;
			else CurrentLine=CurrentLine - LINES-6;
			break;
		case 'T':
		case 't':
			addstr("rouble ticket # ");
                        if ( Updateable ) {
				GetTroubleTicket();
				DrawFrame();	/* Frame will get corrupted */
                        }
                        else { Message("Permission Denied"); sleep(5); }
			break; 
		case 'V':
		case 'v':
			addstr("ersion: ");
			Message(Version);
			sleep( 5 );
			break; 
		case '~':	
			if ( ++TimeStampFormat > 2 ) TimeStampFormat=1;
			DrawFrame();	/* Frame will be different */
			break;
		case '?':
			Message("   Cmds: (U)pdate Problem (H)elp (D)eleteProblem (Q)uit (T)t (V)ersion ~<>");
			sleep(5);
			ClearMessage();
			break;
		case ' ':
			DrawFrame();
			break;
		default:  
			refresh();
            		break;
	}	/* switch */ 
   }	/* While () */
}

/**********************************************************************
 * DeleteProblem() - Allow User to delete a Problem From the Problem  *
 *								 File *
 **********************************************************************/
DeleteProblem()
{
char buffer[200],ProblemNumber[100];
int Num;
extern int NumProblems;
extern struct ProblemType ProblemArray[];/*In Coreversion of existing problems*/

	SetUpdating( 0 );
        Message("  Enter the Problem # of the Problem you want to Delete");
        move(LINES-2,26);
        refresh();
        getstr(ProblemNumber);
        refresh();
	if ((( Num = atoi(ProblemNumber) ) > NumProblems ) || (Num<1)) {
		Message(" That isn't a valid Problem Number ");
		sleep( 5 );
		ClearUpdating();
		return 0;
	} 
	 buffer[199]='\0';
         sprintf(buffer,"%s %s %s %s Problem DELETED BY OPERATOR Old StatusLine: %s\n",
		_Day(ProblemArray[Num-1].TimeStamp),_Time(ProblemArray[Num-1].TimeStamp),
		ProblemArray[Num-1].Name,ProblemArray[Num-1].TestName,
		ProblemArray[Num-1].StatusLine);
         Log(buffer,logfile);
	Problem_Manager( DELETE_PROBLEM, ProblemArray[Num-1].TestName, ProblemArray[Num-1].Name, ProblemArray[Num-1].UniqueID, NULL );

        Message(" Done ");
	ClearUpdating();
	return 1;
}

GetFilter()
{
char buffer[80],Name[40];
int i;

	SetUpdating( 0 );
	strcpy(buffer,"Filters: ");
	for( i=0; i<MAXFILTERS && Filter[i].Name!=NULL && (strlen(buffer)+strlen(Filter[i].Name) < 80); i++) {
		if ( Filter[i].Selected ) strcat(buffer,"*");
		strcat(buffer,Filter[i].Name);
		strcat(buffer," ");
	}
        Message(buffer);
        move(LINES-2,20);
        refresh();
        getstr(Name);
        refresh();
	for( i=0; i<MAXFILTERS && Filter[i].Name!=NULL; i++) {
		if ( strcmp( Name, Filter[i].Name ) == 0 ) {
			Filter[i].Selected^=1; 
			ClearUpdating();
			return;
		}
	}
	Message(" That isn't a valid Filter Name");
	sleep( 3 );
	ClearUpdating();
}

/************************************************************************
 *  GetHelp:	Display Help screen for a particular problem node	*
 ************************************************************************/
GetHelp()
{
char ProblemNumber[100];
int Num;
extern int NumProblems;
extern struct ProblemType ProblemArray[];/*In Coreversion of existing problems*/

        addstr("elp with problem # ");
        Message("  Enter the Problem # of the Problem you want help with");
        move(LINES-2,30);
        refresh();
        getstr(ProblemNumber);
        refresh();
	if ((( Num = atoi(ProblemNumber) ) > NumProblems ) || (Num<1)) {
		Message(" That isn't a valid Problem Number ");
		sleep( 5 );
		return 0;
	} 
	Help( ProblemArray[Num-1].Name );
}

more( NodeName,filename )
char *NodeName;
char *filename;
{
FILE *stream;
char buffer[200];
int i=0;
char ch;

	if ((stream=fopen(filename,"r"))==NULL) {
		sprintf(buffer,"Can't open %s - no help available\n",filename);
		Message( buffer );
		return;
	}
	while( fgets( buffer, sizeof(buffer), stream ) != NULL ) {
		printf(buffer);
		if ( ++i >= LINES-1 ) {
			printf("-- %s File : %s    Strike a key when ready... --",NodeName,filename);
			fflush(stdout);
			ch=getch();
			if ((ch=='Q')||(ch=='q')) break;
			printf("\n");
			i=0;
		}
	}
	fclose(stream);
}

Help( NodeName )
char *NodeName;
{
FILE *stream;
char buffer[200], *file, *p;
int line=1;

	if ((stream=fopen(hostfile,"r"))==NULL) {
		sprintf(buffer,"No hostfile (%s) available - No Help available",
			hostfile );
		Message(buffer);
		return;
	}
	while( ( p=fgets( buffer, sizeof(buffer), stream )) != NULL ) {
		if ( strcmp( NodeName , strtok( buffer," \t\n")) == 0 ) {
			Updating=1;	/* Don't redraw the screen */
			if ( strtok( NULL, " \t\n" ) == NULL ) break;
			if ( (file=strtok( NULL, " \t\n"))==NULL) break;
			clear();
			refresh();
			more(NodeName,file);
			printf("Strike a key when ready..."); fflush(stdout);	
			getch();
			Updating=0;
			return;
		}
		line++;
	}
	if ( p == NULL ) sprintf(buffer," No Help available for Node %s",NodeName);
	else sprintf(buffer," Error in line #%d of hostfile - no help available",line);
	Message( buffer );
}

GetTroubleTicket()
{
	clear();
	Message("Trouble Ticket System Not available yet..");
	sleep(5);
}

SetUpdateable( file )
char *file;
{

	if ( access( file, W_OK | F_OK ) == 0 ) Updateable=1;
	else Updateable=0;
}

/************************************************************************
 *   Main Program:     Start up the curses screen package and start 	*
 *				command processing loop			*
 ************************************************************************/
char *programname;
main(argc,argv)
int argc;
char *argv[];
{
char *pingkydir,*getenv();
char buffer[100];
int i;

    printf("%s\n",Version);
    programname=argv[0];
    for( i=1; i<argc; i++ )
	if ( strcmp( argv[i] , "-T" ) == 0 ) {
		title=argv[i+1];
		i+=2;
	}
	else {
		printf("\n\n\tUsage: %s [ -T Title ]\n",argv[0]);
		exit(1);
	}
    if ((pingkydir=getenv("PINGKYDIR"))==NULL) pingkydir=DefaultPingkyDir;
    if ((tz=getenv("TZ"))==NULL) tz=Defaulttz;

    strcpy(Checking_File,pingkydir); strcat(Checking_File,"/CHECKING");
    strcpy(cycletime,pingkydir); strcat(cycletime,"/pingky.cycle");
    strcpy(logfile,pingkydir); strcat(logfile,"/problemlog");
    strcpy(hostfile,pingkydir); strcat(hostfile,"/hostfile");
    strcpy(ProblemFile,pingkydir); strcat(ProblemFile,"/PROBLEM.FILE");
    strcpy(Filter_File,pingkydir); strcat(Filter_File,"/pingky.filter");
    if (getcwd(buffer,100) == NULL ) strcpy(DisplayType,"Unknown");
    else strcpy(DisplayType,strrchr(buffer,'/')+1);
    strcat(DisplayType," "); strncat(DisplayType,Version,13);
    
    SetupFilters();
    SetUpdateable( ProblemFile );
    initscr();			/* Open Up the curses environment	  */
    Loop( );	/* Call the main command handling loop 	  */
    endwin(); 			/* Close down the curses environment 	  */
}
