/*
 *	Network Queueing System (NQS)
 *  This version of NQS is Copyright (C) 1992  John Roman
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 1, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/*
*  PROJECT:     Network Queueing System
*  AUTHOR:      John Roman
*
*  Modification history:
*
*       Version Who     When            Description
*       -------+-------+---------------+-------------------------
*	V01.1	JRR	26-May-1992    	Initial version
*	     	JRR	17-Jun-1992	Added header
*	     	JRR	21-Sep-1992	Added startup and shutdown processing.
*	     	JRR	07-Oct-1992	Started qacct.c
*	V01.2   JRR	15-Jan-1993	Fixed month offset.
*					Fixed -u processing.
*	V01.3	JRR	26-Feb-1993	Fixed Mids to Mid_t.
*			08-Mar-1993	Added Boeing enhancement for files.
*			05-Apr-1993	Added new init data structure.
*			15-Apr-1993	Added summary data structure as well
*					as input, output files, summary and
*					rollover processing.
*	V01.4	JRR	17-Aug-1993	Cleanup usage message.
*	V01.5	JRR	04-Nov-1993	Added summary report.
*	V01.6	JRR	24-Feb-1994	Fixed bzero (again!).
*	V01.7	JRR	01-Mar-1994	Added support for SOLARIS.
*	V01.8	JRR	05-Aug-1994	Further fixes.
*	V01.9	JRR	09-Nov-1994	Fix memcpy.
*/
/*++ qacct.c - Network Queueing System
 *

 * DESCRIPTION:
 *
 *	Dump the NQS accounting file.
 *
 *
 *	Author:
 *	-------
 *	John Roman,  Monsanto Company.
 *	May 26,  1992.
 *
 *
 * STANDARDS VIOLATIONS:
 *   None.
 */

#include <errno.h>
#include "nqs.h"			/* NQS constants and data types */
#include "nqsdirs.h"			/* NQS directory structures */
#include "nqsxvars.h"			/* NQS global variables */
#include "nqsacct.h"
#include <time.h>
#include <string.h>
#include <malloc.h>
#include <stdlib.h>
/*
 * Summary report data structures
 */
struct userqstruct {
    struct userqstruct *next;	/* Next in queue */
    char user[16];		/* Username */
    char queue [MAX_QUEUENAME+1]; /* Name of queue */
    int count;			/* Number of jobs run in queue by this user */
    double cputime;		/* Total cpu time by jobs in this queue by this user */
};
struct qstruct {
    struct qstruct *next;	/* Next in queue */
    char queue [MAX_QUEUENAME+1]; /* Name of queue */
    int count;		/* Number of jobs run in queue */
    double cputime;		/* Total cpu time by jobs in this queue  */
};  

#ifndef __CEXTRACT__
#if __STDC__

static void add_sum_to_summary ( struct nqsacct_summ *arp );
static void add_term_to_summary ( struct acct_record *arp );
static void do_write_summary ( struct acct_record *arp );
static print_outstanding_req ( struct acct_record *arp, char *type );
static void print_summary ( struct nqsacct_summ *arp );
static void print_termination ( struct acct_record *arp );
static void process_rollover ( struct acct_record *arp, struct nqsacct_startup *acct_startup, int fd_acct );
static void produce_summary_report ( void );
static int q_sortrtn ( struct qstruct *a, struct qstruct *b );
static qacct_sho_version ( void );
static qacct_showhow ( void );
static void bad_syntax ( char *flag );
static void bad_value ( char *flag );
static print_partial_req ( struct acct_record *arp );
static int userq_sortrtn ( struct userqstruct *a, struct userqstruct *b );

#else /* __STDC__ */

static void add_sum_to_summary (/* struct nqsacct_summ *arp */);
static void add_term_to_summary (/* struct acct_record *arp */);
static void do_write_summary (/* struct acct_record *arp */);
static print_outstanding_req (/* struct acct_record *arp, char *type */);
static void print_summary (/* struct nqsacct_summ *arp */);
static void print_termination (/* struct acct_record *arp */);
static void process_rollover (/* struct acct_record *arp, struct nqsacct_startup *acct_startup, int fd_acct */);
static void produce_summary_report (/* void */);
static int q_sortrtn (/* struct qstruct *a, struct qstruct *b */);
static qacct_sho_version (/* void */);
static qacct_showhow (/* void */);
static void bad_syntax (/* char *flag */);
static void bad_value (/* char *flag */);
static print_partial_req (/* struct acct_record *arp */);
static int userq_sortrtn (/* struct userqstruct *a, struct userqstruct *b */);

#endif /* __STDC__ */
#endif /* __CEXTRACT__ */

#define ENDOFTIME 0x7FFFFFFF

/*
 * Global variables
 */

time_t after_time;		/* time range */
time_t before_time;		/* time range */
int write_summary;		/* Flag: true if writing summary, False o/w */
int summary_fd;			/* Summary file descriptor */
int do_rollover;		/* Flag: true if doing rollover, False o/w */
int summary_report;		/* Flag: true if doing summary report, False o/w */
int submission_time;		/* Flag: true if print submission time, False o/w */
struct userqstruct userqhead;	/* Header for userqheader */

/*** main
 *
 *  qacct [-v] [-u username] [-a after-time] [-b before-time] [-f]
 *	    [-i filename]  [-o filename] [-s] [-r]
 *
 *	    -S		Create summary user - queue useage.	
 *	    -a time	prints records completed after that time
 *	    -b time     prints records completed before that time
 *	    -f		prints everything
 *	    -i filename uses that file as input (default is NQSACCT_FILE)	
 *	    -m		prints submission time in report
 *	    -o filename Output filename for binary summary files.
 *	    -r		Rollover nqs accounting file
 *	    -s		Create summary output file	
 *	    -u username prints records for that username
 *	    -v		prints version
 */
main (int argc, char *argv[])
{
    register int i;
    int fd_acct;			/* Accounting file descriptor */
    struct nqsacct_fin acct_fin;	/* Accounting structure to report */
					/* Cpu usage                      */
    struct nqsacct_fin1 acct_fin1;	/* Accounting structure to report */
					/* Cpu usage                      */
    struct nqsacct_init acct_init;
    struct nqsacct_init1 acct_init1;	/* New style init record */
    struct nqsacct_summ  acct_summ;	/* Summary record */
    struct nqsacct_startup acct_startup;
    struct nqsacct_shutdown acct_shutdown;
    struct nqsacct acct_header;
    int status;
    struct acct_record acct_list_head;
    struct acct_record *arp;
    struct acct_record *arp_prev;
    int nqs_is_up;		       /* Logical  -- assumed status of NQS */
    struct tm *tp;
    char *whom;
    char *sourcefilename = NULL;
    char *outputfilename = NULL;
    int	full;
    char *argument;
    double cpu_time;
	
    nqs_is_up = 0;			/* Assume starting down */
    
    whom = NULL;
    full = 0;
    write_summary = 0;
    after_time = 0;
    before_time = ENDOFTIME;
    while (*++argv != NULL && **argv == '-') {
        argument = *argv;
        while (*++argument != '\0') {
            switch (*argument) {
                case 'a':               /* After time specification */
                    if (*++argv == NULL) {
                        fprintf (stderr, "Missing after-time.\n");
                        exit (-1);
                    }
                    if (after_time != (time_t) NULL) {
                        fprintf (stderr, "Multiple -a specifications.\n");
                        exit (-1);
                    }
                    switch (scnftime (*argv, &after_time)) {
                        case 0:
                            break;
                        case -1:
                             bad_syntax ("a"); /* Exit */
                        case -2:
                        case -3:
                             bad_value ("a");   /* Exit */
                        }
                    break;
                case 'b':               /* Before time specification */
                    if (*++argv == NULL) {
                        fprintf (stderr, "Missing before-time.\n");
                        exit (-1);
                    }
                    if (before_time != ENDOFTIME) {
                        fprintf (stderr, "Multiple -b specifications.\n");
                        exit (-1);
                    }
                    switch (scnftime (*argv, &before_time)) {
                        case 0:
                            break;
                        case -1:
                             bad_syntax ("b"); /* Exit */
                        case -2:
                        case -3:
                             bad_value ("b");   /* Exit */
                        }
                    break;
                case 'f':		/* Full information */
                    full++;
                    break;
                case 'i':               /* Input file name specification */
                    if (*++argv == NULL) {
                        fprintf (stderr, "Missing input filename.\n");
                        exit (-1);
                    }
                    if (sourcefilename != NULL) {
                        fprintf (stderr, "Multiple -i specifications.\n");
                        exit (-1);
                    }
                    sourcefilename = *argv;
                    break;
                case 'm':		/* Want submission time. */
                    submission_time++;
                    break;
                case 'o':               /* Output file name specification */
                    if (*++argv == NULL) {
                        fprintf (stderr, "Missing output filename.\n");
                        exit (-1);
                    }
                    if (outputfilename != NULL) {
                        fprintf (stderr, "Multiple -o specifications.\n");
                        exit (-1);
                    }
                    outputfilename = *argv;
                    break;
                case 'r':		/* Do rollover */
                    do_rollover++;
                    break;
                case 's':		/* Produce summary file */
                    write_summary++;
                    break;
                case 'S':		/* Produce summary report */
                    summary_report++;
		    memset (&userqhead, (int) NULL, sizeof (struct userqstruct) );
                    break;
                case 'u':               /* User-name specification */
                    if (*++argv == NULL) {
                        fprintf (stderr, "Missing username.\n");
                        exit (-1);
                    }
                    if (whom != NULL) {
                        fprintf (stderr, "Multiple -u specifications.\n");
                        exit (-1);
                    }
                    whom = *argv;
                    break;
                case 'v':
                    qacct_sho_version();
                    exit(0);
                    break;
                default:
                    fprintf (stderr, "Invalid option flag ");
                    fprintf (stderr, "specified.\n");
                    qacct_showhow();
                    exit (7);
            }
        }
    }
    /*
     * If they want a summary produced, I think that they should indicate
     * the name of the file with the -o switch.  If they don't,  let them
     * know that they should change their ways.
     */
    if (write_summary && outputfilename == NULL) {
	fprintf(stderr,  "No output file name for summary file indicated.\n");
	exit (1);
    }
    /*
     * On the other hand,  if they indicate they want an output file, 
     * but are not producing a summary (either by the -s or -r switch)
     * then tell them they are mistaken,  and that the -o switch only
     * works with those two.
     */
    if (outputfilename && !write_summary ) {
	fprintf(stderr, "Outputfile switch only valid in combination with");
	fprintf(stderr,  " -s or -r switches.\n");
	exit (1);
    }
    if (write_summary && summary_report ) {
	fprintf(stderr, "-s and -S are incompatable qacct switches\n");
	exit (1);	
    }
    memset ( &acct_list_head, (int) NULL, sizeof(acct_list_head) );
    if (sourcefilename != NULL)
	    fd_acct = open(sourcefilename, O_RDONLY, 0644);
    else
	    fd_acct = open(NQSACCT_FILE, O_RDONLY, 0644);
    if (fd_acct < 0) {
	perror ("Error opening NQS accounting file");
	exit(1);
    }
    if (outputfilename != NULL) {
        summary_fd = open(outputfilename, O_WRONLY|O_CREAT, 0644);
        if (summary_fd < 0) {
	    perror ("Error opening summary output file");
	    exit(1);
	}
    }
    while (1) {
        status = read (fd_acct,  &acct_header,  sizeof(acct_header));
	if (status == 0 ) break;	/* End of file encountered */
	if (status != sizeof(acct_header) ) {
	    perror ("Error reading NQS account file"); 
	    exit(1);
	}
	/*
	 * =====================================================================
	 */   
        if (acct_header.type == NQSACCT_SUMM) {
	    /*
	     * Summary records:
	     */
           if (sizeof(acct_summ) != acct_header.length) {
               fprintf(stderr, "Record length error reading SUMM record.\n");
               lseek(fd_acct, acct_header.length - sizeof(acct_header));
               continue;
           }
	    status = read (fd_acct,  &acct_summ.user,  
		    acct_header.length-sizeof(acct_header));
	    if (status == 0 ) break;	/* End of file encountered */
	    if (status != sizeof(acct_summ)-sizeof(acct_header) ) {
	        perror ("Error reading NQS account file summary record"); 
		exit(1);
	    }
	    if (acct_summ.fin_time >= after_time && 
			    acct_summ.fin_time <= before_time) { 
		if (summary_report) add_sum_to_summary(&acct_summ);
	        else print_summary(&acct_summ);
	    }
	}
	/*
	 * ==================================================================
	 */
        else if (acct_header.type == NQSACCT_FIN) {
            if (sizeof(acct_fin) != acct_header.length) {
                fprintf(stderr, "Record length error reading FIN record.\n");
                lseek(fd_acct, acct_header.length - sizeof(acct_header));
                continue;
            }
	    status = read (fd_acct,  &acct_fin.user,  
		    acct_header.length-sizeof(acct_header));
	    if (status == 0 ) break;	/* End of file encountered */
	    if (status != sizeof(acct_fin)-sizeof(acct_header) ) {
	        perror ("Error reading NQS account file"); 
		exit(1);
	    }
	    if (full) {
	         printf(" TERM request %d.%s\n",acct_fin.seqno,  
		        fmtmidname(acct_fin.orig_mid));
	    }
	    arp = acct_list_head.next;
	    arp_prev = &acct_list_head;
	    /*
	     * Search for record corresponding to this termination message.
	     */
	    while (1) {
		if (arp == NULL) {
		    if (full) {
		        printf("Found term but no init for:\n");
		        printf(" %s",  acct_fin.user);
		        printf(" request %d.%s",
				acct_fin.seqno,  
				fmtmidname(acct_fin.orig_mid));
		        printf(" on %s\n",  acct_fin.queue);
#if     HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
		        printf(" System time = %d", acct_fin.tms_stime);
		        printf(" User time = %d\n",  acct_fin.tms_utime); 
#else
#if     BSD43 | ULTRIX | DECOSF
		        printf("    System time = %d, %d\n",acct_fin.s_sec, acct_fin.s_usec);
		        printf("    User time = %d,  %d\n",acct_fin.u_sec, acct_fin.u_usec);
#else
BAD SYSTEM TYPE
#endif
#endif
		    }
		    break;
		}
		if (arp->seqno == acct_fin.seqno 
			&& arp->orig_mid == acct_fin.orig_mid) break;
		arp_prev = arp;
		arp = arp->next;
	    }
	    if (arp == NULL) continue;		    /* Did not find it */
	    if ( ( whom == NULL) || (whom != NULL && !strncmp(whom, acct_fin.user, 
			    strlen(whom))) ){
#if     HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
		arp->tms_stime = acct_fin.tms_stime;
		arp->tms_utime = acct_fin.tms_utime;
#else
#if     BSD43 | ULTRIX | DECOSF
		arp->s_sec = acct_fin1.s_sec;
	        arp->s_usec = acct_fin1.s_usec;
		arp->u_sec = acct_fin1.u_sec;
	        arp->u_usec = acct_fin1.u_usec;
#endif
#endif
		if (summary_report) add_term_to_summary(arp);
		else {
		    if (write_summary) do_write_summary(arp);
		    else print_termination(arp);
		}		 
	    }
	    arp_prev->next = arp->next;
	    free (arp); 
	/*
	 * =====================================================================
	 */   
        } else if (acct_header.type == NQSACCT_FIN_1) {
	    /*
	     * Completion records type 1:
	     *	    These have the completion time in the record.
	     */
            if (sizeof(acct_fin1) != acct_header.length) {
                fprintf(stderr, "Record length error reading FIN1 record.\n");
                lseek(fd_acct, acct_header.length - sizeof(acct_header));
                continue;
            }
	    status = read (fd_acct,  &acct_fin1.user,  
		    acct_header.length-sizeof(acct_header));
	    if (status == 0 ) break;	/* End of file encountered */
	    if (status != sizeof(acct_fin1)-sizeof(acct_header) ) {
	        perror ("Error reading NQS account file"); 
		exit(1);
	    }
	    if (full) {
		if (acct_fin1.fin_time >= after_time && 
			    acct_fin1.fin_time <= before_time) { 
	            printf(" TERM_1 request %d.%s ",acct_fin1.seqno,  
		    fmtmidname(acct_fin1.orig_mid));
		    tp = localtime(&acct_fin1.fin_time);
		    printf(" 19%d-%2.2d-%2.2d:%2.2d:%2.2d:%2.2d\n", tp->tm_year,  
			tp->tm_mon+1,  tp->tm_mday, 
			tp->tm_hour,  tp->tm_min,  tp->tm_sec);
		}
	    }
	    arp = acct_list_head.next;
	    arp_prev = &acct_list_head;
	    while (1) {
		if (arp == NULL) {
		    if (full) {
		        printf("Found term1 but no init for:\n");
		        printf(" %s",  acct_fin1.user);
		        printf(" request %d.%s",
				acct_fin1.seqno,  
				fmtmidname(acct_fin1.orig_mid));
		        printf(" on %s\n",  acct_fin1.queue);
#if     HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
		        printf(" System time = %d", acct_fin1.tms_stime);
		        printf(" User time = %d\n",  acct_fin1.tms_utime); 
#else
#if     BSD43 | ULTRIX | DECOSF
		        printf("    System time = %d, %d\n",acct_fin1.s_sec, acct_fin1.s_usec);
		        printf("    User time = %d,  %d\n",acct_fin1.u_sec, acct_fin1.u_usec);
#else
BAD SYSTEM TYPE
#endif
#endif
		    }
		    
		    break;
		}
		if (arp->seqno == acct_fin1.seqno 
			&& arp->orig_mid == acct_fin1.orig_mid) break;
		arp_prev = arp;
		arp = arp->next;
	    }
	    if (arp == NULL) continue;
	    if ( ( whom == NULL) || (whom != NULL && !strncmp(whom, acct_fin1.user, 
			    strlen(whom))) ){
		if (acct_fin1.fin_time >= after_time && 
			    acct_fin1.fin_time <= before_time) { 
#if     HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
		    arp->tms_stime = acct_fin1.tms_stime;
		    arp->tms_utime = acct_fin1.tms_utime;
#else
#if     BSD43 | ULTRIX | DECOSF
		    arp->s_sec = acct_fin1.s_sec;
		    arp->s_usec = acct_fin1.s_usec;
		    arp->u_sec = acct_fin1.u_sec;
		    arp->u_usec = acct_fin1.u_usec;
#else
BAD SYSTEM TYPE
#endif
#endif
		    arp->fin_time = acct_fin1.fin_time;
		    if (summary_report) add_term_to_summary(arp);
		    else {
			if (write_summary) do_write_summary(arp);
			else print_termination(arp);
		    }		 
		}
	    }
	    arp_prev->next = arp->next;
	    free (arp);    
	/*
	 * =====================================================================
	 */   
	} else if (acct_header.type == NQSACCT_INIT_1) {
            if (sizeof(acct_init1) != acct_header.length) {
                fprintf(stderr, "Record length error reading INIT_1 record.\n");
                lseek(fd_acct, acct_header.length - sizeof(acct_header));
                continue;
            }
	    status = read (fd_acct,  &acct_init1.user,  
		    acct_header.length-sizeof(acct_header));
	    if (status == 0 ) break;	/* End of file encountered */
	    if (status != sizeof(acct_init1)-sizeof(acct_header) ) {
	        perror ("Error reading NQS account file"); 
		exit(1);
	    }
	    arp = malloc(sizeof(acct_list_head) );
	    memset ( arp, (int) NULL, sizeof(acct_list_head) );
	    strcpy(arp->user,  acct_init1.user);
	    if (full) {
	         printf(" INIT_1 request %s (%d.%s) ", acct_init1.reqname, 
		    acct_init1.seqno,  
		    fmtmidname(acct_init1.orig_mid));
		    tp = localtime(&acct_init1.init_time);
		    printf(" 19%d-%2.2d-%2.2d:%2.2d:%2.2d:%2.2d\n", tp->tm_year,  
			tp->tm_mon+1,  tp->tm_mday, 
			tp->tm_hour,  tp->tm_min,  tp->tm_sec);
	    }
	    arp->seqno = acct_init1.seqno;
	    arp->orig_mid = acct_init1.orig_mid;
	    strcpy(arp->reqname,  acct_init1.reqname);
	    strcpy(arp->queue,  acct_init1.queue);
	    arp->sub_time = acct_init1.sub_time;
	    arp->init_time = acct_init1.init_time;
	    arp->next = acct_list_head.next;		
	    acct_list_head.next = arp;
	/*
	 * =====================================================================
	 */   
	} else if (acct_header.type == NQSACCT_INIT) {
            if (sizeof(acct_init) != acct_header.length) {
                fprintf(stderr, "Record length error reading INIT record.\n");
                lseek(fd_acct, acct_header.length - sizeof(acct_header));
                continue;
            }
	    status = read (fd_acct,  &acct_init.user,  
		    acct_header.length-sizeof(acct_header));
	    if (status == 0 ) break;	/* End of file encountered */
	    if (status != sizeof(acct_init)-sizeof(acct_header) ) {
	        perror ("Error reading NQS account file"); 
		exit(1);
	    }
	    arp = malloc(sizeof(acct_list_head) );
	    memset ( arp, (int) NULL, sizeof(acct_list_head) );
	    strcpy(arp->user,  acct_init.user);
	    if (full) {
	         printf(" INIT request %d.%s ",acct_init.seqno,  
		    fmtmidname(acct_init.orig_mid));
		    tp = localtime(&acct_init.init_time);
		    printf(" 19%d-%2.2d-%2.2d:%2.2d:%2.2d:%2.2d\n", tp->tm_year,  
			tp->tm_mon+1,  tp->tm_mday, 
			tp->tm_hour,  tp->tm_min,  tp->tm_sec);
	    }
	    arp->seqno = acct_init.seqno;
	    arp->orig_mid = acct_init.orig_mid;
	    strcpy(arp->queue,  acct_init.queue);
	    arp->sub_time = acct_init.sub_time;
	    arp->init_time = acct_init.init_time;
	    arp->next = acct_list_head.next;		
	    acct_list_head.next = arp;
	/*
	 * =====================================================================
	 */   
        } else if (acct_header.type == NQSACCT_STAT) {
	    /*
	     * Status record:
	     *	    These have the same format as TERM_1 records
	     *	    but may be followed by a TERM_1 record.
	     */
            if (sizeof(acct_fin1) != acct_header.length) {
                fprintf(stderr, "Record length error reading STAT record.\n");
                lseek(fd_acct, acct_header.length - sizeof(acct_header));
                continue;
            }
	    status = read (fd_acct,  &acct_fin1.user,  
		    acct_header.length-sizeof(acct_header));
	    if (status == 0 ) break;	/* End of file encountered */
	    if (status != sizeof(acct_fin1)-sizeof(acct_header) ) {
	        perror ("Error reading NQS account file"); 
		exit(1);
	    }
	    if (full) {
	         printf(" STAT request %d.%s ",acct_fin1.seqno,  
		    fmtmidname(acct_fin1.orig_mid));
#if     HPUX | SGI | SOLARIS  | SYS52 | IBMRS | LINUX
		 printf(" System time = %d", acct_fin1.tms_stime);
		 printf(" User time = %d",  acct_fin1.tms_utime); 
#else
#if     BSD43 | ULTRIX | DECOSF
		 printf(" System time = %d, %d",acct_fin1.s_sec, acct_fin1.s_usec);
		 printf(" User time = %d,  %d",acct_fin1.u_sec, acct_fin1.u_usec);
#else
BAD SYSTEM TYPE
#endif
#endif		    
		 tp = localtime(&acct_fin1.fin_time);
		 printf(" 19%d-%2.2d-%2.2d:%2.2d:%2.2d:%2.2d\n", tp->tm_year,  
			tp->tm_mon+1,  tp->tm_mday, 
			tp->tm_hour,  tp->tm_min,  tp->tm_sec);
	    }
	    arp = acct_list_head.next;
	    while (1) {
		if (arp == NULL) {
		    if (full) {
		        printf("Found stat but no init for:\n");
		        printf(" %s",  acct_fin1.user);
		        printf(" request (%d.%s)",
				acct_fin1.seqno,  
				fmtmidname(acct_fin1.orig_mid));
		        printf(" on %s\n",  acct_fin1.queue);
#if     HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
		        printf(" System time = %d", acct_fin1.tms_stime);
		        printf(" User time = %d\n",  acct_fin1.tms_utime); 
#else
#if     BSD43 | ULTRIX | DECOSF
		        printf("    System time = %d, %d\n",acct_fin1.s_sec, acct_fin1.s_usec);
		        printf("    User time = %d,  %d\n",acct_fin1.u_sec, acct_fin1.u_usec);
#else
BAD SYSTEM TYPE
#endif
#endif
		    }
		    break;
		}
		if (arp->seqno == acct_fin1.seqno 
			&& arp->orig_mid == acct_fin1.orig_mid) break;
		arp = arp->next;
	    }
	    if (arp == NULL) continue;
	    if ( ( whom == NULL) || (whom != NULL && !strncmp(whom, acct_fin1.user, 
			strlen(whom) )) ){
#if     HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
		    arp->tms_stime = acct_fin1.tms_stime;
		    arp->tms_utime = acct_fin1.tms_utime;
#else
#if     BSD43 | ULTRIX | DECOSF
		    arp->s_sec = acct_fin1.s_sec;
		    arp->s_usec = acct_fin1.s_usec;
		    arp->u_sec = acct_fin1.u_sec;
		    arp->u_usec = acct_fin1.u_usec;
#else
BAD SYSTEM TYPE
#endif
#endif
		    arp->fin_time = acct_fin1.fin_time;
	    }

	/*
	 * =====================================================================
	 */   
	} else if (acct_header.type == NQSACCT_STARTUP) {
	    /*
	     * These three lines are for the (possible) rollover.
	     */
	    acct_startup.h.type = NQSACCT_STARTUP;
	    acct_startup.h.length = sizeof(acct_startup);
	    acct_startup.h.jobid = 0;	    
            if (sizeof(acct_startup) != acct_header.length) {
                fprintf(stderr, "Record length error reading STARTUP record.\n");
                lseek(fd_acct, acct_header.length - sizeof(acct_header));
                continue;
            }
	    status = read (fd_acct,  &acct_startup.start_time,  
		    acct_header.length-sizeof(acct_header));
	    if (status == 0 ) break;	/* End of file encountered */
	    if (status != sizeof(acct_startup)-sizeof(acct_header) ) {
	        perror ("Error reading NQS account file"); 
		exit(1);
	    }
	    print_partial_req(&acct_list_head);
	    if (full) {
		printf ("     Startup at %s", 
		      ctime( (time_t *) &acct_startup.start_time) );
		fflush(stdout);	
	        print_outstanding_req(&acct_list_head, "Startup");
	    }	
	/*
	 * =====================================================================
	 */   
	} else if (acct_header.type == NQSACCT_SHUTDOWN) {
            if (sizeof(acct_shutdown) != acct_header.length) {
                fprintf(stderr, "Record length error reading SHUTDOWN record.\n");
                lseek(fd_acct, acct_header.length - sizeof(acct_header));
                continue;
            }
	    status = read (fd_acct,  &acct_shutdown.down_time,  
		    acct_header.length-sizeof(acct_header));
	    if (status == 0 ) break;	/* End of file encountered */
	    if (status != sizeof(acct_shutdown)-sizeof(acct_header) ) {
	        perror ("Error reading NQS account file"); 
		exit(1);
	    }
	    print_partial_req(&acct_list_head);
	    if (full) {
	         printf ("    Shutdown at %s",
			    ctime( (time_t *) &acct_shutdown.down_time) );
	         print_outstanding_req(&acct_list_head, "Shutdown");
	    }	
	/*
	 * =====================================================================
	 */   
	} else {
	    printf ("Unknown accounting record\n");
	    printf ("Type = %d\n",  acct_header.type);
	    exit (1);
	}
    }
    if (full) {
	print_outstanding_req(&acct_list_head, "Present");
    }
    if (do_rollover) process_rollover (&acct_list_head, &acct_startup, fd_acct);
    else close(fd_acct);
    if (summary_report) produce_summary_report();
}
/*** print_outstanding_req
 * 
 *
 * Print out any records that have an init,  but no stat or term.
 */
static print_outstanding_req(
	struct acct_record *arp,
	char *type)
{
    struct acct_record *sa_arp, *arp_ptr;
    int  header_printed = 0;

    sa_arp = arp->next;
    arp->next = NULL;
    while (sa_arp != NULL) {
        if (!header_printed) {
            printf("Outstanding requests at %s:\n", type);
            header_printed = 1;
        }
        printf("  Request %s %d.%s",  sa_arp->reqname,  sa_arp->seqno, 
		fmtmidname(sa_arp->orig_mid));
	printf(" %s", sa_arp->user);
        printf ("   started at %s",
                             ctime( (time_t *) &sa_arp->init_time) );
        arp_ptr = sa_arp;
        sa_arp = sa_arp->next;
        free (arp_ptr);
    }
}
/*** bad_syntax
 *
 *
 *      void bad_syntax():
 *      Display message and exit.
 */
static void bad_syntax (char *flag)
{
        fprintf (stderr, "Invalid syntax following -%s flag.\n", flag);
        exit(0);                    /* Exit */
}


/*** bad_value
 *
 *
 *      void bad_value ():
 *      Display message and exit.
 */
static void bad_value (char *flag)
{
        fprintf (stderr, "Invalid value following -%s flag.\n", flag);
        exit(0);                    /* Exit */
}
/*** print_partial_req
 * 
 *
 * Print all partial requests -- the ones for which we have an init but
 * no termination record (yet have seen a status).  Since it is a
 * startup or shutdown situation we will not see a real termination.
 */
static print_partial_req(struct acct_record *arp)
{
    struct acct_record *sa_arp, *arp_ptr, *arp_prev;
    unsigned long cpu_time;
    struct tm   *tp;
    
    arp_prev = arp;
    sa_arp = arp->next;
    /*
     * Print out all that have stats w/o terms and remove structure.
     */
    while (sa_arp != NULL) {
	if (sa_arp->fin_time != 0) {
	    if (sa_arp->fin_time >= after_time && 
			    sa_arp->fin_time <= before_time) { 
		if (summary_report) add_term_to_summary(sa_arp);
	        else {
		    if (write_summary) do_write_summary (sa_arp);
		    else print_termination(sa_arp);
		}
	    }
	}
	arp_prev->next = sa_arp->next;
 	arp_ptr = sa_arp;
        sa_arp = sa_arp->next;
        free (arp_ptr);
   }
}
/*
 * Print the termination record here.
 */
static
void print_termination (struct acct_record *arp)
{
    struct tm *tp;
    double cpu_time;

    printf("%-8.8s",  arp->user);
    printf("%6d.%-8.8s", arp->seqno,  
	fmtmidname(arp->orig_mid));
    printf(" %-9.9s",  arp->queue);
#if     HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
#if	IBMRS
    cpu_time = (double) (arp->tms_stime+arp->tms_utime);
    printf(" %9.2f",  cpu_time/100.0);
#else
    printf(" %9d", arp->tms_stime+arp->tms_utime);
#endif
#else
#if     BSD43 | ULTRIX | DECOSF
    printf(" %9d", arp->s_sec+arp->u_sec);
#else
BAD SYSTEM TYPE
#endif
#endif
    tp = localtime(&arp->init_time);
    printf(" %d-%2.2d-%2.2d:%2.2d:%2.2d:%2.2d", tp->tm_year,  
	tp->tm_mon+1,  tp->tm_mday, tp->tm_hour,  tp->tm_min,  tp->tm_sec);
    if (arp->fin_time != (time_t) 0) { 
        tp = localtime(&arp->fin_time);
        printf(" %d-%2.2d-%2.2d:%2.2d:%2.2d:%2.2d", tp->tm_year,  
	    tp->tm_mon+1,  tp->tm_mday, tp->tm_hour,  tp->tm_min,  tp->tm_sec);
    }
    if (submission_time) { 
        tp = localtime(&arp->sub_time);
        printf(" %d-%2.2d-%2.2d:%2.2d:%2.2d:%2.2d", tp->tm_year,  
	    tp->tm_mon+1,  tp->tm_mday, tp->tm_hour,  tp->tm_min,  tp->tm_sec);
    }
    printf(" %s", arp->reqname);    
    printf("\n");
}


/*
 * Print the summary record here.
 */
static
void print_summary (struct nqsacct_summ *arp)
{
    struct tm *tp;
    double cpu_time;

    printf("%-8.8s",  arp->user);
    printf("%6d.%-8.8s", arp->seqno,  
	fmtmidname(arp->orig_mid));
    printf(" %-9.9s",  arp->queue);
#if	IBMRS
    cpu_time = (double) (arp->s_time+arp->u_time);
    printf(" %9.2f",  cpu_time/100.0);
#else
    printf(" %9d", arp->s_time+arp->u_time);
#endif
    tp = localtime(&arp->init_time);
    printf(" %d-%2.2d-%2.2d:%2.2d:%2.2d:%2.2d", tp->tm_year,  
	tp->tm_mon+1,  tp->tm_mday, tp->tm_hour,  tp->tm_min,  tp->tm_sec);
    if (arp->fin_time != (time_t) 0) { 
        tp = localtime(&arp->fin_time);
        printf(" %d-%2.2d-%2.2d:%2.2d:%2.2d:%2.2d", tp->tm_year,  
	    tp->tm_mon+1,  tp->tm_mday, tp->tm_hour,  tp->tm_min,  tp->tm_sec);
    }
    if (submission_time) { 
        tp = localtime(&arp->sub_time);
        printf(" %d-%2.2d-%2.2d:%2.2d:%2.2d:%2.2d", tp->tm_year,  
	    tp->tm_mon+1,  tp->tm_mday, tp->tm_hour,  tp->tm_min,  tp->tm_sec);
    }
    printf(" %s", arp->reqname);    
    printf("\n");
}

/*
 * Write out the summary records to the file here
 */
static
void do_write_summary(struct acct_record * arp)
{
    struct nqsacct_summ  acct_summ;
    
    memset ( &acct_summ, (int) NULL, sizeof(acct_summ) );
    acct_summ.h.type = NQSACCT_SUMM;
    acct_summ.h.length = sizeof(acct_summ);
    strcpy(acct_summ.user, arp->user);
    strcpy (acct_summ.queue,  arp->queue);
    strcpy (acct_summ.reqname,  arp->reqname);
    acct_summ.priority = arp->priority;
    acct_summ.sub_time = arp->sub_time;
    acct_summ.start_time = arp->start_time;
    acct_summ.init_time = arp->init_time;
    acct_summ.fin_time = arp->fin_time;
    acct_summ.orig_mid = arp->orig_mid;
    acct_summ.seqno = arp->seqno;
#if     HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
    acct_summ.u_time = arp->tms_utime;
    acct_summ.s_time = arp->tms_stime;
#else
#if     BSD43 | ULTRIX | DECOSF
    acct_summ.u_time = arp->u_sec;
    acct_summ.s_time = arp->s_sec;
#else
BAD SYSTEM TYPE
#endif
#endif
    if (write(summary_fd, (char *)&acct_summ, sizeof(acct_summ)) == -1) {
	perror ("Error writing to summary file");  
	exit (1);
    }  
}
/*
 * Do the rollover processing.
 */
static
void process_rollover(
	struct acct_record *arp,
	struct nqsacct_startup *acct_startup,
	int fd_acct)
{

    struct acct_record *sa_arp, *arp_ptr;
    struct nqsacct_init1 acct_init1; 
    int fd_tmp;
    pid_t mypid;
    char tmp_filename[32];
    char old_filename[256];
    int status;
    
    /*
     * Close fd_acct file.
     */
    if ( close (fd_acct) == -1) {
	perror ("Error closing accounting file");
	exit (1);
    }  
    /*
     * open tmp file as /usr/adm/nqs-acct.PID
     */
    mypid = getpid( );
    sprintf (tmp_filename, "/usr/adm/qacct.%x", mypid);
    fd_tmp = open(tmp_filename, O_WRONLY|O_CREAT|O_APPEND, 0644);
    if (fd_tmp < 0) {
        perror ("Error opening temporary file");
	exit(1);
    }
    /*
     * Write out startup record, and inits for each outstanding
     * request.
     */
    status = write(fd_tmp, (char *)acct_startup, sizeof(struct nqsacct_startup) );
    if (status == -1) {
	perror ("Error writing to temporary file");  
	exit (1);
    }
    sa_arp = arp->next;
    arp->next = NULL;
    while (sa_arp != NULL) {
	/*
	 * Move to external format
	 */
	bytezero((char *)&acct_init1, sizeof(acct_init1));
        acct_init1.h.type = NQSACCT_INIT_1;
        acct_init1.h.length = sizeof(acct_init1);
        acct_init1.h.jobid = 0;
        strncpy(acct_init1.user, sa_arp->user,
                    sizeof(acct_init1.user));
        strncpy(acct_init1.queue, sa_arp->queue,
                     sizeof(acct_init1.queue));
        acct_init1.priority = sa_arp->priority;
        acct_init1.sub_time = sa_arp->sub_time;
        acct_init1.start_time = sa_arp->start_time;
        acct_init1.init_time = sa_arp->init_time;
        acct_init1.orig_mid = sa_arp->orig_mid;
        acct_init1.seqno = sa_arp->seqno;
        strncpy(acct_init1.reqname, sa_arp->reqname,
                                sizeof(acct_init1.reqname));
        status = write(fd_tmp, (char *)&acct_init1, sizeof(acct_init1)); 
	if (status == -1) {
	    perror ("Error writing to temporary file");
	    exit (1);
	}
        arp_ptr = sa_arp;
        sa_arp = sa_arp->next;
        free (arp_ptr);
    }
    /*
     * Close tmp file.
     */
    if ( close (fd_tmp) == -1) {
	perror ("Error closing temporary file");
	exit (1);
    }  
    /*
     * rename NQSACCT_FILE to NQSACCT_FILE.o
     */
    sprintf(old_filename, "%s.o", NQSACCT_FILE);
    if (rename (NQSACCT_FILE, old_filename) == -1) {
	perror ("Error renaming to old filename");
	exit (1);
    }
    /*
     * rename temporary file to NQSACCT_FILE
     */
    if (rename ( tmp_filename, NQSACCT_FILE) == -1) {
	perror ("Error renaming temporary file to accounting file");
	rename (old_filename, NQSACCT_FILE);
	exit (1);
    }
}
/*
 * Add the information to the user/usage summary.
 */
static
void add_sum_to_summary (struct nqsacct_summ *arp)
{
    double record_cputime;
    struct userqstruct *userq_p, *userq_p2;

    userq_p = userqhead.next;
    while (userq_p != (struct userqstruct *) NULL) {
	if ( !strcmp(userq_p->queue, arp->queue) && 
			!strcmp(userq_p->user, arp->user) ){
	    userq_p->count += 1;
	    record_cputime = (double) (arp->s_time+arp->u_time);
#if	IBMRS
	    record_cputime /= 100.0;
#endif
	    userq_p->cputime += record_cputime;
	    return;
	}
	if (userq_p == (struct userqstruct *) NULL) break;
	userq_p = userq_p->next;
    }
    userq_p2 = (struct userqstruct *) malloc (sizeof (struct userqstruct) );
    if (userq_p2 == NULL) {
	fprintf(stderr,  "Error getting space for userqstruct structure.\n");
	exit(1);
    }
    memset (userq_p2, (int) NULL, sizeof (struct userqstruct) );
    userq_p->next = userq_p2;
    strcpy (userq_p2->user, arp->user);
    strcpy (userq_p2->queue, arp->queue);
    userq_p2->count += 1;
	
    record_cputime = (double) (arp->s_time+arp->u_time);
#if	IBMRS
    record_cputime /= 100.0;
#endif
    userq_p2->cputime = record_cputime;

}
/*
 * Add the termination information to the user/queue summary.
 */
static
void add_term_to_summary(struct acct_record  *arp)
{
    double record_cputime;
    struct userqstruct *userq_p, *userq_p2;

    if (userqhead.next == (struct userqstruct *) NULL ) userq_p = &userqhead;
    else userq_p = userqhead.next;
    while (userq_p != (struct userqstruct *) NULL) {
	if ( !strcmp(userq_p->queue, arp->queue) && 
			!strcmp(userq_p->user, arp->user) ){
	    userq_p->count += 1;
#if     HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
	    record_cputime = (double) arp->tms_utime + arp->tms_stime;
#else
#if     BSD43 | ULTRIX | DECOSF
	    record_cputime = (double) arp->u_sec + arp->s_sec;
#else
BAD SYSTEM TYPE
#endif
#endif
#if	IBMRS
	    record_cputime /= 100.0;
#endif
	    userq_p->cputime += record_cputime;
	    return;
	}
	if (userq_p->next == (struct userqstruct *) NULL) break;
	userq_p = userq_p->next;
    }
    userq_p2 = (struct userqstruct *) malloc (sizeof (struct userqstruct) );
    if (userq_p2 == NULL) {
	fprintf(stderr,  "Error getting space for userqstruct structure.\n");
	exit(1);
    }
    memset (userq_p2, (int) NULL, sizeof (struct userqstruct) );
    userq_p->next = userq_p2;
    strcpy (userq_p2->user, arp->user);
    strcpy (userq_p2->queue, arp->queue);
    userq_p2->count = 1;
	
#if     HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
    record_cputime = (double) arp->tms_utime + arp->tms_stime;
#else
#if     BSD43 | ULTRIX | DECOSF
    record_cputime = (double) arp->u_sec + arp->s_sec;
#else
BAD SYSTEM TYPE
#endif
#endif
#if	IBMRS
    record_cputime /= 100.0;
#endif
    userq_p2->cputime = record_cputime;
}

/*
 * produce_summary_report
 * 
 * This routine takes the user/queue information and produces a summary report.
 */
static
void produce_summary_report()
{
    struct userqstruct *userq_p, *userq_p2;
    struct qstruct qhead, *q_p, *q_p2;
    struct qstruct *q_array;
    struct userqstruct *userq_array;
    int userq_count = 0;
    int q_count = 0;
    int i, userq_i;
    char *prev_user;
    int user_count = 0;
    int total_count = 0;
    double user_cputime = 0.0;
    double total_cputime = 0.0;
    char *count_format = "%6d %9.0f   ";
    
    memset (&qhead, (int) NULL, sizeof (struct qstruct) );
    userq_p = userqhead.next;
    
    printf("NQS queue usage summary -- number of jobs and cumulative Cpu seconds\n");
    printf("by user and queue\n");
    
    while (userq_p != (struct userqstruct *) NULL) {
	userq_count++;
	if (qhead.next == (struct qstruct *) NULL) q_p = &qhead;
	else q_p = qhead.next;
	while (q_p != (struct qstruct *) NULL) {
	    if (!strcmp (q_p->queue, userq_p->queue) ) {
		q_p->count += userq_p->count;
		q_p->cputime += userq_p->cputime;
		break;
	    }
	    if (q_p->next == (struct qstruct *) NULL) {
		q_p2 = malloc ( sizeof(struct qstruct) );
		if (q_p2 == (struct qstruct *) NULL) {
		    fprintf(stderr,  "Error getting space for qstructure.\n");
		    exit (1);
		}
		q_count++;
		memset (q_p2, (int) NULL, sizeof(struct qstruct) );
		q_p->next = q_p2;
		strcpy (q_p2->queue, userq_p->queue);
		q_p2->count = userq_p->count;
		q_p2->cputime = userq_p->cputime;
		break;
	    }
	    q_p = q_p->next;
	}
	userq_p = userq_p->next;
    }
    
    q_array = (struct qstruct *) malloc (q_count * sizeof (struct qstruct) );
    if (q_array == (struct qstruct *) NULL) {
	fprintf(stderr,  "Error getting space for q_array.\n");
	exit (1);
    }
    memset (q_array, (int) NULL, q_count * sizeof (struct qstruct));
    q_p2 = q_array;
    q_p = qhead.next;
    while (q_p != (struct qstruct *) NULL ) {
        memcpy (q_p2, q_p, sizeof (struct qstruct) );
	q_p2++;
	q_p = q_p->next;
    }
    qsort (q_array,  q_count,  sizeof (struct qstruct), (void *)q_sortrtn);
    userq_array = (struct userqstruct *) malloc (userq_count * sizeof (struct userqstruct) );
    if (userq_array == (struct userqstruct *) NULL) {
	fprintf(stderr,  "Error getting space for userq_array.\n");
	exit (1);
    }
    memset (userq_array, (int) NULL, userq_count * sizeof (struct userqstruct));
    userq_p2 = userq_array;
    userq_p = userqhead.next;
    while (userq_p != (struct userqstruct *) NULL ) {
        memcpy (userq_p2, userq_p, sizeof (struct userqstruct) );
	userq_p2++;
	userq_p = userq_p->next;
    }
    qsort (userq_array,  userq_count,  sizeof (struct userqstruct), 
				(void *)userq_sortrtn);
    printf ("\n%9s", "");
    q_p = q_array;
    for (i = 0; i < q_count; i++ ){
	printf ("%17s  ", q_p->queue);
	q_p++;
    }
    printf ("%17s\n",  "Total for user");
    printf ("%-10s", "Username");
    for (i = 0; i < q_count+1; i++ ){
	printf ("%6s %9s   ", "Count", "Cputime");
    }
    userq_p = userq_array;
    prev_user = userq_p->user;
    printf ("\n%-10s", userq_p->user);
    userq_i = 0;
    while (userq_i < userq_count) {
	q_p = q_array;
	for (i = 0; i < q_count; i++ ){
	    if ( strcmp (prev_user, userq_p->user)) {
		if (q_p != q_array) {
		    for ( ; i < q_count; i++) {
			printf (count_format, 0,  0.0); 		    
		    }
		}
		break;
	    }
	    if ( strcmp (q_p->queue, userq_p->queue)) {
		printf (count_format, 0,  0.0); 
	    } else {
		printf (count_format, userq_p->count, userq_p->cputime);
		user_count += userq_p->count;
		user_cputime += userq_p->cputime;
		userq_p++;
		userq_i++;		
	    }
	    q_p++;
	}
	prev_user = userq_p->user;
	printf (count_format, user_count, user_cputime);
	user_count = 0;
	user_cputime = 0.0;
	printf ("\n%-10s", userq_p->user);	
    }
    /*  printf (count_format, user_count, user_cputime); */
    printf ("\n%-10s", "Totals");
    q_p = q_array;
    for (i = 0; i < q_count; i++ ){
	printf (count_format, q_p->count, q_p->cputime);
	total_count += q_p->count;
	total_cputime += q_p->cputime;
	q_p++;
    }

    printf (count_format, total_count, total_cputime);
    printf ("\n");
}

static
int q_sortrtn(struct qstruct *a, struct qstruct *b)
{
    return (strcmp (a->queue, b->queue) );
}
static
int userq_sortrtn(struct userqstruct *a, struct userqstruct *b)
{
    if ( !strcmp(a->user, b->user)) return (strcmp (a->queue, b->queue) );
    else return (strcmp (a->user, b->user) );
}
/*** qacct_show_version:
 *
 * Print the current version of qacct.
 */
static qacct_sho_version()
{
     fprintf(stderr, "NQS version is %s\n", NQS_VERSION);
}
/*** qacct_showhow:
 *
 * Print the qacct syntax.
 */
static qacct_showhow()
{
fprintf (stderr, "qacct -- print NQS accounting information\n\n");
fprintf (stderr, "usage:    qacct [-a after-time ] [-b since-time] [-f] [-i inputfile] \\ \n");
fprintf (stderr, "          [-o outputfile] [-r] [-s] [-u username] [-v] \n");
fprintf (stderr, " -S              creates a summary report\n");
fprintf (stderr, " -a after-time   selects records finishing since time\n");
fprintf (stderr, " -b since-time   selects records finishing before time\n");
fprintf (stderr, " -f              full dump of all records\n");
fprintf (stderr, " -i inputfile    indicates location of input file\n");
fprintf (stderr, "                        default is /usr/adm/nqs\n");
fprintf (stderr, " -m              prints submission time in report\n");
fprintf (stderr, " -o outputfile   indicates where summary file is to be written\n");
fprintf (stderr, " -r              accounting file is rolled over\n");
fprintf (stderr, " -s              creates a summary file\n");
fprintf (stderr, " -u username     selects only records of that user\n");
fprintf (stderr, " -v              prints version information\n");
exit (0);
}
