IMD 1.16: 23/05/2007 22:42:17 metal 1.30a source #1 COMP SUBCOMPCFG SUBCTYPE H HMCONFG H h HMH H  !HMH H "#$HMLIB C %&'()*+,-./01234HMLIB C I56789:;<=>LC SUB?LM SUB@LO SUBAMASTER SUBBCMECONFIGC DEFGHIJKLMNOPQRSMECONFIGC -TUVWXYMEINFREQC Z[\]^_`abcdefghiMEINFREQC jMEKILL C =klmnopqrMEMISC C stuvwxyz{|}~MEMISC C  MENTER C MENTER C MENTR ASMera $1.o CII -y15 -x2500 -z2000 -e120 $1.C AS $1.ASM ERA $1.ASM era meconfig.o CII -y6 -x2500 -z2340 -e106 meconfig.C AS meconfig.ASM ERA meconfig.ASM ovln -c 204 meconfig.o hmlib.o xpmio.o metalib.lib /* Copyright (C) 1984 by Manx Software Systems */ extern char ctp_[]; #define isalpha(x) (ctp_[(x)+1]&0x03) #define isupper(x) (ctp_[(x)+1]&0x01) #define islower(x) (ctp_[(x)+1]&0x02) #define isdigit(x) (ctp_[(x)+1]&0x04) #define isxdigit(x) (ctp_[(x)+1]&0x08) #define isalnum(x) (ctp_[(x)+1]&0x07) #define isspace(x) (ctp_[(x)+1]&0x10) #define ispunct(x) (ctp_[(x)+1]&0x40) #define iscntrl(x) (ctp_[(x)+1]&0x20) #define isprint(x) (ctp_[(x)+1]&0xc7) #define isgraph(x) (ctp_[(x)+1]&0x47) #define isascii(x) (((x)&0x80)==0) #define toascii(x) ((x)&127) #define _tolower(x) ((x)|0x20) #define _toupper(x) ((x)&0x5f) /*********************************************************************** * * Options/configuration file for METAL/Z-MSG * * * Copyright (c) 1984 by Tim Gary * All rights reserved. * * FILE: HMCONFG.H * ********************************************************************** * * 1.30xx 5/01/85 Name of MESUMM changed to MESTUFF. * 1.30xx 3/07/85 BYE file name added.. * 1.30xx 2/26/85 ZCPR3 Version changes made. * 1.10e 10/30/84 * 1.10c 10/12/84 OVMISC from OVFEAT. Changed order of files, and added * more room for expansion later without needing to * reconfigure the whole mess. * 1.10b * 1.10a 9/01/84 Overlay files added. * * 1.01a 7/02/84 Multi_user id_loc pointer added. * 1.01a 6/10/84 Changed file structure, names include user/drive. * * 1.0c 5/17/84 Hayes support added. * * 1.0b 4/18/84 Added default user status.. * * 4/13/84 Fixed RTC port order. Added maximum total msgs allowed * variable and changed order of configuration variables. * * 1/24/84 Broke apart HMH.H to form this file of configuration * only declarations. * *********************************************************************/ /************************************************************************ Options are contained in this large structure, and are initialized here. BE VERY VERY CAREFULL NOT TO DELETE A COMMA, OR OTHER SUCH PUNCTUATION IN THE DECLARATIONS, SINCE THE ORDER OF THINGS HAS EVERY- THING TO DO WITH THE WAY IT GETS INITIALIZED!!!!!!!!!!! */ struct options O #ifdef MAIN = { /* and off we go !! */ /************************************************************************ * The following is the initializing of one HUGE structure of options * ************************************************************************/ "Zilch!!!!", /* To allow config to do its job (DON'T TOUCH!!) */ /**********************************************************/ /******* OPTIONS that are likely to need changing.. *******/ /**********************************************************/ /************************************************************************* Each user is of a certian 'type'; here is where types are defined. In this order: 1) Character to use as type identifier (DON'T CHANGE!) 2) Maximum CP/M user area avaliable to him/her (0=no CP/M) 3) Timeout before loging out, if idle (in mins, 0=no access) 4) YES/NO flag, can he/she kill messages 5) YES/NO flag, does he/she get ZCPR privs (era, save, etc) 6) YES/NO flag, can the user read private msgs (not to them)? 7) YES/NO flag, can the user post messages?? 8) Minutes to auto logout (typing or not) 9) Flags for use in command aliases */ /* /--option #'s from above----\ */ /*--------1-----2---3--4---5---6---7---8---9--*/ { SYSOP, 15,30,YES,YES,YES,YES, 0,0xffff,"", /* power hungry SYSOP */ SPECIAL,12,15,YES, NO, NO,YES,90,0x0fff,"", /* SPECIAL user */ NORMAL, 10, 5, NO, NO, NO,YES,60,0x00ff,"", /* etc... */ NOCPM, 0, 5, NO, NO, NO,YES,60,0x000f,"", TWIT, 0, 0, NO, NO, NO, NO, 1,0x0000,"", 'a', 0, 0, 0, 0, 0, 0, 0,0x0000,"", /* room for 3 others */ 'b', 0, 0, 0, 0, 0, 0, 0,0x0000,"", 'c', 0, 0, 0, 0, 0, 0, 0,0x0000,"", 0,0,0,0,0,0,0,0,0,0 /* end of table DON'T CHANGE! !! ! !*/ }, /************************************************************************ Most sysops want the initial user to either be of 'normal' status, or 'nocpm' status, but you can make it whatever you like.. Take the status from the above table (SYSOP, SPECIAL, NORMAL, NOCPM, TWIT (sure!), 'a', 'b', 'c')... */ NORMAL, /* this is what you change */ /************************************************************************ Is the bbs to be a private or pay system? if SO set private to YES,This will require a system id code to logon. if NOT (it's public) then set to NO. */ NO, /* <--- that's what you change */ /* The following definition is for private systems ONLY */ /* (you may ignore it, if the above itset to 'NO') */ /* this is the private system password to get on to the system */ /* it can be UP TO 8 CHARS LONG (NO MORE)! */ "CODXYZZY", /* <--- this is it */ /************************************************************************** The following table MUST BE KEPT IN THE SAME ORDER!!!!!!!! You may change the filenames to suit your tastes, also you may change the location (user area and drive) of each file. Filenames are of the format: [user/][drive:]filename.ext */ { "14/a:bulletin", /* Bulletins file */ "14/a:welcome", /* Welcome file, as typed bye BYE */ "14/a:users", /* Users file */ "14/a:callers", /* callers log */ "14/a:counters", /* system special counters */ "14/a:messages", /* Main Message file */ "14/a:summary", /* Message Summary file */ "14/a:lastcalr", /* Last caller file */ "14/a:newuser", /* file to type for new users */ "14/a:help", /* brief command list help */ "14/a:cpminfo", /* help file when exiting to cpm */ "14/a:system.inf", /* In private sys. account info, etc.*/ "14/a:comments", /* comments file */ "14/a:othersys", /* other systems file */ "14/a:notes.ind", /* Notes index file */ "14/a:feature.ind", /* Features index file */ "14/a:meinfreq.ovr", /* Infrequent routines overlay */ "14/a:menter.ovr", /* Metal Enter overlay (user enter) */ "14/a:mestuff.ovr", /* Summary/Add command overlay */ "14/a:mekill.ovr", /* Kill message overlay */ "14/a:mesend.ovr", /* send message (enter msg) overlay */ "14/a:meuser.ovr", /* User functions (edit, delete, add) */ "14/a:mesysop.ovr", /* Sysop functions.. msg purge, etc.. */ "14/a:memisc.ovr", /* Features/misc other funcs overlay */ "14/a:commands", /* Z3 commands file */ "14/a:mez3.ovr", /* Z3 functions overlay */ "NONE", /* BYE program for disconnect */ /* "14/a:mehelp.ovr", */ /* Help overlay for extended help (coming) */ "", /* room for later */ 0 /* leave alone */ }, /************************************************************************ after a certain number of unsuccessful login attempts, a person is hung-up on. This variable declares that number. NOTE: attempt to logon as sysop counts as TWO tries. */ 5, /* <--- Right here... */ /************************************************************************ You can set an upper limit on the amount of active messages. Each active message will take up 8 bytes of memory. */ 250, /* <--- And here... */ /************************************************************************** Each line of a message can be up to 128 chars, but for a clear display the max length of a line of entered text should be something like 80 */ 80, /* <--- Change this if you like */ /************************************************************************ You can set the maximum number of lines a message can have. This is limited to 250 lines. 100 lines at 80 chars a line (assuming completely full msg) would take 8k (RAM) above the actual program. */ 100, /* <--- Here */ /************************************************************************ A buffer space is needed in Metal to hold message pointers. Each Space in the buffer takes up 8 bytes of memory. This number MUST be larger than the Max Active message count above. */ 400, /* Plenty of space */ /*************************************************************************** There are built-in routines for compupro's real time clock, and for the time from a 10,000 day clock (Mountain Hardware). The date must be manually entered for anything but the Compupro board. This variable defines what type of clock, if any, and the port addresses. NOCLOCK is no clock COMPUPRO is compupro ss1 clock HAYES is Hayes chronograph clock KENMORE is Kenmore Computer Tech. clock board for z-80 systems EPSON is for an epson QX-10 clock CCS is for CCS clock/terminator */ /* this is what you change for type of clock */ NOCLOCK, /* <--- Change this */ /* the following are port addresses, and status info for the ports, for the clock board. Currently setup for NOCLOCK. Note: compupro SS1 setup does NOT use the status mask values below. Also: if the port address is >255, then memory mapped port is assumed */ 0, /* command/status port address */ 1, /* data port address */ 2, /* output ready mask */ 0x20, /* input ready mask */ 16, /* used by compupro ss1 as an offset (ignore for other clocks.. */ /************************************************************************* If your BYE is setup to ask for NULLS, then set the following to YES, else, set it to NO. */ NO, /* <--- Change this as desired */ /*************************************************************************** If you are running ZCPR 1 or 2 in Secure mode, set the following to YES. If ZCPR3 is being used, set this to 3, otherwise set it to NO. */ YES, /* <--- And here */ /* loc of WHEEL, ignore if above isn't set to YES (ZCPR3 gets it from environment descriptor */ 0x003e, /* <--- Here.. */ /*************************************************************************** The following variable is used ONLY for NON-MP/M, MULTI_USER systems. For multiuser systems which make it dificult to tell which console is running the program (like TurboDos...) we need to have a set of 4 locations in memory that will be used to contain a simulated console Id. The following is the starting address of the 4 locations in NON-COMMON memory. Usually there is room someplace in the base page of memory (0-100h) to place this. This is currently set for a "reserved" area in TurboDos. */ 0x003b, /* here.. */ /************************************************************************ Initial default terminal line length.. this is currently not used*****/ 80, /* Alter this if you like */ /************************************************************************ Location of site. Up to 80 chars. Displayed in MENTER */ "\nLos Altos, Ca. (415) 949-1476", /* <--- You got it!! */ /************************************************************************ If you wish callers log and comments to go to printer and not a file then set this to YES, else set to NO. */ NO, /* <--- This one too */ /************************************************************************ If you have a 25th line on your terminal that doesn't scroll, then the following to YES, and set the appropriate control chars, and port addresses below. Otherwise declare the variable to NO */ NO, /* Set this to yes or no */ /* Up to 15 chars can be used to get to or from the 25th line */ /*** You may ignore the following if the above is NO ***/ /* vvvvvvvvvvvvv---- change ALL OF these !!!!! */ "\033\070", /* Sequence to get to the 25th line */ "\n", /* Sequence to return from 25th line */ 0x13, /* terinal output status port */ 0x12, /* terminal data output port */ 0x01, /* terminal status mask.. if bit is one,*/ /* then ready to output char to port */ /* NOTE::::: If you are using a non-I/O mapped video board that has a 25th line (Kaypro people), you can set the following values to obtain the same results as if it were i/o mapped: Set Terminal Output Status AND Terminal Status Mask to 0 (zero). Then find your bios output routine like this: A>ddt DDT Version... -l0 ; list from 0 0000 JMP B603 ; look at where this jump instruction goes to -lb603 ; and list from that address B603 JMP xyz B60x... ; we want the one at B60E in this case ; (first two digits + 0E as last two digits) B60E JMP e877 ; Ok, now write down the address this jump ; instruction goes to (e877h in this ex.) Now With the address you've written down, enter it as the 'Terminal Output Data Port'. That's it! MAJOR NOTE:: This method is BIOS Dependent, so you'll have to go through the same procedure anytime you use a different flavored BIOS!!!!!!!!!!!!!!!!!!!!! */ /************************************************************************ CHANGE THESE!!!!!!!! Sysop built-in password, and name */ "Tim", /* <--- Sysop's first name (change this) */ "Gary", /* <--- Sysop's last name ( " " ) */ "NOPASS", /* <--- Change to a password (UPPER CASE ONLY) */ /************************************************************************ Address of Your ZCPR 3 terminal descriptor buffer in memory. If you don't have ZCPR 3, either get it, or set this value to 0000 */ 0000, /* <------ */ /* leave these alone!!!!!!!! */ "", /* Expansion area declaration */ "KABOOM!!!" /* signal end of structure -------DO NOT TOUCH */ } /* WHEW !!!!!!!!!!!!!!!!! THAT'S IT!!!!!!!!!!!!!!!!!!!!!!!!!! */ #endif /* if main or not.. */ ; /* EOF (HMCONFG.H) */ /************************************************************************** * * Header file for METAL/Z-MSG Version 1.30xx * * * FILE: HMH.H * * Copyright (c) 1984 Tim Gary * Delphi Data Systems * All rights reserved. * ************************************************************************** * * 1.30xx 7/01/85 Print and echo flags moved here.. * 1.30xx 6/09/85 Bye clone space deleted. * 1.30xx 5/26/85 Space for clone of highmem bye area for aliases. * 1.30xx 5/25/85 gp.msg added to global save structure. * 1.30xx 5/15/85 CONFIG define tested for, to reduce global space in it. * 1.30xx 5/03/85 Path space added in alias save structure. * 1.30xx 5/02/85 Moved last_date/last_time here/put in g_save, Z3MSG_OFFSET * define added... * 1.30xx 5/01/85 MESUMM changed to MESTUFF. Added define for return from * alias. * 1.30xx 4/26/85 Added area for command buffer saving.. * 1.30xx 3/07/85 BYE file definition and BBS name define added. * 1.30xx 2/25/85 More defines added. new Z3STUFF.OVR file, and COMMANDS. * 1.30xx 2/20/85 Started Z3 routines. Need >60k CP/M system to compile. * 1.20b 01/18/85 SHOWTIME define added for memisc overlay file.. * 1.20b 01/13/85 KENMORE clock board defines added. * 1.20b 01/05/85 Added CCS and QX10 clock defines. * 1.20a 11/04/84 Version change. * 1.10e 10/30/84 Fixed for new functions moved to overlays. * * 1.10c 10/12/84 Made unused config files null for now (see hmconfg.h), * and now have OVMISC, and its defines. * * 1.10b * 1.10a 8/31/84 Overlay support added. * NOTE: new config fields.. * * 1.01a 7/02/84 Multi_User Id support added. * 1.01a 6/10/84 Now requires Aztec C 1.06 to compile. Added MAIN * declaration for defining external symbols for NON-MAIN * files (like hmlib, ms, etc..). Added CCS clock type. * * 1.0c 5/17/84 Added Hayes clock support. Tested/working 6/8/84. * * 1.0b 4/13/84 Added user defined file stuff, external 'height' variable * and MAXTOTMSGS declaration. NOTE: new config format * incompatible with previous versions. * * 1.0a 1/24/84 Split into two parts one for no changes, other for options * * 1.0 1/17/84 Changes for version 3.0 (more) EXTENSIVE ONES.. * *************************************************************************/ /************************************************************************* * NOTE: the flag MAIN is used to tell if this file is to be setup for * external variables. That is, in the main files (metal.c, menter.c, * etc..) must declare this. *************************************************************************/ /* THE FOLLOWING DEFINE MUST NOT BE CHANGED UNDER ANY CIRCUMSTANCES!!!! */ #define BBSNAME "Metal Message System" /* If ZCPR3 is never to be used, comment out the following line to save code */ /* space.... */ #define Z3 #ifdef Z3 #define ALIASRET ";METAL alias" #define Z3MSG_OFFSET 0x48 /* offset into Z3 message buffer for two */ /* bytes of data (change if confict) */ #endif /************************************************************************ *!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!* *!!!!!! This file has NO options that would need changing in it. !!!!!!* *!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!* *! For configuration options, the file HMCONFG.H has things to change !* *!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!* ************************************************************************/ /******************************************** * so we can compile this file seperately.. * ********************************************/ #ifndef ERROR #include "xpm.h" #endif /***************** * flag stuff... * *****************/ #define TRUE 1 #define FALSE 0 #define ON 1 #define OFF 0 #define YES 1 #define NO 0 #define NULL 0 #define PON '1' /* user parm on char... */ #define POFF '0' /* user parm off char.. */ #define PRINT 1 /* flag print operation */ #define NOPRINT 0 /* flag no print op */ #define MAXLINE 128 /* max line length */ #define MAXMLINES 250 /* absolute maximum lines per msg */ /************************** * user parameter lengths * **************************/ #define FNAMELEN 14 /* first name up to 14 chars */ #define LNAMELEN 20 /* last " " " 20 " */ #define CITYLEN 28 /* up to 28 chars for the city, state */ #define PASSLEN 8 /* up to 8 char password */ #define DATELEN 9 /* date string format mm/dd/yy */ #define TIMELEN 9 /* time string format hh:mm:ss */ /***************************** * message base length stuff * *****************************/ #define TOPICLEN 26 /* 26 char topic for msgs */ /********************* * ask routine flags * *********************/ #define UPLOW 0 /* no modification of input */ #define UP 1 /* convert input to upper case */ #define NOECHO 2 /* don't echo input */ #define NUMBER 4 /* number input as is comes in */ /* #define NOWAIT 8 */ /* <> */ #define MSGLINE 16 /* flag for getl, to get max of MAXMSGLINE chars */ /******************************************************* * for read/write commands and other temp string oprs. * *******************************************************/ #ifdef MAIN #define OVRSPACE 0x1f80 /* almost 8k space for overlay area */ char buffer[257]="Metal Message System (TM) and Z-MSG Copyright (c) 1984 by Tim Gary All rights reserved.\n"; #else extern char buffer[257]; #endif char glbstr[MAXLINE+2]; /* global string used with ask(), etc.. and ';' */ char last_time[TIMELEN],last_date[DATELEN]; #ifdef MAIN char print_flag=OFF; /* flag for print mode */ char echo_flag=ON; /* flag for "half-dup" non echo.. */ int strloc=0; /* current position into glbstr */ int globalch='\0'; /* global char */ char sepstr='\0'; /* if !=0 then this acts same as ';' */ #else extern char print_flag; /* flag for print mode */  extern char echo_flag; /* flag for "half-dup" non echo.. */ extern int strloc,globalch; extern char sepstr; #endif /***************************************************************************** * Multi user console id #.. used for systems without a 'get console id' func. *****************************************************************************/ #ifdef MULTI_USER unsigned id_num; #define ID_STR "Id#" /* 3 char string to use as Id check */ #endif /**************************************** * message base variable definitions * ****************************************/ struct { unsigned number; /* message number */ unsigned seek; /* seek pointer to message header */ unsigned parent; /* parent msg.. original msg in rep. series */ unsigned reply; /* next reply in list... 0 if at end of list */ } *msg; /* POINTER NOW */ unsigned fmsg,lmsg; /* first/last active msg numbers */ int mindex; /* index into above msg array */ #define PRIVMSG 'p' /* Private message stat char */ #define NORMMSG 'n' /* Normal message status */ #define DEADMSG 'x' /* Dead/Killed message */ /********************************************************************** * message structure definition, new format with unencoded #'s, etc.. * **********************************************************************/ #ifndef CONFIG typedef struct { unsigned number; /* message # */ unsigned seek; /* cp/m record, seek loc of msg */ unsigned parent; /* parent message #, if any */ unsigned reply; /* reply message #, if any */ char receiver[FNAMELEN+LNAMELEN+2]; /* who's msg to? */ char fsend[FNAMELEN+1]; /* who's msg from (first name) */ char lsend[LNAMELEN+1]; /* " " " (last name) */ char status; /* private/normal, etc.. */ int lines; /* # of lines in message */ char date[DATELEN]; /* date message entered */ char time[TIMELEN]; /* time message entered */ char topic[TOPICLEN+1]; /* subject of message */ } msg_record; /* new type */ /************************************************************************** * and now, the external, globally known, and loved, one and only msg var * **************************************************************************/ msg_record message; /* Message variable that can be, and is, read */ /* directly from the message/summary file */ int msgcount; /* # of active messages */ int privmsgs; /* # of private messages */ unsigned nextmsg; /* next message number */ unsigned totalmsgs; /* total active and killed msgs allowed */ unsigned callnum; /* caller number NOW UNSIGNED! */ char date[DATELEN]; /* todays date */ char time[TIMELEN]; /* time string */ char nothing[6]; /* place to put BYE parms if inactive */ #endif /* CONFIG */ /*********************************************************************** * global height var, so routines aren't dependent on user.parm.height * ***********************************************************************/ #ifdef MAIN char height=24; /* terminal height for putchar.. assume 24 char screen */ #else extern char height; #endif /*********************** * global BYE pointers * ***********************/ char *bye,*maxuser,*tout,*nulls; /**************** * global files * ****************/ FILE *users; /* users file */ FILE *counters; /* message counters file */ FILE *lastcalr; /* last caller file */ FILE *summary; /* summary file for messages */ FILE *messages; /* message base file */ FILE *callers; /* callers file */ /********************************************************* * Make this a type, so that it can be defined elsewhere * *********************************************************/ #ifndef CONFIG typedef struct { int number; char first[FNAMELEN+1]; char last[LNAMELEN+1]; char pass[PASSLEN+1]; char date[DATELEN]; char time[TIMELEN]; char city[CITYLEN+1]; unsigned lastread; char status; char type; struct { char ulcase; /* upper or lower case */ char jmpcpm; /* jump to cpm/no bbs */ char expert; /* expert user? */ char bell; /* bell on or off */ char rp; /* 'RP' command at bbs */ char video; /* video mode ok? */ char nulls; /* number of nulls */ char height; /* height of terminal */ char width; /* width of terminal */ unsigned tcap; /* tcap ptr (coming) */ unsigned uploads; /* upload count */ unsigned downloads; /* download count */ unsigned minutes; /* time on system today */ unsigned inforec; /* user info file rec */ unsigned calls; /* # of times called */ char reserved[5]; /* reserved for future */ char udef[4]; /* parms for users use */ } parm; } usr; usr user; /* user variable */ #endif /* CONFIG */ struct cmdtype { /* command structure definition */ char *abbr; /* abbreviation */ char *full; /* the rest of the thing */ int (*func)(); /* pointer to function */ int value; /* call the routine with this value */ char *ovname; /* overlay name if used */ }; /********************************************************************** * LEAVE THESE ALONE!! must have correct numbers, and names for later * **********************************************************************/ #define BULLETIN O.files[0] #define WELCOME O.files[1] #define USERFILE O.files[2] #define CALLERS O.files[3] #define COUNTERS O.files[4] #define MESSAGES O.files[5] #define SUMMARY O.files[6] #define LASTCALR O.files[7] #define NEWUSER O.files[8] #define HELP O.files[9] #define CPMINFO O.files[10] #define SYSINFO O.files[11] #define COMMENTS O.files[12] #define OTHERSYS O.files[13] #define NOTEFILE O.files[14] /* notes file index (same format as below) */ #define FEATFILE O.files[15] /* features file table of contents */ #define OVINFREQ O.files[16] #define OVMENTER O.files[17] #define OVSTUFF O.files[18] /* name changed 5/1/85 */ #define OVKILL O.files[19] #define OVSEND O.files[20] #define OVUSER O.files[21] #define OVSYSOP O.files[22] #define OVMISC O.files[23] #define C OMMANDS O.files[24] #define OVZ3 O.files[25] #define BYE O.files[26] /* #define OVXFER O.files[] */ /* #define OVOPSYS O.files[] */ /* #define OVHELP O.files[] */ /* Special function defines for overlay functions... */ #ifndef CONFIG /* OVINFREQ */ #define INIT 0 #define MSGALERT 1 #define READUSERS 2 #define SAVEUSER 3 #define STATS 4 #define CALLS 5 /* OVUSER */ #define EDITUSER 0 #define DELETEUSER 1 /* OVSTUFF */ #define ADDUSER 1 #define SUMM 2 #define WHO 3 #define EXPERT 4 /* OVMISC */ #define FEATURE 0 #define OUTMSG 1 #define GETRANGE 2 #define CHAT 3 #define GOCPM 4 #define GOODBYE 5 #define SHOWTIME 6 /* OVSYSOP */ #define SOSTAT 0 #define READCOMM 1 #define PURGE 2 /* OVKILL */ #define KILL 0 #define UNKILL 1 /* OVZ3 */ #define CMD 0 #endif /* CONFIG */ /***/ #define SYSOP '+' #define SPECIAL 's' /* special user */ #define NORMAL 'n' #define NOCPM 'x' /* no access to cpm */ #define TWIT 'X' /* logout now ! */ /************************** * user type parameters.. * **************************/ typedef struct { char type; /* the character that gets this type */ char maxuser; /* Max CP/M user number this guy gets, 0=nocpm */ char timeout; /* # of mins before no-typing timeout, 0=twit */ char killflag; /* Can he kill messages? */ char zcprflag; /* Does he get master CP/M privs? (with zcpr) */ char readpriv; /* Can he read private mail? */ char postmsg; /* can the guy post messages */ char minutes; /* minutes allowed on system (Metal/MBYE) */ unsigned flags; /* special flags for Z3 operations */ char reserved[10]; /* space for more */ } u_types; /********************************* * clock stuff, don't change!!!! * *********************************/ #define NOCLOCK 0 /* no clock board at all */ #define COMPUPRO 1 /* using compupro clock */ #define HAYES 2 /* using Hayes chronograph thing-a-ma-jig */ #define QX10 3 /* using QX10 bios+45 clock routine */ #define CCS 4 /* using CCS clock/terminator board */ #define KENMORE 5 /* using Kenmore Computer Tech. clock board */ /**************************************************** * All Options are contained in one large structure * ****************************************************/ struct options { char zippo[10]; /* mark start of options */ u_types user_types[9]; /* User types table */ char DSTATUS; /* default user status (login init) */ char PRIVATE; /* private system flag */ char PRIVPASS[PASSLEN+1]; /* private system password */ char files[30][18]; /* file names/locations */ char MAXTRIES; /* Max # of logon attempts */ int MAXMSGS; /* Max # of ACTIVE msgs allowed */ char MAXMSGLINE; /* Max length of msg line */ int MLINES; /* Max # of lines */ int MAXTOTMSGS; /* Max total active + dead msgs */ char RTC; /* using clock??? */ unsigned CLKCMD; /* command/status port */ unsigned CLKDATA; /* data port */ char CLKOMASK; /* clock output status mask */ char CLKIMASK; /* clock input status mask */ unsigned RDTA; /* other clock parm */ char ASKNULLS; /* if bye asks for nulls */ char ZCPR; /* if using ZCPR */ char *SECURELOC; /* location of zcpr wheel */ char *id_loc; /* console id # value pointer */ char INITLENGTH; /* initial terminal length */ char WHERE[81]; /* location of site */ char PRTLOG; /* if using printer for log */ char STATUSLINE; /* you have status line??? */ char TO25[16]; /* up to 15 chars to get there */ char FROM25[16]; /* to get back to last position */ unsigned OSTAT; /* Terminal output stat port */ unsigned ODATA; /* Terminal data port */ char OSMASK; /* output status mask */ char SOFIRST[FNAMELEN+1]; /* sysop's first name */ char SOLAST[LNAMELEN+1]; /* sysop's last name */ char SOPASS[PASSLEN+1]; /* sysop password */ char expansion_room[38]; /* zappo to end this madness */ char zappo[10]; /* "KABOOM!!!" EOO flag */ }; /* global variables needing saving */ #ifndef HMLIB #ifndef CONFIG #ifdef Z3 /* Z3 command line buffer pointer below */ struct cmd_line { char *next_char; /* Pointer to next char in buffer */ char buf_size; /* length of buffer (unused) */ char char_cnt; /* character count */ char buffer[250]; /* buffer area.. */ } ; /* command line pointer structure */ struct envptr { char jmp[3]; /* skip over initial JMP instruction */ char id[5]; /* Id, usually Z3ENV */ char class; /* 1=external, 2 internal */ char *expath; char expaths; /* char *rcp; char rcps; char *iop; char iops; char *fcp; char fcps; char *ndir; char ndirs; */ char t[12]; struct cmd_line *cl; /* command line pointer */ char cls; /* # of bytes in cl */ /* char *env; */ /* ptr to external environment desc. */ /* char envs; */ /* char *shstk; */ /* shell stack */ /* char shstks; */ /* # of (shsize) stack entries */ /* char shsize; */ /* size of shell stack entr y */ char t1[7]; char *msg; /* message buffer */ /* char *extfcb; char *extstk; char quiet; */ char t2[5]; char *wheel; /* secure mode wheel */ /* char speed; */ /* speed in mhz */ /* char maxdisk; char maxuser; */ } *z3env; /* This header structure variable protects globals when doing an alias */ struct g_prot { char n0; /* jmp */ char *obdos; char msgsys[7]; /* "MSGSYS",0 */ char *g_vars; /* pointer to global variables */ /* char bye_a1[32]; */ /* bye data area */ struct { char jmp; char *addr; } bt[6]; char swap; /* push h */ char n1; /* lxi h */ char *prot_bdos; char n2; /* shld 6 */ char *n3; char n6; /* pop h */ char n7; /* ret */ struct { char call; char *addr; char jmp; char *swapbdos; } abt[6]; } g_h #ifdef MAIN = { 0xc3,0000,"MSGSYS",0000, { 0xc3,0000, 0xc3,0000, 0xc3,0000, 0xc3,0000, 0xc3,0000, 0xc3,0000 }, 0xe5,0x21,0000,0x22,0006,0xe1,0xc9, { 0xcd,0000,0xc3,0000, 0xcd,0000,0xc3,0000, 0xcd,0000,0xc3,0000, 0xcd,0000,0xc3,0000, 0xcd,0000,0xc3,0000, 0xcd,0000,0xc3,0000 } } #endif /* MAIN */ ; struct g_prot *z3_hp; /* loc to put this at */ struct g_save { char *msg; char glbstr[MAXLINE+2]; int strloc; int globalchar; char print_flag; /* flag for print mode */ char echo_flag; /* flag for "half-dup" non echo.. */ char sepstr; unsigned fmsg,lmsg; int mindex; int msgcount; int privmsgs; unsigned nextmsg,totalmsgs; unsigned callnum; char date[DATELEN]; char time[TIMELEN]; char height; char last_date[DATELEN]; char last_time[TIMELEN]; struct cmd_line cmdbuf; /* place to save current z3 cmd buffer */ char path[24]; /* allow for 12 path entries */ }; #endif /* Z3 */ #endif /* NOT-CONFIG */ #endif /* HMLIB */ /* EOF (HMH.H) */ 0xc3,0000, 0xc3,0000, 0xc3,0000, 0xc3,0000, 0xc3,0000 }, 0xe5,0x21,0000,0x22,0006,0xe1,0xc9, { 0x/****************************************************************************** * * METAL library functions * * FILE: HMLIB.C version 1.30xx * * * Metal and Metal Message System are Trademarked and * Copyright (c) 1984 Tim Gary * All rights reserved. * * * Common utility finctions used in METAL, MENTER and MUTIL * ****************************************************************************** * * 1.30xx 7/07/85 QX10 routine fixed... * 1.30xx 7/01/85 Echo toggle with ^E implemented.. * 1.30xx 6/23/85 Sysop ^P thing for list output added, new qx10 clock stuff. * 1.30xx 6/19/85 Findbye put back.. * 1.30xx 6/12/85 Modified new date routine to check if ;y was entered earlier * 1.30xx 6/09/85 Enf of line bell every other char after linelen-8 * 1.30xx 5/02/85 Modified Date question to only accept y/n answers. * 1.30xx 3/07/85 LinkBye added for use in cpm 8/16 systems for hanging up. * 1.30xx 3/04/85 LF's converted to spaces in getl.. * 1.30xx 2/26/85 ZCPR3 Support added, and old RESETDRIVE stuff deleted. * 1.20b 01/13/85 MM58167A Kenmore Computer Tech. clock board support added. * 1.20b 01/11/85 Time compare added. * 1.20b 01/07/85 Set getl to ignore linefeeds... * 1.20b 01/05/85 Fix type to strip bit 8 (for WS files), getchar also strips. * 1.20b 01/05/84 New clock routines added.. CCS and QX10. * 1.20a 11/04/84 Counters routines made less dependant upon right info. * 1.10e 11/02/84 Added to pcounters routine, now wil write to specified file * if arg!=0. Put writestat routine here, for easy access. * 1.10e 10/30/84 Parsing routine fixed to zap trailing blanks in ask func. * Future note: CAN'T change in getline!!!!!!!! * * 1.10c 10/0x/84 Hayes clock date order changed to reasonable mm/dd/yy form. * * 1.10b * 1.10a 8/31/84 Overlay stuff started. * * 1.01a 6/27/84 Multi user stuff inserted. * 1.01a 6/10/84 Setup for Aztec C 1.06. CCS clock stuff added. * * 1.0c 5/17/84 Added Hayes clock support. * * 1.0b 4/20/84 (continued) Added usindex() (index ignoring case), distext() * 1.0b 4/14/84 (continued) Findbye routine setup.. * 1.0b 4/13/84 Added Upper/lower case routines. Added Novice user help. * Putchar returns char typed at [More] prompt for checking. * Removed several MENTER only functions (putcaller, writestat..) * External height variable used for screen height now. * * 1.0a 1/25/84 Added terminal height code. Now pauses with [more] prompt * * 1.0 1/17/84 Version 3.0a new format of files. * *****************************************************************************/ #define HMLIB #include "xpm.h" #include "hmh.h" #include "hmconfg.h" #include "ctype.h" #define dgetchar getchar /******************************** * Return index into type table * ********************************/ get_type(t_char) char t_char; { register int i; for (i=0; O.user_type[i].type!='\0'; i++) if (O.user_type[i].type==t_char) break;  if (O.user_type[i].type=='\0') --i; /* return last real type */ return i; } /******************************** * type a file, stop on a break * ********************************/ type(filename) char *filename; { FILE *fil; register int cfast; if ((fil=open(filename,F_RD | F_UNLOCK))==NULL) return ERROR; /* open text file for read */ novhelp(); /* help novice if he is one */ read(fil,1); /* getc doesn't do this initially, sigh */ while ( (cfast=getc(fil))!=ERROR && cfast!=EOF ) { cfast&=0x7f; /* strip high bit so doc mode ws files work */ if (cfast!='\r') if (bcputchar(cfast)==ERROR) break; } close(fil); return NULL; } /******************************************************* * display text from pointer to array of char pointers * * (eg static char *it_hlp[] = { "1","2","3",0 }; )* *******************************************************/ dis_text(sp) register char **sp; { while (*sp && (send(*sp++)!=ERROR)); } /* A one liner to help novices out on the system */ novhelp() { if (user.parm.expert==POFF) send("\nControl-K to abort, Control-S to pause.\n"); } /* Regular old putchar, but checks for break (^K, etc..) chars. Returns ERROR if one hit, else it returns the char */ bcputchar(ch) int ch; { register int a; if (!breakkey()) { if ( ((a=toupper(putchar(ch)))=='N') || ((a & 0x1f)==0xb) ) a=ERROR; } else a=ERROR; return a; } send(s) /* send string while checking for break */ register char *s; { while (*s) if (bcputchar(*s++)==ERROR) return ERROR;; return 0; /* else ok */ } /**************************************** * Print string to printer (LST:) * ****************************************/ print(str) register char *str; { while (*str) bdos(5,*str++); } /******************************** * check for ^k ^x, ^s, etc.. * ********************************/ breakkey() /* check for break */ { register int key; if (key=bdos(6,0xff)) { if (((key & 0x1f)==0xb) /* stop if ^K, ^X, etc.. */ || ((key & 0x1f)==0x18)) { putchar('\n'); return ERROR; } if ((key & 0x1f)==0x13) /* pause if ^S */ getd(); globalchar=key; /* save the char for later reference */ } return 0; /* none pressed */ } /****************************************************************************** * ask: get string after prompting user. * * question= prompt string (will NOT be printed if ';' used * to get mult. commands/options.. * str = place to put string * maxchars= max # of chars to be accepted from terminal * options = following BITs that may be combined: * UPLOW : accept upper/lower case input * UP : upper case conversion is made * NOECHO : turn off display of chars as they are entered * NUMBER : a 'position' number is output instead of chars typed * MSGLINE : ONLY FOR getl(*).. for it to accept MAXMSGLINE chars * *****************************************************************************/ ask(question,str,maxchars,options) char *question,*str; int maxchars; register char options; { register char *temp,*temp1; if ((strloc==0) || (glbstr[strloc]=='\0')) { send(question); /* ask the question */ getl(glbstr,options); /* get the line */ strloc=0; /* make sure of this */ } /* now that we have line input */ temp1=0xffff; /* initial not found flag */ temp=index(&glbstr[strloc],';'); if (temp==0) temp=0xffff; /* convert 0 to ffff for easy compare */ if (sepstr) { temp1=index(&glbstr[strloc],sepstr); if (temp1==0) temp1=0xffff; } if (temp!=temp1) /* if not both 0xffff */ temp=maxchars) glbstr[strloc+maxchars]='\0'; /* same here */ if (options & UP) upcase(&glbstr[strloc]); /* all upper case if spec'd */ strcpy(str,&glbstr[strloc]); /* copy it to where it's wanted */ if (temp!=0xffff) strloc=(temp-glbstr)+1; /* updates strloc if ';' */ else strloc=0; /* else start over */ /* kill trailing blanks */ /* for (temp=str+strlen(str); temp!=str && isspace(*temp); *(temp--)='\0'); */ } /* ask */ /* read a line of input from console */ getl(s,options) char *s,options; { register char c; register int llen; char *temp; register int count; if (!echo_flag) options|=NOECHO; /* force no echo if this set */ if (options&MSGLINE) llen=O.MAXMSGLINE; else llen=MAXLINE; temp=s; count=0; s=temp; if ((user.parm.bell)==PON) putchar(7); /* ring bell */ while ((c=getd()) != '\r' ) { if (c=='\n') c=' '; /* convert a linefeed to a space.. */ if (c=='\b' || c==0x7f) { /* backspace or DEL */ if (!count) continue; --count; --s; if (!(options & NOECHO) || (options & NUMBER)) send("\b \b"); continue; } if (c>31) /* tab goes in directly */ { if (count=llen-8 && (count&1)) /* bell every other char */ putchar(7); } if (c==9) { if (count+1631) putchar(c); } else if ((options & NUMBER) != 0) putchar( (count % 10)+'0'); } /* while */ *s='\0'; /* end of string */ putchar('\n'); return temp; } static char line=1; /* current line (relative to last input) */ /************** * New putchar that counts columns *************/ putchar(ch) register int ch; { static int pos=0; /* position on line */ register int i,c; c=NULL; /* returned character for MORE prompt */ if (ch=='\t') /* check for tab and expand */ { for (i = 0; i <= (pos+8)-((pos+8) & 0xf8); i++) putchar(' '); return; } if (ch=='\n') { pos=0; line++; outc('\r'); outc(ch); } else { if (ch>=' ') pos++; outc(ch); } if (ch=='\r') pos=0; /* reset posistion on line to zip */ if (line>=height && height!=0) { new_page(); /* MUST BE HERE OR RECURSIVE PUTCHAR WILL CROKE */ if (user.parm.expert==POFF) { printf("[Press RETURN to continue]"); c=getd(); /* get char which clears lines */ putchar('\n'); } else { printf(" "); c=getd(); putchar('\r'); } } return c; } /* new putchar */ outc(ch) register int ch; { bdos(6,ch); if (print_flag==ON) bdos(5,ch); /* if printer flag on, write to it */ } /* new getchar to clear line variable, etc.. */ getchar() { register int cfast; cfast=(getd()&0x7f); if (echo_flag) putchar(cfast); return cfast; } getd() { register int cfast; new_page(); /* reset line counter */ cfast=c_wait(); if (user.status==SYSOP) if (cfast==0x10) /* ^P */ { if (print_flag) print_flag=OFF; else print_flag=ON; return c_wait(); } if (cfast==5) /* ^E */ { if (echo_flag) echo_flag=OFF; else echo_flag=ON; return c_wait(); } return cfast; } c_wait() { register int cfast; while(!(cfast = (bdos(6,0xff) & 0x7f) ) ); return cfast; } new_page() { line=1; } /************************************************************************ * To add your own clock routine, define the RTC clock setup to COMPUPRO or * HAYES then change the routine below to read your clock board, and put the * time and date (formated hh:mm:ss and mm-dd-yy) in the variables * (character pointers) time and date. ************************************************************************/ /****************************************************** * CompuPro/ccs/qx10/Hayes, etc. read clock functions * ******************************************************/ readclock() { register int i; int match_flag; char t; /* compupro/ccs vars */ char clock_buf[6]; /* place to put what's read */ static int p[12] = { 5,4,3,2,1,0,10,9,8,7,12,11 }; /* commands */ setmem(clock_buf,6,0); /* clear bcd digit buffer */ if (O.RTC==COMPUPRO || O.RTC==CCS) { if (O.RTC==COMPUPRO) { for (i=0; i<12; i++) { out((char)O.CLKCMD,p[i]+0x10); t=in((char)O.CLKDATA); if (!i) t=(t-8)&0xf; clock_buf[i/2] |= (i&1) ? t : (t<<4); } /* for */ } /* end of compupro routine */ else if (O.RTC==CCS) { out((char)O.CLKCMD,0xf); out((char)O.CLKCMD,3); /* A chan. output mode/no interupts */ out((char)O.CLKCMD+2,0xcf); out((char)O.CLKCMD+2,0xcf); /* B chan. input mode */ for (i=0; i<12; i++) { out((char)O.CLKDATA,p[i]|0x40); /* hold digit */ while (in((char)O.CLKDATA+2) & 0x40); /* wait til held */ out((char)O.CLKDATA,in((char)O.CLKDATA)+0x10); while (in((char)O.CLKDATA+2) & 0x80); /* ok data wait */ /* all that to read a darn digit... */ t=in((char)O.CLKDATA+2); if (!i) t=(t-8)&0xf; clock_buf[i/2] |= (i&1) ? t : (t<<4); out(O.CLKDATA,0); /* clear wait flag */ } /* for loop */ } /* end of ccs routine */ /* common ccs/compupro code */ clock_ascii(time,date,clock_buf,clock_buf+3); } /* ccs+compupro */ else if (O.RTC==HAYES) /* hayes chronogragh */ { char tdate[DATELEN+1]; outpstr(O.CLKCMD,O.CLKOMASK,O.CLKDATA,"\r\r\r\r\rATVD-\r"); inpstr(O.CLKCMD,O.CLKIMASK,O.CLKDATA,'\r',2,date); outpstr(O.CLKCMD,O.CLKOMASK,O.CLKDATA,"\r\r\r\r\rATVT:\r"); inpstr(O.CLKCMD,O.CLKIMASK,O.CLKDATA,'\r',2,time); for (i=0; i<500; i++); outpstr(O.CLKCMD,O.CLKOMASK,O.CLKDATA,"\r\r\r\r\rATRD\r"); inpstr(O.CLKCMD,O.CLKIMASK,O.CLKDATA,'\r',9,tdate); /* get date */ outpstr(O.CLKCMD,O.CLKOMASK,O.CLKDATA,"\r\r\r\r\rATRT\r"); inpstr(O.CLKCMD,O.CLKIMASK,O.CLKDATA,'\r',9,time); /* get time */ tdate[2]='\0'; tdate[8]='\0'; sprintf(date,"%s/%s",tdate+3,tdate); /* change to mm/dd/yy */ } /* Hayes.. */ if (O.RTC==QX10) { #asm push h push d push b lxi h,buffer_ ; location to put results in.. call CREAD ; read the sucker... pop b pop d pop h jmp ENDQX10 ; ; new QX10 routine for independent of os use... ; Submitted by Norm Greggory Used with permission.. (comments a re his): ;**************************************************************************** ; (bye version) ; v1.2 June 1985 Norm Gregory Seattle 'downspout' 206-325-1325 ; ; NOTE: It is no longer required that the user define the starting address ; of the BIOS area. Modifications allowing fuller compatibility ; with most versions of CP/M have been incorporated (i.e. removing ; the need for indirect access to clock via BIOS jump table. ; ; Since we are accessing the CMOS chip directly this insert should ; work under any QX-10 operating system. ; ; THANKS TO: Roger Lanphere (Mt Vernon, WA) for invaluable help whenever ; asked. Mick Gaitor (Brooklyn, NY) for much of the code here. I ; borrowed heavily from his MXC-QX10.ASM. If you are using MEX on ; your QX-10 you should be using his overlays. Super job Mick! ; And Wayne Masters (San Jose, CA) for all his BYE3 work and help ; with the inserts. - NG (06/14/85) ; ;**************************************************************************** ; BUSY EQU 80H ;Mask to test for clock busy condition CLKADR EQU 3DH ;QX-10 clock command register port CLKDAT EQU 3CH ;QX-10 clock data register port STBYTE EQU 0AH ;Clock memory addr to check for clock busy CENTURY EQU 015h ;Hey, it's the 1900's (for a while). ; ;========= ; ; CREAD: gets date/time directly from the LSI 46818 chip. ; (doing it this way rather than through the QX-10 BIOS ; call makes this overlay independent of the version ; of the Epson CPM (A or B) being used.) ; ; Time/Date buffer consists of 6 bytes of packed BCD-encoded information ; located in buffer TBFR. Time/Date buffer is of the following format: ; ; TBFR + 0 = month ; + 1 = day ; + 2 = year ; + 3 = hour ; + 4 = minute ; + 5 = second ; CREAD: CALL CLKBSY ;Wait till clock is idle ; we pass this! LXI H,TBFR ;Point to time/date buffer LXI D,ADRTBL ;Point to clock memory data address table MVI B,6 ;Number of data items to read TDLOOP: LDAX D ;Get next clock memory data address OUT CLKADR ;and send addr to clock port IN CLKDAT ;Read clock data MOV M,A ;and store in time/date buffer INX H ;Set next time/date buffer position INX D ;Set next clock memory data address DCR B ;One less data item JNZ TDLOOP ; - continue read till done RET ; CLKBSY: ;Routine to wait till clock is idle MVI A,STBYTE ;Check if clock is updating OUT CLKADR ;Send address IN CLKDAT ;Read clock ANI BUSY ;Test bit 7  JNZ CLKBSY ;Jump if clock busy RET ; ADRTBL: DB 8,7,9,4,2,0 ;Clock memory addr tbl for mo,da,yr,hr,min,sec ; 9,8,7,4,2,0 (was) yr,mo,da... ;*************************************************************************** ; ENDQX10: nop ; #endasm ; /* will this work? */ clock_ascii(time,date,buffer+3,buffer); } /* if QX10 */ if (O.RTC==KENMORE) { do { for (i=7; i>1; i--) buffer[7-i]=in((char)O.CLKDATA+i); match_flag=TRUE; for (i=7; i>1; i--) if (buffer[7-i] != in((char)O.CLKDATA+i)) match_flag=FALSE; } while (!match_flag); buffer[2]=O.CLKCMD; /* get year from config.. */ clock_ascii(time,date,buffer+3,buffer); } } /* readclock */ clock_ascii(t,d,tb,db) char *t,*d,*tb,*db; { sprintf(t,"%02x:%02x:%02x",(int)tb[0],(int)tb[1],(int)tb[2]); sprintf(d,"%02x/%02x/%02x",(int)db[0],(int)db[1],(int)db[2]); } getdate() { if (O.RTC!=NOCLOCK) readclock(); return date; } /****************************************************************** * compare times, return minutes difference. Works for 47:59 hours. * passed: current time, compare time, current date, compare date ******************************************************************/ timecomp(t1,t2,d1,d2) char *t1,*t2,*d1,*d2; { register int mins1,mins2,mins; mins1=atoi(t1+3); mins1+=(atoi(t1)*60); mins2=atoi(t2+3); mins2+=(atoi(t2)*60); if (strcmp(d1,d2)) mins1+=1440; /* dates have changed. Add a day */ return mins1-mins2; } /************************************* * output string to port specified. * * expected is: 1) status port * * 2) data out mask 3) data port * * 4) the string to be sent * * note: nulls may not be sent via * * this routine!! 0 terminates str * * if data/status>255 then memory * * mapped i/o is performed * *************************************/ outpstr(stat,stmask,data,string) unsigned stat,data; char stmask; register char *string; { while (*string) outchar(stat,stmask,data,*string++); } outchar(stat,stmask,data,c) register unsigned stat,data; char stmask,c; { if (stat>255) { while (!( (*(char *)stat & stmask))); *(char *)data=c; } else { while (!( in((char)stat) & stmask)); out((char)data,c); } } /******************************** * input string from port... * * parms: 1) status port * * 2) status mask 3) data port * * 4) end of string char * * 5) max chars over all * * 6) string pointer to place * * string * * note: same as above for * * memory mapped ports.. * ********************************/ inpstr(stat,stmask,data,eos,max,string) unsigned stat,data,max; char stmask,eos; register char *string; { char inpchar(); *(string+max)='\0'; /* make sure of termination */ while (max--) { *string=inpchar(stat,stmask,data) & 0x7f; if (*(string++)==eos) { *(string-1)='\0'; /* terminate, and abort */ break; } } } /* inpstr */ char inpchar(stat,stmask,data) register unsigned stat,data; char stmask; { unsigned to=50000; while ( to-- && !( ((stat>255) ? *(char *)stat : in((char)stat)) & stmask) ); return ( (data>255) ? *(char *)data : in((char)data) ); } /************************************************************************ * String functions... */ /* convert string to all upper case */ upcase(str) register char *str; { for (;*str;++str) *str=toupper(*str); } /* check for a space in the given string (returns 1 if space, else 0) */ isspc(str) register char *str; { while(*str) if (*str++<=' ') return TRUE; /* return TRUE if space/cntrl */ return FALSE; /* else FALSE returned */ } /* capitalize a string (all words in the string */ capstr(str) char *str; { register char *tp; *str=toupper(*str); /* first char is easy */ for ( tp=str; tp0); s1++, s2++, n--) if (*s1=='\0') return (toupper(*s1)-toupper(*s2)); /* if at end of first string, test for=length/return */ return (n ? (toupper(*s1)-toupper(*s2)) : 0); } /***************************** * Same as index(s,c), but with two strings *****************************/ sindex(s1,s2) register char *s1,*s2; { register char *sp; for (sp=s2; strlen(s1)<=strlen(sp); sp++) if (!strncmp(s1,sp,strlen(s1))) return sp; return 0; /* no match */ } /***************************** * Same as sindex(s1,s2), but IGNORE CASE in comparison *****************************/ usindex(s1,s2) char *s1,*s2; { register char *sp; for (sp=s2; strlen(s1)<=strlen(sp); sp++) if (!ustrncmp(s1,sp,strlen(s1))) return sp; return 0; /* no match */ } /* get the counters from counters file */ gcntrs(f) char *f; { char tdate[DATELEN+1]; #ifndef MULTI_USER int id_num; /* make dumby local copy if single user */ #endif /* in case of failure.... */ callnum=id_num=0; /* for defaults in case nothing found */ if ((counters=open(f ? f : COUNTERS,F_RD | F_LOCK))!=0) { read(counters,0); sscanf(bufloc(counters),"%d %d %d %s %d %d",&msgcount, &callnum,&nextmsg,(O.RTC ? tdate : date),&id_num,&privmsgs); #ifdef TURBODOS if (strncmp(in_loc,ID_STR,3)) { sprintf(bufloc(counters),"%d %u %u %s %d %d",msgcount,callnum, nextmsg,date,++id_num,privmsgs); write(counters,0); strcpy(id_loc,ID_STR); *(id_loc+3)=(char)id_num; } #endif /* TurboDos */ close(counters); } } /* put the counters file on disk */ pcounters(f) char *f; { #ifndef MULTI_USER int id_num=0; #endif counters=open(f ? f : COUNTERS,F_RW | F_LOCK); sprintf(bufloc(counters),"%d %u %u %s %d %d",msgcount,callnum, nextmsg,date,id_num,privmsgs); write(counters,0); close(counters); } /* output string to the 25th status line.. */ writestat(str) register char *str; { if (O.STATUSLINE) /* 25th line status display on???? */ { if (O.OSMASK) { outpstr(O.OSTAT,O.OSMASK,O.ODATA,O.TO25); /* get to 25th line */ outpstr(O.OSTAT,O.OSMASK,O.ODATA,str); /* output string */ outpstr(O.OSTAT,O.OSMASK,O.ODATA,O.FROM25); /* get back */ } else { /* otherwise O.ODATA is address of output routine BIOS DEP! */ outsbios(O.TO25); outsbios(str); outsbios(O.FROM25); } } } outsbios(s) register char *s; { while (*s) outcbios(*s++); } char glob_ch; /* so I can find the char easily */ char *glob_ad; outcbios(c) register char c; { glob_ch=c; glob_ad=O.ODATA; #asm lda glob_ch_ mov c,a sillyk: lxi d,sillyk+8 ; terrible kluge, do not change following push d ; instructions without changing this too!!! lhld glob_ad_ pchl nop #endasm } /* Linkbye... This routine loads and runs BYE.COM, this will disconnect * the current caller. BYE86 needs this to be done since the location * 0 (c3/cd) disconnect trick doesn't work under it. * * 3/7/85 */ linkbye() { setusr(fcbinit(BYE,0x5c)); /* setup correct default FCB and right user */ #asm ; This is taken from mentr.asm routine.. ; ; The routine moves itself into the bffer at 80h, and procedes to load the ; program that is called for... Kludgey, but effective.. ; lxi sp,0ffh mvi c,0fh ; open code.. lxi d,005ch ; fcb call 0005 ; bdos cpi 0ffh ; error? jz 0 ; yes boot... lxi d,loader lxi h,0080h ; dma.. lxi b,07fh movloop: ldax d mov m,a inx d inx h dcr c jnz movloop ; lxi sp,0ffh ; stack goes to a safe place lxi b,005ch ; fcb jmp 080h ; jump to loader... ; loader: lxi d,100h ; tpa load1: push d push b mvi c,1ah ; set dma.. call 0005 ; bdos pop d push d mvi c,14h ; read seq. call 0005 ; bdos pop b pop d ora a ; error? jz 80h+(bumpdma-loader) ; jrz bumpdma a1: mvi c,1ah ; set dma lxi d,0080h ; reset default dma address.. call 0005 ; bdos mvi c,10h ; close lxi d,05ch call 0005 jmp 100h ; tpa.. (program just loaded) ; bumpdma: lxi h,0080h dad d xchg jz 80h+(load1-loader) ; jr loader ; lend: db 0 #endasm } /****************************************************************** * initialize the bye variable to point to location if BYE active */ findbye() { register char *p; bye=(*(char *)2)*256+*(char *)1-2; bye=(*(char *)(bye+1))*256+*(char *)bye+6; /* bye keeps mucking everyone up by changing the locations of strings */ for (p=bye; p<=bye+35; p++) if (!ustrncmp("bye",p,3)) break; if (p>=bye+25) bye=nothing; /* now for the values that we use the bye variable for.. */ maxuser=bye; tout=bye+2; nulls=bye+3;  } /* EOF hmlib.c */  pop b pop d ora a ; error? jz 80h+(bumpdma-loader) ; jrz bumpdma a1: mvi c,1ah ; set dma lxi d,0080h ; reset default dma address.. call 0005 ; bdos mvi c,10h ; close lxi d,05ch call 0005 jmp 100h ; tpa.. (program just loaded) ; bumpdma: lxi h,0080h dad d xchg jz 80h+(load1-loader) ; jr loader ; lend: db 0 #endasm } /****************************************************************** * initialize the bye variable to point to location if BYE active */ findbye() { register char *p; bye=(*(char *)2)*256+*(char *)1-2; bye=(*(char *)(bye+1))*256+*(char *)bye+6; /* bye keeps mucking everyone up by changing the locations of strings */ for (p=bye; p<=bye+35; p++) if (!ustrncmp("bye",p,3)) break; if (p>=bye+25) bye=nothing; /* now for the values that we use the bye variable for.. */ maxuser=bye; tout=bye+2; nulls=bye+3; ovln -c 204 meconfig.o -l hmlib.o xpmio.o metalib.libHMH H  !HMH H "#$HMLIB C %&'()*+,-./01234HMLIB C I56789:;<=>LC SUBovln -c 204 -r metal.o mes.o hmlib.o xpmio.o metalib.lib ovln meinfreq.o movbgn.o metal.rsm ovln menter.o movbgn.o metal.rsm ovln mestuff.o movbgn.o metal.rsm ovln mekill.o movbgn.o metal.rsm ovln mesend.o movbgn.o metal.rsm ovln memisc.o movbgn.o metal.rsm ovln meuser.o movbgn.o metal.rsm ovln mesysop.o movbgn.o metal.rsm ovln mez3.o movbgn.o metal.rsm ; If using ZCPR3: ** REMEMBER TO USE ZMCONFIG and Z3INS to INSTALL Z-MSG!! ** ;Link overlay file (usage: submit lo ) ;This one copies the overlay to user 14 afterwards ovln $1.o movbgn.o metal.rsm insov $1 LC SUB?LM SUB@LO SUB; Compile/Link Metal System Aztec C 1.05g Version (8080) era xpmio.o CII -y12 -x2500 -z2000 -e106 xpmio.C AS xpmio.ASM ERA xpmio.ASM era hmlib.o CII -y12 -x2500 -z2000 -e106 hmlib.C AS hmlib.ASM ERA hmlib.ASM era metal.o CII -y12 -x2500 -z2000 -e106 metal.c AS metal.asm ERA metal.ASM era mes.o CII -y12 -x2500 -z2000 -e106 mes.C AS mes.ASM ERA mes.ASM era mesend.o CII -y12 -x2500 -z2000 -e106 mesend.C AS mesend.ASM ERA mesend.ASM era mekill.o CII -y12 -x2500 -z2000 -e106 mekill.C AS mekill.ASM ERA mekill.ASM era mestuff.o CII -y12 -x2500 -z2000 -e106 mestuff.C AS mestuff.ASM ERA mestuff.ASM era meinfreq.o CII -y12 -x2500 -z2000 -e106 meinfreq.C AS meinfreq.ASM ERA meinfreq.ASM era memisc.o CII -y12 -x2500 -z2000 -e106 memisc.C AS memisc.ASM ERA memisc.ASM era menter.o CII -y12 -x2500 -z2000 -e106 menter.C AS menter.ASM ERA menter.ASM era mesysop.o CII -y12 -x2500 -z2000 -e106 mesysop.C AS mesysop.ASM ERA mesysop.ASM era meuser.o CII -y30 -x2500 -z2000 -e106 meuser.C AS meuser.ASM ERA meuser.ASM era meconfig.o CII -y6 -x2500 -z2340 -e106 meconfig.C AS meconfig.ASM ERA meconfig.ASM era mez3.o CII -y12 -x2500 -z2000 -e106 mez3.C AS mez3.ASM ERA mez3.ASM ovln -c 204 meconfig.o hmlib.o xpmio.o metalib.lib ovln -c 204 -r metal.o mes.o hmlib.o xpmio.o metalib.lib ovln meinfreq.o movbgn.o metal.rsm ovln menter.o movbgn.o metal.rsm ovln mestuff.o movbgn.o metal.rsm ovln mekill.o movbgn.o metal.rsm ovln mesend.o movbgn.o metal.rsm ovln memisc.o movbgn.o metal.rsm ovln meuser.o movbgn.o metal.rsm ovln mesysop.o movbgn.o metal.rsm ovln mez3.o movbgn.o metal.rsm -z2000 -e106 memisc.C AS memisc.ASM ERA memisc.ASM era menter.o CII -y12 -x2500 -z2000 -e106 menter.C AS menter.ASM ERA menter.ASM era mesysop.o CII -y12 -x2500 -z2000 -e106 mesysop.C AS mesysop.ASM ERA mesysop.ASM era meuser.o CII -y30 -/************************************************************* * Configuration program for Metal * * FILE: MECONFIG.C version 1.30xx * * Metal and Metal Message System are Trademarked and * Copyright (c) 1984 Tim Gary * All rights reserved. * * * This is the configuration program which enables you to * setup the message system without recompiling. * ************************************************************* * * 1.30xx 7/10/85 Epson clock config questions cleared up. * 1.30xx 6/15/85 Minutes for auto timeout added to user val parms. * 1.30xx 5/15/85 CONFIG define added to cut space on global vars in hmh.h * 1.30xx 4/26/85 More Z3 stuff added (Z3ENV string stored with perm save) * 1.30xx 3/05/85 Z3 Stuff added.. * 1.20b 01/16/85 Kenmore tech clock stuff added.. * 1.20b 01/05/85 CCS stuff back in, and QX10 clock option added. * 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 #define CONFIG #include "xpm.h" /* cpmio header file */ #include "hmh.h" /* METAL bbs header file */ #include "hmconfg.h" /* config/options defines */ #include "ctype.h" #define TITLE " Configuration 1.30xx\nCopyright 1984,1985 Tim Gary\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(),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, "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",savem, 0,0 /* mark end of menu */ }; /********************* * Main program loop * *********************/ main() { char *menu(); int (*fn)(); send(BBSNAME); 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. Time spent reconfiguring several options\nover again can be saved.\n"); continue; } if (*buffer!='Y') break; /* nope, so forget it */ else { saveconf(); /* save configuration file */ break; } } send("\nEntering The Operating System."); 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 Message System COM 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("The main message system COM file, and this configuration program are\nthe main files that can have the configuration saved in them.\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<>\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; addr0); 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\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 = '%c'\ \nMax User area is %d Timeouts: Inactivity %d mins., General %d mins.\ \nWheel Privs are %3s. ZCPR3 Command file mask is %4x", uptr->type,(int)(uptr->maxuser),(int)(uptr->timeout),(int)(uptr->minutes), uptr->zcprflag==YES ? "ON" : "OFF",uptr->flags); printf("\nKill other messages: %s\nRead private messages: %s\ \nPost public messages: %s\n", uptr->killflag==YES ? "YES" : "NO", uptr->readpriv==YES ? "YES" : "NO", uptr->postmsg==YES ? "YES" : "NO"); ask("\nAlter this user? ",buffer,2,UP); if (*buffer=='Y') { asknum("Max CP/M user number (0-15)",&uptr->maxuser,15,CHAR); asknum("Sleepy caller timeout (minutes)",&uptr->timeout,0,CHAR); asknum("Minutes till bbs auto logout (0=NO LIMIT) (clock systems only)", &uptr->minutes,0,CHAR); askyesno("Does the user have WHEEL Privs (ZCPR Wheel)", &uptr->zcprflag); asknum("Special ZCPR3 command mask (in hex!)",&uptr->flags,0,HEX|INT); askyesno("Can this user kill msgs",&uptr->killflag); 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 ( 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("/n0) NO Clock (or incompatible one)\n1) CompuPro SS1 Clock\n2) Hayes Chronograph\n3) QX10 clock\n4) CCS Clock board\n5) Kenmore Computer Tech. clock\n"); send("Note: a port address>255 will enable memory mapped I/O\n"); asknum("\nNew setup",&O.RTC,5,CHAR); switch (O.RTC) { case COMPUPRO: case HAYES: printf("%xH is the clock comand/status port, %xH is data.\n", O.CLKCMD,O.CLKDATA); 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==HAYES) { printf("%xH is the output status mask, %xH is the input status mask\n",(unsigned)O.CLKOMASK,(unsigned)O.CLKIMASK); asknum("New output status mask (IN HEX!)",&O.CLKOMASK, 0,HEX|CHAR); asknum("New input status mask (IN HEX!)",&O.CLKIMASK, 0,HEX|CHAR); } break; case CCS: O.CLKDATA-=4; printf("Clock board base address set at %xh (4 is added later for clock PIO base)\n",O.CLKDATA); asknum("New base address in HEX (usually 38h)",&O.CLKDATA, 0,HEX|INT); O.CLKDATA+=4; /* add offset of 4 */ O.CLKCMD=O.CLKDATA+1; /* and set command port address */ break; case KENMORE: printf("Clock base address set at %xh, year is '%02x.\n", O.CLKDATA,O.CLKCMD); asknum("New base clock i/o port address (IN HEX!)",&O.CLKDATA, 0,HEX|INT); asknum("New two digit year (85, 86 etc..)",&O.CLKCMD, 0,HEX|INT); break; default: break; } /* switch */ } /********************************* * BYE stuff... if ask for 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%s.\n",O.ZCPR ? "" : " NOT",O.ZCPR==3 ? "3" : O.ZCPR ? "(1 or 2)" : "(1,2 or 3)"); asknum("Enter the MAJOR version of ZCPR to be used (1,2 or 3),\ \nor 0 if ZCPR is not to be used",&O.ZCPR,3,CHAR); if (O.ZCPR && O.ZCPR!=3) { printf("Wheel byte currently at %xH\n",O.SECURELOC); asknum("New location (IN HEX!)",&O.SECURELOC,0,HEX|INT); } if (O.ZCPR==3) send("\nREMEMBER to configure the main BBS COM file with the ZCPR3 environment\ \ninstallation utility (Z3INS).\n"); #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); } /************************** * 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 */ .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/************************************************************************ * This file contains infrequently used (or used once) routines for the * overlay version of The Metal/Z-MSG * * Name: MEINFREQ.C * Version 1.30xx * * Copyright (c) 1984 Tim Gary * All rights reserved. * ************************************************************************ * * 1.30xx 06/09/85 No more allocation for msg.. * 1.30xx 05/25/85 Put msg allocation here also...(see also menter!) * 1.30xx 05/02/85 last_date/time extern defs deleted. * 1.30xx 03/03/85 Z3 Stuff looked into in this file. * 1.20b 01/18/85 Stats changes to conform to last_time, last_date. * 1.20b 01/09/85 Few more cosmetics.. * 1.20b 01/07/85 Fixed misc routines, added count of msgs user's left.. * 1.20a 11/11/84 Ok, final cosmetic touches and release for this * series of changes. * 1.20 11/09/84 Version message changed for new mes.c file. * 1.20 11/07/84 Lastread fixed. * 1.20 11/04/84 Version change, and additions to lastcalr file info. * 1.10e 11/03/84 Added more info to the '#' command. * 1.10e 11/02/84 Msg number=parent thing deleted. New put/get counters. * 1.10e 11/01/84 Untypo'd a typo. * 1.10e 10/31/84 Added stuff for private message counts/width stuff. * 1.10d 10/21/84 First time system setup bug (from 1.10a/b/c) fixed. * 1.10c 10/12/84 Routines added to verify parent/replys to msgs. * if not found in the table, then zero'd. * 1.10b 10/04/84 Message table build from message file corrected. * 1.10b 10/03/84 Version change, touch-ups, height properly initialized. * 1.10a 09/27/84 Cosmetic fixes (U command). * 1.10a 09/09/84 Cosmetic fixes. * 1.10a 08/31/84 Created. * ************************************************************************/ #include "xpm.h" #include "hmh.h" #include "hmconfg.h" #include "ctype.h" #define TITLE "Version 1.30a\n" /********************************************************** * Overlay main program, dispatches to contained routines. **********************************************************/ ovmain(routine,parm) int routine,parm; { switch(routine) { case INIT: return init(parm); case MSGALERT: return msg_alert(parm); case READUSERS: return readusers(parm); case SAVEUSER: return saveuser(parm); case CALLS: return calls(parm); case STATS: return stats(parm); default: send("\nUnknown routine called for.\n"); break; } } /* ovmain */ /*************************** * INITIALIZE EVERYTHING * ***************************/ /* flag is argc and is used to tell if menter was run or not for getlastcalr */ init(flag) register int flag; { #ifdef Z3 if (O.ZCPR==3) *(z3env->wheel)=0; /* turn off ZCPR3 wheel */ else /* fall through to next if */ #endif if (O.ZCPR) *O.SECURELOC=0; /* if zcpr 1 or 2, turn off hazzards */ #ifdef MULTI_USER #ifdef MPM if (strncmp(id_loc,ID_STR,3)) { send("\n[Please wait]\n"); /* ovloader(OVMENTER,(char *)0,0); won't work here..*//* not setup with lastcalr.. */ } #endif #endif printf("\n%s.\n%s",BBSNAME,TITLE); /* display banner identifing the system */ /* done only if menter not used */ if (flag<=1) { gcntrs(0); getlastcaller(); /* get lastcalr file saved in menter.c */ } height=user.parm.height; /* reset height */ if (O.RTC!=NOCLOCK) getdate(); /* get time and date from clock board */ /* * Find out if BYE is active and setup BYE parms.. */ findbye(); /* Get bye, maxuser,tout, etc.. variables */ /* setup timeout values for different people (different stats) */ *tout=O.user_types[user.type].timeout; *maxuser=O.user_types[user.type].maxuser; *nulls=user.parm.nulls-'0'; /* setup users nulls */ if (msg==0) { register char *zero=0; send("\nAllocation ERROR: Memory full.\n"); if (user.status==SYSOP) send("\n** Use the Configuration program and decrease total # of msgs **\n"); else { send("\n** Sorry, but the BBS is in need of repair. Please try it later! **"); *zero=0xcd; /* make sure bye hangs up on this one.. */ } if (!strcmp(BYE,"NONE")) exit(0); else linkbye(); } stats(0); /* display user number/message stats */ flag=msg_alert(0); /* check for msgs/build index */ putchar('\n'); return flag; } /* init */ /************************ * Get user from lastcalr file, file was created with menter ************************/ getlastcaller() { unsigned trec,tlast; char td[DATELEN+1],tt[TIMELEN+1]; if ((lastcalr=open(LASTCALR,F_RD | F_UNLOCK))==NULL) /* open for read only */ { send("\nERROR: No LASTCALLER FILE!"); exit(); /* exit to cpm in case it's sysop and can be fixed.. */ } #ifdef MULTI_USER #ifdef MPM trec=bdos(153,0)%16; /* get mp/m user console id */ #else trec=id_num; #endif /* MP/M */ setarec(lastcalr,trec); /* seek to console position to get caller */ #endif /* Multi_User */ read(lastcalr,0); sscanf(bufloc(lastcalr),"%*[^\032] %*1s %d %d %s %s", &trec,&tlast,last_date,last_time); close(lastcalr); users=open(USERFILE,F_RD | F_UNLOCK); /* open for read */ setarec(users,trec); /* seek to user */ read(users,0); /* get the record */ movmem(bufloc(users),&user,128); /* copy the record into our variable */ close(users); user.lastread=tlast; /* get lastread back */ height=user.parm.height; /* get height setup */ } /************************************************ * All new and improved msg summary builder... * * Will use messages file alone and skip bad * * sectors that it finds... * ************************************************/ msg_alert(n) { msg_record *mptr; /* for easy access to values in the field */ register int flag; int fileflag; /* summary or message index build */ int lines; /* line count of message */ int from_you; /* count of messages left by you and still on sys */ from_you=flag=msgcount=totalmsgs=privmsgs=mindex=fmsg=lmsg=0; fileflag=TRUE;  if (!n) send("\n[Checking for your Mail]\n"); if ((summary=open(SUMMARY,F_RD | F_UNLOCK))!=NULL) { /* summary file opened.. */ toeof(summary); if (getrec(summary)<=1) close(summary); else { setarec(summary,1); fileflag=FALSE; } } else { /* create summary to keep things happy */ summary=open(SUMMARY,F_RW); sprintf(bufloc(summary),"0 \n"); write(summary,1); close(summary); } if (fileflag) /* summary file not opened correctly, try messages */ summary=open(MESSAGES,F_RD | F_UNLOCK); #ifdef DEBUG printf("\nBuilding message index from %s file.\n",fileflag ? "message" : "summary"); #endif mptr=bufloc(summary); /* setup buffer pointer */ while (summary && read(summary,1)==128) { msg[mindex].number=mptr->number; msg[mindex].seek=mptr->seek; msg[mindex].parent=mptr->parent; msg[mindex].reply=mptr->reply; if (mptr->status==DEADMSG) msg[mindex].parent=msg[mindex].reply=0; #ifdef DEBUG printf("\nnum %u seek %u parent %u reply %u",mptr->number,mptr->seek, mptr->parent,mptr->reply); #endif if (mptr->lines && fileflag) { /* message file skip text */ if ( (mptr->status!=PRIVMSG && mptr->status!=NORMMSG && mptr->status!=DEADMSG) || mptr->seek!=(getrec(summary)-1) ) { /* not valid record, skip till one found.. */ send("\n[Bad records found, skipping..]\n"); #ifdef DEBUG printf("\nFile seek=%u, Message stat='%c' %x hex.",getrec(summary)-1,mptr->status,mptr->status); #endif do lines=read(summary,1); while (lines==128 && (mptr->status!=NORMMSG && mptr->status!=PRIVMSG && mptr->status!=DEADMSG) || mptr->lines>O.MLINES || mptr->seek!=(getrec(summary)-1) ); setrrec(summary,-1); if (lines==128) continue; /* don't increment mindex, etc.. */ else break; } } if ( /* mptr->status!=DEADMSG && */ mptr->number) { lmsg=mptr->number; if (!fmsg) fmsg=lmsg; } if (mptr->status==PRIVMSG) privmsgs++; /* count # of private msgs */ if (!n && mptr->status!=DEADMSG && thisis(mptr->receiver)) { /* if message active and addressed to them */ if (!flag) { send("\n[You have Mail!]\n\nNumber: From:\n"); ++flag; } printf("%5u %s %s%s%s\n", mptr->number, mptr->fsend,mptr->lsend, mptr->number>user.lastread ? " [new]" : " ", mptr->status==PRIVMSG ? " [private]" : ""); } if (mptr->status!=DEADMSG) { char tbuf[FNAMELEN+LNAMELEN+3]; ++msgcount; ++totalmsgs; sprintf(tbuf,"%s %s",mptr->fsend,mptr->lsend); if (thisis(tbuf)) from_you++; } if (mptr->lines && fileflag) { lines=mptr->lines+1; read(summary,1); while (lines--) while (getc(summary)!='\n'); /* flush line */ } ++mindex; /* killed msg in table too, for upcoming unkill,etc. */ if (mindex>=O.MAXTOTMSGS) { printf("\nTo many msgs, (%d is max, including deleted ones)\n", O.MAXTOTMSGS); close(summary); return ERROR; } } /* while */ if (!n && from_you) printf("\n[%d active messages left by you!]",from_you); if (!n && !flag) send("\n[Sorry, you have no Mail]"); if (summary && fileflag) { send("\n[One moment please. Be sure to do a message purge BEFORE anything else!]\n"); for (flag=0; flagnumber==0) return NULL; sprintf(tb,"%s %s %s",up->first,up->last,up->city); upcase(tb); /* make upper case only */ if (isdigit(*sstr)) { if (atoi(sstr) > up->number) return NULL; } else if (!sindex(sstr,tb) && *sstr!='\0') return NULL; /* no match-no show */ if (user.status==SYSOP) sprintf(temp," -- '%c' (%s) pw=%s",up->status, up->status==SYSOP ? "sysop" : up->status==SPECIAL ? "special" : up->status==NORMAL ? "normal" : up->status==NOCPM ? "NO CP/M" : up->status==TWIT ? "*TWIT*" : "OTHER",up->pass); else { mode=NOPRINT; *temp='\0'; } sprintf(buffer,"\n%-5u %s %s%s from %s",up->number,up->first,up->last, temp,up->city); if (send(buffer)==ERROR) return ERROR; if (mode==PRINT) { print(buffer); /* send to list device */ print("\r"); } strcpy(temptime,up->time); timefix(temptime); sprintf(buffer,"\n\\---- Called %u time(s). Last message was %u on %s %s%s\n", up->parm.calls,up->lastread,up->date, O.RTC!=NOCLOCK ? "at " : "",O.RTC!=NOCLOCK ? temptime : ""); if (send(buffer)==ERROR) return ERROR; if (mode==PRINT) { print(buffer); /* send to list device */ print("\r"); } return NULL; /* alls done */ } /******************************* * display the user on console * *******************************/ disuser() { printf("\nYou are user #%d.\n",user.number); sprintf(buffer,"\ \n(1) Experience=%s (2) Bell=%s (3) Nulls=%c\ \n(4) %s (5) %s messages when first entering the bbs.\ \n(6) Change Password. (7) Terminal height=%d (if 0, no pause)\ \n(8) Terminal width=%d\n", (user.parm.expert==PON) ? "EXPERT" : "NOVICE", (user.parm.bell==PON) ? "ON" : "OFF",user.parm.nulls, (user.parm.jmpcpm==PON) ? "Skip BBS " : "Enter BBS", (user.parm.rp==POFF) ? "Read" : "Don't read", user.parm.height,user.parm.width); send(buffer); } /******************** * Save users stuff * ********************/ saveuser(n) register unsigned n; { char temp[10]; disuser(); /* display the user */ do { if (user.parm.expert==PON) sprintf(buffer,"\nChange :"); else sprintf(buffer,"\nEnter number to change (Return to End, '?' to redisplay menu) :"); ask(buffer,temp,9,UP); switch (*temp) { case '1': user.parm.expert==PON ? (send("\n[Novice mode]"),user.parm.expert=POFF) : (send("\n[Expert mode]"),user.parm.expert=PON); break; case '2': user.parm.bell==PON ? (send("\n[Bell off]"),user.parm.bell=POFF) : (send("\n[Bell on]"),user.parm.bell=PON); break; case '3': ask("\nNumber of nulls (0-9)? ",buffer,5,UP); if ((*temp>='0') && (*temp<='9') && *buffer!='\0') { *nulls=*buffer-'0'; user.parm.nulls=*buffer; } else send("\n[Nulls NOT changed]"); break; case '4': if (O.user_types[user.type].maxuser==0) { send("\nYou don't have access to the Operating System..."); break; } user.parm.jmpcpm==PON ? (send("\nYou will now enter the message system when you call."), user.parm.jmpcpm=POFF) : (send("\nYou will now enter the Operating System instead of the BBS when you call.") ,user.parm.jmpcpm=PON); break; case '5': user.parm.rp==POFF ? (send("\n[AUTO read of messages OFF]"),user.parm.rp=PON) : (send("\n[AUTO read of messages ON]"),user.parm.rp=POFF); break; case '6': do ask ("\nEnter new password, or RETURN if no change -> ",buffer,PASSLEN,UP+NOECHO+NUMBER); /* no spaces, or number for first char */ while (isspc(buffer) || isdigit(*buffer)); if (*buffer!='\0') { ask("\nNow enter it again for verification -> ",buffer+50,PASSLEN,UP+NOECHO+NUMBER); if (!strcmp(buffer,buffer+50)) strcpy(user.pass,buffer); else send("[Incorrect. Password was NOT changed]\n"); } else send("\n[Password NOT changed]"); break; case '7': ask("\nTerminal Height (0=no pause)? ",buffer,5,UP); if (*buffer!='\0') user.parm.height=height=atoi(buffer); else send("\n[Height not changed]"); break; case '8': ask("\nTerminal width? ",buffer,5,UP); if (*buffer!='\0') { if (atoi(buffer)<20) send("\n[Must be >=20, unchanged]"); else user.parm.width=atoi(buffer); } else send("\n[Width not changed]"); break; case '\0': break; case '?': disuser(); break; default: send("\n[Invalid option]"); } /* switch */ } while (*temp!='\0'); ask("\nMake changes permanent for future calls (y/n)? ",temp,2,UP); if (*temp=='Y') { char temp_time[TIMELEN+1],temp_date[DATELEN+1]; send("\n[Saving]"); n=user.lastread; user.lastread=nextmsg ? nextmsg-1 : 0; strcpy(temp_time,user.time); strcpy(temp_date,user.date); strcpy(user.time,time); strcpy(user.date,date); putuser(user.number); /* write new user info back to file */ strcpy(user.time,temp_time); strcpy(user.date,temp_date); user.lastread=n; /* restore last read msg number */ } } /* saveuser */ /* write user specified to correct place in users file, 0=write to current possition in the OPEN users file */ putuser(un) register unsigned un; { register int *tnum; register int flag; if ((users=open(USERFILE,F_RW | F_LOCK))==NULL) return ERROR; /* else, open it */ tnum=bufloc(users); /* pointer to user # */ do flag=read(users,1); while (*tnum!=un && flag==128); /* til match, or eof.. */ if (flag==128) setrrec(users,-1); /* if not eof, backup one */ movmem(&user,bufloc(users),128); /* get user vars */ write(users,0); /* write record, no increment... */ close(users); /* either method called closes the users file */ return NULL; /* NULL=all's well */ } /************************* * Display message stats * *************************/ stats(n) { char temptime[TIMELEN+1];  strcpy(temptime,last_time); /* make pretty time */ timefix(temptime); sprintf(buffer,"\nYou're caller number %u (User #%u).\n\ You've called %u time(s), last one being on %s%s%s.\n\n\ There are %d active messages.",callnum,user.number,user.parm.calls, last_date,O.RTC!=NOCLOCK ? " at " : "",O.RTC!=NOCLOCK ? temptime : "", msgcount); if (send(buffer)==ERROR) return; if (privmsgs) sprintf(buffer," (%d are private)",privmsgs); else *buffer='\0'; if (send(buffer)==ERROR) return; sprintf(buffer,"\nYour last read message was %u.\n\ The current high message is %u.\n",user.lastread,nextmsg ? nextmsg-1 : 0); send(buffer); } /********************* * List callers file * *********************/ calls(n) { if (type(CALLERS)!=ERROR) /* type the callers of today */ if (user.status==SYSOP) { ask("[Purge callers file?]",buffer,2,UP); if (*buffer=='Y') { send("[Purging]"); unlink(CALLERS); } } } /* End of File */ /******************************************************************** * Kill message overlay for Metal * * File: MEKILL.C 1.20b * * Copyright (c) 1984 Tim Gary * Delphi Data Systems * All Rights Reserved. * ******************************************************************** * * 1.20b 01/06/84 Message changes fixed, and unkill to public * disallowed for users who can't leave msgs. * 1.20a 11/04/84 Version change. * 1.10e 11/03/84 Fixed for new put/get counters routine. * 1.10e 11/02/84 Msg number=parent in dead msgs deleted. * 1.10e 10/31/84 Unkill added. * 1.10b 10/02/84 Novice users bug when 'r;100;102;300' and wants * to kill a message addressed to them. * 1.10a 9/01/84 Created from MS.C, for overlay version of metal. * ********************************************************************/ #include "xpm.h" #include "hmh.h" #include "hmconfg.h" #include "ctype.h" ovmain(func,parm) int func; unsigned parm; { switch (func) { case KILL: return kill(parm); case UNKILL: return unkill(parm); default: send("\nUnknown Overlay function called for.\n"); return ERROR; } } /********************************************************* * kill(delete) a message.... first ask for confirmation * *********************************************************/ kill(mn) register unsigned mn; { char msearch(); char temp[51],tpass,killok[3],sender[FNAMELEN+LNAMELEN+2]; register unsigned msgnum; register int a; msg_record *mptr; /* for summary/message file stuff */ int n,lns; int sreq=0; int flag=FALSE; #ifdef MULTI_USER mu_update(); /* if multi user, check for new messages */ #endif if (!msgcount) { send("\nNo messages.\n"); return; } if (!mn){ sprintf(buffer,"\nKill which msg (%u-%u)? ",fmsg,lmsg); ask(buffer,temp,50,UP); msgnum=atoi(temp); if (!(sreq=msearch(temp)) && !msgnum) return; } else msgnum=mn; if ( (a=getindex(msgnum))==ERROR && msgnum && !sreq) { send("[Message NOT found]"); return; } if (a==ERROR) a=0; /* fix for loop, and search request */ *killok='Y'; /* simulte always Yes to confirm.... for later */ do { /* massive do loop */ messages=open(MESSAGES,F_RW | F_UNLOCK); setarec(messages,msg[a].seek); /* absolute seek to that pos */ if ((lns=msgheader(messages,msgnum,3))==ERROR) break; if (!sreq && lns==0) { send("\n[Message private, or not found]"); break; /* say what happened, and leave */ } else if (lns!=0 && user.parm.expert==POFF) { send("\nConfirm (y/n)? "); if (mn) { *killok=toupper(getd()); if (*killok=='Y') send("[yes]\n"); else send("[no]"); } else ask("",killok,2,UP); } /* printf("\nlns=%d, msg[a].number=%u, a=%d, mn=%u, msgnum=%u, sreq=%d\n",lns, msg[a].number,a,mn,msgnum,sreq); */ if (lns && msg[a].number) { if (*killok=='Y') { sprintf(temp,"\n[Deleting message #%u]",msg[a].number); if (send(temp)==ERROR || breakkey()) { printf("\n[*** Aborted ***]\n"); break; /* exit and close file */ } mptr=bufloc(messages); mptr->parent=mptr->reply=0; if (mptr->status==PRIVMSG) privmsgs--; mptr->status=DEADMSG; setrrec(messages,-1); /* backup */ write(messages,0); close(messages); /* now to fix summary file */ summary=open(SUMMARY,F_RW | F_UNLOCK); sprintf(bufloc(summary),"%5u",--msgcount); write(summary,1); mptr=bufloc(summary); do n=read(summary,1); while (n==128 && mptr->number!=msg[a].number); setrrec(summary,-1); /* seek relative to current */ mptr->parent=mptr->reply=0; mptr->status=DEADMSG; /* will be used later */ write(summary,0); /* to kill message */ close(summary); /* following two compares are for special reply handling... they will be slower * than killing msgs without replys since up to two added disk operations * are needed for killing of a reply */ if (msg[a].parent!=0) /* this is a reply msg ? */ { /* yep */ msg[(n=getindex(msg[a].parent))].reply=msg[a].reply; /* move this msg reply to parent reply... */ updatemsg(n); /* rewrite parent message... */ } if (msg[a].reply!=0) /* is there reply to this message ?? */ { /* uh huh */ msg[(n=getindex(msg[a].reply))].parent=msg[a].parent; /* move this msg parent to reply parent... */ updatemsg(n); /* rewrite parent msg */ } flag=TRUE; msg[a].parent=msg[a].reply=0; } /* successfull *temp=='Y' */ } /* if lns.. */ if (!sreq || mn) break; /* stop here if no search request */ messages=close(messages); /* close this each time through */ } while (++aparent=mptr->reply=0; mptr->status=newstat; setrrec(messages,-1); /* backup */ write(messages,0); close(messages); /* now to fix summary file */ summary=open(SUMMARY,F_RW | F_UNLOCK); sprintf(bufloc(summary),"%5u",++msgcount); write(summary,1); mptr=bufloc(summary); do n=read(summary,1); while (n==128 && mptr->number!=msg[a].number); setrrec(summary,-1); /* seek relative to current */ mptr->parent=mptr->reply=0; mptr->status=newstat; /* will be used later */ write(summary,0); /* to kill message */ close(summary); flag=TRUE; msg[a].parent=msg[a].reply=0; } /* successfull *unkillok=='Y' */ } /* if lns.. */ if (!sreq || mn) break; /* stop here if no search request */ messages=close(messages); } while (++areply=0; mptr->status=newstat; setrrec(messages,-1); /* backup */ write(messages,0); close(messages); /* now to fix summary file */ summary=open(SUMMARY,F_RW | F_UNLOCK); sprintf(bufloc(summary),"%5u",++msgcount); write(summary,1); mptr=bufloc(summary); do n=read(summary,1); while (n==128 && mptr->number!=msg[a].number); setrrec(sum/***************************************************************** * Articles/Notes, Misc. other functions overlay * * File: MEMISC.C 1.30xx * * Metal and Metal Message System are Trademarks and * Copyright (c) 1984 Tim Gary * All Rights Reserved. * ****************************************************************** * * 1.30xx 6/23/85 Cosmetic change in getrange text. * 1.30xx 6/19/85 A very few changes in feature(), cpm(), etc.. * 1.30xx 5/05/85 Comments routine fixed. * 1.30xx 3/07/85 Fixed for chaining to BYE program on exit.. * 1.30xx 3/03/85 ZCPR stuff added.. * 1.20b 01/18/85 Showtime put here.. * 1.20b 01/16/85 Recursive use of notes/features allowed... * 1.20b 01/06/85 Allow 'Q' as in 'K' for read silent mode. * 1.20a 11/11/84 Comments messages made more universal, and Goodbye * routine made to not display info if G;N thing * typed. * 1.20a 11/07/84 Comments routine fixed for two CONSECUTIVE CR's * to stop. * 1.10e 11/01/84 Getrange now allows to read dead msgs. * 1.10e 10/29/84 Added chat(), cpm() and goodbye() to this overlay. * * 1.10c 10/12/84 Created from MEFEAT.C 4 parameters passed * to all of these routines. * ******************************************************************/ #include "xpm.h" #include "hmh.h" #include "hmconfg.h" #include "ctype.h" /********************************************************** * Overlay main program, dispatches to contained routines. **********************************************************/ ovmain(routine,p1,p2,p3,p4) int routine,p1,p2,p3,p4; { switch(routine) { case FEATURE: return feature(p1,(char *)0,1); case GETRANGE: return get_range(p1,p2,p3,p4); case OUTMSG: return out_msg(p1,p2); case CHAT: return chat(p1); case GOODBYE: return goodbye(p1); case GOCPM: return cpm(p1); case SHOWTIME: return showtime(); default: send("\nUnknown routine called for.\n"); break; } } /* ovmain */ /***************************************** * Show current time, and time on system * *****************************************/ extern char last_time[],last_date[]; showtime() { if (O.RTC) { char ttime[TIMELEN+1],tutime[TIMELEN+1]; int mins; /* mins on system */ strcpy(tutime,user.time); timefix(tutime); readclock(); strcpy(ttime,time); timefix(ttime); mins=timecomp(time,user.time,date,user.date); sprintf(buffer,"\nToday is %s. The time is %s.\nYou've been on since %s (%d minutes).\n",date,ttime,tutime,mins); send(buffer); } else send("\nNo clock installed on this system.\n"); } /****************************************************************** * Articles function to list files in the Articles/Features ('A') * or Notes ('N') index file. Index file has a fixed format of: * chars 1-17 = filename/location of file (C format) * chars 19-end of line = Description of file (displayed * in the index * Each line must have at least 19 chars in it. A standard text * editor may be used to create the index file. Each line is * the index for one file. A line may NOT exceed 128 chars. * A maximum of 25 articles may be contained in the index files. * If the line begins with a ';', it is considered a comment line * and will be ignored.. * If the line starts with a * then the line is printed during * the menu display. ******************************************************************/ feature(ch,file_name,level) int ch; char *file_name; int level; /* level number */ { char *in[25]; /* pointer array to each filename */ char level_name[MAXLINE]; register int i,n; char tot,*tp,*space; FILE *indfile,*tf; int ret_flag; ret_flag=NULL; space=malloc(450); /* up to 25 files of 18 chars each name */ if (!space) return ERROR; /* error, return to previos level */ for (i=0; i<25; i++) in[i]=space+(18*i); /* setup pointer array */ tot=0; if (level==1) strcpy(level_name,"main"); else *level_name='\0'; do { if ( (indfile=open((ch=='A' ? FEATFILE : (ch=='N' ? NOTEFILE : file_name) ),0)) == NULL) { send("\nSorry, nothing in menu.\n"); ret_flag=ERROR; break; /* free space, and return ERROR flag */ } read(indfile,1); for (i=0; i<25; ++i) { int file_flag; do { file_flag=FALSE; /* flag for valid file to type.. */ if (fgets(buffer,indfile)==ERROR) break; for (tp=buffer; *tp=='\n' || *tp=='\r'; tp++); /* ignore cr/lf */ if (!ustrncmp(tp,";MENU ",6)) strcpy(level_name,tp+6); /* new level name */ if (*tp==';') continue; if (*tp=='*') /* display line in menu */ { printf("\n%s",tp+1); continue; } if (*tp<' ' || strlen(tp)<20) break; /* logical or phys. eof? */ sscanf(buffer,"%18s",in[i]); sprintf(buffer,"\n%2d. %s",i+1,tp+18); if (!strloc) if (send(buffer)==ERROR) break; /* allow abort */ file_flag=TRUE; /* only ok at bottom of loop */ } while (!file_flag); if (!file_flag) break; /* true abort of menu... */ } /* for */ close(indfile); /* That's all we need here for now */ if (i==0) /* nothing found */ { ret_flag=ERROR; break; } if (tottot) continue; if (n==0) { ret_flag=TRUE; break; } if ((tf=open(in[n-1],0))!=ERROR) /* open file, test for ';MENU' */ { read(tf,1); fgets(buffer,tf); close(tf); if (!ustrncmp(buffer,";MENU",5)) { /* another menu, do recursive.. */ ret_flag=feature(0,in[n-1],level+1); /* recursive */ if (ret_flag==TRUE) break; /* massive withdrawl */ } else type(in[n-1]); /* type the file if not menu */ } else send("\nSorry, that item is empty.\n"); } while (1); /* just a loop forever */ free(space); return ret_flag; } /************************************ * Get range function for read msgs * ************************************/ get_range(pmode,pfirst,plast,pinc) int *pmode,*pfirst,*plast,*pinc; { char *tp; unsigned msgnum; int bit; register int sreq; /* search request flag.. */ bit=0; do { /* silly do concept */ if (*pmode!='P') /* new message read function has no options */ { do { if (user.parm.expert!=PON) sprintf(buffer,"\nRead which message (first=%u, last=%u, '?' for help)? ",fmsg,lmsg); else sprintf(buffer,"\nRead (first=%u, last=%u)? ",fmsg,lmsg); ask(buffer,buffer,MAXLINE,UP); if (*buffer=='?') read_help(*pmode | 128); } while (*buffer=='?'); } else { /* if it is 'P' mode */ *buffer='N'; /* for new messages */ *pmode='S'; /* selective read mode */ } if (*buffer=='K') /* K must be BEFORE Q if both are used */ { sprintf(buffer,"%s",buffer+1); bit=0x80; } if (*buffer=='Q') /* flag quiet no prompts when to you mode */ { sprintf(buffer,"%s",buffer+1); bit|=0x100; } sreq=msearch(buffer); /* setup search if there is one */ if (*buffer=='N') msgnum=user.lastread+1; else { if (isdigit(*buffer)) { if ( ((msgnum=atoi(buffer))>lmsg) || (msgnum<0)) { if (*buffer!='\0') send("\n[Message out of range]\n"); strloc=0; /* clear this global pointer */ return ERROR; } } else if (sreq) { /* can have search alone */ msgnum=1; /* and start at msg 1 */ *buffer='+'; } else return ERROR; } /* 'N' else test */ if (*pmode=='S') *buffer='+'; /* Forward selective read */ if (*pmode=='R') /* Reverse selective read */ { *buffer='-'; *pmode='S'; } if (msgnum==0) ++msgnum; /* convert a zero to a one */ /* All options of modes are reduced by now, so start setting * * up values to be returned */ if ( ( (tp=index(buffer,'+')) 0) { setrrec(comments,-1); /* backup */ read(comments,0); while (getc(comments)!=EOF); setbuf(comments,getbuf(comments)-1); /* point to eof */ } fputs(buffer,comments); } else print(buffer); /* output string to printer */ do { send("->"); getl(buffer,UPLOW+MSGLINE); if (!O.PRTLOG) { fputs(buffer,comments); putc('\n',comments); } else { print(buffer); print("\r\n"); } /* send to lineprinter */ if (*buffer=='\0') { if (!cr_flag) cr_flag=TRUE; else break; } else cr_flag=FALSE; } while (1); if (!O.PRTLOG) { putc(0x1a,comments); /* CP/M EOF */ while (!eobuf(comments)) putc('\0',comments); /* fill rest with null*/ write(comments,0); /* last minute cleanup */ close(comments); } } else if (*buffer!='N') { send("\n[Returning..]\n"); return ERROR; /* didn't want to leave */ } return NULL; } /******************* * chat with sysop * *******************/ chat(mode) int mode; { register int a,cfast; register char temp; int col; /* current column count for auto wrap */ if (mode) { /* display user if no 25th status line */ putchar('\n'); if (!O.STATUSLINE) printf("%s %s is ",user.first,user.last); else wustat(" [CHAT]"); send("Yelling for Sysop "); globalchar='\0'; for (temp=0; temp<25; temp++) { if (send("\07 .\07 .\07 .")==ERROR) return; /* bell */ for (cfast=0; cfast<1000; cfast++) if (breakkey()) return; else if ((a=globalchar)==0x1b) /* ESC hit by sysop? */ { wustat(0); /* put back just user info */ send("\nThe Sysop is available, please go ahead...\n"); break; } if (send("\b\07\b\b\07\b\b\07")==ERROR) return; /* backup most of the way */ if (a==0x1b) break; /* ESCAPE is Sysop's "I'm here" char */ } if (!a) { send("\nThe Sysop is currently not available.\n"); askcomments("Would you like to leave private comments to the System Operator? "); return; } } /* end mode check... if mode=0 then just start chat mode */ globalchar=0; /* for next pass through */ send("\n** [^K to abort chat] **\n"); col=1; /* first column */ while((cfast=getchar())!=11) /* while not ^K */ { if (cfast=='\r') { col=1; putchar('\n'); } else if (cfast=='\b' || cfast==0x7f) { --col; send(" \b"); } else { if ( ( ++col>user.parm.width-7 && (isspace(cfast) || ispunct(cfast)) ) || (col>user.parm.width-1) ) { col=1; send("\n"); /* force CRLF */ } } } putchar('\n'); } /* Write user status line info (name, stat, time called, city).. */ wustat(s) char *s; { char buf[128]; sprintf(buf,"%8s -- %s %s - '%c' %s -- %s",s ? s : "",user.first, user.last,user.status,time,user.city); writestat(buf); } /******************* * jump to CP/M... * *******************/ cpm(mode) register int mode; /* if mode=1 then ask if leave comments */ { #ifdef Z3 char **msgbuff; #endif if (O.user_types[user.type].maxuser==0 || !index("+snxabc",user.status) ) { send("\nYou don't have access to the operating system."); return; } if (mode==1) if (askcomments(0)==ERROR) return; /* didn't want to leave */ if (user.parm.expert!=PON) type(CPMINFO); /* experts know this already */ *tout=O.user_types[user.type].timeout; *maxuser=O.user_types[user.type].maxuser; if (O.ZCPR) if (O.user_types[user.type].zcprflag==YES) if (O.ZCPR!=3) *O.SECURELOC=255; else *(z3env->wheel)=255; #ifdef Z3 msgbuff=z3env->msg+0x48; *msgbuff=(char *)0; /* clear this anytime we go here.. */ #endif #ifndef MULTI_USER *(char *)0=0xc3; #endif sprintf(buffer,"\nYou have access thru user area %d.\n\n\ [Entering Operating System]",*maxuser); send(buffer); exit(0); /* enter OS */ } /******************************* * Leave the system *******************************/ goodbye(mode) register int mode; { if (mode==1) /* if 'g' entered. else no comments */ if (askcomments(0)==ERROR) return; /* didn't want to leave */ printf("\n%s (TM)\nCopyright (c) 1984 Tim Gary\nAll rights reserved.\n\n", BBSNAME); if (strcmp(BYE,"NONE")) /* If the file is called NONE, then do old method */ linkbye(); /* used if there is a file defined in this field */ #ifndef MULTI_USER *(char *)0=0xcd; /* to loggout with bye */ #endif exit(0); /* go to cpm and exit */ } /* EOF MEMISC.C */ 3 msgbuff=z3env->msg+0x48; *msgbuff=(char *)0; /* clear this anytime we go here.. */ #endif #ifndef MULTI_USER *(char *)0=0xc3; #endif sprintf(buffer,"\nYou have access thru user area %d.\n\n\ [Entering Operating System]",*maxuser); send(buffer); exit(0); /* enter OS */ } /******************************* * Leave the system *******************************/ goodbye(mode) register int mode; { if (mode==1) /* if 'g' entered. else no comments */ if (askcomments(0)==ERROR) re/****************************************************************************** * * Enter program for METAL/Z-MSG System * * FILE: MENTER.C version 1.30xx of this file * * Copyright (c) 1984 Tim Gary * All Rights reserved. * * * This file gets the users' name/password/etc.. * ****************************************************************************** * * 1.30xx 6/23/85 wustat routine moved here.. * 1.30xx 6/19/85 Askdate routine moved here.. g_restore stuff done in main. * 1.30xx 6/19/85 Putuser routine not used anymore.. user saved directly.. * 1.30xx 6/12/85 + can be typed on one OS command line * for a quicker sysop toggle.. * 1.30xx 6/09/85 New bye save stuff deleted, change for new save stuff.. * 1.30xx 6/01/85 rsvstk reserves more space.... 512 more.. * 1.30xx 5/30/85 rsvstk called to make sure 'msg' array don't get clobbered. * 1.30xx 5/25/85 msg pointer gets setup here for Z3.. (top of memory) * 1.30xx 5/19/85  Read clock before getting user stuff if RTC != NOCLOCK. * 1.30xx 5/12/85 Things should be working better.. * 1.30xx 5/11/85 Uh.. hopefully works with bye.. * 1.30xx 5/09/85 Protect mode works fine!!! * 1.30xx 5/08/85 Fix bug in saving cmdline.. * 1.30xx 5/05/85 Path stuff changed, protection stuff added. * 1.30xx 5/03/85 Path stuff added for aliases. * 1.30xx 5/02/85 Z3BUG turned off, Z3MSG_OFFSET added, last_date/time * put in g_save.. * 1.30xx 5/01/85 Message buffer pointers fixed/Saved buffers locs corrected. * 1.30xx 4/28/85 Debug (Z3) stuff added.. * 1.30xx 4/26/85 Restoration of command buffer added for Z3. * 1.30xx 3/13/85 Return from OS bug fixed.. * 1.30xx 3/07/85 BYE link stuff added, and also BBSNAME define. * 1.30xx 2/21/85 Start Z3 additions. * 1.20b 01/18/85 Last time called bug fixed.. * 1.20b 01/11/85 User.time/date saved in different order. * 1.20b 01/05/85 Fast status change if 'METAL +' issued.. Password length * bug fixed. Log twit calls IF default user type is TWIT. * 1.20a 11/11/84 Release version. * 1.20a 11/07/84 Lastmsg fixes. * 1.20a 11/04/84 Wrapping up changes from past few weeks, added new info * to lastcalr file. * 1.10e 11/03/84 Fixed for new pcounters/gcntrs. Moved writestat to hmlib. * 1.10e 10/30/84 Version # changed. * 1.10d 10/21/84 First time setup bug created in 1.10a/b (in meinfreq) fixed. * 1.10c 10/17/84 Status line support for non i/o mapped terminals. * 1.10b 10/01/84 Version change needed to keep track. Spelling error * corrected. * 1.10a 9/27/84 User # and ID info displayed, help added at name prompt. * 1.10a 9/09/84 Fixed last message read bug (for overlays). * 1.10a 8/31/84 Overlay version started, this WHOLE file is now and overlay. * * 1.01a 7/02/84 Multiuser lastcalr file items installed (whew). * 1.01a 6/10/84 Fixed for Aztec C 1.06. Will not function under earlier * versions of the compiler due to new/changed functions. * * 1.0b 4/13/84 Modified to allow Upper and Lower case names. * * 1.0a 3/16/84 First release version.. fixed jump to cpm bug. * * 1.0 2/08/84 Random seek to user # (if entered) implemented. * Format for users/messages/summary changed. * * 12/13/83 Pre-release Version 3.0 Mconfig operation mods. * *****************************************************************************/ #define TITLE "\n\nEnter Version 1.30a\n" #include "xpm.h" /* get small os i/o routine header file */ #include "hmh.h" /* get header file */ #include "hmconfg.h" /* get options/config */ #include "ctype.h" /* isxxxxxxx stuff now defines.. */ static int dead_rec_no=0; /* for new user, write over old '0' user */ static int dead_user_no=0; /* dead user #, above is record in file */ static char tpass[PASSLEN+1]; /**************** * Main routine * ****************/ ovmain(argc, argv) register int argc; register char *argv[]; { unsigned tlastread; char **m_buff; if (argc>=2 && (!strcmp(argv[1],"+") || !strcmp(argv[1],"!")) ) /* check for quick sysop status */ { if (argc==3) strcpy(buffer,argv[2]); else ask("Prove it!",buffer,PASSLEN+1,UP+NOECHO); if (!strcmp(buffer,O.SOPASS)) { user.status=SYSOP; user.type=get_type(SYSOP); send("[Sysop Toggle ON]\n"); findbye(); gocpm(0); } else { send("[Sorry...]"); exit(0); /* don't change a thing */ } } argc=1; /* so that MEINFREQ is used to read scan messages.. */ init(); gcntrs(0); /* get counters */ if (O.RTC!=NOCLOCK) readclock(); /* get time here if RTC being used */ getuser(); /* gets user name and other vital info */ /* NOTE::: The users file has been opened in getuser, and all we do now * is get the time/date, and write the users info into the files.. * remember, they are already open and putuser() closes them.. */ putchar('\n'); if (O.RTC==NOCLOCK) askdate(); /* get date now if no clock */ send("\n[Updating logs]\n"); if (O.ASKNULLS) user.parm.nulls=*nulls+'0'; /* setup user nulls */ if (!user.parm.calls) { strcpy(user.date,date); strcpy(user.time,time); } strcpy(last_date,user.date); strcpy(last_time,user.time); /* putlastcaller writes these */ strcpy(user.date,date); /* setup current login date/time */ strcpy(user.time,time); putlastcaller(); tlastread=user.lastread; /* save real current lastmsg count */ user.lastread=(nextmsg ? nextmsg-1 : 0); /* new user.lastread msg */ user.parm.calls++; if (user.parm.width<20 || user.parm.width>132) user.parm.width=O.INITLENGTH; movmem(&user,bufloc(users),128); /* write to open users file, and close */ write(users,0); close(users); user.lastread=tlastread; /* restore real lastread msg (overlay req.) */ ++callnum; /* add to # of callers variable */ putcaller(); /* write caller to callers file or printer (PRTLOG?) */ pcounters(0); /* write counter info */ /* logoff a TWIT now (timeout=0, then can't get on the system */ #ifndef MULTI_USER if (O.user_types[user.type].timeout==0) { if (strcmp(BYE,"NONE")) exit(); else linkbye(); } #endif if (user.parm.jmpcpm==PON) gocpm(1); /* setup parms and goto CP/M */ /* NOTE: with overlays, all we do is return, and that'll put us back main */ return argc; /* tells whether or not to be quiet or not */ } /* main */ /* get date from keyboard (for those without a clock) */ askdate() { int temp; strcpy(time,"00:00:00"); if (!strloc || tolower(glbstr[strloc])!='y') { printf("\nIs %s today's date [y/n]? ",*date=='\0' ? "00/00/00" : date); do temp=tolower(getd()); while (temp!='y' && temp!='n'); if (temp=='y') send("[yes]\n"); else { /* no need for this really */ send("[no]\n"); do ask("What's todays date (mm/dd/yy)? ",date,DATELEN,UP); while (strlen(date)!=DATELEN-1); } } else if (strloc) ask("",buffer,1,UPLOW); /* kill 'y' */ return date; } /* askdate */ gocpm(i) int i; /* if 0, no cpminfo shown, else shown for novice users */ { height=user.parm.height; if (i && user.parm.expert!=PON) type(CPMINFO); *tout=O.user_types[user.type].timeout; *maxuser=O.user_types[user.type].maxuser; if (O.ZCPR) if (O.user_types[user.type].zcprflag) { if (O.ZCPR!=3) *O.SECURELOC=255; else *(z3env->wheel)=255; } #ifndef MULTI_USER *(char *)0=0xc3; /* avoids caller logout for BYE */ #endif sprintf(buffer,"\nYou have access thru user %d.\n\n\[Entering OS]",(int)O.user_types[user.type].maxuser); send(buffer); exit(0); } /****************************** * Initialize the whole works * ******************************/ init() { #ifndef MULTI_USER *(char *)0=0xcd; /* disable ^C for warm boot, uses BYE program method */ #endif findbye(); /* setup bye, and related, variables */ *maxuser=1; /* setup maxuser and define variable */ *tout=3; /* setup 3 minute timeout */ if (!O.ASKNULLS) /* if bye asked for nulls, don't reset... */ *nulls=0; /* no nulls */ send(TITLE); /* sign-on info */ putchar('\n'); send(O.WHERE); putchar('\n'); if (msg==0) /* message allocated by settop in main */ { register char *zero=0; send("\nAllocation ERROR: Memory full.\n"); if (user.status==SYSOP) send("\n** Use the Configuration program and decrease total # of msgs **\n"); else { send("\n** Sorry, but the BBS is in need of repair. Please try it later! **"); *zero=0xcd; /* make sure bye hangs up on this one.. */ } if (!strcmp(BYE,"NONE")) exit(0); else linkbye(); } type(BULLETIN); /* display any recent changes, BULLETIN is a file */ if (O.ZCPR) if (O.ZCPR!=3) *O.SECURELOC=0; /* if using ZCPR in secure mode, then make */ else *(z3env->wheel)=0; } /* init */ /* This is shown should the person type in a name of one char (like '?') */ /* when prompted for their name or Metal Id. */ login_help() { static char *lo_help[] = { "\nIf this is your first time on this system,\n", "please enter your full name.\n", "\nIf you have called this system before, either\n", "enter your full name, or your system access\n", "code (SYSTEM ID code). The access code should\n", "have been given to you the first time you logged\n", "onto the system. The format of a SYSTEM ID is:\n", " \n", "So, if you are user # 453, and your password is ROCKER\n", "you could enter 453ROCKER in place of your name to speed\n", "up the login process.\n", "\nUse the '#' command on the BBS to find out what your\n", "user # is if you've forgotten it.\n\n", 0 }; dis_text(lo_help); } /* Getuser gets the users name, and password, or if new user gets info * to be used in later logons. A check is made for sysop, and special * parameters are then set-up. */ char temp1[64]; getuser() { register int cfast; /* fast c variable */ register int tries,usernum; int dir; /* search direction */ char first[FNAMELEN+1],last[LNAMELEN+1]; /* temp name */ char temp[MAXLINE+1]; tries=0; /* number of unsuccessful tries (wrong password tries) */ do { /* main input name/etc loop */ usernum=-1; /* no user number yet, if ever */ *tpass='\0'; /* no temp password yet */ /* PRIVATE SYSTEM ONLY STUFF */ if (O.PRIVATE) { do { ask("SYSTEM ID CODE (not personal ID): ",temp,PASSLEN,UP); if (!strcmp(O.PRIVPASS,temp)) break; } while (++tries=O.MAXTRIES) { type(SYSINFO); /* info about this private system before quiting */ exit(); } else tries=0; /* reset to 0 for name test */ } /* private */ /* Here we actually get the name of the person.. can give both first and last names on a line, or seperately, or a user id.. */ getfirst: do { *last='\0'; /* terminate last name initially */ /* get first name or user # and password, TRUE says convert to upper */ sepstr=' '; /* seperator char is space for first/last name sep */ ask("\nWhat's your name (or SYSTEM ID)? ",first,FNAMELEN,UPLOW); sepstr='\0'; capstr(first); /* capitalize first name */ if (!isdigit(*first) && strlen(first)<2) login_help(); /* print login help msg */ } while(!isdigit(*first) && strlen(first)<2); if (ustrcmp(first,"SYSOP")==0) *last='\0'; else if ( ( !isdigit(*first)) && (*last=='\0') ) { ask("\nWhat's your LAST name? ",last,LNAMELEN,UPLOW); if (*last=='\0') goto getfirst; /* if return pressed, start over */ } capstr(last); /* check for sysop */ if ( ((ustrcmp(first,"SYSOP")==0) && (*last=='\0')) || ( (ustrcmp(first,O.SOFIRST)==0) && (ustrcmp(last,O.SOLAST)==0) ) || (atoi(first)==1) ) { ask("\nSysop Pass? ",tpass,PASSLEN,UP+NOECHO); /* get sysop pass */ if (strcmp(tpass,O.SOPASS)!=0) /* not sysop? */ { tries+=2; /* double penalty for false sysop try */ continue; /* bad practice I think */ } send("\n[Ok]"); /* say your ok so far */ *tpass='\0'; /* user num, not entered */ strcpy(first,O.SOFIRST); strcpy(last,O.SOLAST); /* for sysop respons */ } /* if user # was entered, get password  if any, or pass through */ if (isdigit(*first)) { usernum=atoi(first); /* if #, then get it all */ for (cfast=0; isdigit(first[cfast]); ++cfast); if (first[cfast]!='\0') strcpy(tpass,&first[cfast]); /* else get password */ dir=1; /* set flag for user number entered */ } else dir=0; if ((users=open(USERFILE,1))!=NULL) { /* open users file, and create it if it doesn't exist now */ if (dir) setarec(users,usernum-1); /* skip to a place */ if (O.PRIVATE) send("\n[Verifying ID]"); else send("\n[Checking for previous login]\n[."); /* NOTE: you can't successfully delete the sysop (first user actually) using the following method... */ dead_user_number=dead_rec_no=0; cfast=read(users,0); /* get first record */ if (cfast!=128) if (dir) { dir=-1; toeof(users); setrrec(users,-1); /* backup one */ cfast=read(users,0); /* re-read */ } do { if (cfast!=128) break; /* exit loop if eof */ /* let user know things are still being looked into */ if ((getrec(users)%25)==0) putchar('.'); /* get desired info from users file... */ if (dead_rec_no==0) dead_user_no=user.number; movmem(bufloc(users),&user,128); /* get vars, now src,dest */ if (user.number==0 && dead_rec_no==0) { dead_user_no++; /* get user after last since this one is 0 right now... */ dead_rec_no=getrec(users); } /* see if past user number */ if (dir && user.number) /* if user number entered, and this ain't 0 */ { /* check for past user, and exit if so */ if ( (dir==-1) && (usernum>user.number) ) break; else if (usernum