/*static char *SCCSID = "@(#)pset.c	6.1 90/11/17";*/
/*  SCCSID = @(#)pset.c 13.4 89/07/18 */

/*  pset.c - change profile settings
*
*   AUTHOR:	Benjamin W. Slivka
*
*   USAGE:	See Usage()
*
*   HISTORY:	10-May-1987 bws Initial version
*		11-May-1987 bws Improved help
*		13-May-1987 bws Fixed crc code
*		26-Oct-1987 pwu Changed pset init to accept module list
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "casts.h"
#include "profile.h"

/*  Check return code */
#define	    chk(a,msg)	doChk ((a),msg,__FILE__,__LINE__)

#define MAXNLEN 9	    /* max length for a null terminated module name */
#define MAXLIST 50*MAXNLEN  /* buffer size for at least 50 module names */
#define MAXSLEN 200	    /* longest string allowed in module file */

/* local procedures */
char *addname(char *szDest, char *szName, char *szpchEnd);
int psetclear(int c, char *v[]);
int psetdump(int c, char *v[]);
int psetfree(int c, char *v[]);
int psetinit(int c, char *v[]);
int psetoff(int c, char *v[]);
int pseton(int c, char *v[]);

/* built-in system module name list */
char *rgszModule[] = {
  "ansicall",
  "bkscalls",
  "bvscalls",
  "doscall1",
  "kbdcalls",
  "moucalls",
  "msg",
  "nls",
  "quecalls",
  "sesmgr",
  "viocalls",
};

#define cModNames (sizeof(rgszModule)/sizeof(char *))

doChk (rc,msg,file,line)
int	rc;
char   *msg;
char   *file;
int	line;
{
    if (rc != 0) {
	printf ("%s:(%d) %s failed with rc=%d\n", file, line, msg, rc);
	exit (rc);
    }
}

int	argc;
char  **argv;

/* dispatch table */

typedef struct {
    char   *sf_szName;	/* argument keyword */
    int (*sf_pfunc)();	/* function */
} SF;

SF rgsf[] = {
    {"clear" ,psetclear },
    {"dump"  ,psetdump	},
    {"free"  ,psetfree	},
    {"init"  ,psetinit	},
    {"on"    ,pseton	},
    {"off"   ,psetoff	},
};

#define csf (sizeof(rgsf)/sizeof(SF))

main (c,v)
int	c;
char   *v[];
{
    int	   iSubFunc;

    argc = c;
    argv = v;

    if (c == 1)
	Usage ("", "");

    /* dispatch sub-function */
    for (iSubFunc=0; iSubFunc < csf; iSubFunc++) {
	if (stricmp(v[1], rgsf[iSubFunc].sf_szName) == 0)
	    break;  /* found */
    }
    if (iSubFunc < csf) {  /* found */
	rgsf[iSubFunc].sf_pfunc(c-1,&v[1]);
    } else {  /* invalid sub-function */
	Usage ("invalid parameter", v[1]);
    }
}

/***	psetinit - process pset init command line
*
*	Initialize profiling data area (counters) for system profiling.
*	usage:	init [-i] [-f <file>]
*		-i (optional) means not to use the built-in system
*		   module name list;
*		-f <file> (optional) means add the module names from
*		   <file>. The file format is
*		   <name1> <space or new line>
*		   <name2> <space or new line> ...
*
*	ENTRY:	c - arguement count starting with the "init" key word
*		v - argv starting from the "init" key word
*
*	EXIT:	return to caller = success
*		abort program = failed
*/

psetinit(c,v)
int c;
char *v[];
{
  int iArg;			/* current command line arg */
  char modlist[MAXLIST];	/* module name list */
  char *pchTail;		/* point to next free byte in modlist */
  char *pchEnd;			/* end of modlist */
  int fIgnore;			/* flag: ignore built-in module list */
  int fModFile;			/* flag: use module list file */
  int fDosProfile = PT_SYSTEM|PT_USEKP;

  int i;			/* loop index */
  int rc;
  FILE *fpModule;		/* file pointer for module list file */
  char *szModFile;		/* module list file name */
  char szBuf[MAXSLEN];		/* to hold a string temporarily */

  iArg = 1;			/* first arg after "INIT" */
  pchTail = modlist;		/* modlist is empty */
  pchEnd = modlist + MAXLIST;	/* pchEnd->last usable byte in modlist */

  fIgnore = FALSE;		/* default = use built-in name list */
  fModFile = FALSE;		/* default = no module list file */
  szModFile = NULL;

  while (iArg < c) {

    if (v[iArg][0] == '-') {  /* process flags */

      switch (v[iArg][1]) {

	case 'i':
	case 'I':
	  fIgnore = TRUE;  /* ignore built-in module name list */
	  break;

	case 'f':
	case 'F':
	  fModFile = TRUE;  /* use module list file */
	  break;

	case 'v':
	case 'V':
	  fDosProfile |= PT_VERBOSE;  /* do detail kernel profiling */
	  break;

	case 'd':
	case 'D':
	  fDosProfile |= PT_USEDD;    /* do device profiling */
	  fDosProfile &= ~PT_USEKP;   /* don't do kernel profiling */
	  break;

	case 'k':
	case 'K':
	  fDosProfile |= PT_USEKP;    /* do kernel profiling */
	  fDosProfile &= ~PT_USEDD;   /* don't do device profiling */
	  break;

	case 'b':
	case 'B':
	  fDosProfile |= PT_USEKP|PT_USEDD; /* do both */
	  break;

	default:
	  Usage("invalid flag", v[iArg]);
      }
    } else
      if (fModFile)
	szModFile = v[iArg];  /* module list file name */
      else
	Usage("invalid parameter",v[iArg]);

    iArg++;
  }

  /* add built-in module name list to modlist */
  if (fIgnore == FALSE) {
    for (i=0; i < cModNames; i++) {
      pchTail = addname(pchTail,rgszModule[i],pchEnd);	/* add name to module list */
    }
  }

  if (fModFile) {  /* use module list file */
    if (szModFile != NULL) {   /* file name given */

      /* open module list file */
      fpModule = fopen(szModFile, "r");
      if (fpModule == NULL) {
	Fatal("cannot open",szModFile);
      }

      /* read one string at a time */
      rc = fscanf(fpModule,"%s",szBuf);
      while (rc == 1) {
	pchTail = addname(pchTail,szBuf,pchEnd);  /* add string to modlist */
	rc = fscanf(fpModule,"%s",szBuf);
      }

      fclose(fpModule);
    } else
      Usage("missing module list file name","");
  }

  addname(pchTail,"",pchEnd);  /* terminate name list with a null string */
  chk( PROFINIT(fDosProfile, (FPC)modlist), "ProfInit");
}

/***	addname - copy a string
*
*	Also perform pchEnd check to make sure the copy doesn't exceed the
*	destination's pchEnd.
*
*	ENTRY:	dest  = destination to copy string to
*		name  = string to copy
*		pchEnd = last usable byte of dest
*
*	EXIT:	addname = next available space in dest
*/

char *
addname(dest,name,pchEnd)
char *dest;	/* where to add the name to */
char *name;	/* name to add */
char *pchEnd;	 /* pchEnd of dest */
{
  int cszName;

  cszName = strlen(name) + 1;
  if (dest + cszName > pchEnd) {
    Fatal("Too many module names. Recompile with larger MAXLIST","");
  }
  strcpy(dest,name);
  return dest + cszName;
}

/*	psetclear - clear profile counters
*
*	ENTRY:	c - arguement count starting with the "clear" key word
*		v - argv starting from the "clear" key word
*
*	EXIT:	return to caller = success
*		abort program = failed
*/
psetclear(c,v)
int c;
char *v[];
{
  checkparm(c,1);
  chk (PROFCLEAR(PT_SYSTEM), "ProfClear");
}

/*	pseton - turn on profiler
*
*	start the timer tick and also set whether or not to use
*	the profile device driver
*
*	ENTRY:	c - arguement count starting with the "on" key word
*		v - argv starting from the "on" key word
*
*	EXIT:	return to caller = success
*		abort program = failed
*/
pseton(c,v)
int c;
char *v[];
{
  checkparm(c,1);
  chk (PROFON(PT_SYSTEM), "ProfOn");
}

/*	psetoff - turn off profiler
*
*	ENTRY:	c - arguement count starting with the "off" key word
*		v - argv starting from the "off" key word
*
*	EXIT:	return to caller = success
*		abort program = failed
*/
psetoff(c,v)
int c;
char *v[];
{
  checkparm(c,1);
  chk (PROFOFF(PT_SYSTEM), "ProfOff");
}

/*	psetfree - release profiler buffers
*
*	ENTRY:	c - arguement count starting with the "free" key word
*		v - argv starting from the "free" key word
*
*	EXIT:	return to caller = success
*		abort program = failed
*/
psetfree(c,v)
int c;
char *v[];
{
  checkparm(c,1);
  chk (PROFFREE(PT_SYSTEM), "ProfFree");
}

/*	psetdump - dump profiler buffers to user
*
*	ENTRY:	c - arguement count starting with the "dump" key word
*		v - argv starting from the "dump" key word
*
*	EXIT:	return to caller = success
*		abort program = failed
*/
psetdump(c,v)
int c;
char *v[];
{
  checkparm(c,2);
  chk (PROFDUMP(PT_SYSTEM, (FPC)v[1]), "ProfDump");
}

checkparm(c,num)
int c;	/* number of parameters to be checked */
int num;  /* number of parameters expected */
{
  if (c != num) {
    Usage("Incorrect number of parameters","");
  }
}

IssueMessage(fExit,msg,parm)
char *msg;
char *parm;
{
    if (strlen(parm) > 0)
	printf ("%s: %s -> %s\n", argv[0], msg, parm);
    else
	printf ("%s: %s\n", argv[0], msg);
    if (fExit)
	exit(2);
}


/*  Print error message and exit */
Fatal (msg, parm)
char *msg;
char *parm;
{
    IssueMessage (TRUE,msg,parm);
}


/*  Display usage, then issue message and exit */
Usage (msg, parm)
char *msg;
char *parm;
{
    IssueMessage (FALSE,msg,parm);
    printf ("USAGE: %s INIT [-ivdkb] [-f file] | CLEAR | ON | OFF | DUMP file | FREE\n",argv[0]);
    printf ("   CLEAR                reset counts to zero\n");
    printf ("   DUMP file            dump profile data to <file>\n");
    printf ("   FREE                 deallocate system profiling data space\n");
    printf ("   INIT [-ivdkb] [-f file]  allocate system profiling data space\n");
    printf ("     -i      = ignore built-in system module list\n");
    printf ("     -v      = do detail kernel profiling\n");
    printf ("     -d      = do only device profiling\n");
    printf ("     -k      = do only kernel profiling\n");
    printf ("     -b      = do both kernel and device profiling\n");
    printf ("     -f file = read module names from file\n");
    printf ("               file contains module names (case insensitive)\n");
    printf ("               separated by spaces or line breaks\n");
    printf ("   OFF                  turn off profiling\n");
    printf ("   ON                   turn on profiling\n");

    exit(1);
}
