/*************************************************************
 *   Configuration program for Metal version 1.20a
 *	and HMH.H version 1.10c
 *
 *	FILE: MECONFIG.C version 1.20a
 *
 *		   Copyright (c) 1984 Tim Gary
 *			Delphi Data Systems
 *   			All rights reserved.
 *
 *
 *  This is the configuration program which enables you to
 * setup the message system without recompiling.
 *
 *************************************************************
 *
 * 1.20a 11/04/84  CCS stuff deleted.
 * 1.10d 10/21/84  Sysop pass bug fixed.
 * 1.10c 10/18/84  New config format (from hmh.h)...
 * 1.10b 10/04/84  Cosmetic changes for new version of Metal.
 * 1.01a  7/15/84  Fixed minor bug in menu routine.
 * 1.01a  7/10/84  Fixed some bugs introduced in 1.06 changes.
 * 1.01a  7/02/84  Multi_User Id location added as option.
 * 1.01a  6/10/84  Aztec c 1.06 changes made.  Added CCS Clock
 *		  stuff.  Got rid of
 *		  all the USER/DRIVE stuff, since files now 
 *		  include that in their name.
 * 1.0c	  5/17/84  Hayes clock support added.
 * 1.0b   4/18/84  Added default user status in 'usertypes'.
 * 1.0b   4/14/84  (continue fixing things)
 * 1.0b	  4/13/84  Extensive changes to internal format.  Menu
 *		   changes, addition of message parm options,
 *		   and save configuration function.  The save-
 *		   perm function has been made general, and
 *		   is grouped under one command.
 * 1.0a   2/07/84  Cosmetic and functional additions/fixes.
 * 1.0    1/22/84  Provide for pre-release version 3.0a changes.
 *		   First working version of this !!!
 *
 *************************************************************/

#define MAIN

#include "xpm.h"	/* cpmio header file		*/
#include "hmh.h"	/* METAL bbs header file	*/
#include "hmconfg.h"	/* config/options defines	*/
#include "ctype.h"

#define TITLE  "Metal Configuration, Version 1.20a\nCopyright 1984 Tim Gary  Delphi Data Systems\n\n"

#define	CHAR	1	/* flag to return a 1 byte number	*/
#define	INT	2	/* flag to return an int (2 bytes)	*/
#define	HEX	4	/* flag to make it a hex number		*/

/* new menu function declarations.. */

struct menutype	{
	char *itemdesc;		/* description string  */
	int  (*func)();		/* pointer to function */
	};

int finishup(),usertypes(),privatesys(),mfiles(),maxtry(),clock(),
    byestuff(),zcprstuff(),location(),printer(),fiveinch(),statline(),
    sysopstuff(),getconf(),savem(),msgstuff(),saveconf();

struct menutype main_menu[] = {
  "Return to operating system (optionally save configuration file)",finishup,
  "Edit user types",usertypes,
  "Private/Public system setup",privatesys,
  "Files: Names and locations",mfiles,
  "Maximum tries user gets before being logged out",maxtry,
  "Real time clock setup",clock,
  "BYE parameters",byestuff,
  "Operating system setup (ZCPR)",zcprstuff,
  "Location of system (Sign-on message)",location,
  "Printer log option",printer,
  "Five inch drive timer reset",fiveinch,
  "25th status line setup",statline,
  "Sysop name and password",sysopstuff,
  "Message-base options (max messages, etc..)",msgstuff,
  "Save current configuration in configuration file for later use",saveconf,
  "Recall a previously saved configuration file",getconf,
  "Permanently save current configuration in METAL or MECONFIG",
	savem,
  0,0	/* mark end of menu */
};


/*********************
 * Main program loop *
 *********************/

main()
{
char *menu();
int (*fn)();
send(TITLE);
height=0;	/* no screen pause */

getconf();	/* see if he has a config file to use, and get it if so */
while (1) 
	{
	send("\n-=<(| Configuration Menu |)>=-\n");
	fn=menu(main_menu);	/* endless loop */
	(*fn)();
	}

} /* main program loop */


/***********************************************************************/


/******************************************************************
 * display menu, and get response. Return pointer to the function *
 ******************************************************************/

char *menu(menu_table)		/* really returns pointer to function, but
				   how the hell do you declare that? */
 struct menutype *menu_table;
{
struct menutype *mp;
register int i;
char n;

n=255;	/* so that RETURN will look like invalid entry */

/* for (items=0,mp=menu_table; mp->itemdesc; ++items,++mp); */ /* get items count */

do {
  for (i=0,mp=menu_table; mp->itemdesc; i++,++mp)
	{
	sprintf(buffer,"\n%2d. %s.",i+1,mp->itemdesc);
	if (send(buffer)==ERROR) break;	/* so you can cheat if you know the item number already */
	}
   asknum("\n\nSelect",&n,0,CHAR);
   } while (n>i  /*  tems  */   || n==0);
for (mp=menu_table; n-1; --n,++mp);

return mp->func;
}


/*********************************************
 * Get file/drive/user seting the user and   *
 * drive up for an open, and returning a     *
 * string pointer to the filename.	     *
 *********************************************/

char *getfile(str)	/* prints prompt string 'str' */
 char *str;
{
static char fname[18];		/* static so value don't go away on return! */

ask(str,fname,17,UP);	/* ask for file name */
if (*fname=='\0') return NULL;	/* signal nothing entered */

return fname;
}


/********************************
 * get config file from disk if *
 * he has one to get.. else all *
 * defaults are used ...        *
 ********************************/

getconf()
{
FILE *config;	/* config file */
char *addr;	/* address pointer to variable block */
char *fn;	/* file name */

while (1) {
	if ((fn=getfile("Configuration file name? "))==NULL) break;
	if ((*fn=='?') || (!ustrcmp("help",fn)))	/* help wanted */
		{
		send("If you have a configuration file saved from running this program\n");
		send("previously, you may retrieve it here.\n");
		}
	config=open(fn,F_RD | F_UNLOCK);  /* open for read */
	if (config==NULL)
		{               /* error, file not found */
		send("Can't find that file.\n");
		continue;       /* ask again */
		}

	uread(config,O.zippo,O.zappo-O.zippo);	/* unix style read.. */

	send("Configuration file loaded.\n");
	close(config);
	break;	/* and exit */
	}	/* endless while loop */
} /* get config */


/*************************************
 * Finish up by saving config. file  *
 * and possibly making changes to the*
 * metal and menter files themselves *
 *************************************/

finishup()
{
while (1) {
	ask("\nSave current configuration in seperate configuration file? ",buffer,2,UP);
	if ( (*buffer=='\0') || (*buffer=='?') || !ustrcmp("help",buffer))
		{
		send("\nYou may save the current configuration in a file that may be read\n at a later time with MECONFIG.  This saves reconfiguring several\n options over again.\n");
		continue;
		}
	if (*buffer!='Y') break;        /* nope, so forget it */
		else { saveconf();	/* save configuration file */
		       break; }
	}
send("\nEntering CP/M.");
exit();
}


/**************************************
 * save current configuration in file *
 * that getconf() can read later on.. *
 **************************************/

saveconf()
{
register FILE *mfile;           /* metal/menter file descriptor */
register char *addr;            /* address used to write config */
char *fn;

while (1) {	/* seemingly endless loop */
	if ((fn=getfile("File name to save configuration under? "))==0) break;
	mfile=open(fn,F_RW | F_LOCK);   /* open for write */
	if (mfile==NULL)
		{		/* couldn't open file */
		send("Unable to open file, invalid drive, disk full, or write protected. Try again.\n\n");
		continue;       /* ask filename again */
		}
	send("Saving.\n");
	for (addr=O.zippo; addr<=O.zappo+128; addr+=128)
		{
               movmem(addr,bufloc(mfile),128);
               write(mfile,1);         /* write/increment pos */
               }
       close(mfile);
       break;
       }       /* endless while */
}


/*********************************************
 * Save config in metal or menter, or most   *
 * any other file by that matter............ *
 *********************************************/

savem()
{
FILE *fil;
char *addr,*addr2;
char *fn;

do {
	if ((fn=getfile("Save configuration permanently in which METAL Message system file? "))==NULL) break;
	if (*fn=='?' || !ustrcmp("help",fn))
		{	/* help person out */
		send("\nTo make the options you have changed permanent (operational),\n\
you must save the configuration in the appropriate COM files.\n");
		send("METAL.COM and MECONFIG.COM or any\n\
other compiled Aztec C program which includes HMH.H (source vers.)\nare valid files.\n");
		continue;
		} 
	if (!index(fn,'.')) strcat(fn,".COM");
	fil=open(fn,F_RW | F_LOCK);	/* open for r/w */

	send("[Searching...]\n");
	toeof(fil);		/* go to end of file */
	if (getrec(fil)==0 || fil==NULL) {
			send("\n<<File not found...>>\n");
			close(fil);
			unlink(fn);	/* cleanup open's mess */
			return;
			}
	setrrec(fil,-1);
	read(fil,0);
	movmem(bufloc(fil),buffer,128);
    do	{
	setrrec(fil,-1);
	movmem(buffer,buffer+128,128);
	read(fil,0);
	movmem(bufloc(fil),buffer,128);

	printf("Record %d\r",getrec(fil));

	for (addr=buffer; addr<buffer+250; addr++)
               if (!strncmp(O.zippo,addr,10)) break;
	if (!strcmp(O.zippo,addr)) break;

	} while (getrec(fil)>0);

if (getrec(fil)<=0) { send("\nNothing found to replace..\n"); break; }

	send("\nFound option area, saving.");
/* we have the address in the buffer of zilch */

if (addr>=buffer+128)
	{
	addr2=(buffer+256)-addr;
	movmem(O.zippo,bufloc(fil)+(addr-(buffer+128)),addr2);
	setrrec(fil,-1);
	}
  else	{
	addr2=(buffer+128)-addr;
	movmem(O.zippo,bufloc(fil)+(addr-buffer),addr2);
	}

	write(fil,1);           /* write portion of record+incr. */
	putchar('.');
	for (addr=O.zippo+(int)addr2; addr+128<O.zappo; addr+=128)
        	{
        	movmem(addr,bufloc(fil),128);
        	write(fil,1);   /* write/incr */
        	}

	putchar('.');
	read(fil,0);	/* get background of buffer */
	movmem(addr,bufloc(fil),(O.zappo-addr)+1);	/* put rest of O in */
	write(fil,0);	/* write it	*/
	close(fil);     /* boy, that was easy (well, not really) */
	send("<done>\n");

       } while (0); /* one-shot loop */
} /* savem */


/********************************
 * Get number (as a char) 0-255 *
 ********************************/

asknum(str,nptr,max,flag)
 register char *str,flag;
 int *nptr;
 register int max;
{
char ts[10];
char *cptr;
int tint;	/* temp int */

cptr=nptr;	/* make a char pointer to the same thing as int */

sprintf(buffer,"%s? ",str);

do	{
	ask(buffer,ts,10,UP);
	if ( !(flag&HEX) )
		{
		if (isdigit(*ts))
			{
			if (max!=0)
				if (atoi(ts)>max)
					{
					printf("Maximum value is %d.\n",max);
					continue;
					}
			return (flag&INT ? (*nptr=atoi(ts)) : (*cptr=atoi(ts)));
			}
		}
		else	/* wants a hex char */
		    {	if (*ts=='\0') break;
			sscanf(ts,"%x",&tint);
			if (max!=0) if (tint>max)
					{
					printf("Maximum value is %xH.\n",max);
					continue;
					}
			return (flag&INT ? (*nptr=tint) : (*cptr=tint));
		    }
	}
	while (*ts!='\0');

return (*nptr);
}


/*********************
 * Get string, etc.. *
 *********************/

askstr(outstr,str,max)
 register char *outstr;
 char *str,max;
{
char ts[MAXLINE+1];

sprintf(ts,"%s? ",outstr);

ask(ts,ts,max,UPLOW);
	
if (*ts!='\0') strcpy(str,ts);
}


/***********************
 * Get yes/no question *
 ***********************/

askyesno(outstr,yn)
 register char *outstr;
 char *yn;
{
char ts[2];

sprintf(buffer,"%s (YES/NO)? ",outstr);

do	{
	ask(buffer,ts,1,UP);
	if (*ts=='\0') break;
	if (*ts=='Y') { *yn=YES;  break; }
	if (*ts=='N') { *yn=NO;   break; }
	} while (1);	/* crumby way to do this */
}


/*****************************
 * Edit avaliable user types *
 *****************************/

usertypes()
{
u_types *uptr;	/* pointer to user structure */

send("Alter User types and parameters.\n\n");

printf("Default user status='%c'\n\n",O.DSTATUS);
ask("New default status character (see doc) ? ",buffer,1,UPLOW);
if ( (*buffer!='\0') && (index("+snxXabc",*buffer)) ) O.DSTATUS=*buffer;

for (uptr=O.user_types; uptr->type; uptr++)
	{
	printf("\nUser Char\t Maxuser\t Timeout\t Kill Msgs?\n    '%c'\t\t   %d\
\t\t %d mins. \t     %s\n\nMaster CPM privs?\t Read priv. msgs?\t Post Messages?\n\
      %s     \t\t       %s     \t\t      %s\n\n",
		uptr->type,uptr->maxuser,uptr->timeout,
		uptr->killflag==YES ? "YES" : "NO",
		uptr->zcprflag==YES ? "YES" : "NO",
		uptr->readpriv==YES ? "YES" : "NO",
		uptr->postmsg==YES ? "YES" : "NO");
	ask("Alter this user? ",buffer,2,UP);
	if (*buffer=='Y')
		{
		asknum("Max CP/M user number (0-15)",&uptr->maxuser,15,CHAR);
		asknum("Timeout (minutes)",&uptr->timeout,0,CHAR);
		askyesno("Can this user kill msgs",&uptr->killflag);
		askyesno("Does the user have MASTER CP/M Privs",
			&uptr->zcprflag);
		askyesno("Can the user read private msgs",&uptr->readpriv);
		askyesno("Can the user post messages",&uptr->postmsg);
		}
	} /* for */

}	/* user types */


/************************
 * Private system stuff *
 ************************/

privatesys()
{
send("Make system PRIVATE/PUBLIC.\n\n");

printf("System is now %s.  Password is %s (applies only if PRIVATE system).\n",
	O.PRIVATE ? "PRIVATE" : "PUBLIC",O.PRIVPASS);

askyesno("Make system Private",&O.PRIVATE);
if (O.PRIVATE) askstr("System Password",O.PRIVPASS,PASSLEN);
upcase(O.PRIVPASS);
}


/*********************************
 * Alter file names/drives/users *
 *********************************/

mfiles()
{
register int ind;

send("Alter filenames of BBS files.\n\n");

for (ind=0; O.files[ind][0]!='\0'; ind++)
	{
	sprintf(buffer,"\nFile %s :: new name (<return> if no change) ",
		O.files[ind]);
	askstr(buffer,O.files[ind],17);
	}
}


/***********************************
 * Get max # of tries till hang-up *
 ***********************************/

maxtry()
{
printf("Maximum tries before logout=%d.\n",O.MAXTRIES);

asknum("New value (0-255)",&O.MAXTRIES,255,CHAR);
}


/**********************
 * Change clock setup *
 **********************/

clock()
{

send("Change clock setup.\n\n");

printf("Currently #%d selected.\n",O.RTC);
send("0) NO Clock (or incompatible one)\n1) CompuPro SS1 Clock\n2) Hayes Chronograph\n");
/* 3) CCS Clock board\n"); */

send("Note: a port address>255 will enable memory mapped I/O\n");

asknum("\nNew setup",&O.RTC,3,CHAR);

if (O.RTC!=NOCLOCK)
	{
	printf("%xH is the clock comand/status port, %xH is data.\n",O.CLKCMD,O.CLKDATA);
	if (O.RTC!=COMPUPRO /* && O.RTC!=CCS */ )
		printf("%xH is the output status mask, %xH is the input status mask\n",(unsigned)O.CLKOMASK,(unsigned)O.CLKIMASK);
	asknum("New command port (IN HEX!)",&O.CLKCMD,0,HEX|INT);
	asknum("New Data port (IN HEX!)",&O.CLKDATA,0,HEX|INT);
	if (O.RTC!=COMPUPRO /* && O.RTC!=CCS */ )
		{
		asknum("New output status mask (IN HEX!)",&O.CLKOMASK,0,HEX|CHAR);
		asknum("New input status mask (IN HEX!)",&O.CLKIMASK,0,HEX|CHAR);
		}
	}
}


/**************************************
 * BYE stuff... if ask for CaSe/NULLS *
 **************************************/

byestuff()
{

send("Setup for BYE.\n\n");

printf("Current setting:\n\tBYE %s ask for NULLS.\n",
		O.ASKNULLS ? "DOES" : "DOES NOT" );

askyesno("Is BYE set to ask for NULLS when you login",&O.ASKNULLS);

}


/*********************************
 * ZCPR setup, WHEEL LOC/No ZCPR *
 *********************************/

zcprstuff()
{

send("Operating System setup.\n\n");

#ifndef MULTI_USER

printf("Currently setup%s to use ZCPR.\n",O.ZCPR ? "" : " NOT");

askyesno("Is ZCPR to be used",&O.ZCPR);

if (O.ZCPR)
	{
	printf("Wheel byte currently at %xH\n",O.SECURELOC);
	asknum("New location (IN HEX!)",&O.SECURELOC,0,HEX|INT);
	}


#else

#ifndef MPM
  printf("Location of 4 char id string currently at %xH\n",O.id_loc);
  asknum("New location (IN HEX!)",&O.id_loc);
#endif

#endif
}


/************************************
 * Location change.. up to 80 chars *
 ************************************/

location()
{

send("Change Metal site location (sign-on msg).\n\nCurrently is: ");

send(O.WHERE); send("\n");

askstr("Enter new location/phone (up to 80 chars)",O.WHERE,80);
}


/*****************
 * Printer log ? *
 *****************/

printer()
{
send("Change printer log option.\n\n");
printf("Printer log is %s.\n",O.PRTLOG ? "ON" : "OFF");
askyesno("Log callers/comments to printer",&O.PRTLOG);
}


/************************
 * Fiveinch drive thing *
 ************************/

fiveinch()
{
send("Set peek location for slow 5 inch drives.\n\n");
printf("Current location is %xH\n",O.RESETDRIVE);
asknum("New location (IN HEX!)? ",&O.RESETDRIVE,0,HEX|INT);
}


/**************************
 * 25th status line stuff *
 **************************/

statline()
{
register int n;		/* temp loop variable */
char tbuf[MAXLINE];

send("Setup for 25th status line.\n\n");

printf("Currently 25th line %s setup.",O.STATUSLINE ? "IS" : "IS NOT");

if (O.STATUSLINE)
	{
	printf("\nOutput STATUS port=%xH\nOutput DATA port=%xH\nOutput status mask=%xH\n",O.OSTAT,O.ODATA,O.OSMASK);
	printf("Hex values to move cursor to 25th line:\n");
	for (n=0; O.TO25[n] && n<15; ++n)
		printf("%2xH ",O.TO25[n]);
	printf("\nHex values to return cursor to where it was before it got to the 25th line:\n");
	for (n=0; O.FROM25[n] && n<15; ++n)
		printf("%2xH ",O.FROM25[n]);
	}

askyesno("\n\nDo you have a 25th status line on your terminal",&O.STATUSLINE);

if (O.STATUSLINE)	/* only do this if he has one, else it doesn't apply */
	{
	send("\nNote: If you have a non-I/O mapped screen with 25th line capability\n\
you may enter the address of your BIOS output routine in place of the\n");
send("Output Data port below.  Make sure the Status port, and Status mask are set\nto Zero in this case!\n\n"); 
	asknum("Output Status port (IN HEX!)",&O.OSTAT,0,HEX|INT);
	asknum("Output Data port (IN HEX!)",&O.ODATA,0,HEX|INT);
	asknum("Output status ready mask (IN HEX!)",&O.OSMASK,255,HEX|CHAR);

	send("String to get TO 25th line. (Up to 15 chars. 0 for end of string).\n");
	for (n=0; n<15; n++)
		{
		sprintf(tbuf,"Hex Byte %2d (was %2xH): New value (IN HEX!)",
			n+1,O.TO25[n]);
		asknum(tbuf,&O.TO25[n],255,HEX|CHAR);
		if (!O.TO25[n]) break;	/* done */
		}
	O.TO25[15]='\0';	/* make sure it's terminated */

	send("String to get FROM 25th line. (Up to 15 chars. 0 for end of string).\n");
	for (n=0; n<15; n++)
		{
		sprintf(tbuf,"Hex Byte %2d (was %2xH): New value (IN HEX!)",
			n+1,O.FROM25[n]);
		asknum(tbuf,&O.FROM25[n],255,HEX|CHAR);
		if (!O.FROM25[n]) break;	/* done */
		}
	O.FROM25[15]='\0';	/* make sure it's terminated */
	}	/* block for status line check */

}	/* statusline */


/*******************************************************************
 * Message-base options..  MAXMSGS, MAXTOTMSGS, MLINES, MAXMSGLINE *
 *******************************************************************/

msgstuff()
{
send("Message-base options.\n\n");
printf("Currently:\n\tMaximum active messages allowed is %d\n\tMaximum TOTAL messages allowed (killed+active) is %d\n\tMaximum lines per message is %d\n\tMaximum message line length is %d\n\n",O.MAXMSGS,O.MAXTOTMSGS,O.MLINES,O.MAXMSGLINE);

while (1) {
 asknum("Maximum active messages allowed",&O.MAXMSGS,2000,INT);
 asknum("Maxumum TOTAL active and killed msgs allowed",&O.MAXTOTMSGS,3000,INT);
 if (O.MAXMSGS>O.MAXTOTMSGS)
	{
	send("\nMaximum TOTAL messages MUST BE >= Maximum active msgs!!!\n");
	continue;
	}
	else break;
   } 

asknum("Maxumum lines per message",&O.MLINES,MAXMLINES,INT);
asknum("Maximum characters per message line",&O.MAXMSGLINE,MAXLINE,CHAR);
}


/*********************
 * Sysop name, etc.. *
 *********************/

sysopstuff()
{
send("Change sysop name/password.\n\n");
printf("Sysop's name is %s %s, and password is %s\n\n",
	O.SOFIRST,O.SOLAST,O.SOPASS);

sepstr=' ';
askstr("New Sysop name",O.SOFIRST,FNAMELEN);
sepstr='\0';
capstr(O.SOFIRST);
askstr("New Last name",O.SOLAST,LNAMELEN);
capstr(O.SOLAST);
askstr("New Password",O.SOPASS,PASSLEN);
upcase(O.SOPASS);
}


/* EOF  MECONFIG.C */

