diff --git a/xtrn/sbj/sbj.c b/xtrn/sbj/sbj.c
new file mode 100644
index 0000000000000000000000000000000000000000..bf895c6701e29502b45d5435a32d52010e8b1dd2
--- /dev/null
+++ b/xtrn/sbj/sbj.c
@@ -0,0 +1,1871 @@
+/* SBJ.C */
+
+/* Developed 1990-1997 by Rob Swindell; PO Box 501, Yorba Linda, CA 92885 */
+
+/************************/
+/* Synchronet Blackjack */
+/************************/
+
+/*******************************************************/
+/* Multiuser Blackjack game for Synchronet BBS systems */
+/*******************************************************/
+
+/****************************************************************************/
+/* This source code is completely Public Domain and can be modified and 	*/
+/* distributed freely (as long as changes are documented).					*/
+/* It is meant as an example to programmers of how to use the XSDK			*/
+/****************************************************************************/
+
+/***********/
+/* History */
+/****************************************************************************\
+
+		Many bugs. Especially multiplayer.
+v1.0
+		Many bugs fixed. Timing problems still exist.
+v1.01
+		Fixed yet more bugs. No more timing problems. Appears bullet-proof.
+v1.02
+		Fixed dealer card up always showing card symbol (even when symbols off).
+		Added ctrl-e answer detection and user notification.
+		Fixed three 7's bug.
+		Raised maximum number of decks to 100 for large multinode systems.
+		Fixed /<CR> bug.
+		Fixed multiple split bug.
+		Fixed non-symbols being sent to other nodes bug.
+		Changed this node's hands to say "You" instead of the user name.
+v1.03
+		Changed the warning and timeout times
+v1.04
+		Fixed symbols being displayed on dealer's hand even when disabled.
+		Made different inactivity warning and timeout values for the main
+		menu and when in play.
+v1.05
+		Fixed invalid (usually negative) card bug. THELP random() doc error.
+		Card now actually contains all the cards minus one.
+		Fixed multinode play join and hang bug.
+v1.06
+		If player gets blackjack and dealer gets 21, player wins. Used to push.
+v1.07
+		Fixed split, then double bug.
+v1.08
+		Replaced bioskey(1) calls with inkey() and used XSDK v2.0 with node
+		intercommunication with users on BBS or in other external programs.
+v2.00
+		Fixed problem with loosing first character of chat lines
+		Added DESQview awareness
+v2.01
+		Replaced all calls to delay() with fdelay()
+v2.02
+		Listing users now displays what external program they're running.
+		Fixed problem with max bet being too small when users have over
+		65mb of credit.
+v2.02
+		XSDK (and consequently SBJ) now supports shrinking Synchronet to run
+		(available in v1b r1).
+		SBBSNODE environment variable will be used for the current node's dir
+		if the node dir is not specified on the command line.
+v2.03
+		XSDK and SBJ now support the new message services of Synchronet
+		(added in v1b r2) for more full-proof internode messaging.
+v2.10
+		New XSDK that supports new file retrieval node status display.
+v2.11
+		Changed getnodemsg to eliminate tiny void where messages could fall.
+
+
+\****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "xsdk.h"
+
+#define MAX_DECKS	100
+#define MAX_CARDS	10		/* maximum number of cards per hand */
+#define MAX_HANDS	4		/* maximum number of hands per player */
+
+#define DEBUG 0
+
+#define J 11	/* jack */
+#define Q 12	/* queen */
+#define K 13	/* king */
+#define A 14	/* ace */
+
+#define H 0		/* heart */
+#define D 1 	/* diamond */
+#define C 2		/* club */
+#define S 3		/* spade */
+									/* bits used in misc variable */
+#define INPLAY		(1<<0)			/* hand in play */
+
+enum {								/* values for status bytes */
+	 BET							/* betting */
+	,WAIT							/* waiting for turn */
+	,PLAY							/* playing his hand */
+	,SYNC_P 						/* In sync area - player */
+	,SYNC_D 						/* In sync area - dealer */
+	};
+
+typedef struct { char value, suit; } card_t;
+
+card_t newdeck[52]={
+	 2,H, 2,D, 2,C, 2,S,
+	 3,H, 3,D, 3,C, 3,S,
+	 4,H, 4,D, 4,C, 4,S,
+	 5,H, 5,D, 5,C, 5,S,
+	 6,H, 6,D, 6,C, 6,S,
+	 7,H, 7,D, 7,C, 7,S,
+	 8,H, 8,D, 8,C, 8,S,
+	 9,H, 9,D, 9,C, 9,S,
+	10,H,10,D,10,C,10,S,
+	 J,H, J,D, J,C, J,S,
+	 Q,H, Q,D, Q,C, Q,S,
+	 K,H, K,D, K,C, K,S,
+	 A,H, A,D, A,C, A,S };
+
+uchar	misc;
+uchar	curplayer;
+uchar	total_decks,sys_decks;
+uchar	total_nodes;
+int 	cur_card;
+uchar	dc;
+card_t	dealer[MAX_CARDS];
+int 	gamedab;					 /* file handle for data file */
+card_t	card[MAX_DECKS*52];
+card_t	player[MAX_HANDS][MAX_CARDS];
+char	hands,pc[MAX_HANDS];
+uchar	total_players;
+uchar	symbols=1;
+char	autoplay=0;
+int 	logit=0,tutor=0;
+uint	node[MAX_NODES];   /* the usernumber in each node */
+char	status[MAX_NODES];
+ulong	credits;
+uint	bet[MAX_HANDS],ibet,min_bet,max_bet;
+char	tmp[81];
+char	*UserSays="\1n\1m\1h%s \1n\1msays \"\1c\1h%s\1n\1m\"\r\n";
+char	*UserWhispers="\1n\1m\1h%s \1n\1mwhispers \"\1c\1h%s\1n\1m\"\r\n";
+char	*ShoeStatus="\r\n\1_\1w\1hShoe: %u/%u\r\n";
+
+#ifndef SBJCLEAN
+
+void play(void);
+char *cardstr(card_t card);
+char hand(card_t card[MAX_CARDS], char count);
+char soft(card_t card[MAX_CARDS], char count);
+char pair(card_t card[MAX_CARDS], char count);
+void getgamedat(char lockit);
+void putgamedat(void);
+void getcarddat(void);
+void putcarddat(void);
+void shuffle(void);
+void waitturn(void);
+void nextplayer(void);
+char lastplayer(void);
+char firstplayer(void);
+void getnodemsg(void);
+void putnodemsg(char *msg,char nodenumber);
+void putallnodemsg(char *msg);
+void syncplayer(void);
+void syncdealer(void);
+void moduserdat(void);
+char *hit(void);
+char *stand(void);
+char *doubit(void);
+char *split(void);
+void open_gamedab(void);
+void create_gamedab(void);
+char *activity(char status_type);
+void chat(void);
+void listplayers(void);
+char *joined(void);
+char *left(void);
+void strip_symbols(char *str);
+void debug(void);
+
+/****************************************************************************/
+/* Entry point																*/
+/****************************************************************************/
+int main(int argc, char **argv)
+{
+	char str[81],ch,*p;
+	int i,file;
+	FILE *stream;
+
+node_dir[0]=0;
+for(i=1;i<argc;i++)
+	if(!stricmp(argv[i],"/L"))
+		logit=1;
+	else if(!stricmp(argv[i],"/T"))
+		tutor=2;
+	else if(!stricmp(argv[i],"/S"))
+		tutor=1;
+	else strcpy(node_dir,argv[i]);
+
+p=getenv("SBBSNODE");
+if(!node_dir[0] && p)
+	strcpy(node_dir,p);
+
+if(!node_dir[0]) {	  /* node directory not specified */
+	bputs("usage: sbj <node directory> [/options]\r\n");
+	bputs("\r\noptions: L = log wins/losses for each day\r\n");
+	getch();
+	return(1); }
+
+if(node_dir[strlen(node_dir)-1]!='\\')  /* make sure node_dir ends in '\' */
+	strcat(node_dir,"\\");
+
+initdata(); 								/* read XTRN.DAT and more */
+credits=user_cdt;
+total_nodes=sys_nodes;
+
+remove("DEBUG.LOG");
+
+if((file=nopen("SBJ.CFG",O_RDONLY))==-1) {  /* open config file */
+	bputs("Error opening SBJ.CFG\r\n");
+	pause();
+	return(1); }
+if((stream=fdopen(file,"rb"))==NULL) {      /* convert to stream */
+	bputs("Error converting SBJ.CFG handle to stream\r\n");
+	pause();
+	return(1); }
+fgets(str,81,stream);						/* number of decks in shoe */
+total_decks=sys_decks=atoi(str);
+fgets(str,81,stream);						/* min bet (in k) */
+min_bet=atoi(str);
+fgets(str,81,stream);						/* max bet (in k) */
+max_bet=atoi(str);
+fgets(str,81,stream);						/* default bet (in k) */
+ibet=atoi(str);
+fclose(stream);
+if(!total_decks || total_decks>MAX_DECKS) {
+	bputs("Invalid number of decks in SBJ.CFG\r\n");
+	pause();
+	return(1); }
+if(!max_bet) {
+	bputs("Invalid max bet in SBJ.CFG\r\n");
+	pause();
+	return(1); }
+if(min_bet>max_bet) {
+	bputs("Invalid min bet in SBJ.CFG\r\n");
+	pause();
+    return(1); }
+if(ibet>max_bet || ibet<min_bet) {
+	bputs("Invalid default bet in SBJ.CFG\r\n");
+	pause();
+    return(1); }
+
+if(!fexist("CARD.DAB")) {
+	cur_card=0;
+	dc=0;
+	memset(dealer,0,sizeof(dealer));
+	memset(card,0,sizeof(card));
+	putcarddat(); }
+else {
+	getcarddat();
+	if(total_decks!=sys_decks) {
+		remove("CARD.DAB");
+		total_decks=sys_decks;
+		putcarddat(); } }
+
+if(!fexist("GAME.DAB"))         /* File's not there */
+	create_gamedab();
+
+open_gamedab();
+
+getgamedat(0);
+if(total_nodes!=sys_nodes) {  /* total nodes changed */
+	close(gamedab);
+	total_nodes=sys_nodes;
+	create_gamedab();
+	open_gamedab(); }
+
+randomize();
+
+while(_bios_keybrd(1))	 /* clear input buffer */
+	_bios_keybrd(0);
+putchar(5); /* ctrl-e */
+mswait(500);
+if(_bios_keybrd(1)) {
+	while(_bios_keybrd(1))
+		_bios_keybrd(0);
+	bputs("\r\n\1r\1h\1i*** ATTENTION ***\1n\1h\r\n");
+	bputs("\r\nSynchronet Blackjack uses Ctrl-E (ENQ) for the 'club' card "
+		"symbol.");
+	bputs("\r\nYour terminal responded to this control character with an "
+		"answerback string.");
+	bputs("\r\nYou will need to disable all Ctrl-E (ENQ) answerback "
+		"strings (Including \r\nCompuserve Quick B transfers) if you wish to "
+		"toggle card symbols on.\r\n\r\n");
+	symbols=0;
+	pause(); }
+
+cls();
+bputs("\1n \1h\1cSynchronet\r\n");
+bputs("\1n\0011\1k Blackjack! \r\n");
+bputs("\1n\1r\1h   v2.33\r\n");
+bprintf("\1n\1r(XSDK v%s)\r\n",xsdk_ver);
+
+getgamedat(1);
+node[node_num-1]=0;
+putgamedat();
+
+sec_warn=120;	/* Override default inactivity timeout values */
+sec_timeout=180;
+
+while(1) {
+	aborted=0;
+	mnemonics("\r\n~Instructions\r\n");
+	mnemonics("~Join Game\r\n");
+	mnemonics("~List Players\r\n");
+	mnemonics("~Rules of the Game\r\n");
+	mnemonics("~Toggle Card Symbols\r\n");
+	sprintf(str,"~Quit to %s\r\n",sys_name);
+	mnemonics(str);
+	nodesync();
+	bprintf("\1_\r\n\1y\1hWhich: \1n");
+	switch(getkeys("IJLRTQ|!",0)) {
+		#if DEBUG
+		case '!':
+			if(!com_port)
+				autoplay=1;
+			break;
+		case '|':
+			debug();
+			break;
+		#endif
+		case 'I':
+			printfile("SBJ.MSG");
+			break;
+		case 'L':
+			listplayers();
+			bprintf(ShoeStatus,cur_card,total_decks*52);
+			break;
+		case 'R':
+			bprintf("\1n\1c\r\nMinimum bet: \1h%uk",min_bet);
+			bprintf("\1n\1c\r\nMaximum bet: \1h%uk\r\n",max_bet);
+			bprintf("\1w\1h\r\nCard decks in shoe: \1h%u\r\n",sys_decks);
+			break;
+		case 'T':
+			symbols=!symbols;
+			bprintf("\1_\1w\r\nCard symbols now: %s\r\n",symbols ? "ON":"OFF");
+			break;
+		case 'Q':
+			exit(0);
+		case 'J':
+			sec_warn=60;	/* Override default inactivity timeout values */
+			sec_timeout=90;
+			play();
+			sec_warn=120;
+			sec_timeout=180;
+			break; } }
+}
+
+#if DEBUG
+void debug()
+{
+	int i;
+
+if(user_level<90)
+	return;
+getgamedat(0);
+getcarddat();
+
+bprintf("\r\nDeck (%d) Current: %d\r\n\r\n",total_decks,cur_card);
+for(i=0;i<total_decks*52;i++) {
+	if(!(i%11))
+		bputs("\r\n");
+	bprintf("%3d:%-11s",i,cardstr(card[i])); }
+
+pause();
+bprintf("\1n\r\nDealer (%d)\r\n\r\n",dc);
+for(i=0;i<dc;i++)
+	bprintf("%s ",cardstr(dealer[i]));
+bprintf("\1n\r\nNodes (%d) Current: %d\r\n\r\n"
+	,total_nodes,curplayer);
+for(i=0;i<total_nodes;i++)
+	bprintf("%d: node=%d status=%d %s\r\n",i+1,node[i]
+		,status[i],activity(status[i]));
+}
+
+void debugline(char *line)
+{
+	char str[256];
+	int file;
+	time_t now;
+	struct dosdate_t date;
+	struct dostime_t curtime;
+
+#if 1
+now=time(NULL);
+unixtodos(now,&date,&curtime);
+if((file=nopen("DEBUG.LOG",O_WRONLY|O_APPEND|O_CREAT))==-1)
+	return;
+sprintf(str,"%d %02u:%02u:%02u %s\r\n"
+	,node_num,curtime.ti_hour,curtime.ti_min,curtime.ti_sec,line);
+write(file,str,strlen(str));
+close(file);
+#endif
+}
+
+#endif
+
+void suggest(char action)
+{
+bputs("Dealer suggests you ");
+switch(action) {
+	case 'H':
+		bputs("hit");
+		break;
+	case 'S':
+		bputs("stand");
+		break;
+	case 'D':
+		bputs("double");
+		break;
+	case 'P':
+		bputs("split");
+		break; }
+bputs("\r\n");
+}
+
+void wrong(char action)
+{
+sound(100);
+mswait(500);
+nosound();
+bputs("Dealer says you should have ");
+switch(action) {
+	case 'H':
+		bputs("hit");
+		break;
+	case 'S':
+		bputs("stood");
+		break;
+	case 'D':
+		bputs("doubled");
+		break;
+	case 'P':
+		bputs("split");
+		break; }
+bputs("\r\n");
+}
+
+/****************************************************************************/
+/* This function is the actual game playing loop.							*/
+/****************************************************************************/
+void play()
+{
+	char str[256],str2[256],log[81],done,doub,dh,split_card,suggestion
+		,*YouWereDealt="\1n\1k\0015 You \1n\1m were dealt: %s\r\n"
+		,*UserWasDealt="\1n\1m\1h%s\1n\1m was dealt: %s\r\n"
+		,*YourHand="\1n\1k\0015 You \1n\1m                     (%2d) %s"
+		,*UserHand="\1n\1m\1h%-25s \1n\1m(%2d) %s"
+		,*DealerHand="\1n\1hDealer                    \1n\1m(%2d) "
+		,*Bust="\1n\1r\1hBust\1n\r\n"
+		,*Natural="\1g\1h\1iNatural "
+		,*Three7s="\1r\1h\1iThree 7's "
+		,*Blackjack="\1n\0011\1k Blackjack! \1n\r\n"
+		,*TwentyOne="\1n\0012\1k Twenty-one \1n\r\n";
+	int h,i,j,file;
+	uint max;
+	long val;
+	time_t start,now;
+	struct dosdate_t date;
+
+sprintf(str,"MESSAGE.%d",node_num);         /* remove message if waiting */
+if(fexist(str))
+    remove(str);
+
+getgamedat(0);
+if(node[node_num-1]) {
+	getgamedat(1);
+	node[node_num-1]=0;
+	putgamedat();
+	getgamedat(0); }
+
+if(total_players && misc&INPLAY) {
+	bputs("\r\n\1hWaiting for end of hand (^A to abort)...\1n");
+	start=now=time(NULL);
+	getgamedat(0);
+	while(total_players && misc&INPLAY) {
+		if((i=inkey(0))!=0) {	 /* if key was hit */
+			if(i==1) {		 /* if ctrl-a */
+				bputs("\r\n");
+				return; } }  /* return */
+		mswait(100);
+		getgamedat(0);
+		now=time(NULL);
+		if(now-start>300) { /* only wait up to 5 minutes */
+			bputs("\r\ntimeout\r\n");
+			return; } }
+	bputs("\r\n"); }
+
+getgamedat(1);
+node[node_num-1]=user_number;
+putgamedat();
+
+if(!total_players)
+    shuffle();
+else
+	listplayers();
+
+sprintf(str,"\1n\1m\1h%s \1n\1m%s\r\n",user_name,joined());
+putallnodemsg(str);
+
+while(1) {
+	aborted=0;
+	#if DEBUG
+	debugline("top of loop");
+	#endif
+	if(autoplay)
+		lncntr=0;
+	bprintf(ShoeStatus,cur_card,total_decks*52);
+	if(cur_card>(total_decks*52)-(total_players*10)-10 && lastplayer())
+		shuffle();
+	getgamedat(1);
+	misc&=~INPLAY;
+	status[node_num-1]=BET;
+	node[node_num-1]=user_number;
+	putgamedat();
+
+	bprintf("\r\n\1n\1cYou have \1h%s\1n\1ck credits\r\n"
+		,ultoac(credits/1024L,str));
+	if(credits<min_bet/1024) {
+		bprintf("\1n\1cMinimum bet: \1h%uk\r\n",min_bet);
+		bputs("\1n\1r\1hCome back when you have more credits.\r\n");
+        break; }
+	if(credits/1024L>(ulong)max_bet)
+		max=max_bet;
+	else
+		max=credits/1024L;
+	sprintf(str,"\r\nBet amount (in kilobytes) or ~Quit [%u]: "
+		,ibet<credits/1024L ? ibet : credits/1024L);
+	chat();
+	mnemonics(str);
+	if(autoplay && _bios_keybrd(1))
+		autoplay=0;
+	if(autoplay)
+		i=ibet;
+	else
+		i=getnum(max);
+	if(i==-1)	/* if user hit ^C or 'Q' */
+		break;
+	bputs("\r\n");
+	if(i)		/* if user entered a value */
+		bet[0]=i;
+	else		/* if user hit enter */
+		bet[0]=ibet<credits/1024L ? ibet : credits/1024L;
+	if(bet[0]<min_bet) {
+		bprintf("\1n\1cMinimum bet: \1h%uk\r\n",min_bet);
+		bputs("\1n\1r\1hCome back when you're ready to bet more.\r\n");
+		break; }
+	ibet=bet[0];
+	getgamedat(0);	/* to get all new arrivals */
+	sprintf(str,"\1m\1h%s\1n\1m bet \1n\1h%u\1n\1mk\r\n",user_name,bet[0]);
+	putallnodemsg(str);
+
+	pc[0]=2;						/* init player's 1st hand to 2 cards */
+	for(i=1;i<MAX_HANDS;i++)		/* init player's other hands to 0 cards */
+		pc[i]=0;
+	hands=1;						/* init total player's hands to 1 */
+
+	getgamedat(1);					/* first come first serve to be the */
+	for(i=0;i<total_nodes;i++)		/* dealer in control of sync */
+		if(node[i] && status[i]==SYNC_D)
+			break;
+	if(i==total_nodes) {
+		#if DEBUG
+		debugline("syncdealer");
+		#endif
+		syncdealer();  }			/* all players meet here */
+	else {							/* first player is current after here */
+		#if DEBUG
+		debugline("syncplayer");
+		#endif
+		syncplayer(); } 			/* game is closed (INPLAY) at this point */
+
+	#if DEBUG
+	debugline("waitturn 1");
+	#endif
+    waitturn();
+	getnodemsg();
+										/* Initial deal card #1 */
+	getcarddat();
+	player[0][0]=card[cur_card++];
+	putcarddat();
+	sprintf(str,YouWereDealt,cardstr(card[cur_card-1]));
+	if(!symbols)
+		strip_symbols(str);
+    bputs(str);
+	sprintf(str,UserWasDealt,user_name,cardstr(card[cur_card-1]));
+    putallnodemsg(str);
+	
+	if(lastplayer()) {
+		getcarddat();
+		dealer[0]=card[cur_card++];
+		dc=1;
+		putcarddat(); }
+	nextplayer();
+	#if DEBUG
+	debugline("waitturn 2");
+	#endif
+	waitturn();
+	getnodemsg();
+
+	getcarddat();					   /* Initial deal card #2 */
+	player[0][1]=card[cur_card++];
+	putcarddat();
+	sprintf(str,YouWereDealt,cardstr(card[cur_card-1]));
+	if(!symbols)
+		strip_symbols(str);
+	bputs(str);
+	sprintf(str,UserWasDealt,user_name,cardstr(card[cur_card-1]));
+    putallnodemsg(str);
+	
+	if(lastplayer()) {
+		getcarddat();
+		dealer[1]=card[cur_card++];
+		dc=2;
+		putcarddat(); }
+	nextplayer();
+	#if DEBUG
+	debugline("waitturn 3");
+	#endif
+	waitturn();
+	getnodemsg();
+	getcarddat();
+
+	for(i=0;i<hands;i++) {
+		if(autoplay)
+			lncntr=0;
+		done=doub=0;
+		while(!done && pc[i]<MAX_CARDS && cur_card<total_decks*52) {
+			h=hand(player[i],pc[i]);
+			str[0]=0;
+			for(j=0;j<pc[i];j++) {
+				strcat(str,cardstr(player[i][j]));
+				strcat(str," "); }
+			j=bstrlen(str);
+			while(j++<19)
+				strcat(str," ");
+			if(h>21) {
+				strcat(str,Bust);
+				sprintf(str2,YourHand,h,str);
+				if(!symbols)
+					strip_symbols(str2);
+				bputs(str2);
+				sprintf(str2,UserHand,user_name,h,str);
+				putallnodemsg(str2);
+				break; }
+			if(h==21) {
+				if(pc[i]==2) {	/* blackjack */
+					if(player[i][0].suit==player[i][1].suit)
+						strcat(str,Natural);
+					strcat(str,Blackjack); }
+				else {
+					if(player[i][0].value==7
+						&& player[i][1].value==7
+						&& player[i][2].value==7)
+						strcat(str,Three7s);
+					strcat(str,TwentyOne); }
+				sprintf(str2,YourHand,h,str);
+				if(!symbols)
+					strip_symbols(str2);
+				bputs(str2);
+				sprintf(str2,UserHand,user_name,h,str);
+                putallnodemsg(str2);
+				// fdelay(500);
+				break; }
+			strcat(str,"\r\n");
+			sprintf(str2,YourHand,h,str);
+			if(!symbols)
+				strip_symbols(str2);
+			bputs(str2);
+			sprintf(str2,UserHand,user_name,h,str);
+			putallnodemsg(str2);
+			if(doub)
+				break;
+			sprintf(str,"\1n\1hDealer\1n\1m card up: %s\r\n"
+				,cardstr(dealer[1]));
+			if(!symbols)
+				strip_symbols(str);
+			bputs(str);
+
+			if(tutor) {
+				if(pc[i]==2)
+					split_card=pair(player[i],pc[i]);
+				else
+					split_card=0;
+                if(split_card==A
+					|| (split_card==9 && (dealer[1].value<7
+						|| (dealer[1].value>7 && dealer[1].value<10)))
+                    || split_card==8
+					|| (split_card==7 && dealer[1].value<9)
+					|| (split_card==6 && dealer[1].value<7)
+					|| (split_card==4 && dealer[1].value==5)
+					|| (split_card && split_card<4 && dealer[1].value<8))
+					suggestion='P';
+                else if(soft(player[i],pc[i])) {
+                    if(h>18)
+						suggestion='S';
+					else if(pc[i]==2
+						&& ((h==18
+							&& dealer[1].value>3 && dealer[1].value<7)
+                        || (h==17
+							&& dealer[1].value>2 && dealer[1].value<7)
+                        || (h>13
+							&& dealer[1].value>3 && dealer[1].value<7)
+                        || (h==12
+							&& dealer[1].value>4 && dealer[1].value<7)))
+						suggestion='D';
+                    else
+						suggestion='H'; }
+                else { /* hard */
+					if(h>16 || (h>13 && dealer[1].value<7)
+						|| (h==12 && dealer[1].value>3 && dealer[1].value<7))
+						suggestion='S';
+					else if(pc[i]==2
+						&& (h==11 || (h==10 && dealer[1].value<10)
+						|| (h==9 && dealer[1].value<7)))
+						suggestion='D';
+                    else
+						suggestion='H'; } }
+
+			if(tutor==1)
+				suggest(suggestion);
+			strcpy(str,"\r\n~Hit");
+			strcpy(tmp,"H\r");
+			if(bet[i]+ibet<=credits/1024L && pc[i]==2) {
+				strcat(str,", ~Double");
+				strcat(tmp,"D"); }
+			if(bet[i]+ibet<=credits/1024L && pc[i]==2 && hands<MAX_HANDS
+				&& player[i][0].value==player[i][1].value) {
+				strcat(str,", ~Split");
+				strcat(tmp,"S"); }
+			strcat(str,", or [Stand]: ");
+			chat();
+			mnemonics(str);
+			if(autoplay && _bios_keybrd(1))
+				autoplay=0;
+
+
+			if(autoplay) {
+				lncntr=0;
+				bputs("\r\n");
+				strcpy(str,stand());
+				bputs(str);
+				putallnodemsg(str);
+				done=1; }
+			else
+			switch(getkeys(tmp,0)) {
+				case 'H':     /* hit */
+					if(tutor==2 && suggestion!='H')
+						wrong(suggestion);
+					strcpy(str,hit());
+					bputs(str);
+					putallnodemsg(str);
+					getcarddat();
+					player[i][pc[i]++]=card[cur_card++];
+					putcarddat();
+					break;
+				case 'D':   /* double down */
+					if(tutor==2 && suggestion!='D')
+                        wrong(suggestion);
+					strcpy(str,doubit());
+					bputs(str);
+					putallnodemsg(str);
+					getcarddat();
+					player[i][pc[i]++]=card[cur_card++];
+					putcarddat();
+					doub=1;
+					bet[i]+=ibet;
+					break;
+				case 'S':   /* split */
+					if(tutor==2 && suggestion!='P')
+                        wrong(suggestion);
+					strcpy(str,split());
+					bputs(str);
+					putallnodemsg(str);
+					player[hands][0]=player[i][1];
+					getcarddat();
+					player[i][1]=card[cur_card++];
+					player[hands][1]=card[cur_card++];
+					putcarddat();
+					pc[hands]=2;
+					bet[hands]=ibet;
+					hands++;
+					break;
+				case CR:
+					if(tutor==2 && suggestion!='S')
+                        wrong(suggestion);
+					strcpy(str,stand());
+					bputs(str);
+					putallnodemsg(str);
+					done=1;
+					break; } } }
+
+	if(lastplayer()) {	/* last player plays the dealer's hand */
+		getcarddat();
+		while(hand(dealer,dc)<17 && dc<MAX_CARDS && cur_card<total_decks*52)
+			dealer[dc++]=card[cur_card++];
+		putcarddat(); }
+
+	nextplayer();
+	#if DEBUG
+	debugline("waitturn 4");
+	#endif
+	waitturn();
+	getnodemsg();
+
+	if(firstplayer()==node_num) {
+		strcpy(str,"\1n\0014\1h Final \1n\r\n");
+		bputs(str);
+		putallnodemsg(str); }
+	getcarddat();
+	dh=hand(dealer,dc); 					/* display dealer's hand */
+	sprintf(str,DealerHand,dh);
+	for(i=0;i<dc;i++) {
+		strcat(str,cardstr(dealer[i]));
+		strcat(str," "); }
+	i=bstrlen(str);
+	while(i++<50)				/* was 50 */
+		strcat(str," ");
+	if(dh>21) {
+		strcat(str,Bust);
+		if(!symbols)
+			strip_symbols(str);
+		bputs(str); }
+	else if(dh==21) {
+		if(dc==2) { 	/* blackjack */
+			if(dealer[0].suit==dealer[1].suit)
+				strcat(str,Natural);
+			strcat(str,Blackjack); }
+		else {			/* twenty-one */
+			if(dc==3 && dealer[0].value==7 && dealer[1].value==7
+				&& dealer[2].value==7)
+				strcat(str,Three7s);
+			strcat(str,TwentyOne); }
+		if(!symbols)
+            strip_symbols(str);
+		bputs(str); }
+	else {
+		if(!symbols)
+            strip_symbols(str);
+		bprintf("%s\r\n",str); }
+
+	for(i=0;i<hands;i++) {						/* display player's hand(s) */
+		h=hand(player[i],pc[i]);
+		str[0]=0;
+		for(j=0;j<pc[i];j++) {
+			strcat(str,cardstr(player[i][j]));
+			strcat(str," "); }
+		j=bstrlen(str);
+		while(j++<19)
+			strcat(str," ");
+		if(logit) {
+			_dos_getdate(&date);
+			sprintf(log,"%02d%02d%02d.LOG"                  /* log winnings */
+				,date.month,date.day,date.year-1900);
+			if((file=nopen(log,O_RDONLY))!=-1) {
+				read(file,tmp,filelength(file));
+				tmp[filelength(file)]=0;
+				val=atol(tmp);
+				close(file); }
+			else
+				val=0L;
+			if((file=nopen(log,O_WRONLY|O_CREAT|O_TRUNC))==-1) {
+				bprintf("error opening %s\r\n",log);
+				return; } }
+		if(h<22 && (h>dh || dh>21	/* player won */
+			|| (h==21 && pc[i]==2 && dh==21 && dh>2))) {	/* blackjack */
+			j=bet[i];								  /* and dealer got 21 */
+			if(h==21 && 	/* natural blackjack or three 7's */
+				((player[i][0].value==7 && player[i][1].value==7
+				&& player[i][2].value==7)
+				|| (pc[i]==2 && player[i][0].suit==player[i][1].suit)))
+				j*=2;
+			else if(h==21 && pc[i]==2)	/* regular blackjack */
+				j*=1.5; /* blackjack pays 1� to 1 */
+			sprintf(tmp,"\1n\1h\1m\1iWon!\1n\1h %u\1n\1mk",j);
+			strcat(str,tmp);
+			credits+=j*1024L;
+			val-=j*1024L;
+			moduserdat(); }
+		else if(h<22 && h==dh)
+			strcat(str,"\1n\1hPush");
+		else {
+			strcat(str,"\1nLost");
+			credits-=bet[i]*1024L;
+			val+=bet[i]*1024L;
+			moduserdat(); }
+		if(logit) {
+			sprintf(tmp,"%ld",val);
+			write(file,tmp,strlen(tmp));
+			close(file); }					/* close winning log */
+		strcat(str,"\1n\r\n");
+		sprintf(str2,YourHand,h,str);
+		if(!symbols)
+			strip_symbols(str2);
+		bputs(str2);
+		sprintf(str2,UserHand,user_name,h,str);
+		putallnodemsg(str2); }
+
+	nextplayer();
+	if(!lastplayer()) {
+		#if DEBUG
+		debugline("lastplayer waitturn");
+		#endif
+		waitturn();
+		nextplayer(); }
+	#if DEBUG
+	debugline("end of loop");
+	#endif
+	getnodemsg(); }
+
+getgamedat(1);
+node[node_num-1]=0;
+putgamedat();
+sprintf(str,"\1n\1m\1h%s \1n\1m%s\r\n",user_name,left());
+putallnodemsg(str);
+}
+
+/****************************************************************************/
+/* This function returns a static string that describes the status byte 	*/
+/****************************************************************************/
+char *activity(char status_type)
+{
+	static char str[50];
+
+switch(status_type) {
+	case BET:
+		strcpy(str,"betting");
+		break;
+	case WAIT:
+		strcpy(str,"waiting for turn");
+		break;
+	case PLAY:
+		strcpy(str,"playing");
+		break;
+	case SYNC_P:
+		strcpy(str,"synchronizing");
+		break;
+	case SYNC_D:
+		strcpy(str,"synchronizing (dealer)");
+        break;
+	default:
+		strcat(str,"UNKNOWN");
+		break; }
+return(str);
+}
+
+/****************************************************************************/
+/* This function returns the string that represents a playing card. 		*/
+/****************************************************************************/
+char *cardstr(card_t card)
+{
+	static char str[20];
+	char tmp[20];
+
+strcpy(str,"\1n\0017"); /* card color - background always white */
+if(card.suit==H || card.suit==D)
+	strcat(str,"\1r");  /* hearts and diamonds - foreground red */
+else
+	strcat(str,"\1k");  /* spades and clubs - foreground black */
+if(card.value>10)	/* face card */
+	switch(card.value) {
+		case J:
+			strcat(str,"J");
+			break;
+		case Q:
+			strcat(str,"Q");
+			break;
+		case K:
+			strcat(str,"K");
+			break;
+		case A:
+			strcat(str,"A");
+			break; }
+else {
+	sprintf(tmp,"%d",card.value);
+	strcat(str,tmp); }
+switch(card.suit) {  /* suit */
+	case H:
+		strcat(str,"\3");
+		break;
+	case D:
+		strcat(str,"\4");
+		break;
+	case C:
+		strcat(str,"\5");
+		break;
+	case S:
+		strcat(str,"\6");
+		break; }
+strcat(str,"\1n");
+return(str);
+}
+
+
+/****************************************************************************/
+/* This function returns the best value of a given hand.					*/
+/****************************************************************************/
+char hand(card_t card[MAX_CARDS],char count)
+{
+	char c,total=0,ace=0;
+
+for(c=0;c<count;c++) {
+	if(card[c].value==A) {		/* Ace */
+		if(total+11>21)
+			total++;
+		else {
+			ace++;
+			total+=11; } }
+	else if(card[c].value>=J)	/* Jack, Queen, King */
+		total+=10;
+	else						/* Number cards */
+		total+=card[c].value; }
+while(total>21 && ace) { /* ace is low if bust */
+	total-=10;
+	ace--; }
+return(total);
+}
+
+/****************************************************************************/
+/* This function returns number of soft aces in a given hand				*/
+/****************************************************************************/
+char soft(card_t card[MAX_CARDS],char count)
+{
+	char c,total=0,ace=0;
+
+for(c=0;c<count;c++) {
+	if(card[c].value==A) {		/* Ace */
+		if(total+11>21)
+			total++;
+		else {
+			ace++;
+			total+=11; } }
+	else if(card[c].value>=J)	/* Jack, Queen, King */
+		total+=10;
+	else						/* Number cards */
+		total+=card[c].value; }
+while(total>21 && ace) { /* ace is low if bust */
+	total-=10;
+	ace--; }
+return(ace);
+}
+
+/****************************************************************************/
+/* This function returns card that is paired in the hand					*/
+/****************************************************************************/
+char pair(card_t card[MAX_CARDS],char count)
+{
+	char c,d;
+
+for(c=0;c<count;c++)
+	for(d=c+1;d<count;d++)
+		if(card[c].value==card[d].value)	   /* Ace */
+			return(card[c].value);
+return(0);
+}
+
+
+/****************************************************************************/
+/* This function shuffles the deck. 										*/
+/****************************************************************************/
+void shuffle()
+{
+	char str[81];
+	uint i,j;
+	card_t shufdeck[52*MAX_DECKS];
+
+
+getcarddat();
+
+sprintf(str,"\1_\1w\1h\r\nShuffling %d Deck Shoe...",total_decks);
+bputs(str);
+strcat(str,"\r\n");     /* add crlf for other nodes */
+putallnodemsg(str);
+
+for(i=0;i<total_decks;i++)
+	memcpy(shufdeck+(i*52),newdeck,sizeof(newdeck));	  /* fresh decks */
+
+i=0;
+while(i<(total_decks*52)-1) {
+	j=random((total_decks*52)-1);
+	if(!shufdeck[j].value)	/* card already used */
+		continue;
+	card[i]=shufdeck[j];
+	shufdeck[j].value=0;	/* mark card as used */
+	i++; }
+
+cur_card=0;
+for(i=0;i<MAX_HANDS;i++)
+	pc[i]=0;
+hands=0;
+dc=0;
+putcarddat();
+bputs("\r\n");
+}
+
+/****************************************************************************/
+/* This function reads and displays a message waiting for this node, if 	*/
+/* there is one.															*/
+/****************************************************************************/
+void getnodemsg()
+{
+	char str[81], *buf;
+	int file;
+	ulong length;
+
+nodesync();
+sprintf(str,"MESSAGE.%d",node_num);
+if(flength(str)<1L) 					/* v1.02 fix */
+	return;
+if((file=nopen(str,O_RDWR))==-1) {
+	bprintf("Couldn't open %s\r\n",str);
+	return; }
+length=filelength(file);
+if((buf=malloc(length+1L))==NULL) {
+	close(file);
+	bprintf("\7\r\ngetnodemsg: Error allocating %lu bytes of memory for %\r\n"
+		,length+1L,str);
+	return; }
+buf[read(file,buf,length)]=0;
+chsize(file,0);
+close(file);
+if(!symbols)
+	strip_symbols(buf);
+bputs(buf);
+free(buf);
+}
+
+/****************************************************************************/
+/* This function creates a message for a certain node.						*/
+/****************************************************************************/
+void putnodemsg(char *msg, char nodenumber)
+{
+	char str[81];
+	int file;
+
+sprintf(str,"MESSAGE.%d",nodenumber);
+if((file=nopen(str,O_WRONLY|O_CREAT|O_APPEND))==-1) {
+	bprintf("\r\n\7putnodemsg: error opening/creating %s\r\n",str);
+	return; }
+write(file,msg,strlen(msg));
+close(file);
+}
+
+/****************************************************************************/
+/* This function creates a message for all nodes in the game.				*/
+/****************************************************************************/
+void putallnodemsg(char *msg)
+{
+	int i;
+
+for(i=0;i<total_nodes;i++)
+	if(node[i] && i+1!=node_num)
+		putnodemsg(msg,i+1);
+}
+
+/****************************************************************************/
+/* This function waits until it is the current player.						*/
+/****************************************************************************/
+void waitturn()
+{
+	time_t start,now;
+
+start=now=time(NULL);
+getgamedat(1);
+status[node_num-1]=WAIT;
+putgamedat();
+while(curplayer!=node_num) {
+	chat();
+	mswait(100);
+	getgamedat(0);
+	if(curplayer && !node[curplayer-1] /*  || status[curplayer-1]==BET */ )
+		nextplayer();		/* current player is not playing? */
+
+	if(!node[node_num-1]) { /* current node not in game? */
+		getgamedat(1);
+		node[node_num-1]=user_number;	/* fix it */
+		putgamedat(); }
+
+	now=time(NULL);
+	if(now-start>300) { /* only wait upto 5 minutes */
+		bputs("\r\nwaitturn: timeout\r\n");
+		break; } }
+getgamedat(1);
+status[node_num-1]=PLAY;
+putgamedat();
+}
+
+/****************************************************************************/
+/* This is the function that is called to see if the user has hit a key,	*/
+/* and if so, read in a line of chars and send them to the other nodes. 	*/
+/****************************************************************************/
+void chat()
+{
+	char str1[150],str2[256],ch;
+	int i;
+
+aborted=0;
+if((ch=inkey(0))!=0 || wordwrap[0]) {
+	if(ch=='/') {
+		bputs("\1n\1y\1hCommand: \1n");
+		ch=getkeys("?LS|%\r",0);
+		switch(ch) {
+			case CR:
+				return;
+			#if DEBUG
+			case '|':
+				debug();
+				return;
+			#endif
+			case '%':
+				if(!com_port)	/* only if local */
+					exit(0);
+				break;
+			case '?':
+				mnemonics("\r\n~List Players");
+				mnemonics("\r\n~Send Private Message to Player");
+				bputs("\r\n");
+				return;
+			case 'L':
+				listplayers();
+				bprintf(ShoeStatus,cur_card,total_decks*52);
+				return;
+			case 'S':
+				listplayers();
+				bputs("\1n\r\n\1y\1hWhich node: \1n");
+				i=getnum(sys_nodes);
+				getgamedat(0);
+				if(i>0 && i!=node_num && node[i-1]) {
+					bputs("\r\n\1n\1y\1hMessage: ");
+					if(getstr(str1,50,K_LINE)) {
+						sprintf(str2,UserWhispers,user_name
+							,str1);
+						putnodemsg(str2,i); } }
+				else
+					bputs("\1n\r\n\1r\1hInvalid node.\1n\r\n");
+				return; } }
+	ungetkey(ch);
+	if(!getstr(str1,50,K_CHAT|K_WRAP))
+		return;
+	sprintf(str2,UserSays,user_name,str1);
+	putallnodemsg(str2); }
+getnodemsg();
+}
+
+/****************************************************************************/
+/* This function returns 1 if the current node is the highest (or last) 	*/
+/* node in the game, or 0 if there is another node with a higher number 	*/
+/* in the game. Used to determine if this node is to perform the dealer   */
+/* function 															  */
+/****************************************************************************/
+char lastplayer()
+{
+	int i;
+
+getgamedat(0);
+if(total_players==1 && node[node_num-1])	/* if only player, definetly */
+	return(1);								/* the last */
+
+for(i=node_num;i<total_nodes;i++)			  /* look for a higher number */
+	if(node[i])
+		break;
+if(i<total_nodes)							  /* if one found, return 0 */
+	return(0);
+return(1);									/* else return 1 */
+}
+
+/****************************************************************************/
+/* Returns the node number of the lower player in the game					*/
+/****************************************************************************/
+char firstplayer()
+{
+	int i;
+
+for(i=0;i<total_nodes;i++)
+	if(node[i])
+		break;
+if(i==total_nodes)
+	return(0);
+return(i+1);
+}
+
+/****************************************************************************/
+/* This function is only run on the highest node number in the game. It 	*/
+/* waits until all other nodes are waiting in their sync routines, and then */
+/* releases them by changing the status byte from SYNC_P to PLAY			*/
+/* it is assumed that getgamedat(1) is called immediately prior.			*/
+/****************************************************************************/
+void syncdealer()
+{
+	char *Dealing="\1n\1hDealing...\r\n\1n";
+	int i;
+	time_t start,now;
+
+status[node_num-1]=SYNC_D;
+putgamedat();
+start=now=time(NULL);
+// fdelay(1000);				 /* wait for stragglers to join game v1.02 */
+getgamedat(0);
+while(total_players) {
+	for(i=0;i<total_nodes;i++)
+		if(i!=node_num-1 && node[i] && status[i]!=SYNC_P)
+			break;
+	if(i==total_nodes)		  /* all player nodes are waiting */
+		break;
+	chat();
+	mswait(100);
+	getgamedat(0);
+	if(!node[node_num-1]) { /* current node not in game? */
+		getgamedat(1);
+		node[node_num-1]=user_number;	/* fix it */
+		putgamedat(); }
+	now=time(NULL);
+	if(now-start>300) { /* only wait upto 5 minutes */
+		bputs("\r\nsyncdealer: timeout\r\n");
+        break; } }
+
+getgamedat(1);
+misc|=INPLAY;
+curplayer=firstplayer();
+putgamedat();
+
+getnodemsg();
+bputs(Dealing);
+putallnodemsg(Dealing);
+
+getgamedat(1);
+for(i=0;i<total_nodes;i++)		  /* release player nodes */
+	if(node[i])
+		status[i]=PLAY;
+putgamedat();
+}
+
+
+/****************************************************************************/
+/* This function halts this node until the dealer releases it by changing	*/
+/* the status byte from SYNC_P to PLAY										*/
+/* it is assumed that getgamedat(1) is called immediately prior.			*/
+/****************************************************************************/
+void syncplayer()
+{
+	time_t start,now;
+
+status[node_num-1]=SYNC_P;
+putgamedat();
+start=now=time(NULL);
+while(node[node_num-1] && status[node_num-1]==SYNC_P) {
+	chat();
+	mswait(100);
+	getgamedat(0);
+	if(!node[node_num-1]) { /* current node not in game? */
+		getgamedat(1);
+		node[node_num-1]=user_number;	/* fix it */
+		putgamedat(); }
+	now=time(NULL);
+	if(now-start>300) { /* only wait upto 5 minutes */
+		bputs("\r\nsyncplayer: timeout\r\n");
+        break; } }
+}
+
+/****************************************************************************/
+/* Updates the MODUSER.DAT file that SBBS reads to ajust the user's credits */
+/* This function is called whenever the user's credits are adjust so that   */
+/* the file will be current in any event.									*/
+/****************************************************************************/
+void moduserdat()
+{
+	char str[128];
+	FILE *stream;
+
+sprintf(str,"%sMODUSER.DAT",node_dir);
+if((stream=fopen(str,"wt"))==NULL) {
+	bprintf("Error opening %s for write\r\n",str);
+	return; }
+fprintf(stream,"%ld",credits-user_cdt);
+fclose(stream);
+
+}
+
+/****************************************************************************/
+/* This function reads the entire shoe of cards and the dealer's hand from  */
+/* the card database file (CARD.DAB)										*/
+/****************************************************************************/
+void getcarddat()
+{
+	int file;
+
+if((file=nopen("CARD.DAB",O_RDONLY))==-1) {
+	bputs("getcarddat: Error opening CARD.DAB\r\n");
+	return; }
+read(file,&dc,1);
+read(file,dealer,sizeof(dealer));
+read(file,&total_decks,1);
+read(file,&cur_card,2);
+read(file,card,total_decks*52*sizeof(card_t));
+close(file);
+}
+
+/****************************************************************************/
+/* This function writes the entire shoe of cards and the dealer's hand to   */
+/* the card database file (CARD.DAB)										*/
+/****************************************************************************/
+void putcarddat()
+{
+	int file;
+
+if((file=nopen("CARD.DAB",O_WRONLY|O_CREAT))==-1) {
+	bputs("putcarddat: Error opening CARD.DAB\r\n");
+	return; }
+write(file,&dc,1);
+write(file,dealer,sizeof(dealer));
+write(file,&total_decks,1);
+write(file,&cur_card,2);
+write(file,card,total_decks*52*sizeof(card_t));
+close(file);
+}
+
+/****************************************************************************/
+/* This function creates random ways to say "hit"                           */
+/****************************************************************************/
+char *hit()
+{
+	static char str[81];
+
+strcpy(str,"\1n\1r\1h");
+switch(rand()%10) {
+	case 1:
+		strcat(str,"Hit it.");
+		break;
+	case 2:
+		strcat(str,"Hit me, Baby!");
+		break;
+	case 3:
+		strcat(str,"Give me an ace.");
+		break;
+	case 4:
+		strcat(str,"One more.");
+		break;
+	case 5:
+		strcat(str,"Just one more.");
+		break;
+	case 6:
+		strcat(str,"Give me a baby card.");
+		break;
+	case 7:
+		strcat(str,"Hit it, Dude.");
+		break;
+	case 8:
+		strcat(str,"Hit.");
+		break;
+	case 9:
+		strcat(str,"Um... Hit.");
+		break;
+	case 10:
+		strcat(str,"Thank you Sir, may I have another.");
+		break;
+	default:
+		strcat(str,"Face card, please.");
+		break; }
+strcat(str,"\1n\r\n");
+return(str);
+}
+
+/****************************************************************************/
+/* This function creates random ways to say "double"                        */
+/****************************************************************************/
+char *doubit()
+{
+	static char str[81];
+
+strcpy(str,"\1n\1b\1h");
+switch(rand()%10) {
+	case 1:
+		strcat(str,"Double.");
+		break;
+	case 2:
+		strcat(str,"Double Down, Man.");
+		break;
+	case 3:
+		strcat(str,"Double it, Dude.");
+		break;
+	case 4:
+		strcat(str,"One more card for double the dough.");
+		break;
+	case 5:
+		strcat(str,"Double me.");
+		break;
+	case 6:
+		strcat(str,"Oh yeah... Double!");
+		break;
+	case 7:
+		strcat(str,"I shouldn't do it, but... Double!");
+		break;
+	case 8:
+		strcat(str,"Double my bet and give me one more card.");
+		break;
+	case 9:
+		strcat(str,"Um... Double.");
+		break;
+	case 10:
+		strcat(str,"Thank you Sir, may I Double?");
+		break;
+	default:
+		strcat(str,"Double - face card, please.");
+		break; }
+strcat(str,"\1n\r\n");
+return(str);
+}
+
+/****************************************************************************/
+/* This function creates random ways to say "stand"                         */
+/****************************************************************************/
+char *stand()
+{
+	static char str[81];
+
+strcpy(str,"\1n\1c\1h");
+switch(rand()%10) {
+	case 1:
+		strcat(str,"Stand.");
+		break;
+	case 2:
+		strcat(str,"Stay.");
+		break;
+	case 3:
+		strcat(str,"No more.");
+		break;
+	case 4:
+		strcat(str,"Just right.");
+		break;
+	case 5:
+		strcat(str,"I should hit, but I'm not gonna.");
+		break;
+	case 6:
+		strcat(str,"Whoa!");
+		break;
+	case 7:
+		strcat(str,"Hold it.");
+		break;
+	case 8:
+		strcat(str,"No way, Jose!");
+		break;
+	case 9:
+		strcat(str,"Um... Stand.");
+		break;
+	case 10:
+		strcat(str,"Thanks, but no thanks.");
+		break;
+	default:
+		strcat(str,"No card, no bust.");
+		break; }
+strcat(str,"\1n\r\n");
+return(str);
+}
+
+/****************************************************************************/
+/* This function creates random ways to say "split"                         */
+/****************************************************************************/
+char *split()
+{
+	static char str[81];
+
+strcpy(str,"\1n\1y\1h");
+switch(rand()%10) {
+	case 1:
+		strcat(str,"Split.");
+		break;
+	case 2:
+		strcat(str,"Split 'em.");
+		break;
+	case 3:
+		strcat(str,"Split it.");
+		break;
+	case 4:
+		strcat(str,"Split, please.");
+		break;
+	case 5:
+		strcat(str,"I should hit, but I'm gonna split instead.");
+		break;
+	case 6:
+		strcat(str,"Whoa! Split them puppies...");
+		break;
+	case 7:
+		strcat(str,"Split 'em, Dude.");
+		break;
+	case 8:
+		strcat(str,"Double the cards, for double the money.");
+		break;
+	case 9:
+		strcat(str,"Um... Split.");
+		break;
+	case 10:
+		strcat(str,"Thank you Sir, I think I'll split 'em.");
+		break;
+	default:
+		strcat(str,"Banana Split.");
+		break; }
+strcat(str,"\1n\r\n");
+return(str);
+}
+
+/****************************************************************************/
+/* This function creates random ways to say "joined"                        */
+/****************************************************************************/
+char *joined()
+{
+	static char str[81];
+
+switch(rand()%10) {
+	case 1:
+		strcpy(str,"joined.");
+		break;
+	case 2:
+		strcpy(str,"sat down to play.");
+		break;
+	case 3:
+		strcpy(str,"plopped on the chair next to you.");
+		break;
+	case 4:
+		strcpy(str,"belched loudly to announce his entrance.");
+		break;
+	case 5:
+		strcpy(str,"dropped in.");
+		break;
+	case 6:
+		strcpy(str,"joined our game.");
+		break;
+	case 7:
+		strcpy(str,"fell on his face entering the casino!");
+		break;
+	case 8:
+		strcpy(str,"slams a roll of credits on the table.");
+		break;
+	case 9:
+		strcpy(str,"rolled in to join the game.");
+		break;
+	case 10:
+		strcpy(str,"smiles widely as he takes your wife's seat.");
+		break;
+	default:
+		strcpy(str,"spills a drink on your pants while sitting down.");
+		break; }
+return(str);
+}
+
+/****************************************************************************/
+/* This function creates random ways to say "left"                          */
+/****************************************************************************/
+char *left()
+{
+	static char str[81];
+
+switch(rand()%10) {
+	case 1:
+		strcpy(str,"left abruptly.");
+		break;
+	case 2:
+		strcpy(str,"sneaked away.");
+		break;
+	case 3:
+		strcpy(str,"took the credits and ran.");
+		break;
+	case 4:
+		strcpy(str,"fell out of the chair.");
+		break;
+	case 5:
+		strcpy(str,"left the game.");
+		break;
+	case 6:
+		strcpy(str,"slipped out the door.");
+		break;
+	case 7:
+		strcpy(str,"giggled as he left the table.");
+		break;
+	case 8:
+		strcpy(str,"left clenching empty pockets.");
+		break;
+	case 9:
+		strcpy(str,"went to the pawn shop to hawk a watch.");
+		break;
+	case 10:
+		strcpy(str,"bailed out the back door.");
+		break;
+	default:
+		strcpy(str,"made like a train and left.");
+		break; }
+return(str);
+}
+
+/****************************************************************************/
+/* This function creates the file "GAME.DAB" in the current directory.      */
+/****************************************************************************/
+void create_gamedab()
+{
+
+if((gamedab=sopen("GAME.DAB"
+	,O_WRONLY|O_CREAT|O_BINARY,SH_DENYNO,S_IWRITE|S_IREAD))==-1) {
+	bputs("Error creating GAME.DAB\r\n");
+	pause();
+	exit(1); }
+misc=0;
+curplayer=0;
+memset(node,0,sizeof(node));
+memset(status,0,sizeof(status));
+write(gamedab,&misc,1);
+write(gamedab,&curplayer,1);
+write(gamedab,&total_nodes,1);
+write(gamedab,node,total_nodes*2);
+write(gamedab,status,total_nodes);
+close(gamedab);
+}
+
+/****************************************************************************/
+/* This function opens the file "GAME.DAB" in the current directory and     */
+/* leaves it open with deny none access. This file uses record locking		*/
+/* for shared access.														*/
+/****************************************************************************/
+void open_gamedab()
+{
+if((gamedab=sopen("GAME.DAB",O_RDWR|O_BINARY,SH_DENYNO))==-1) {
+    bputs("Error opening GAME.DAB\r\n");                /* open deny none */
+    pause();
+	exit(1); }
+}
+
+/****************************************************************************/
+/* Lists the players currently in the game and the status of the shoe.		*/
+/****************************************************************************/
+void listplayers()
+{
+	int i;
+
+getgamedat(0);
+bputs("\r\n");
+if(!total_players) {
+	bputs("\1_\1w\1hNo game in progress\r\n");
+	return; }
+for(i=0;i<total_nodes;i++)
+	if(node[i])
+		bprintf("\1-\1mNode %2d: \1h%s \1n\1m%s\r\n"
+			,i+1,username(node[i]),activity(status[i]));
+getcarddat();
+/***
+bprintf("\r\nCurrent player=Node %d user #%d\r\n",curplayer,node[curplayer-1]);
+***/
+}
+
+/****************************************************************************/
+/* This function replaces the card symbols in 'str' with letters to         */
+/* represent the different suits.											*/
+/****************************************************************************/
+void strip_symbols(char *str)
+{
+	int i,j;
+
+j=strlen(str);
+for(i=0;i<j;i++)
+	if(str[i]>=3 && str[i]<=6)
+		switch(str[i]) {
+			case 3:
+				str[i]='H';
+				break;
+			case 4:
+				str[i]='D';
+				break;
+			case 5:
+				str[i]='C';
+				break;
+			case 6:
+				str[i]='S';
+				break; }
+}
+
+#endif	/* end of function not needed for SBJCLEAN.C */
+
+/****************************************************************************/
+/* Reads information from GAME.DAB file. If 'lockit' is 1, the file is      */
+/* and putgamedat must be called to unlock it. If your updating the info	*/
+/* in GAME.DAB, you must first call getgamedat(1), then putgamedat().		*/
+/****************************************************************************/
+void getgamedat(char lockit)
+{
+    int i=0;
+
+/* retry 100 times taking at least 3 seconds */
+while(lock(gamedab,0L,filelength(gamedab))==-1 && i++<100)
+	mswait(30);  /* lock the whole thing */
+if(i>=100) {
+//	  printf("gamedab=%d %04X:%p %04X\r\n",gamedab,_psp,&gamedab,_DS);
+	printf("\7getgamedat: error locking GAME.DAB\r\n"); }
+
+lseek(gamedab,0L,SEEK_SET);
+read(gamedab,&misc,1);
+read(gamedab,&curplayer,1);
+read(gamedab,&total_nodes,1);
+read(gamedab,node,total_nodes*2);	   /* user number playing for each node */
+read(gamedab,status,total_nodes);	   /* the status of the player */
+total_players=0;
+for(i=0;i<total_nodes;i++)
+    if(node[i])
+        total_players++;
+if(!lockit)
+	unlock(gamedab,0L,filelength(gamedab));
+}
+
+/****************************************************************************/
+/* Writes information to GAME.DAB file. getgamedat(1) MUST be called before  */
+/* this function is called.                                                 */
+/****************************************************************************/
+void putgamedat()
+{
+
+lseek(gamedab,0L,SEEK_SET);
+write(gamedab,&misc,1);
+write(gamedab,&curplayer,1);
+write(gamedab,&total_nodes,1);
+write(gamedab,node,total_nodes*2);
+write(gamedab,status,total_nodes);
+unlock(gamedab,0L,filelength(gamedab));
+}
+
+/***************************************************************/
+/* This function makes the next active node the current player */
+/***************************************************************/
+void nextplayer()
+{
+    int i;
+
+getgamedat(1);                      /* get current info and lock */
+
+if((!curplayer						/* if no current player */
+    || total_players==1)            /* or only one player in game */
+    && node[node_num-1]) {          /* and this node is in the game */
+	curplayer=node_num; 			/* make this node current player */
+    putgamedat();                   /* write data and unlock */
+    return; }                       /* and return */
+
+for(i=curplayer;i<total_nodes;i++)	/* search for the next highest node */
+    if(node[i])                     /* that is active */
+        break;
+if(i>=total_nodes) {				/* if no higher active nodes, */
+	for(i=0;i<curplayer-1;i++)		/* start at bottom and go up  */
+        if(node[i])
+            break;
+	if(i==curplayer-1)				/* if no active nodes found */
+		curplayer=0;				/* make current player 0 */
+    else
+		curplayer=i+1; }
+else
+	curplayer=i+1;					/* else active node current player */
+
+putgamedat();                       /* write info and unlock */
+}
+
+/* End of SBJ.C */
diff --git a/xtrn/sbj/sbj.cfg b/xtrn/sbj/sbj.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..9e5236e6ed78c03c3743f109abd513151bd2308a
--- /dev/null
+++ b/xtrn/sbj/sbj.cfg
@@ -0,0 +1,4 @@
+6					Number of decks
+10					Minimum bet (in kilobytes)
+1000					Maximum bet (in kilobytes)
+100					Default bet (in kilobytes)
diff --git a/xtrn/sbj/sbj.doc b/xtrn/sbj/sbj.doc
new file mode 100644
index 0000000000000000000000000000000000000000..8066b29359739102e2da37ed88d4cfdb2047633b
Binary files /dev/null and b/xtrn/sbj/sbj.doc differ
diff --git a/xtrn/sbj/sbj.msg b/xtrn/sbj/sbj.msg
new file mode 100644
index 0000000000000000000000000000000000000000..647b4534fe6a8f6fe88d8587d1bc71a1d7df6362
--- /dev/null
+++ b/xtrn/sbj/sbj.msg
@@ -0,0 +1,22 @@
+                                  chSynchronet
+                                 n1k Blackjack! n
+c
+        As you might have guessed, this is a blackjack game. But not just any
+blackjack game. With Synchronet Blackjack, you play against the house for
+credits and your friends can play along side you simultaneously from the same
+card shoe. You can even carry on a conversation while a hand is in play!
+
+        Beware, you are gambling with your credits if you choose to play, but
+the odds are pretty good. House pays double on a natural blackjack or three
+sevens, 1� to 1 on regular blackjack, even money on other winning hands and 
+ties (pushes) break even. You can double down if you have enough dough and even
+split pairs!
+
+        If you choose to play, note that it is much more enjoyable with a
+friend at the table with you. While the dealer is fast and curtious, he doesn't
+make a very good conversation.
+
+	If you notice that you're not seeing the card symbols for 'clubs' you
+may have Compuserve Quick B transfers enabled or another answerback to Ctrl-E.
+This should be disabled, or you should toggle card symbols OFF.
+
diff --git a/xtrn/sbj/sbjclean.c b/xtrn/sbj/sbjclean.c
new file mode 100644
index 0000000000000000000000000000000000000000..814f3edba80f9e58918c9c01703bd4a719860729
--- /dev/null
+++ b/xtrn/sbj/sbjclean.c
@@ -0,0 +1,48 @@
+/* SBJCLEAN.C */
+
+/* Developed 1990-1997 by Rob Swindell; PO Box 501, Yorba Linda, CA 92885 */
+
+/* Clean-up program for Synchronet Blackjack Online External Program */
+
+#define SBJCLEAN
+
+#include "sbj.c"
+
+
+int main(int argc, char **argv)
+{
+	char *p;
+	int i;
+
+node_dir[0]=0;
+for(i=1;i<argc;i++)
+	if(!stricmp(argv[i],"/L"))
+		logit=1;
+	else strcpy(node_dir,argv[i]);
+
+p=getenv("SBBSNODE");
+if(!node_dir[0] && p)
+	strcpy(node_dir,p);
+
+if(!node_dir[0]) {	  /* node directory not specified */
+	printf("usage: sbjclean <node directory>\r\n");
+	getch();
+	return(1); }
+
+if(node_dir[strlen(node_dir)-1]!='\\')  /* make sure node_dir ends in '\' */
+    strcat(node_dir,"\\");
+
+initdata();                                 /* read XTRN.DAT and more */
+
+if((gamedab=open("GAME.DAB",O_RDWR|O_DENYNONE|O_BINARY))==-1) {
+	printf("Error opening GAME.DAB\r\n");                /* open deny none */
+    return(1); }
+getgamedat(1);
+node[node_num-1]=0;
+status[node_num-1]=0;
+putgamedat();
+if(curplayer==node_num)
+	nextplayer();
+close(gamedab);
+return(0);
+}
diff --git a/xtrn/sbl/makefile.bor b/xtrn/sbl/makefile.bor
new file mode 100644
index 0000000000000000000000000000000000000000..e9477a7212c17b477d7ef78a67e31628c6b06758
--- /dev/null
+++ b/xtrn/sbl/makefile.bor
@@ -0,0 +1,40 @@
+##################################################################
+# Makefile for SBL (Synchronet BBS List Online External Program) #
+# For use with Turbo C++ 			  		 #
+# Tabstop=8				 	  		 #
+##################################################################
+
+# Macros
+CC	= bcc
+LD	= tlink
+SDK	= ..\sdk
+INCLUDE = \bc31\include;$(SDK)
+LIB     = \bc31\lib
+MODEL	= l
+CFLAGS	= -N -d -C -m$(MODEL) -I$(INCLUDE)
+LFLAGS  = /n /c
+OBJS    = xsdk.obj xsdkvars.obj ..\..\mswait\dos\mswait$(MODEL).obj
+HEADERS = $(SDK)\xsdk.h $(SDK)\xsdkdefs.h $(SDK)\xsdkvars.c sbldefs.h
+
+# Implicit C Compile Rule
+.c.obj:
+    	@echo Compiling $*.c to $*.obj ...
+	$(CC) $(CFLAGS) -c $*.c
+
+# Main EXE Link Rule
+sbl.exe: $(OBJS) sbl.obj
+    	@echo Linking $< ...
+	$(LD) $(LFLAGS) @&&!
+$(LIB)\c0$(MODEL) $(OBJS) sbl.obj
+!, $*, $*, $(LIB)\c$(MODEL).lib $(LIB)\math$(MODEL).lib $(LIB)\emu.lib
+
+# All .obj modules
+sbl.obj: $(HEADERS)
+
+xsdk.obj: $(SDK)\xsdk.c $(HEADERS)
+	@echo Compiling $(SDK)\$*.c to $*.obj ...
+	$(CC) $(CFLAGS) -c $(SDK)\$*.c
+
+xsdkvars.obj: $(SDK)\xsdkvars.c $(SDK)\xsdkdefs.h
+	@echo Compiling $(SDK)\$*.c to $*.obj ...
+	$(CC) $(CFLAGS) -c $(SDK)\$*.c
diff --git a/xtrn/sbl/sbbslist.c b/xtrn/sbl/sbbslist.c
new file mode 100644
index 0000000000000000000000000000000000000000..2bf0c444fac727778a202b254dfc7fb91986129b
--- /dev/null
+++ b/xtrn/sbl/sbbslist.c
@@ -0,0 +1,250 @@
+/* SBBSLIST.C */
+
+/* Developed 1990-1997 by Rob Swindell; PO Box 501, Yorba Linda, CA 92885 */
+
+/* Converts Synchronet BBS List (SBL.DAB) to text file */
+
+#include "xsdk.h"
+#include "sbldefs.h"
+
+char *wday[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
+char *mon[]={"Jan","Feb","Mar","Apr","May","Jun"
+            ,"Jul","Aug","Sep","Oct","Nov","Dec"};
+char *nulstr="";
+char tmp[256];
+struct date date;
+struct time curtime;
+
+extern int daylight=0;
+extern long timezone=0L;
+
+typedef struct {
+
+	char	str[13];
+	short	offset;
+
+    } sortstr_t;
+
+
+int sortstr_cmp(sortstr_t **str1, sortstr_t **str2)
+{
+return(stricmp((*str1)->str,(*str2)->str));
+}
+
+/****************************************************************************/
+/* Generates a 24 character ASCII string that represents the time_t pointer */
+/* Used as a replacement for ctime()										*/
+/****************************************************************************/
+char *timestr(time_t *intime)
+{
+	static char str[256];
+    char mer[3],hour;
+    struct tm *gm;
+
+gm=localtime(intime);
+if(gm->tm_hour>=12) {
+    if(gm->tm_hour==12)
+        hour=12;
+    else
+        hour=gm->tm_hour-12;
+    strcpy(mer,"pm"); }
+else {
+    if(gm->tm_hour==0)
+        hour=12;
+    else
+        hour=gm->tm_hour;
+    strcpy(mer,"am"); }
+sprintf(str,"%s %s %02d %4d %02d:%02d %s"
+    ,wday[gm->tm_wday],mon[gm->tm_mon],gm->tm_mday,1900+gm->tm_year
+    ,hour,gm->tm_min,mer);
+return(str);
+}
+
+/****************************************************************************/
+/* Converts unix time format (long - time_t) into a char str MM/DD/YY		*/
+/****************************************************************************/
+char *unixtodstr(time_t unix, char *str)
+{
+
+if(!unix)
+	strcpy(str,"00/00/00");
+else {
+	unixtodos(unix,&date,&curtime);
+	if((unsigned)date.da_mon>12) {	  /* DOS leap year bug */
+		date.da_mon=1;
+		date.da_year++; }
+	if((unsigned)date.da_day>31)
+		date.da_day=1;
+	sprintf(str,"%02u/%02u/%02u",date.da_mon,date.da_day
+		,date.da_year>=2000 ? date.da_year-2000 : date.da_year-1900); }
+return(str);
+}
+
+
+void long_bbs_info(FILE *out, bbs_t bbs)
+{
+	int i;
+
+fprintf(out,"BBS Name: %s since %s\r\n"
+	,bbs.name,unixtodstr(bbs.birth,tmp));
+fprintf(out,"Operator: ");
+for(i=0;i<bbs.total_sysops && i<MAX_SYSOPS;i++) {
+	if(i) {
+		if(bbs.total_sysops>2)
+			fprintf(out,", ");
+		else
+			fputc(SP,out);
+		if(!(i%4))
+			fprintf(out,"\r\n          ");
+		if(i+1==bbs.total_sysops)
+			fprintf(out,"and "); }
+	fprintf(out,"%s",bbs.sysop[i]); }
+fprintf(out,"\r\n");
+fprintf(out,"Software: %-15.15s Nodes: %-5u "
+	"Users: %-5u Doors: %u\r\n"
+	,bbs.software,bbs.nodes,bbs.users,bbs.xtrns);
+fprintf(out,"Download: %lu files in %u directories of "
+	"%luMB total space\r\n"
+	,bbs.files,bbs.dirs,bbs.megs);
+fprintf(out,"Messages: %lu messages in %u sub-boards\r\n"
+	,bbs.msgs,bbs.subs);
+fprintf(out,"Networks: ");
+for(i=0;i<bbs.total_networks && i<MAX_NETS;i++) {
+	if(i) {
+		if(bbs.total_networks>2)
+			fprintf(out,", ");
+		else
+			fputc(SP,out);
+		if(!(i%3))
+			fprintf(out,"\r\n          ");
+		if(i+1==bbs.total_networks)
+			fprintf(out,"and "); }
+	fprintf(out,"%s [%s]",bbs.network[i],bbs.address[i]); }
+fprintf(out,"\r\n");
+fprintf(out,"Terminal: ");
+for(i=0;i<bbs.total_terminals && i<MAX_TERMS;i++) {
+	if(i) {
+		if(bbs.total_terminals>2)
+			fprintf(out,", ");
+		else
+			fputc(SP,out);
+		if(i+1==bbs.total_terminals)
+			fprintf(out,"and "); }
+	fprintf(out,"%s",bbs.terminal[i]); }
+fprintf(out,"\r\n\r\n");
+for(i=0;i<bbs.total_numbers && i<MAX_NUMBERS;i++)
+	fprintf(out,"%-30.30s %12.12s %5u %-15.15s "
+		"Minimum: %u\r\n"
+		,i && !strcmp(bbs.number[i].location,bbs.number[i-1].location)
+			? nulstr : bbs.number[i].location
+		,bbs.number[i].number
+		,bbs.number[i].max_rate,bbs.number[i].modem
+		,bbs.number[i].min_rate);
+
+fprintf(out,"\r\n");
+for(i=0;i<5;i++) {
+	if(!bbs.desc[i][0])
+		break;
+	fprintf(out,"%15s%s\r\n",nulstr,bbs.desc[i]); }
+
+fprintf(out,"\r\n");
+fprintf(out,"Entry created on %s by %s\r\n"
+	,timestr(&bbs.created),bbs.user);
+if(bbs.updated && bbs.userupdated[0])
+	fprintf(out," Last updated on %s by %s\r\n"
+		,timestr(&bbs.updated),bbs.userupdated);
+if(bbs.verified && bbs.userverified[0])
+	fprintf(out,"Last verified on %s by %s\r\n"
+        ,timestr(&bbs.verified),bbs.userverified);
+}
+
+int main(int argc, char **argv)
+{
+	char str[128];
+	int i,j,file,ff;
+	FILE *in,*shrt,*lng;
+	bbs_t bbs;
+	sortstr_t **sortstr=NULL;
+
+if((i=open("SBL.DAB",O_RDONLY|O_BINARY|O_DENYNONE))==-1) {
+	printf("error opening SBL.DAB\n");
+	return(1); }
+
+if((in=fdopen(i,"rb"))==NULL) {
+	printf("error opening SBL.DAB\n");
+	return(1); }
+
+if((shrt=fopen("SBBS.LST","wb"))==NULL) {
+	printf("error opening/creating SBBS.LST\n");
+	return(1); }
+
+if((lng=fopen("SBBS_DET.LST","wb"))==NULL) {
+	printf("error opening/creating SBBS_DET.LST\n");
+	return(1); }
+
+fprintf(shrt,"Synchronet BBS List exported from Vertrauen on %s\r\n"
+			 "======================================================="
+			 "\r\n\r\n"
+	,unixtodstr(time(NULL),str));
+
+fprintf(lng,"Detailed Synchronet BBS List exported from Vertrauen on %s\r\n"
+			"================================================================"
+			"\r\n\r\n"
+	,unixtodstr(time(NULL),str));
+
+printf("Sorting...");
+fseek(in,0L,SEEK_SET);
+i=j=0;
+while(1) {
+	if(!fread(&bbs,sizeof(bbs_t),1,in))
+		break;
+	j++;
+	printf("%4u\b\b\b\b",j);
+	if(!bbs.name[0] || strnicmp(bbs.software,"SYNCHRONET",10))
+		continue;
+	i++;
+	strcpy(str,bbs.number[0].number);
+	if((sortstr=(sortstr_t **)farrealloc(sortstr
+		,sizeof(sortstr_t *)*i))==NULL) {
+		printf("\r\n\7Memory allocation error\r\n");
+		return(1); }
+	if((sortstr[i-1]=(sortstr_t *)farmalloc(sizeof(sortstr_t)
+		))==NULL) {
+		printf("\r\n\7Memory allocation error\r\n");
+		return(1); }
+	strcpy(sortstr[i-1]->str,str);
+	sortstr[i-1]->offset=j-1; }
+
+qsort((void *)sortstr,i,sizeof(sortstr[0])
+	,(int(*)(const void *, const void *))sortstr_cmp);
+
+printf("\nCreating index...");
+sprintf(str,"SBBSSORT.NDX");
+if((file=open(str,O_RDWR|O_CREAT|O_TRUNC|O_BINARY,S_IWRITE|S_IREAD))==-1) {
+	printf("\n\7Error creating %s\n",str);
+	return(1); }
+for(j=0;j<i;j++)
+	write(file,&sortstr[j]->offset,2);
+printf("\n");
+
+lseek(file,0L,SEEK_SET);
+ff=0;
+while(1) {
+	if(read(file,&i,2)!=2)
+		break;
+	fseek(in,(long)i*sizeof(bbs_t),SEEK_SET);
+	if(!fread(&bbs,sizeof(bbs_t),1,in))
+		break;
+	long_bbs_info(lng,bbs);
+	if(ff)
+		fprintf(lng,"\x0c\r\n");
+	else
+		fprintf(lng,"\r\n---------------------------------------------"
+			"----------------------------------\r\n\r\n");
+	ff=!ff;
+	for(i=0;i<bbs.total_numbers && i<MAX_NUMBERS;i++)
+		fprintf(shrt,"%-25.25s  %-30.30s  %12.12s  %u\r\n"
+			,i ? "" : bbs.name,bbs.number[i].location,bbs.number[i].number
+			,bbs.number[i].max_rate);
+	}
+}
diff --git a/xtrn/sbl/sbl.c b/xtrn/sbl/sbl.c
new file mode 100644
index 0000000000000000000000000000000000000000..9063f29fdd2b85120f2359152a39d3d5c9edb998
--- /dev/null
+++ b/xtrn/sbl/sbl.c
@@ -0,0 +1,1355 @@
+/* SBL.C */
+
+/* Developed 1990-1997 by Rob Swindell; PO Box 501, Yorba Linda, CA 92885 */
+
+/****************************/
+/* Synchronet BBS List Door */
+/****************************/
+
+/****************************************************************************/
+/* This source code is completely Public Domain and can be modified and 	*/
+/* distributed freely (as long as changes are documented).                  */
+/* It is meant as an example to programmers of how to use the XSDK			*/
+/****************************************************************************/
+
+/***********
+ * History *
+ ***********
+
+******************
+* RELEASE: v1.00 *
+******************
+
+07/03/93 03:16am
+Fixed bug with "Auto-deleting" already deleted entries. This would cause a
+long list of "Auto-deletion" messages once a day.
+
+07/03/93 03:30am
+The name of the user who last updated the entry is now stored and displayed.
+
+07/03/93 03:45am
+Adding/Updating entries is now much easier and user friendly.
+
+07/03/93 04:00am
+Added support for user "verification" of BBS entries.
+
+07/03/93 04:10am
+Users may now update or remove entries using partial system names.
+
+07/03/93 04:30am
+Sysops may now un-delete purged entries with the '*' key.
+
+******************
+* RELEASE: v1.10 *
+******************
+
+10/18/93 06:04pm
+Fixed bug that would cause entries to be purged almost immediately.
+
+10/18/93 07:01pm
+(F)ind text now searches user names who last updated and verified.
+
+10/19/93 01:34am
+Added option for users to change the format of BBS listings.
+
+******************
+* RELEASE: v1.20 *
+******************
+
+10/20/93 04:44pm
+Fixed cosmetic problem with opening menu (for users, not sysop).
+
+******************
+* RELEASE: v1.21 *
+******************
+
+11/29/93 09:40pm
+More cosmetic changes.	Added "Saving..." message.
+
+******************
+* RELEASE: v1.22 *
+******************
+
+02/02/94
+Added warning for pending auto-deletion of BBS entries.
+
+02/02/94
+Added option for turning screen pause off/on.
+
+02/03/94
+Added option in SBL.CFG for sysop/co-sysop notification of changes made to
+BBS list by users.
+
+02/03/94
+Converted all file operations from handles to streams for buffered i/o (speed).
+
+02/09/94
+Added options for generating a sort index and displaying sorted list based on
+various criteria.
+
+02/09/94
+Added nodesync() calls to display any messages waiting for this user/node.
+
+02/10/94
+Added search for duplicate names when adding new BBS entries.
+
+02/10/94
+Notification notice of actual auto-deletion sent to author of BBS entry upon
+auto-deletion.
+
+******************
+* RELEASE: v1.30 *
+******************
+
+03/14/94
+Added /M switch to force daily maintenance.
+
+03/22/94
+Fixed occasional double pause after listings.
+
+03/22/94
+Added total entries found to find text listings.
+
+03/22/94
+If a user verifies an entry, the user who created the entry is notified.
+
+03/29/94
+Sysop can define/change the "owner" of an entry when adding or updating.
+
+04/18/94
+Fixed bug in the sort-by-string functions that caused lock-ups when sorting
+more than 312 entries.
+
+04/18/94
+Lowered memory requirements for all sort functions.
+
+******************
+* RELEASE: v1.31 *
+******************
+
+08/23/94
+BBS entries now know whether they were created by a user or by SMB2SBL (via
+message base).
+
+08/23/94
+Fixed problem with hitting Ctrl-C locally during regular (not extended)
+listing. Returning to main menu would not clear screen or have correct colors.
+'aborted' variable is now reset in main() loop.
+
+******************
+* RELEASE: v1.32 *
+******************
+
+08/30/94
+Fixed stack overflow that would cause periodic lock-ups on some systems.
+
+******************
+* RELEASE: v1.33 *
+******************
+
+09/08/94
+When deleting an entry, the name of the BBS deleted wasn't being printed.
+
+02/01/95
+Import utility made mistake of ignoring READ messages to SBL. This has been
+fixed.
+
+*/
+
+#include "xsdk.h"
+#include "sbldefs.h"
+
+unsigned _stklen=16000; 		  /* Set stack size in code, not header */
+
+typedef struct {
+
+	char	str[32];
+	short	offset;
+
+	} sortstr_t;
+
+typedef struct {
+
+	long	i;
+	short	offset;
+
+	} sortint_t;
+
+char *nulstr="";
+char *wday[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
+char *mon[]={"Jan","Feb","Mar","Apr","May","Jun"
+            ,"Jul","Aug","Sep","Oct","Nov","Dec"};
+char tmp[256];
+char list_fmt[128];
+uint del_days,add_ml,update_ml,remove_ml,verify_ml,sbl_pause=1,notify_user;
+struct date date;
+struct time curtime;
+time_t now;
+
+/****************************************************************************/
+/* Generates a 24 character ASCII string that represents the time_t pointer */
+/* Used as a replacement for ctime()										*/
+/****************************************************************************/
+char *timestr(time_t *intime)
+{
+	static char str[256];
+    char mer[3],hour;
+    struct tm *gm;
+
+gm=localtime(intime);
+if(gm->tm_hour>=12) {
+    if(gm->tm_hour==12)
+        hour=12;
+    else
+        hour=gm->tm_hour-12;
+    strcpy(mer,"pm"); }
+else {
+    if(gm->tm_hour==0)
+        hour=12;
+    else
+        hour=gm->tm_hour;
+    strcpy(mer,"am"); }
+sprintf(str,"%s %s %02d %4d %02d:%02d %s"
+    ,wday[gm->tm_wday],mon[gm->tm_mon],gm->tm_mday,1900+gm->tm_year
+    ,hour,gm->tm_min,mer);
+return(str);
+}
+
+
+/****************************************************************************/
+/* Converts unix time format (long - time_t) into a char str MM/DD/YY		*/
+/****************************************************************************/
+char *unixtodstr(time_t unix, char *str)
+{
+
+if(!unix)
+	strcpy(str,"00/00/00");
+else {
+	unixtodos(unix,&date,&curtime);
+	if((unsigned)date.da_mon>12) {	  /* DOS leap year bug */
+		date.da_mon=1;
+		date.da_year++; }
+	if((unsigned)date.da_day>31)
+		date.da_day=1;
+	sprintf(str,"%02u/%02u/%02u",date.da_mon,date.da_day
+		,date.da_year>=2000 ? date.da_year-2000 : date.da_year-1900); }
+return(str);
+}
+
+
+/****************************************************************************/
+/* Converts a date string in format MM/DD/YY into unix time format			*/
+/****************************************************************************/
+time_t dstrtounix(char *str)
+{
+
+if(!strcmp(str,"00/00/00"))
+	return(NULL);
+curtime.ti_hour=curtime.ti_min=curtime.ti_sec=0;
+if(str[6]<7)
+	date.da_year=2000+((str[6]&0xf)*10)+(str[7]&0xf);
+else
+	date.da_year=1900+((str[6]&0xf)*10)+(str[7]&0xf);
+date.da_mon=((str[0]&0xf)*10)+(str[1]&0xf);
+date.da_day=((str[3]&0xf)*10)+(str[4]&0xf);
+return(dostounix(&date,&curtime));
+}
+
+void dots(int show)
+{
+	static int i;
+
+if(!show) {  /* reset */
+	i=0;
+	return; }
+if(++i>5) {
+	bputs("\b\b\b\b\b     \b\b\b\b\b");
+	i=0;
+	return; }
+outchar('.');
+}
+
+
+/* Displays short information about BBS. Returns 0 if aborted. */
+
+char short_bbs_info(bbs_t bbs)
+{
+	int i,j;
+
+for(i=0;i<bbs.total_numbers && i<MAX_NUMBERS;i++) {
+	for(j=0;list_fmt[j];j++) {
+		if(j) bputs(" ");
+		switch(toupper(list_fmt[j])) {
+			case 'N':
+				bprintf("\1h\1m%-25.25s",i ? nulstr : bbs.name);
+				break;
+			case 'S':
+				bprintf("\1h\1c%-15.15s",i ? nulstr : bbs.software);
+				break;
+			case 'P':
+				bprintf("\1n\1g%12.12s",bbs.number[i].number);
+				break;
+			case 'B':
+				bprintf("\1h\1g%5u",bbs.number[i].max_rate);
+				break;
+			case 'M':
+				bprintf("\1h\1b%-15.15s",bbs.number[i].modem);
+				break;
+			case 'Y':
+				bprintf("\1h\1y%-25.25s",i ? nulstr : bbs.sysop[0]);
+				break;
+			case 'T':
+				if(i) bputs("   ");
+				else  bprintf("\1n\1h%3u",bbs.nodes);
+				break;
+			case 'U':
+				if(i) bputs("     ");
+				else  bprintf("\1n\1r%5u",bbs.users);
+                break;
+			case 'H':
+				if(i) bprintf("%10.10s",nulstr);
+				else  bprintf("\1h\1r%10u",bbs.megs);
+				break;
+			case 'L':
+				bprintf("\1n\1c%-25.25s",bbs.number[i].location);
+				break;
+			case 'F':
+				bprintf("\1n\1b%s",i ? nulstr : unixtodstr(bbs.birth,tmp));
+				break;
+			case 'V':
+				bprintf("\1n\1m%s",i ? nulstr : unixtodstr(bbs.verified,tmp));
+				break;
+			case 'D':
+				bprintf("\1n%s",i ? nulstr : unixtodstr(bbs.updated,tmp));
+				break;
+			case 'C':
+				bprintf("\1n\1y%s",i ? nulstr : unixtodstr(bbs.created,tmp));
+                break;
+			default:
+				bprintf("%c",list_fmt[j]);
+				break;
+				} }
+	bputs("\r\n"); }
+if(kbhit())
+	return(0);
+return(1);
+}
+
+char long_bbs_info(bbs_t bbs)
+{
+	int i;
+
+cls();
+bprintf("\1n\1gBBS Name: \1h%s \1n\1gsince \1h%s\r\n"
+	,bbs.name,unixtodstr(bbs.birth,tmp));
+bprintf("\1n\1gOperator: ");
+for(i=0;i<bbs.total_sysops && i<MAX_SYSOPS;i++) {
+	if(i) {
+		if(bbs.total_sysops>2)
+			bputs(", ");
+		else
+			outchar(SP);
+		if(!(i%4))
+            bputs("\r\n          ");
+		if(i+1==bbs.total_sysops)
+			bputs("and "); }
+	bprintf("\1h%s\1n\1g",bbs.sysop[i]); }
+CRLF;
+bprintf("\1n\1gSoftware: \1h%-15.15s \1n\1gNodes: \1h%-5u \1n\1g"
+	"Users: \1h%-5u \1n\1gDoors: \1h%u\r\n"
+	,bbs.software,bbs.nodes,bbs.users,bbs.xtrns);
+bprintf("\1n\1gDownload: \1h%lu \1n\1gfiles in \1h%u \1n\1gdirectories of \1h"
+	"%luMB \1n\1gtotal space\r\n"
+	,bbs.files,bbs.dirs,bbs.megs);
+bprintf("Messages: \1h%lu \1n\1gmessages in \1h%u \1n\1gsub-boards\r\n"
+	,bbs.msgs,bbs.subs);
+bprintf("Networks: ");
+for(i=0;i<bbs.total_networks && i<MAX_NETS;i++) {
+	if(i) {
+		if(bbs.total_networks>2)
+			bputs(", ");
+		else
+			outchar(SP);
+		if(!(i%3))
+            bputs("\r\n          ");
+		if(i+1==bbs.total_networks)
+            bputs("and "); }
+	bprintf("\1h%s [%s]\1n\1g",bbs.network[i],bbs.address[i]); }
+CRLF;
+bprintf("Terminal: ");
+for(i=0;i<bbs.total_terminals && i<MAX_TERMS;i++) {
+	if(i) {
+		if(bbs.total_terminals>2)
+			bputs(", ");
+		else
+			outchar(SP);
+		if(i+1==bbs.total_terminals)
+            bputs("and "); }
+	bprintf("\1h%s\1n\1g",bbs.terminal[i]); }
+CRLF;
+CRLF;
+for(i=0;i<bbs.total_numbers && i<MAX_NUMBERS;i++)
+	bprintf("\1h\1b%-30.30s \1n\1g%12.12s \1h%5u \1b%-15.15s \1n\1c"
+		"Minimum: \1h%u\r\n"
+		,i && !strcmp(bbs.number[i].location,bbs.number[i-1].location)
+			? nulstr : bbs.number[i].location
+		,bbs.number[i].number
+		,bbs.number[i].max_rate,bbs.number[i].modem
+		,bbs.number[i].min_rate);
+
+bputs("\r\n\1w\1h");
+for(i=0;i<5;i++) {
+	if(!bbs.desc[i][0])
+		break;
+	bprintf("%15s%s\r\n",nulstr,bbs.desc[i]); }
+
+CRLF;
+if(bbs.misc&FROM_SMB)
+	bputs("\1r\1hImported from message base.\r\n");
+bprintf("\1n\1cEntry created on \1h%s\1n\1c by \1h%s\r\n"
+	,timestr(&bbs.created),bbs.user);
+if(bbs.updated && bbs.userupdated[0])
+	bprintf("\1n\1c Last updated on \1h%s\1n\1c by \1h%s\r\n"
+		,timestr(&bbs.updated),bbs.userupdated);
+if(bbs.verified && bbs.userverified[0])
+	bprintf("\1n\1cLast verified on \1h%s\1n\1c by \1h%s\r\n"
+		,timestr(&bbs.verified),bbs.userverified);
+CRLF;
+if(aborted) {
+	aborted=0;
+	return(0); }
+if(!sbl_pause) {
+	if(kbhit())
+		return(0);
+	return(1); }
+nodesync();
+return(yesno("More"));
+}
+
+/* Gets/updates BBS info from user. Returns 0 if aborted. */
+
+char get_bbs_info(bbs_t *bbs)
+{
+	char str[128];
+	int i;
+
+aborted=0;
+if(!(bbs->user[0]))
+	strcpy(bbs->user,user_name);
+if(SYSOP) {
+	bputs("\1y\1hUser Name (Creator/Owner of Entry): ");
+	if(!getstr(bbs->user,25,K_EDIT|K_LINE|K_AUTODEL))
+		return(0); }
+bputs("\1y\1hSystem Name: ");
+if(getstr(bbs->name,25,K_EDIT|K_LINE|K_AUTODEL)<2)
+	return(0);
+if(!bbs->software[0])
+	strcpy(bbs->software,"Synchronet");
+bprintf("\1y\1hSoftware: \1w",bbs->software);
+if(!getstr(bbs->software,15,K_AUTODEL|K_EDIT))
+	return(0);
+
+for(i=0;i<MAX_SYSOPS && !aborted;i++) {
+	bprintf("\1y\1hName of System Operator #%d [\1wNone\1y]: ",i+1);
+	if(!getstr(bbs->sysop[i],25,K_EDIT|K_LINE|K_AUTODEL))
+		break; }
+bbs->total_sysops=i;
+if(aborted)
+	return(0);
+
+unixtodstr(bbs->birth,str);
+bprintf("\1y\1hFirst Day Online (MM/DD/YY): \1w");
+if(getstr(str,8,K_UPPER|K_EDIT|K_AUTODEL))
+	bbs->birth=dstrtounix(str);
+if(aborted) return(0);
+
+for(i=0;i<MAX_NETS && !aborted;i++) {
+	bprintf("\1y\1hName of Network #%d [\1wNone\1y]: ",i+1);
+	if(!getstr(bbs->network[i],15,K_EDIT|K_AUTODEL|K_LINE))
+		break;
+	bprintf("\1y\1hNetwork \1wAddress\1y #%d [\1wNone\1y]: \1w",i+1);
+	getstr(bbs->address[i],25,K_EDIT|K_AUTODEL); }
+bbs->total_networks=i;
+if(aborted) return(0);
+
+for(i=0;i<MAX_TERMS && !aborted;i++) {
+	bprintf("\1y\1hSupported Terminal Type #%d (i.e. TTY, ANSI, RIP) "
+		"[\1wNone\1y]: ",i+1);
+	if(!getstr(bbs->terminal[i],15,K_EDIT|K_AUTODEL|K_LINE))
+		break; }
+bbs->total_terminals=i;
+if(aborted) return(0);
+
+if(!bbs->nodes)
+	bbs->nodes=1;
+bprintf("\1y\1hNodes (maximum number of simultaneous REMOTE users): \1w");
+sprintf(str,"%u",bbs->nodes);
+if(getstr(str,5,K_NUMBER|K_EDIT|K_AUTODEL))
+	bbs->nodes=atoi(str);
+if(!bbs->nodes)
+	bbs->nodes=1;
+if(aborted) return(0);
+
+for(i=0;i<MAX_NUMBERS;i++) {
+	if(!i && !bbs->number[i].number[0])
+		sprintf(bbs->number[i].number,"%.8s",user_phone);
+	bprintf("\1y\1hPhone Number #%d (###-###-####) [\1wNone\1y]: ",i+1);
+	if(!getstr(bbs->number[i].number,12,K_EDIT|K_LINE))
+		break;
+	if(!bbs->number[i].location[0]) {
+		if(!i)
+			strcpy(bbs->number[i].location,user_location);
+		else
+			strcpy(bbs->number[i].location,bbs->number[i-1].location); }
+	bprintf("\1y\1hLocation (City, State): \1w");
+	if(!getstr(bbs->number[i].location,30,K_EDIT|K_AUTODEL))
+		break;
+	if(aborted) return(0);
+
+	if(!bbs->number[i].min_rate) {
+		if(i)
+			bbs->number[i].min_rate=bbs->number[i-1].min_rate;
+		else
+			bbs->number[i].min_rate=300; }
+	bprintf("\1y\1hMinimum Connect Rate: \1w");
+	sprintf(str,"%u",bbs->number[i].min_rate);
+	if(getstr(str,5,K_NUMBER|K_EDIT|K_AUTODEL))
+		bbs->number[i].min_rate=atoi(str);
+	if(aborted) return(0);
+
+	if(!bbs->number[i].max_rate) {
+        if(i)
+            bbs->number[i].max_rate=bbs->number[i-1].max_rate;
+        else
+            bbs->number[i].max_rate=2400; }
+	if(bbs->number[i].max_rate<bbs->number[i].min_rate)
+        bbs->number[i].max_rate=bbs->number[i].min_rate;
+	bprintf("\1y\1hMaximum Connect Rate (i.e. 2400, 9600, 14400, etc): \1w");
+	sprintf(str,"%u",bbs->number[i].max_rate);
+	if(getstr(str,5,K_NUMBER|K_EDIT|K_AUTODEL))
+		bbs->number[i].max_rate=atoi(str);
+	if(aborted) return(0);
+
+	bprintf("\1y\1hModem Description (i.e. Hayes, HST, V.32, etc): \1w");
+	getstr(bbs->number[i].modem,15,K_EDIT|K_AUTODEL);  }
+if(!i)
+	return(0);
+bbs->total_numbers=i;
+if(aborted)
+	return(0);
+
+if(!bbs->users)
+	bbs->users=100;
+bprintf("\1y\1hTotal Number of Users: \1w");
+sprintf(str,"%u",bbs->users);
+if(getstr(str,5,K_NUMBER|K_EDIT|K_AUTODEL))
+	bbs->users=atoi(str);
+if(aborted) return(0);
+
+if(!bbs->subs)
+	bbs->subs=10;
+bprintf("\1y\1hTotal Number of Sub-boards (Message Areas): \1w");
+sprintf(str,"%u",bbs->subs);
+if(getstr(str,5,K_NUMBER|K_EDIT|K_AUTODEL))
+	bbs->subs=atoi(str);
+if(aborted) return(0);
+
+if(!bbs->msgs)
+	bbs->msgs=500;
+bprintf("\1y\1hTotal Number of Public Messages: \1w");
+sprintf(str,"%lu",bbs->msgs);
+if(getstr(str,10,K_NUMBER|K_EDIT|K_AUTODEL))
+	bbs->msgs=atol(str);
+if(aborted) return(0);
+
+if(!bbs->dirs)
+	bbs->dirs=5;
+bprintf("\1y\1hTotal Number of Directories (File Areas): \1w");
+sprintf(str,"%u",bbs->dirs);
+if(getstr(str,5,K_NUMBER|K_EDIT|K_AUTODEL))
+	bbs->dirs=atoi(str);
+if(aborted) return(0);
+
+if(!bbs->files)
+	bbs->files=250;
+bprintf("\1y\1hTotal Number of Downloadable Files: \1w");
+sprintf(str,"%lu",bbs->files);
+if(getstr(str,10,K_NUMBER|K_EDIT|K_AUTODEL))
+	bbs->files=atol(str);
+if(aborted) return(0);
+
+if(!bbs->xtrns)
+    bbs->xtrns=5;
+bprintf("\1y\1hTotal Number of External Programs (Doors): \1w");
+sprintf(str,"%u",bbs->xtrns);
+if(getstr(str,5,K_NUMBER|K_EDIT|K_AUTODEL))
+    bbs->xtrns=atoi(str);
+if(aborted) return(0);
+
+if(!bbs->megs)
+	bbs->megs=40;
+bprintf("\1y\1hTotal Storage Space (in Megabytes): \1w");
+sprintf(str,"%lu",bbs->megs);
+if(getstr(str,10,K_NUMBER|K_EDIT|K_AUTODEL))
+	bbs->megs=atol(str);
+if(aborted) return(0);
+
+for(i=0;i<5;i++) {
+	bprintf("\1y\1hBBS Description (%d of 5): ",i+1);
+	if(!getstr(bbs->desc[i],50,K_EDIT|K_AUTODEL|K_LINE))
+		break; }
+
+return(1);
+}
+
+char partname(char *inname, char *inpart)
+{
+	char name[128],part[128],str[256];
+
+strcpy(name,inname);
+strupr(name);
+strcpy(part,inpart);
+strupr(part);
+if(inname[0] && (strstr(name,part) || strstr(part,name))) {
+	sprintf(str,"\r\nDo you mean %s",inname);
+	if(yesno(str))
+		return(1); }
+return(0);
+}
+
+int sortint_cmp(sortint_t *int1, sortint_t *int2)
+{
+
+if(int1->i>int2->i)
+	return(-1);
+if(int1->i<int2->i)
+	return(1);
+return(0);
+}
+
+int sortstr_cmp(sortstr_t *str1, sortstr_t *str2)
+{
+return(stricmp(str1->str,str2->str));
+}
+
+void main(int argc, char **argv)
+{
+	char	str[512],name[128],*p,ch;
+	short	i,j,file,done,sort_by_str;
+	int 	maint=0;
+	long	l,found;
+	bbs_t	bbs,tmpbbs;
+	FILE	*stream;
+	sortstr_t *sortstr;
+	sortint_t *sortint;
+
+for(i=1;i<argc;i++)
+	if(argv[i][0]=='/')
+		switch(toupper(argv[i][1])) {
+			case 'M':
+				maint=1;
+				break; }
+
+p=getenv("SBBSNODE");
+if(p)
+	strcpy(node_dir,p);
+else {
+	printf("\nSBBSNODE environment variable must be set\n");
+	exit(0); }
+
+if(node_dir[strlen(node_dir)-1]!='\\')
+	strcat(node_dir,"\\");
+
+strcpy(str,"SBL.CFG");
+if((file=open(str,O_RDONLY|O_DENYNONE))==-1) {
+	printf("error opening %s\r\n",str);
+	exit(1); }
+if((stream=fdopen(file,"rb"))==NULL) {
+	printf("fdopen error with %s\r\n",str);
+	exit(1); }
+fgets(str,81,stream);
+del_days=atoi(str);
+fgets(str,81,stream);
+add_ml=atoi(str);
+fgets(str,81,stream);
+update_ml=atoi(str);
+fgets(str,81,stream);
+remove_ml=atoi(str);
+fgets(str,81,stream);
+verify_ml=atoi(str);
+fgets(str,81,stream);
+notify_user=atoi(str);
+fclose(stream);
+
+initdata();
+if(maint)
+	user_misc=(ANSI|COLOR);
+
+if((file=open("SBL.DAB",O_RDWR|O_BINARY|O_DENYNONE|O_CREAT
+	,S_IWRITE|S_IREAD))==-1) {
+	bprintf("\r\n\7Error opening/creating SBL.DAB\r\n");
+	exit(1); }
+if((stream=fdopen(file,"w+b"))==NULL) {
+	bprintf("\r\n\7Error converting SBL.DAB file handle to stream\r\n");
+	exit(1); }
+setvbuf(stream,0L,_IOFBF,2048);
+
+if(del_days) {
+    now=time(NULL);
+	strcpy(str,"SBLPURGE.DAB");
+	if((file=nopen(str,O_RDWR|O_CREAT))==-1) {
+		printf("Error creating %s\r\n",str);
+		exit(1); }
+	l=NULL;
+	read(file,&l,4);
+	if(now-l>(24L*60L*60L) || maint) {	 /* more than a day since update */
+		bputs("\r\n\1n\1hRunning daily maintenance for Synchronet BBS List...");
+		lseek(file,0L,SEEK_SET);
+		write(file,&now,4);
+		close(file);
+		fseek(stream,0L,SEEK_SET);
+		while(!feof(stream)) {
+			if(!fread(&bbs,sizeof(bbs_t),1,stream))
+				break;
+			if(bbs.name[0] && !(bbs.misc&FROM_SMB)) {
+				if((now-bbs.updated)/(24L*60L*60L)>del_days
+					&& (now-bbs.created)/(24L*60L*60L)>del_days
+					&& (now-bbs.verified)/(24L*60L*60L)>del_days) {
+					lncntr=0;
+					bprintf("\r\n\1n\1hAuto-deleting \1m%s\r\n",bbs.name);
+					sprintf(str,"\1n\1hSBL: \1mYour BBS entry for \1y%s\1m\r\n"
+						"     was auto-deleted from the \1cSynchronet BBS "
+						"List\r\n",bbs.name);
+					i=usernumber(bbs.user);
+					if(i) putsmsg(i,str);
+					bbs.name[0]=0;
+					fseek(stream,-(long)(sizeof(bbs_t)),SEEK_CUR);
+					fwrite(&bbs,sizeof(bbs_t),1,stream); }
+				else { /* Warn user */
+					l=bbs.created;
+					if(l<bbs.updated)
+						l=bbs.updated;
+					if(l<bbs.verified)
+						l=bbs.verified;
+					if((now-l)/(24L*60L*60L)>=(del_days/2)) {
+						bprintf("\r\n\1n\1hWarning \1y%s\r\n",bbs.user);
+						lncntr=0;
+						sprintf(str,"\1n\1hSBL: \1mPlease verify your BBS "
+							"entry for \1y%s\1m\r\n     "
+							"in the \1cSynchronet BBS List "
+							"\1mor it will be deleted in \1i\1r%u "
+							"\1n\1h\1mdays.\r\n"
+							,bbs.name
+							,del_days-((now-l)/(24L*60L*60L)));
+						i=usernumber(bbs.user);
+						if(i) putsmsg(i,str); } } } } }
+	else
+		close(file); }
+
+if(maint)
+	return;
+
+strcpy(list_fmt,DEF_LIST_FMT);
+while(1) {
+	aborted=0;
+	attr(LIGHTGRAY);
+	cls();
+	bprintf("\1n\1m\1hSynchronet \1wBBS List \1mv1.37 (XSDK v%s)  "
+		"Developed 1994-1997 Rob Swindell\r\n\r\n",xsdk_ver);
+	sprintf(str,"~List all systems (condensed)\r\n"
+				"~Change list format\r\n"
+				"~Extended information on all systems\r\n"
+				"~Turn screen pause %s\r\n"
+				"~Find text in BBS entries\r\n"
+				"~Generate sorted list\r\n"
+				"~Display sorted list\r\n"
+				"~New entry scan\r\n"
+				"~Add a BBS entry\r\n"
+				"~Update a BBS entry\r\n"
+				"~Verify a BBS entry\r\n"
+				"~Remove a BBS entry\r\n"
+				"~Quit back to BBS\r\n"
+				,sbl_pause ? "OFF" : "ON");
+	mnemonics(str);
+	if(SYSOP)
+		mnemonics(	"~* Undelete entries\r\n");
+
+	bputs("\r\n");
+
+	l=filelength(fileno(stream));
+	if(l>0)
+		bprintf("\1n\1cThere are \1h%lu\1n\1c entries in the online BBS list "
+			"database.\r\n",l/(long)sizeof(bbs_t));
+
+	if(del_days) {
+		bprintf("\1n\1cEntries are auto-deleted \1h%u\1n\1c days after "
+			"last update or verification.\r\n",del_days);
+		bputs("Users are encouraged to \1hV\1n\1cerify (vouch for) any listed "
+			"BBSs they call.\r\n"); }
+
+	nodesync(); 			/* Display any waiting messages */
+
+	bputs("\r\n\1y\1hWhich: \1w");
+	switch(getkeys("CLGDEFSNAURTQV!*",0)) {
+		case '!':
+			bprintf("\r\nsizeof(bbs_t)=%u\r\n",sizeof(bbs_t));
+			pause();
+			break;
+		case '*':
+			cls();
+			if(!SYSOP)
+				break;
+			fseek(stream,0L,SEEK_SET);
+			while(!feof(stream) && !aborted) {
+				if(!fread(&bbs,sizeof(bbs_t),1,stream))
+					break;
+				if(!bbs.name[0]) {
+					bbs.name[0]='?';
+					bbs.verified=time(NULL);
+					sprintf(bbs.userverified,"%.25s",user_name);
+					if(yesno(bbs.name)) {
+						bprintf("\1n\1gFirst char: \1h");
+						bbs.name[0]=getkey(0);
+						bprintf("%s\r\n",bbs.name);
+						fseek(stream,-(long)sizeof(bbs_t),SEEK_CUR);
+						fwrite(&bbs,sizeof(bbs_t),1,stream); } } }
+            break;
+		case 'L':
+			cls();
+			fseek(stream,0L,SEEK_SET);
+			i=0;
+			while(!feof(stream) && !aborted) {
+				if(!fread(&bbs,sizeof(bbs_t),1,stream))
+					break;
+				if(!bbs.name[0])
+					continue;
+				i++;
+				if(!short_bbs_info(bbs))
+					break;
+				if(!sbl_pause)
+					lncntr=0; }
+			bprintf("\r\n\1n\1h%u systems listed.\r\n",i);
+			if(kbhit())
+				getch();
+			if(lncntr)
+				pause();
+			break;
+		case 'C':
+			cls();
+			bputs("\1n\1c\1hList Format Specifier Definitions:\1n\r\n\r\n");
+			bputs("\1h\1w(\1mN\1w) \1n\1mName of System\r\n");
+			bputs("\1h\1w(\1mS\1w) \1n\1mSoftware Used\r\n");
+			bputs("\1h\1w(\1mP\1w) \1n\1mPhone Number\r\n");
+			bputs("\1h\1w(\1mB\1w) \1n\1mMaximum Connect Rate (in bps)\r\n");
+			bputs("\1h\1w(\1mM\1w) \1n\1mModem Type\r\n");
+			bputs("\1h\1w(\1mY\1w) \1n\1mSysop's Name\r\n");
+			bputs("\1h\1w(\1mT\1w) \1n\1mTotal Number of Nodes\r\n");
+			bputs("\1h\1w(\1mU\1w) \1n\1mTotal Number of Users\r\n");
+			bputs("\1h\1w(\1mH\1w) \1n\1mTotal Storage Capacity (in megabytes)\r\n");
+			bputs("\1h\1w(\1mL\1w) \1n\1mLocation (City, State)\r\n");
+			bputs("\1h\1w(\1mF\1w) \1n\1mDate System was First Online\r\n");
+			bputs("\1h\1w(\1mC\1w) \1n\1mDate Entry was Created\r\n");
+			bputs("\1h\1w(\1mV\1w) \1n\1mDate Entry was Last Verified\r\n");
+			bputs("\1h\1w(\1mD\1w) \1n\1mDate Entry was Last Updated\r\n");
+			bprintf("\r\n\1n\1gDefault Format: \1h%s",DEF_LIST_FMT);
+			bprintf("\r\n\1n\1gCurrent Format: \1h%s\r\n",list_fmt);
+			bprintf("\r\n\1y\1hNew Format: ");
+			if(getstr(tmp,10,K_UPPER|K_LINE)) {
+				if(!strchr(tmp,'P') || !strchr(tmp,'N')) {
+					bputs("\r\n\1h\1mP\1n\1mhone and \1hN\1n\1mame specifiers "
+						"must be present in format.\r\n\r\n");
+					pause(); }
+				else
+					strcpy(list_fmt,tmp); }
+			break;
+		case 'E':
+			fseek(stream,0L,SEEK_SET);
+			while(!feof(stream) && !aborted) {
+				if(!fread(&bbs,sizeof(bbs_t),1,stream))
+					break;
+				if(bbs.name[0] && !long_bbs_info(bbs))
+					break;
+				if(!sbl_pause)
+					lncntr=0; }
+            break;
+		case 'F':   /* Find text */
+			cls();
+			bputs("\1y\1hText to search for: ");
+			if(!getstr(name,25,K_UPPER|K_LINE))
+				break;
+			ch=yesno("\r\nDisplay extended information");
+
+			found=0;
+			bputs("\1n\1h\r\nSearching...\r\n\r\n");
+			fseek(stream,0L,SEEK_SET);
+			while(!feof(stream) && !aborted) {
+				if(!sbl_pause)
+					lncntr=0;
+				if(!fread(&bbs,sizeof(bbs_t),1,stream))
+					break;
+				if(!bbs.name[0])
+					continue;
+				tmpbbs=bbs;
+				strupr(tmpbbs.name);
+				strupr(tmpbbs.user);
+				strupr(tmpbbs.userverified);
+				strupr(tmpbbs.userupdated);
+				strupr(tmpbbs.software);
+				if(strstr(tmpbbs.name,name)
+					|| strstr(tmpbbs.user,name)
+					|| strstr(tmpbbs.software,name)
+					|| strstr(tmpbbs.userverified,name)
+					|| strstr(tmpbbs.userupdated,name)
+					) {
+					found++;
+					if(ch && !long_bbs_info(bbs))
+						break;
+					if(!ch && !short_bbs_info(bbs))
+						break;
+					continue; }
+
+				for(i=0;i<tmpbbs.total_sysops;i++) {
+					strupr(tmpbbs.sysop[i]);
+					if(strstr(tmpbbs.sysop[i],name))
+						break; }
+				if(i<tmpbbs.total_sysops) {
+					found++;
+					if(ch && !long_bbs_info(bbs))
+						break;
+					if(!ch && !short_bbs_info(bbs))
+						break;
+					continue; }
+
+				for(i=0;i<tmpbbs.total_networks;i++) {
+					strupr(tmpbbs.network[i]);
+					strupr(tmpbbs.address[i]);
+					if(strstr(tmpbbs.network[i],name)
+						|| strstr(tmpbbs.address[i],name))
+						break; }
+				if(i<tmpbbs.total_networks) {
+					found++;
+					if(ch && !long_bbs_info(bbs))
+						break;
+					if(!ch && !short_bbs_info(bbs))
+						break;
+                    continue; }
+
+				for(i=0;i<tmpbbs.total_terminals;i++) {
+					strupr(tmpbbs.terminal[i]);
+					if(strstr(tmpbbs.terminal[i],name))
+						break; }
+				if(i<tmpbbs.total_terminals) {
+					found++;
+					if(ch && !long_bbs_info(bbs))
+						break;
+					if(!ch && !short_bbs_info(bbs))
+						break;
+                    continue; }
+
+				for(i=0;i<tmpbbs.total_numbers;i++) {
+					strupr(tmpbbs.number[i].number);
+					strupr(tmpbbs.number[i].modem);
+					strupr(tmpbbs.number[i].location);
+					if(strstr(tmpbbs.number[i].number,name)
+						|| strstr(tmpbbs.number[i].modem,name)
+						|| strstr(tmpbbs.number[i].location,name))
+						break; }
+				if(i<tmpbbs.total_numbers) {
+					found++;
+					if(ch && !long_bbs_info(bbs))
+						break;
+					if(!ch && !short_bbs_info(bbs))
+						break;
+					continue; } }
+			if(!ch || !found) {
+				CRLF;
+				if(kbhit())
+					getch();
+				if(found)
+					bprintf("\1n\1h%u systems listed.\r\n",found);
+				pause(); }
+            break;
+		case 'G':   /* Generated sorted list */
+			cls();
+			if(!filelength(fileno(stream))) {
+                bprintf("No BBS list exists.\r\n");
+                pause();
+                break; }
+			bputs("\1n\1c\1hSort Options:\1n\r\n\r\n");
+			bputs("\1h\1w(\1mN\1w) \1n\1mName of System\r\n");
+			bputs("\1h\1w(\1mS\1w) \1n\1mSoftware Used\r\n");
+			bputs("\1h\1w(\1mP\1w) \1n\1mPhone Number\r\n");
+			bputs("\1h\1w(\1mB\1w) \1n\1mMaximum Connect Rate (in bps)\r\n");
+			bputs("\1h\1w(\1mM\1w) \1n\1mModem Type\r\n");
+			bputs("\1h\1w(\1mY\1w) \1n\1mSysop's Name\r\n");
+			bputs("\1h\1w(\1mT\1w) \1n\1mTotal Number of Nodes\r\n");
+			bputs("\1h\1w(\1mU\1w) \1n\1mTotal Number of Users\r\n");
+			bputs("\1h\1w(\1mH\1w) \1n\1mTotal Storage Capacity (in megabytes)\r\n");
+			bputs("\1h\1w(\1mL\1w) \1n\1mLocation (City, State)\r\n");
+			bputs("\1h\1w(\1mF\1w) \1n\1mDate System was First Online\r\n");
+			bputs("\1h\1w(\1mC\1w) \1n\1mDate Entry was Created\r\n");
+			bputs("\1h\1w(\1mV\1w) \1n\1mDate Entry was Last Verified\r\n");
+            bputs("\1h\1w(\1mD\1w) \1n\1mDate Entry was Last Updated\r\n");
+			bprintf("\r\n\1y\1hSort by (\1wQ\1y=Quit): \1w");
+			ch=getkeys("NSPBMYTUHLFCVDQ",0);
+			if(!ch || ch=='Q')
+				break;
+			cls();
+			bputs("\1n\1hSorting...     \1m");
+			fseek(stream,0L,SEEK_SET);
+			i=j=done=0;
+			sort_by_str=0;
+			sortstr=NULL;
+			sortint=NULL;
+			while(!feof(stream) && !done) {
+				if(!fread(&bbs,sizeof(bbs_t),1,stream))
+					break;
+				j++;
+				bprintf("\b\b\b\b%4u",j);
+				if(!bbs.name[0])	/* don't sort deleted entries */
+                    continue;
+				i++;
+				switch(ch) {
+					case 'N':
+						sprintf(str,"%.30s",bbs.name);
+						sort_by_str=1;
+						break;
+					case 'S':
+						sprintf(str,"%.30s",bbs.software);
+						sort_by_str=1;
+						break;
+					case 'P':
+						sprintf(str,"%.30s",bbs.number[0].number);
+						sort_by_str=1;
+						break;
+					case 'M':
+						sprintf(str,"%.30s",bbs.number[0].modem);
+						sort_by_str=1;
+						break;
+					case 'Y':
+						sprintf(str,"%.30s",bbs.sysop[0]);
+						sort_by_str=1;
+						break;
+					case 'L':
+						sprintf(str,"%.30s",bbs.number[0].location);
+						sort_by_str=1;
+						break;
+					case 'B':
+						l=bbs.number[0].max_rate;
+						break;
+					case 'T':
+						l=bbs.nodes;
+						break;
+					case 'U':
+						l=bbs.users;
+						break;
+					case 'H':
+						l=bbs.megs;
+						break;
+					case 'F':
+						l=bbs.birth;
+						break;
+					case 'C':
+						l=bbs.created;
+						break;
+					case 'V':
+						l=bbs.verified;
+						break;
+					case 'D':
+						l=bbs.updated;
+						break; }
+				if(sort_by_str) {
+					if((sortstr=(sortstr_t *)farrealloc(sortstr
+						,sizeof(sortstr_t)*i))==NULL) {
+						bprintf("\r\n\7Memory allocation error\r\n");
+						farfree(sortstr);
+						done=1;
+						continue; }
+					strcpy(sortstr[i-1].str,str);
+					sortstr[i-1].offset=j-1; }
+				else {
+					if((sortint=(sortint_t *)farrealloc(sortint
+						,sizeof(sortint_t)*i))==NULL) {
+						bprintf("\r\n\7Memory allocation error\r\n");
+						farfree(sortint);
+						done=1;
+						continue; }
+					sortint[i-1].i=l;
+					sortint[i-1].offset=j-1; } }
+
+			if(done) {
+				pause();
+				break; }
+
+			if(sort_by_str)
+				qsort((void *)sortstr,i,sizeof(sortstr[0])
+					,(int(*)(const void *, const void *))sortstr_cmp);
+			else
+				qsort((void *)sortint,i,sizeof(sortint[0])
+					,(int(*)(const void *, const void *))sortint_cmp);
+
+			bprintf("\r\n\r\n\1h\1gCreating index...");
+			sprintf(str,"SORT_%03d.NDX",node_num);
+			if((file=nopen(str,O_WRONLY|O_CREAT|O_TRUNC))==-1) {
+				bprintf("\r\n\7Error creating %s\r\n",str);
+				if(sort_by_str)
+					farfree(sortstr);
+				else
+					farfree(sortint);
+				pause();
+				break; }
+			for(j=0;j<i;j++)
+				if(sort_by_str)
+					write(file,&sortstr[j].offset,2);
+				else
+					write(file,&sortint[j].offset,2);
+			close(file);
+			if(sort_by_str)
+				farfree(sortstr);
+			else
+				farfree(sortint);
+			bputs("\r\n\r\n\1n\1hDone.\r\n");
+			pause();
+			break;
+		case 'D':
+			cls();
+			sprintf(str,"SORT_%03d.NDX",node_num);
+			if((file=nopen(str,O_RDONLY))==-1) {
+				bputs("\1n\1r\1hSorted list not generated.\r\n");
+				pause(); }
+			ch=yesno("Display extended information");
+			cls();
+			while(!eof(file) && !aborted) {
+				if(read(file,&i,2)!=2)
+					break;
+				fseek(stream,(long)i*sizeof(bbs_t),SEEK_SET);
+				if(!sbl_pause)
+					lncntr=0;
+				if(!fread(&bbs,sizeof(bbs_t),1,stream))
+					break;
+				if(!bbs.name[0])
+                    continue;
+				if(ch && !long_bbs_info(bbs))
+					break;
+				if(!ch && !short_bbs_info(bbs))
+					break; }
+			close(file);
+			if(kbhit())
+				getch();
+			if(lncntr)
+                pause();
+			break;
+		case 'N':   /* New (updated) entry scan */
+			cls();
+			bputs("\1y\1hLast update (MM/DD/YY): ");
+			if(!getstr(str,8,K_UPPER|K_LINE))
+				break;
+			l=dstrtounix(str);
+			ch=yesno("\r\nDisplay extended information");
+			found=0;
+			bputs("\1n\1h\r\nSearching...\r\n\r\n");
+			fseek(stream,0L,SEEK_SET);
+			while(!feof(stream) && !aborted) {
+				if(!sbl_pause)
+					lncntr=0;
+				if(!fread(&bbs,sizeof(bbs_t),1,stream))
+					break;
+				if(!bbs.name[0])
+					continue;
+				if(bbs.updated>=l || bbs.created>=l) {
+					if(ch && !long_bbs_info(bbs))
+						break;
+					if(!ch && !short_bbs_info(bbs))
+						break;
+					found++;
+					continue; } }
+			if(!ch || !found) {
+				CRLF;
+				pause(); }
+            break;
+		case 'A':
+			cls();
+			if(user_level<add_ml) {
+				bprintf("\1h\1rYou have insufficient access.\r\n\r\n");
+				pause();
+				break; }
+			bputs("\1g\1hAdding a BBS entry:\1n\r\n\r\n");
+			bputs("\1n\1gHit ENTER for unknown data items.\r\n\r\n");
+			memset(&bbs,NULL,sizeof(bbs_t));
+			if(!get_bbs_info(&bbs))
+				break;
+			bputs("\1n\1h\r\nSearching for duplicates...");
+			fseek(stream,0L,SEEK_SET);
+			i=0;
+			dots(0);
+			while(!feof(stream) && !i) {
+				dots(1);
+				if(!fread(&tmpbbs,sizeof(bbs_t),1,stream))
+					break;
+				if(!stricmp(tmpbbs.name,bbs.name)) i=1; }
+			if(i) {
+				bprintf("\7\1n\1h\1r\1i\r\n\r\n%s \1n\1h\1ralready exists!"
+					"\r\n\r\n"
+					,bbs.name);
+				pause();
+				break; }
+
+			bputs("\1n\1h\r\nSaving...");
+			fseek(stream,0L,SEEK_SET);
+			dots(0);
+			while(!feof(stream)) {
+				dots(1);
+				if(!fread(&ch,1,1,stream))
+					break;
+				if(!ch) {			/* first byte is null */
+					fseek(stream,-1L,SEEK_CUR);
+					break; }
+				fseek(stream,(long)sizeof(bbs_t)-1L,SEEK_CUR); }
+			bbs.created=time(NULL);
+			fwrite(&bbs,sizeof(bbs_t),1,stream);
+			if(notify_user && notify_user!=user_number) {
+				sprintf(str,"\1n\1hSBL: \1y%s \1madded \1c%s\1m "
+					"to the BBS List\r\n",user_name,bbs.name);
+				putsmsg(notify_user,str); }
+			break;
+		case 'R':       /* Remove an entry */
+			cls();
+			if(user_level<remove_ml) {
+				bprintf("\1h\1rYou have insufficient access.\r\n\r\n");
+				pause();
+                break; }
+			bprintf("\1y\1hRemove which system: ");
+			if(!getstr(name,25,K_LINE|K_UPPER))
+				break;
+			bputs("\1n\1h\r\nSearching...");
+			fseek(stream,0L,SEEK_SET);
+			found=0;
+			dots(0);
+			while(!feof(stream) && !aborted) {
+				dots(1);
+				if(!fread(&bbs,sizeof(bbs_t),1,stream))
+					break;
+				if(!stricmp(bbs.name,name) || partname(bbs.name,name)) {
+					found=1;
+					for(i=0;i<bbs.total_sysops && i<MAX_SYSOPS;i++)
+						if(!stricmp(bbs.sysop[i],user_name))
+							break;
+					if(SYSOP || !stricmp(bbs.user,user_name)
+						|| i<bbs.total_sysops) {
+						fseek(stream,-(long)(sizeof(bbs_t)),SEEK_CUR);
+						strcpy(tmp,bbs.name);
+						bbs.name[0]=0;
+						bbs.updated=time(NULL);
+						fwrite(&bbs,sizeof(bbs_t),1,stream);
+						bprintf("\r\n\r\n\1m%s\1c deleted."
+							,tmp);
+						if(notify_user && notify_user!=user_number) {
+							sprintf(str,"\1n\1hSBL: \1y%s \1mremoved \1c%s\1m "
+								"from the BBS List\r\n",user_name,tmp);
+							putsmsg(notify_user,str); } }
+					else
+						bprintf("\r\n\r\n\1rYou did not create \1m%s\1n."
+							,bbs.name);
+					break; } }
+			if(!found)
+				bprintf("\r\n\r\n\1m%s\1c not found.",name);
+			CRLF;
+			CRLF;
+			pause();
+            break;
+		case 'T':
+			sbl_pause=!sbl_pause;
+			break;
+		case 'V':       /* Verify an entry */
+			cls();
+			if(user_level<verify_ml) {
+				bprintf("\1h\1rYou have insufficient access.\r\n\r\n");
+				pause();
+                break; }
+			bprintf("\1y\1hVerify which system: ");
+			if(!getstr(name,25,K_LINE|K_UPPER))
+				break;
+			bputs("\1n\1h\r\nSearching...");
+			fseek(stream,0L,SEEK_SET);
+			found=0;
+			dots(0);
+			while(!feof(stream) && !aborted) {
+				dots(1);
+				if(!fread(&bbs,sizeof(bbs_t),1,stream))
+					break;
+				if(!stricmp(bbs.name,name) || partname(bbs.name,name)) {
+					found=1;
+					bbs.verified=time(NULL);
+					sprintf(bbs.userverified,"%.25s",user_name);
+					fseek(stream,-(long)(sizeof(bbs_t)),SEEK_CUR);
+					fwrite(&bbs,sizeof(bbs_t),1,stream);
+					bprintf("\r\n\r\n\1m%s\1c verified. \1r\1h\1iThank you!"
+						,bbs.name);
+					sprintf(str,"\1n\1hSBL: \1y%s \1mverified \1c%s\1m "
+						"in the BBS List\r\n",user_name,bbs.name);
+					if(notify_user && notify_user!=user_number)
+						putsmsg(notify_user,str);
+					if(stricmp(bbs.user,user_name)) {
+						i=usernumber(bbs.user);
+						if(i && i!=user_number) putsmsg(i,str); }
+					break; } }
+			if(!found)
+				bprintf("\r\n\r\n\1m%s\1c not found.",name);
+			CRLF;
+			CRLF;
+			pause();
+            break;
+		case 'U':       /* Update an entry */
+			cls();
+			if(user_level<update_ml) {
+				bprintf("\1h\1rYou have insufficient access.\r\n\r\n");
+				pause();
+                break; }
+			bprintf("\1y\1hUpdate which system: ");
+			if(!getstr(name,25,K_LINE|K_UPPER))
+				break;
+			bputs("\1n\1h\r\nSearching...");
+			fseek(stream,0L,SEEK_SET);
+			found=0;
+			dots(0);
+			while(!feof(stream) && !aborted) {
+				dots(1);
+				l=ftell(stream);
+				if(!fread(&bbs,sizeof(bbs_t),1,stream))
+					break;
+				if(!stricmp(bbs.name,name) || partname(bbs.name,name)) {
+					found=1;
+					break; } }
+			if(found) {
+				for(i=0;i<bbs.total_sysops && i<MAX_SYSOPS;i++)
+					if(!bbs.sysop[i][0] || !stricmp(bbs.sysop[i],user_name))
+						break;
+				if(SYSOP || !stricmp(bbs.user,user_name)
+					|| i<bbs.total_sysops) {
+					CRLF;
+					CRLF;
+					if(get_bbs_info(&bbs)) {
+						bbs.misc&=~FROM_SMB;
+						bbs.updated=time(NULL);
+						sprintf(bbs.userupdated,"%.25s",user_name);
+						fseek(stream,l,SEEK_SET);
+						fwrite(&bbs,sizeof(bbs_t),1,stream);
+						bprintf("\r\n\1h\1m%s\1c updated.",bbs.name);
+						if(notify_user && notify_user!=user_number) {
+							sprintf(str,"\1n\1hSBL: \1y%s \1mupdated \1c%s\1m "
+								"in the BBS List\r\n",user_name,bbs.name);
+							putsmsg(notify_user,str); } } }
+                else
+					bprintf("\r\n\r\n\1h\1rYou did not create \1m%s\1n."
+					   ,bbs.name); }
+			else
+				bprintf("\r\n\r\n\1h\1m%s\1c not found.",name);
+			CRLF;
+			CRLF;
+			pause();
+            break;
+		case 'Q':
+			return; } }
+}
+
+/* End of SBL.C */
diff --git a/xtrn/sbl/sbl.cfg b/xtrn/sbl/sbl.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..6b27510098b8ea9a8263a4eb90caa1e94b90af3d
--- /dev/null
+++ b/xtrn/sbl/sbl.cfg
@@ -0,0 +1,6 @@
+90					Days to keep non-updated entries
+10					Minimum security level to add
+10 					Minimum security level to update
+10					Minimum security level to remove
+10					Minimum security level to verify
+1					User to notify of changes (0=none)
diff --git a/xtrn/sbl/sbl.doc b/xtrn/sbl/sbl.doc
new file mode 100644
index 0000000000000000000000000000000000000000..22852c29f82d78e76013a0658b444ee4527946f0
--- /dev/null
+++ b/xtrn/sbl/sbl.doc
@@ -0,0 +1,88 @@
+Synchronet BBS List v1.36
+=========================
+
+Create the directory: SBL off of your Synchronet XTRN directory.
+(Example: C:\SBBS\XTRN\SBL) 
+
+Make sure SBL.EXE and SBL.CFG are in this directory.
+
+Use an ASCII editor to edit the Synchronet BBS List config file (SBL.CFG) to 
+your liking.
+
+Under SCFG->External Programs->Online Programs->Main, hit INS and add 
+Synchronet BBS List with the following configuration:
+
+Name				Synchronet BBS List
+Internal Code			SBL
+Start-up Directory		..\XTRN\SBL
+Command Line			SBL
+Clean-up Command Line
+Multiuser			Yes
+Intercept I/O Interrupts	Yes
+Shrink BBS			No
+Modify User Data		No
+BBS Data File Type		Synchronet    XTRN.DAT
+Place Drop File In		Node Directory
+
+SBL Utilities
+=============
+
+SBLPACK.EXE	Remove deleted entries to save disk space and speed up listings
+SBL2TXT.EXE     Convert SBL listing to a text file
+
+Networking the Synchronet BBS List
+==================================
+
+If you wish to link your SBL database with other Synchronet BBSs, you must
+carry the Synchronet Data echo via DOVE-Net, FidoNet, or RIME and add
+the following two events in SCFG->External Programs->Timed Events:
+
+Internal Code			SMB2SBL
+Start-up Directory		..\XTRN\SBL
+Command Line			SMB2SBL %jSUBS\SYNCDATA SBL.DAB 
+Execution Node			1
+Execution Days			Sun Mon Tue Wed Thu Fri Sat
+Execution Time			04:30
+Requires Exclusive Execution	No
+Force Users Off-line for Event	No
+
+Internal Code			SBL2SMB
+Start-up Directory		..\XTRN\SBL
+Command Line			SBL2SMB SBL.DAB %jSUBS\SYNCDATA /S:SYN
+Execution Node			1
+Execution Days			Sun Mon Tue Wed Thu Fri Sat
+Execution Time			04:31
+Requires Exclusive Execution	No
+Force Users Off-line for Event  No
+
+Change "SYNCDATA" to the internal code of the Synchronet Data sub-board on
+your BBS. We used SYNCDATA, since that is the example given in the Synchronet
+manual for the DOVE-Net Synchronet Data sub-board internal code. It is okay
+if you used a completely different internal code, as long as you specify that
+code instead of "SYNCDATA" on the above command lines.
+
+Make note that all networked SBL entries will be deleted from Vertrauen
+(Home of the Synchronet BBS List) after 90 days unless the entry is
+retransmitted in the message base or updated manually on Vertrauen in that
+time. To cause a retransmission from your BBS, you must (U)pdate the entry
+using SBL and then SBL2SMB will re-export it automatically next time it is run
+(via Timed Event). (V)erified entries are not re-exported by SBL2SMB, only new
+and updated entries. You can also create a BBS entry message by hand (not
+using SBL). Download SBBSLIST.ZIP from Vertrauen for more details.
+
+Source Code
+===========
+
+If you are a C programmer, you may find the included source code interesting
+and/or useful as an example for writing your own XSDK and/or SMB utilities.
+
+MAKEFILE is for use with Borland C++ v3.x or v4.x and the Synchronet XSDK
+to create SBL.EXE. SBL.C and SBLDEFS.H must also in the current directory when
+MAKE is run.
+
+The SMB C library (SMB_120.ZIP) is required to compile SBL2SMB.C and SMB2SBL.C
+(MAKE.BAT included to compile them).
+
+SBL2TXT.C is included as an SBL.DAB to SBL.TXT converter.
+
+/* End of SBL.DOC */
diff --git a/xtrn/sbl/sbl2smb.c b/xtrn/sbl/sbl2smb.c
new file mode 100644
index 0000000000000000000000000000000000000000..a66ab1b105c3b5e31655f578c963f945e824ebc2
--- /dev/null
+++ b/xtrn/sbl/sbl2smb.c
@@ -0,0 +1,330 @@
+/* SBL2SMB.C */
+
+/* Developed 1990-1997 by Rob Swindell; PO Box 501, Yorba Linda, CA 92885 */
+
+/* Scans SBL database and posts any additions/updates into the an SMB base */
+
+#define  uint unsigned int
+
+#include <dos.h>
+#include "smblib.h"
+#include "sbldefs.h"
+
+smb_t		smb;
+extern int	daylight=0;
+extern long timezone=0L;
+
+unsigned	_stklen=16000;
+
+/****************************************************************************/
+/* Checks the disk drive for the existence of a file. Returns 1 if it       */
+/* exists, 0 if it doesn't.                                                 */
+/****************************************************************************/
+char fexist(char *filespec)
+{
+    struct ffblk f;
+
+if(findfirst(filespec,&f,0)==0)
+    return(1);
+return(0);
+}
+
+
+/****************************************************************************/
+/* Updates 16-bit "rcrc" with character 'ch'                                */
+/****************************************************************************/
+void ucrc16(uchar ch, ushort *rcrc) {
+	ushort i, cy;
+    uchar nch=ch;
+ 
+for (i=0; i<8; i++) {
+    cy=*rcrc & 0x8000;
+    *rcrc<<=1;
+    if (nch & 0x80) *rcrc |= 1;
+    nch<<=1;
+    if (cy) *rcrc ^= 0x1021; }
+}
+
+/****************************************************************************/
+/* Returns 16-crc of string (not counting terminating NULL) 				*/
+/****************************************************************************/
+ushort crc16(char *str)
+{
+	int 	i=0;
+	ushort	crc=0;
+
+ucrc16(0,&crc);
+while(str[i])
+	ucrc16(str[i++],&crc);
+ucrc16(0,&crc);
+ucrc16(0,&crc);
+return(crc);
+}
+
+
+/****************************************************************************/
+/* Converts unix time format (long - time_t) into a char str MM/DD/YY		*/
+/****************************************************************************/
+char *unixtodstr(time_t unix, char *str)
+{
+	struct time curtime;
+	struct date date;
+
+if(!unix)
+	strcpy(str,"00/00/00");
+else {
+	unixtodos(unix,&date,&curtime);
+	if((unsigned)date.da_mon>12) {	  /* DOS leap year bug */
+		date.da_mon=1;
+		date.da_year++; }
+	if((unsigned)date.da_day>31)
+		date.da_day=1;
+	sprintf(str,"%02u/%02u/%02u",date.da_mon,date.da_day
+		,date.da_year>=2000 ? date.da_year-2000 : date.da_year-1900); }
+return(str);
+}
+
+#define BUF_LEN 8192
+
+int main(int argc, char **argv)
+{
+	uchar	str[128],tmp[128],buf[BUF_LEN],*p,software[128];
+	int 	i,file;
+	ushort	xlat;
+	long	length;
+	ulong	offset;
+	time_t	last,t;
+	bbs_t	bbs;
+	smbmsg_t msg;
+	smbstatus_t status;
+	FILE	*stream;
+
+fprintf(stderr,"\nSBL2SMB v2.00 - Write SBL to SMB - Developed 1994-1997 "
+	"Rob Swindell\n\n");
+if(argc<3) {
+	fprintf(stderr,"usage: sbl2smb <sbl.dab> <smb_file> [/s:software]\n\n");
+	fprintf(stderr,"ex: sbl2smb c:\\sbbs\\xtrn\\sbl\\sbl.dab "
+		"c:\\sbbs\\data\\subs\\syncdata /s:syn\n");
+	return(1); }
+
+software[0]=0;
+if(argc>3 && !strnicmp(argv[3],"/S:",3))
+	strcpy(software,argv[3]+3);
+
+strcpy(smb.file,argv[2]);
+strupr(smb.file);
+
+strcpy(str,argv[1]);
+strupr(str);
+if((file=open(str,O_RDONLY|O_BINARY|O_DENYNONE))==-1) {
+	printf("error opening %s\n",str);
+	return(1); }
+if((stream=fdopen(file,"rb"))==NULL) {
+	printf("error fdopening %s\n",str);
+	return(1); }
+strcpy(tmp,str);
+p=strrchr(tmp,'.');
+if(p) {
+	(*p)=0;
+	strcat(tmp,"2SMB.DAB");
+	if((file=open(tmp,O_RDWR|O_BINARY|O_CREAT,S_IWRITE|S_IREAD))==-1) {
+		printf("error opening %s\n",str);
+		return(1); }
+	t=time(NULL);
+	if(read(file,&last,sizeof(time_t))!=sizeof(time_t))
+		last=t;
+	lseek(file,0L,SEEK_SET);
+	write(file,&t,sizeof(time_t));
+	close(file); }
+
+sprintf(str,"%s.SHD",smb.file);
+if(!fexist(str)) {
+	printf("%s doesn't exist\n",smb.file);
+	return(0); }
+fprintf(stderr,"Opening %s\n",smb.file);
+smb.retry_time=30;
+if((i=smb_open(&smb))!=0) {
+	printf("smb_open returned %d\n",i);
+	return(1); }
+i=smb_locksmbhdr(&smb);
+if(i) {
+	printf("smb_locksmbhdr returned %d\n",i);
+	return(1); }
+i=smb_getstatus(&smb);
+smb_unlocksmbhdr(&smb);
+if(i) {
+	printf("smb_getstatus returned %d\n",i);
+	return(1); }
+
+while(!feof(stream)) {
+	if(!fread(&bbs,sizeof(bbs_t),1,stream))
+		break;
+	if(!bbs.name[0] || bbs.misc&FROM_SMB
+		|| (bbs.updated<last && bbs.created<last))
+		continue;
+	if(software[0] && strnicmp(software,bbs.software,strlen(software)))
+		continue;
+
+	printf("%s\r\n",bbs.name);
+	memset(buf,0,BUF_LEN);
+
+	sprintf(str,"%-15.15s%s\r\n"
+		,"Name:",bbs.name);
+	strcat(buf,str);
+
+	sprintf(str,"%-15.15s%s\r\n"
+		,"Birth:",unixtodstr(bbs.birth,tmp));
+    strcat(buf,str);
+
+	sprintf(str,"%-15.15s%s\r\n"
+		,"Software:",bbs.software);
+    strcat(buf,str);
+
+	for(i=0;i<bbs.total_sysops;i++) {
+		sprintf(str,"%-15.15s%s\r\n"
+			,"Sysop:",bbs.sysop[i]);
+		strcat(buf,str); }
+
+	strcat(buf,"\r\n");
+	for(i=0;i<bbs.total_numbers;i++) {
+		sprintf(str,"%-15.15s%s\r\n"
+			,"Number:",bbs.number[i].number);
+		strcat(buf,str);
+		sprintf(str,"%-15.15s%s\r\n"
+			,"Modem:",bbs.number[i].modem);
+        strcat(buf,str);
+		sprintf(str,"%-15.15s%s\r\n"
+			,"Location:",bbs.number[i].location);
+        strcat(buf,str);
+		sprintf(str,"%-15.15s%u\r\n"
+			,"MinRate:",bbs.number[i].min_rate);
+        strcat(buf,str);
+		sprintf(str,"%-15.15s%u\r\n"
+			,"MaxRate:",bbs.number[i].max_rate);
+		strcat(buf,str);
+		if(i+1<bbs.total_numbers)
+			strcat(buf,"\r\n"); }
+
+	if(bbs.total_networks)
+		strcat(buf,"\r\n");
+	for(i=0;i<bbs.total_networks;i++) {
+		sprintf(str,"%-15.15s%s\r\n"
+			,"Network:",bbs.network[i]);
+        strcat(buf,str);
+		sprintf(str,"%-15.15s%s\r\n"
+			,"Address:",bbs.address[i]);
+		strcat(buf,str);
+		if(i+1<bbs.total_networks)
+			strcat(buf,"\r\n"); }
+
+	strcat(buf,"\r\n");
+	for(i=0;i<bbs.total_terminals;i++) {
+		sprintf(str,"%-15.15s%s\r\n"
+			,"Terminal:",bbs.terminal[i]);
+		strcat(buf,str); }
+
+	strcat(buf,"\r\n");
+	sprintf(str,"%-15.15s%lu\r\n"
+		,"Megs:",bbs.megs);
+	strcat(buf,str);
+	sprintf(str,"%-15.15s%lu\r\n"
+		,"Msgs:",bbs.msgs);
+    strcat(buf,str);
+	sprintf(str,"%-15.15s%lu\r\n"
+		,"Files:",bbs.files);
+    strcat(buf,str);
+	sprintf(str,"%-15.15s%u\r\n"
+		,"Nodes:",bbs.nodes);
+    strcat(buf,str);
+	sprintf(str,"%-15.15s%u\r\n"
+		,"Users:",bbs.users);
+    strcat(buf,str);
+	sprintf(str,"%-15.15s%u\r\n"
+		,"Subs:",bbs.subs);
+    strcat(buf,str);
+	sprintf(str,"%-15.15s%u\r\n"
+		,"Dirs:",bbs.dirs);
+    strcat(buf,str);
+	sprintf(str,"%-15.15s%u\r\n"
+		,"Xtrns:",bbs.xtrns);
+    strcat(buf,str);
+
+	if(bbs.desc[0][0])
+		strcat(buf,"\r\n");
+	for(i=0;i<5;i++) {
+		if(!bbs.desc[i][0])
+			break;
+		sprintf(str,"%-15.15s%s\r\n"
+			,"Desc:",bbs.desc[i]);
+		strcat(buf,str); }
+
+	strcat(buf,"\r\n--- SBL2SMB v1.10");
+
+	length=strlen(buf);   /* +2 for translation string */
+
+	if(status.attr&SMB_HYPERALLOC)
+		offset=smb_hallocdat(&smb);
+	else {
+		i=smb_open_da(&smb);
+		if(i) {
+			printf("smb_open_da returned %d\n",i);
+			exit(1); }
+		offset=smb_allocdat(&smb,length+2,1);
+		fclose(smb.sda_fp); }
+
+	fseek(smb.sdt_fp,offset,SEEK_SET);
+	xlat=XLAT_NONE;
+	fwrite(&xlat,2,1,smb.sdt_fp);
+	fwrite(buf,length,1,smb.sdt_fp);
+	length+=2;
+
+	memset(&msg,0,sizeof(smbmsg_t));
+	memcpy(msg.hdr.id,"SHD\x1a",4);
+	msg.hdr.version=SMB_VERSION;
+	msg.hdr.when_written.time=time(NULL);
+    
+	msg.hdr.offset=offset;
+
+	strcpy(str,"SBL");
+	i=smb_hfield(&msg,RECIPIENT,strlen(str),str);
+	if(i) {
+		printf("smb_hfield returned %d\n",i);
+		smb_freemsgdat(&smb,offset,length,1);
+		exit(1); }
+	strlwr(str);
+	msg.idx.to=crc16(str);
+
+	strcpy(str,bbs.user);
+	i=smb_hfield(&msg,SENDER,strlen(str),str);
+	if(i) {
+		printf("smb_hfield returned %d\n",i);
+		smb_freemsgdat(&smb,offset,length,1);
+		exit(1); }
+	strlwr(str);
+	msg.idx.from=crc16(str);
+
+	strcpy(str,bbs.name);
+	i=smb_hfield(&msg,SUBJECT,strlen(str),str);
+	if(i) {
+		printf("smb_hfield returned %d\n",i);
+		smb_freemsgdat(&smb,offset,length,1);
+		exit(1); }
+	strlwr(str);
+	msg.idx.subj=crc16(str);
+
+	i=smb_dfield(&msg,TEXT_BODY,length);
+	if(i) {
+		printf("smb_dfield returned %d\n",i);
+		smb_freemsgdat(&smb,offset,length,1);
+		exit(1); }
+
+	i=smb_addmsghdr(&smb,&msg,status.attr&SMB_HYPERALLOC);
+	if(i) {
+		printf("smb_addmsghdr returned %d\n",i);
+		smb_freemsgdat(&smb,offset,length,1);
+		exit(1); }
+	smb_freemsgmem(&msg); }
+return(0);
+}
+
+/* End of SBL2SMB.C */
diff --git a/xtrn/sbl/sbl2txt.c b/xtrn/sbl/sbl2txt.c
new file mode 100644
index 0000000000000000000000000000000000000000..ee3b720ce866e8642361f9d525f9f98849c55914
--- /dev/null
+++ b/xtrn/sbl/sbl2txt.c
@@ -0,0 +1,178 @@
+/* SBL2TXT.C */
+
+/* Developed 1990-1997 by Rob Swindell; PO Box 501, Yorba Linda, CA 92885 */
+
+/* Converts Synchronet BBS List (SBL.DAB) to text file */
+
+#include "xsdk.h"
+#include "sbldefs.h"
+
+char *wday[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
+char *mon[]={"Jan","Feb","Mar","Apr","May","Jun"
+            ,"Jul","Aug","Sep","Oct","Nov","Dec"};
+char *nulstr="";
+char tmp[256];
+struct date date;
+struct time curtime;
+
+extern int daylight=0;
+extern long timezone=0L;
+
+/****************************************************************************/
+/* Generates a 24 character ASCII string that represents the time_t pointer */
+/* Used as a replacement for ctime()										*/
+/****************************************************************************/
+char *timestr(time_t *intime)
+{
+	static char str[256];
+    char mer[3],hour;
+    struct tm *gm;
+
+gm=localtime(intime);
+if(gm->tm_hour>=12) {
+    if(gm->tm_hour==12)
+        hour=12;
+    else
+        hour=gm->tm_hour-12;
+    strcpy(mer,"pm"); }
+else {
+    if(gm->tm_hour==0)
+        hour=12;
+    else
+        hour=gm->tm_hour;
+    strcpy(mer,"am"); }
+sprintf(str,"%s %s %02d %4d %02d:%02d %s"
+    ,wday[gm->tm_wday],mon[gm->tm_mon],gm->tm_mday,1900+gm->tm_year
+    ,hour,gm->tm_min,mer);
+return(str);
+}
+
+/****************************************************************************/
+/* Converts unix time format (long - time_t) into a char str MM/DD/YY		*/
+/****************************************************************************/
+char *unixtodstr(time_t unix, char *str)
+{
+
+if(!unix)
+	strcpy(str,"00/00/00");
+else {
+	unixtodos(unix,&date,&curtime);
+	if((unsigned)date.da_mon>12) {	  /* DOS leap year bug */
+		date.da_mon=1;
+		date.da_year++; }
+	if((unsigned)date.da_day>31)
+		date.da_day=1;
+	sprintf(str,"%02u/%02u/%02u",date.da_mon,date.da_day
+		,date.da_year>=2000 ? date.da_year-2000 : date.da_year-1900); }
+return(str);
+}
+
+
+void long_bbs_info(FILE *out, bbs_t bbs)
+{
+	int i;
+
+fprintf(out,"BBS Name: %s since %s\r\n"
+	,bbs.name,unixtodstr(bbs.birth,tmp));
+fprintf(out,"Operator: ");
+for(i=0;i<bbs.total_sysops;i++) {
+	if(i) {
+		if(bbs.total_sysops>2)
+			fprintf(out,", ");
+		else
+			fputc(SP,out);
+		if(!(i%4))
+			fprintf(out,"\r\n          ");
+		if(i+1==bbs.total_sysops)
+			fprintf(out,"and "); }
+	fprintf(out,"%s",bbs.sysop[i]); }
+fprintf(out,"\r\n");
+fprintf(out,"Software: %-15.15s Nodes: %-5u "
+	"Users: %-5u Doors: %u\r\n"
+	,bbs.software,bbs.nodes,bbs.users,bbs.xtrns);
+fprintf(out,"Download: %lu files in %u directories of "
+	"%luMB total space\r\n"
+	,bbs.files,bbs.dirs,bbs.megs);
+fprintf(out,"Messages: %lu messages in %u sub-boards\r\n"
+	,bbs.msgs,bbs.subs);
+fprintf(out,"Networks: ");
+for(i=0;i<bbs.total_networks;i++) {
+	if(i) {
+		if(bbs.total_networks>2)
+			fprintf(out,", ");
+		else
+			fputc(SP,out);
+		if(!(i%3))
+			fprintf(out,"\r\n          ");
+		if(i+1==bbs.total_networks)
+			fprintf(out,"and "); }
+	fprintf(out,"%s [%s]",bbs.network[i],bbs.address[i]); }
+fprintf(out,"\r\n");
+fprintf(out,"Terminal: ");
+for(i=0;i<bbs.total_terminals;i++) {
+	if(i) {
+		if(bbs.total_terminals>2)
+			fprintf(out,", ");
+		else
+			fputc(SP,out);
+		if(i+1==bbs.total_terminals)
+			fprintf(out,"and "); }
+	fprintf(out,"%s",bbs.terminal[i]); }
+fprintf(out,"\r\n\r\n");
+for(i=0;i<bbs.total_numbers;i++)
+	fprintf(out,"%-30.30s %12.12s %5u %-15.15s "
+		"Minimum: %u\r\n"
+		,i && !strcmp(bbs.number[i].location,bbs.number[i-1].location)
+			? nulstr : bbs.number[i].location
+		,bbs.number[i].number
+		,bbs.number[i].max_rate,bbs.number[i].modem
+		,bbs.number[i].min_rate);
+
+fprintf(out,"\r\n");
+for(i=0;i<5;i++) {
+	if(!bbs.desc[i][0])
+		break;
+	fprintf(out,"%15s%s\r\n",nulstr,bbs.desc[i]); }
+
+fprintf(out,"\r\n");
+fprintf(out,"Entry created on %s by %s\r\n"
+	,timestr(&bbs.created),bbs.user);
+fprintf(out," Last updated on %s\r\n\r\n",timestr(&bbs.updated));
+}
+
+
+void main(int argc, char **argv)
+{
+	char software[16]="";
+	int i,in;
+	FILE *out;
+	bbs_t bbs;
+
+for(i=1;i<argc;i++)
+	if(argv[i][0]=='s' && argv[i][1]=='=')
+		sprintf(software,"%.15s",argv[i]+2);
+
+if((in=open("SBL.DAB",O_RDONLY|O_BINARY))==-1) {
+	printf("error opening SBL.DAB\n");
+	return; }
+
+if((out=fopen("SBL.TXT","wb"))==NULL) {
+	printf("error opening/creating SBL.TXT\n");
+	return; }
+
+while(!eof(in)) {
+	read(in,&bbs,sizeof(bbs_t));
+	if(!bbs.name[0])
+		continue;
+	if(software[0] && strnicmp(bbs.software,software,strlen(software)))
+		continue;
+	// long_bbs_info(out,bbs);
+	for(i=0;i<bbs.total_numbers;i++)
+		fprintf(out,"%-25.25s  %12.12s  %5u  %s\r\n"
+			,bbs.name,bbs.number[i].number
+			,bbs.number[i].max_rate
+			,bbs.number[i].modem);
+	}
+close(in);
+fclose(out);
+}
diff --git a/xtrn/sbl/sbldefs.h b/xtrn/sbl/sbldefs.h
new file mode 100644
index 0000000000000000000000000000000000000000..7c4183cf81c59a46c73ecbba26b1335629f3870a
--- /dev/null
+++ b/xtrn/sbl/sbldefs.h
@@ -0,0 +1,63 @@
+/* SBLDEFS.H */
+
+/* Developed 1990-1997 by Rob Swindell; PO Box 501, Yorba Linda, CA 92885 */
+
+/* Macros, constants, and type definitions for Synchronet BBS List */
+
+#define MAX_SYSOPS   5
+#define MAX_NUMBERS 20
+#define MAX_NETS	10
+#define MAX_TERMS	 5
+#define DEF_LIST_FMT "NSPBM"
+
+/* Misc bits */
+
+#define FROM_SMB	(1L<<0) 	/* BBS info imported from message base */
+
+typedef struct {
+	char	 number[13] 				/* Phone number */
+			,modem[16]					/* Modem description */
+			,location[31];				/* Location of phone number */
+	uint	 min_rate					/* Minimum connect rate */
+			,max_rate;					/* Maximum connect rate */
+			} number_t;
+
+typedef struct {
+	char	 name[26]					/* System name */
+			,user[26]					/* User who created entry */
+			,software[16]				/* BBS software */
+			,total_sysops
+			,sysop[MAX_SYSOPS][26]		/* Sysop names */
+			,total_numbers
+			,total_networks
+			,network[MAX_NETS][16]		/* Network names */
+			,address[MAX_NETS][26]		/* Network addresses */
+			,total_terminals
+			,terminal[MAX_TERMS][16]	/* Terminals supported */
+			,desc[5][51]				/* 5 line description */
+			;
+	uint	 nodes						/* Total nodes */
+			,users						/* Total users */
+			,subs						/* Total sub-boards */
+			,dirs						/* Total file dirs */
+			,xtrns						/* Total external programs */
+			;
+	time_t	 created					/* Time/date entry was created */
+			,updated					/* Time/date last updated */
+			,birth						/* Birthdate of BBS */
+			;
+	ulong	 megs						/* Storage space in megabytes */
+			,msgs						/* Total messages */
+			,files						/* Total files */
+			,misc						/* Miscellaneous bits */
+			;
+	number_t number[MAX_NUMBERS];		/* Access numbers */
+
+	char	userupdated[26];			/* User who last updated */
+	time_t	verified;					/* Time/Date last vouched for */
+	char	userverified[26];			/* User who last vouched */
+	char	unused[444];				/* Unused space */
+			} bbs_t;
+
+/* End of SBL.H */
+
diff --git a/xtrn/sbl/sblpack.c b/xtrn/sbl/sblpack.c
new file mode 100644
index 0000000000000000000000000000000000000000..9e590b2c0357cf9855a669b8439793a21be2173b
--- /dev/null
+++ b/xtrn/sbl/sblpack.c
@@ -0,0 +1,55 @@
+/* SBLPACK.C */
+
+/* Developed 1990-1997 by Rob Swindell; PO Box 501, Yorba Linda, CA 92885 */
+
+/***************************************/
+/* Synchronet BBS List Database Packer */
+/***************************************/
+
+#include <stdio.h>
+#include <share.h>
+#include <time.h>
+#include <io.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include "gen_defs.h"
+#include "sbldefs.h"
+
+int main(void)
+{
+	int file;
+	FILE *in,*out;
+	bbs_t bbs;
+
+printf("\nSBLPACK v1.00  Developed 1995-1997 Rob Swindell\n\n");
+
+if((file=open("SBL.DAB",O_RDWR|O_BINARY|O_DENYNONE|O_CREAT
+	,S_IWRITE|S_IREAD))==-1) {
+	printf("\n\7Error opening/creating SBL.DAB\n");
+	exit(1); }
+if((in=fdopen(file,"w+b"))==NULL) {
+	printf("\n\7Error converting SBL.DAB file handle to stream\n");
+	exit(1); }
+setvbuf(in,0L,_IOFBF,2048);
+if((out=fopen("SBL.TMP","wb"))==NULL) {
+	printf("\n\7Error opening SBL.TMP file\n");
+	exit(1); }
+
+while(!feof(in)) {
+	if(!fread(&bbs,sizeof(bbs_t),1,in))
+		break;
+	putchar('.');
+	if(!bbs.name[0])
+		continue;
+	fwrite(&bbs,sizeof(bbs_t),1,out); }
+fcloseall();
+putchar('\n');
+if(remove("SBL.DAB")) {
+	printf("\n\7Data file in use, can't remove.\n");
+	remove("SBL.TMP");
+	exit(1); }
+rename("SBL.TMP","SBL.DAB");
+printf("\nDone.\n");
+return(0);
+}
+
diff --git a/xtrn/sbl/smb2sbl.c b/xtrn/sbl/smb2sbl.c
new file mode 100644
index 0000000000000000000000000000000000000000..e82e560fa4ec7c224eb1e68fc725a28199129dbf
--- /dev/null
+++ b/xtrn/sbl/smb2sbl.c
@@ -0,0 +1,457 @@
+/* SMB2SBL */
+
+/* Developed 1990-1997 by Rob Swindell; PO Box 501, Yorba Linda, CA 92885 */
+
+/* Scans SMB message base for messages to "SBL" and adds them to the SBL    */
+/* database. */
+
+#define  uint unsigned int
+
+#include <dos.h>
+#include "smblib.h"
+#include "sbldefs.h"
+
+extern int	daylight=0;
+extern long timezone=0L;
+smb_t		smb;
+
+char *loadmsgtxt(smbmsg_t msg, int tails)
+{
+	char	*buf=NULL,*lzhbuf;
+	ushort	xlat;
+	int 	i,lzh;
+	long	l=0,lzhlen,length;
+
+for(i=0;i<msg.hdr.total_dfields;i++) {
+	if(!(msg.dfield[i].type==TEXT_BODY
+		|| (tails && msg.dfield[i].type==TEXT_TAIL)))
+		continue;
+	fseek(smb.sdt_fp,msg.hdr.offset+msg.dfield[i].offset
+		,SEEK_SET);
+	fread(&xlat,2,1,smb.sdt_fp);
+	lzh=0;
+	if(xlat==XLAT_LZH) {
+		lzh=1;
+		fread(&xlat,2,1,smb.sdt_fp); }
+	if(xlat!=XLAT_NONE) 		/* no other translations supported */
+		continue;
+
+	length=msg.dfield[i].length-2;
+	if(lzh) {
+		length-=2;
+		if((lzhbuf=MALLOC(length))==NULL) {
+			printf("ERR_ALLOC lzhbuf of %lu\n",length);
+			return(buf); }
+		fread(lzhbuf,1,length,smb.sdt_fp);
+		lzhlen=*(long *)lzhbuf;
+		if((buf=REALLOC(buf,l+lzhlen+3))==NULL) {
+			FREE(lzhbuf);
+			printf("ERR_ALLOC lzhoutbuf of %l\n",l+lzhlen+1);
+			return(buf); }
+		lzh_decode(lzhbuf,length,buf+l);
+		FREE(lzhbuf);
+		l+=lzhlen; }
+	else {
+		if((buf=REALLOC(buf,l+msg.dfield[i].length+3))==NULL) {
+			printf("ERR_ALLOC of %lu\n",l+msg.dfield[i].length+1);
+			return(buf); }
+		l+=fread(buf+l,1,length,smb.sdt_fp); }
+	buf[l]=CR;
+	l++;
+	buf[l]=LF;
+	l++;
+	buf[l]=0; }
+return(buf);
+}
+
+
+/***************************************************************************/
+/* Truncates white-space chars off end of 'str' and terminates at first CR  */
+/****************************************************************************/
+void truncsp(char *str)
+{
+	char c;
+
+str[strcspn(str,"\r")]=0;
+c=strlen(str);
+while(c && (uchar)str[c-1]<=SP) c--;
+str[c]=0;
+}
+
+/****************************************************************************/
+/* Checks the disk drive for the existence of a file. Returns 1 if it       */
+/* exists, 0 if it doesn't.                                                 */
+/****************************************************************************/
+char fexist(char *filespec)
+{
+    struct ffblk f;
+
+if(findfirst(filespec,&f,0)==0)
+    return(1);
+return(0);
+}
+
+/****************************************************************************/
+/* Returns the length of the file in 'filespec'                             */
+/****************************************************************************/
+long flength(char *filespec)
+{
+    struct ffblk f;
+
+if(findfirst(filespec,&f,0)==0)
+    return(f.ff_fsize);
+return(-1L);
+}
+
+
+/****************************************************************************/
+/* Converts a date string in format MM/DD/YY into unix time format			*/
+/****************************************************************************/
+time_t dstrtounix(char *str)
+{
+	struct date date;
+	struct time curtime;
+
+if(!strncmp(str,"00/00/00",8))
+	return(0);
+curtime.ti_hour=curtime.ti_min=curtime.ti_sec=0;
+if(str[6]<7)
+	date.da_year=2000+((str[6]&0xf)*10)+(str[7]&0xf);
+else
+	date.da_year=1900+((str[6]&0xf)*10)+(str[7]&0xf);
+date.da_mon=((str[0]&0xf)*10)+(str[1]&0xf);
+date.da_day=((str[3]&0xf)*10)+(str[4]&0xf);
+return(dostounix(&date,&curtime));
+}
+
+/****************************************************************************/
+/* Updates 16-bit "rcrc" with character 'ch'                                */
+/****************************************************************************/
+void ucrc16(uchar ch, ushort *rcrc) {
+	ushort i, cy;
+    uchar nch=ch;
+ 
+for (i=0; i<8; i++) {
+    cy=*rcrc & 0x8000;
+    *rcrc<<=1;
+    if (nch & 0x80) *rcrc |= 1;
+    nch<<=1;
+    if (cy) *rcrc ^= 0x1021; }
+}
+
+/****************************************************************************/
+/* Returns 16-crc of string (not counting terminating NULL) 				*/
+/****************************************************************************/
+ushort crc16(char *str)
+{
+	int 	i=0;
+	ushort	crc=0;
+
+ucrc16(0,&crc);
+while(str[i])
+	ucrc16(str[i++],&crc);
+ucrc16(0,&crc);
+ucrc16(0,&crc);
+return(crc);
+}
+
+time_t checktime()
+{
+	struct tm tm;
+
+memset(&tm,0,sizeof(tm));
+tm.tm_year=94;
+tm.tm_mday=1;
+return(mktime(&tm)^0x2D24BD00L);
+}
+
+int main(int argc, char **argv)
+{
+	uchar	str[128],*buf,*p;
+	int 	i,file,sysop,number,network,terminal,desc;
+	ulong	l,last,high;
+	ushort	sbl;
+	bbs_t	bbs;
+	smbmsg_t msg;
+	FILE	*stream;
+
+fprintf(stderr,"\nSMB2SBL v2.00 - Updates SBL via SMB - Developed 1994-1997 "
+	"Rob Swindell\n\n");
+
+if(checktime()) {
+    printf("Time problem!\n");
+    return(-1); }
+
+if(argc<3) {
+	fprintf(stderr,"usage: smb2sbl <smb_file> <sbl.dab>\n\n");
+	fprintf(stderr,"ex: smb2sbl c:\\sbbs\\data\\subs\\syncdata "
+		"c:\\sbbs\\xtrn\\sbl\\sbl.dab \n");
+	return(1); }
+
+strcpy(smb.file,argv[1]);
+strupr(smb.file);
+
+strcpy(str,argv[2]);
+strupr(str);
+if((file=open(str,O_RDWR|O_BINARY|O_DENYNONE|O_CREAT,S_IWRITE|S_IREAD))==-1) {
+	printf("error opening %s\n",str);
+	return(1); }
+if((stream=fdopen(file,"r+b"))==NULL) {
+	printf("error fdopening %s\n",str);
+	return(1); }
+setvbuf(stream,NULL,_IOFBF,4096);
+
+sprintf(str,"%s.SBL",smb.file);
+if((file=open(str,O_RDWR|O_BINARY|O_CREAT,S_IWRITE|S_IREAD))==-1) {
+	printf("error opening %s\n",str);
+	return(1); }
+if(read(file,&last,4)!=4)
+	last=0;
+high=last;
+
+sprintf(str,"%s.SHD",smb.file);
+if(!fexist(str)) {
+	printf("%s doesn't exist\n",smb.file);
+	return(0); }
+sprintf(str,"%s.SID",smb.file);
+if(!flength(str)) {
+	printf("%s is empty\n",smb.file);
+    return(0); }
+fprintf(stderr,"Opening %s\n",smb.file);
+smb.retry_time=30;
+if((i=smb_open(&smb))!=0) {
+	printf("smb_open returned %d\n",i);
+	return(1); }
+
+sbl=crc16("sbl");
+
+if((i=smb_locksmbhdr(&smb))!=0) {				/* Be sure noone deletes or */
+	printf("Error locking %d\n",i);             /* adds while we're reading */
+	return(1); }
+
+while(!feof(smb.sid_fp)) {
+	if(!fread(&msg.idx,sizeof(idxrec_t),1,smb.sid_fp))
+        break;
+	fprintf(stderr,"\r%lu  ",msg.idx.number);
+	if(msg.idx.number<=last || msg.idx.to!=sbl)
+		continue;
+	high=msg.idx.number;
+	if((i=smb_lockmsghdr(&smb,&msg))!=0) {
+		printf("\7Error %d locking msg #%lu\n",i,msg.idx.number);
+		continue; }
+	if((i=smb_getmsghdr(&smb,&msg))!=0) {
+		smb_unlockmsghdr(&smb,&msg);
+		printf("\7Error %d reading msg #%lu\n",i,msg.idx.number);
+		continue; }
+	smb_unlockmsghdr(&smb,&msg);
+	if(!msg.from_net.type) {		/* ignore local message */
+		smb_freemsgmem(&msg);
+        continue; }
+
+	printf("\nMessage #%lu by %s on %.24s\n"
+		,msg.hdr.number,msg.from,ctime(&(time_t)msg.hdr.when_written.time));
+
+	truncsp(msg.subj);
+	if(!msg.subj[0]) {
+		smb_freemsgmem(&msg);
+		continue; }
+	fprintf(stderr,"Searching for %s...",msg.subj);
+	fseek(stream,0L,SEEK_SET);
+	memset(&bbs,0,sizeof(bbs_t));
+	while(1) {
+		l=ftell(stream);
+		if(!fread(&bbs,sizeof(bbs_t),1,stream)) {
+			memset(&bbs,0,sizeof(bbs_t));
+			break; }
+		if(msg.subj[0] && !stricmp(bbs.name,msg.subj)) {
+			fseek(stream,l,SEEK_SET);
+			break; } }
+	fprintf(stderr,"\n");
+	if(bbs.name[0] && strnicmp(bbs.user,msg.from,25)) {
+		printf("%s didn't create the entry for %s\n",msg.from,msg.subj);
+		smb_freemsgmem(&msg);
+		continue; }
+	if(!bbs.name[0]) {
+		fprintf(stderr,"Searching for unused record...");
+		fseek(stream,0L,SEEK_SET);
+		while(1) {					/* Find deleted record */
+			l=ftell(stream);
+			if(!fread(&bbs,sizeof(bbs_t),1,stream))
+				break;
+			if(!bbs.name[0]) {
+				fseek(stream,l,SEEK_SET);
+				break; } }
+		fprintf(stderr,"\n");
+		memset(&bbs,0,sizeof(bbs_t));
+		bbs.created=time(NULL);
+		if(!bbs.birth)
+			bbs.birth=bbs.created;
+		sprintf(bbs.user,"%-.25s",msg.from); }
+	sprintf(bbs.name,"%-.25s",msg.subj);
+	bbs.updated=time(NULL);
+	bbs.misc|=FROM_SMB;
+	sprintf(bbs.userupdated,"%-.25s",msg.from);
+	buf=loadmsgtxt(msg,0);
+	sysop=number=network=terminal=desc=0;
+	l=0;
+	while(buf[l]) {
+		while(buf[l] && buf[l]<=SP) 		/* Find first text on line */
+			l++;
+		if(!strnicmp(buf+l,"NAME:",5)) {
+			l+=5;
+			while(buf[l] && buf[l]<=SP && buf[l]!=CR)
+				l++;
+			sprintf(bbs.name,"%-.25s",buf+l);
+			truncsp(bbs.name); }
+		if(!strnicmp(buf+l,"BIRTH:",6)) {
+			l+=6;
+			while(buf[l] && buf[l]<=SP && buf[l]!=CR)
+				l++;
+			bbs.birth=dstrtounix(buf+l); }
+		if(!strnicmp(buf+l,"SOFTWARE:",9)) {
+			l+=9;
+			while(buf[l] && buf[l]<=SP && buf[l]!=CR)
+				l++;
+			sprintf(bbs.software,"%-.15s",buf+l);
+			truncsp(bbs.software); }
+		if(!strnicmp(buf+l,"SYSOP:",6)) {
+			l+=6;
+			while(buf[l] && buf[l]<=SP && buf[l]!=CR)
+				l++;
+			sprintf(bbs.sysop[sysop],"%-.25s",buf+l);
+			truncsp(bbs.sysop[sysop]);
+			if(sysop<MAX_SYSOPS-1)
+				sysop++; }
+		if(!strnicmp(buf+l,"NUMBER:",7)) {
+			l+=7;
+			while(buf[l] && buf[l]<=SP && buf[l]!=CR)
+				l++;
+			sprintf(bbs.number[number].number,"%-.12s",buf+l);
+			truncsp(bbs.number[number].number);
+			if(number<MAX_NUMBERS-1)
+				number++; }
+		if(!strnicmp(buf+l,"MODEM:",6)) {
+			l+=6;
+			while(buf[l] && buf[l]<=SP && buf[l]!=CR)
+				l++;
+			i=number;
+			if(i) i--;
+			sprintf(bbs.number[i].modem,"%-.15s",buf+l);
+			truncsp(bbs.number[i].modem); }
+		if(!strnicmp(buf+l,"LOCATION:",9)) {
+			l+=9;
+			while(buf[l] && buf[l]<=SP && buf[l]!=CR)
+				l++;
+			i=number;
+            if(i) i--;
+			sprintf(bbs.number[i].location,"%-.30s",buf+l);
+			truncsp(bbs.number[i].location); }
+		if(!strnicmp(buf+l,"MINRATE:",8)) {
+			l+=8;
+			while(buf[l] && buf[l]<=SP && buf[l]!=CR)
+				l++;
+			i=number;
+            if(i) i--;
+			bbs.number[i].min_rate=atoi(buf+l); }
+		if(!strnicmp(buf+l,"MAXRATE:",8)) {
+			l+=8;
+			while(buf[l] && buf[l]<=SP && buf[l]!=CR)
+				l++;
+			i=number;
+            if(i) i--;
+			bbs.number[i].max_rate=atoi(buf+l); }
+		if(!strnicmp(buf+l,"NETWORK:",8)) {
+			l+=8;
+			while(buf[l] && buf[l]<=SP && buf[l]!=CR)
+				l++;
+			sprintf(bbs.network[network],"%-.15s",buf+l);
+			truncsp(bbs.network[network]);
+			if(network<MAX_NETS-1)
+				network++; }
+		if(!strnicmp(buf+l,"ADDRESS:",8)) {
+			l+=8;
+			while(buf[l] && buf[l]<=SP && buf[l]!=CR)
+				l++;
+			i=network;
+			if(i) i--;
+			sprintf(bbs.address[i],"%-.25s",buf+l);
+			truncsp(bbs.address[i]); }
+		if(!strnicmp(buf+l,"TERMINAL:",9)) {
+			l+=9;
+			while(buf[l] && buf[l]<=SP && buf[l]!=CR)
+				l++;
+			sprintf(bbs.terminal[terminal],"%-.15s",buf+l);
+			truncsp(bbs.terminal[terminal]);
+			if(terminal<MAX_TERMS-1)
+				terminal++; }
+		if(!strnicmp(buf+l,"DESC:",5)) {
+			l+=5;
+			while(buf[l] && buf[l]<=SP && buf[l]!=CR)
+				l++;
+			sprintf(bbs.desc[desc],"%-.50s",buf+l);
+			truncsp(bbs.desc[desc]);
+			if(desc<4)
+				desc++; }
+
+		if(!strnicmp(buf+l,"MEGS:",5)) {
+			l+=5;
+			while(buf[l] && buf[l]<=SP && buf[l]!=CR)
+				l++;
+			bbs.megs=atol(buf+l); }
+		if(!strnicmp(buf+l,"MSGS:",5)) {
+			l+=5;
+			while(buf[l] && buf[l]<=SP && buf[l]!=CR)
+				l++;
+			bbs.msgs=atol(buf+l); }
+		if(!strnicmp(buf+l,"FILES:",6)) {
+			l+=6;
+			while(buf[l] && buf[l]<=SP && buf[l]!=CR)
+				l++;
+			bbs.files=atol(buf+l); }
+		if(!strnicmp(buf+l,"NODES:",6)) {
+			l+=6;
+			while(buf[l] && buf[l]<=SP && buf[l]!=CR)
+				l++;
+			bbs.nodes=atoi(buf+l); }
+		if(!strnicmp(buf+l,"USERS:",6)) {
+			l+=6;
+			while(buf[l] && buf[l]<=SP && buf[l]!=CR)
+				l++;
+			bbs.users=atoi(buf+l); }
+		if(!strnicmp(buf+l,"SUBS:",5)) {
+			l+=5;
+			while(buf[l] && buf[l]<=SP && buf[l]!=CR)
+				l++;
+			bbs.subs=atoi(buf+l); }
+		if(!strnicmp(buf+l,"DIRS:",5)) {
+			l+=5;
+			while(buf[l] && buf[l]<=SP && buf[l]!=CR)
+				l++;
+			bbs.dirs=atoi(buf+l); }
+		if(!strnicmp(buf+l,"XTRNS:",6)) {
+			l+=6;
+			while(buf[l] && buf[l]<=SP && buf[l]!=CR)
+				l++;
+			bbs.xtrns=atoi(buf+l); }
+		while(buf[l] && buf[l]>=SP) {	 /* Go to end of line */
+			putchar(buf[l]);
+			l++; }
+		printf("\n"); }
+	if(bbs.total_sysops<sysop)
+		bbs.total_sysops=sysop;
+	if(bbs.total_networks<network)
+		bbs.total_networks=network;
+	if(bbs.total_terminals<terminal)
+		bbs.total_terminals=terminal;
+	if(bbs.total_numbers<number)
+		bbs.total_numbers=number;
+	fwrite(&bbs,sizeof(bbs_t),1,stream);
+	FREE(buf);
+	smb_freemsgmem(&msg);
+	}
+lseek(file,0L,SEEK_SET);
+write(file,&high,4);
+close(file);
+return(0);
+}
+
diff --git a/xtrn/scb/instruct.msg b/xtrn/scb/instruct.msg
new file mode 100644
index 0000000000000000000000000000000000000000..26b95fdb3681763bf2c1e4ece288c9f7c083866b
--- /dev/null
+++ b/xtrn/scb/instruct.msg
@@ -0,0 +1,22 @@
+l                              chSynchronet Callback
+
+	ngWhen you continue the automatic validation process, chSynchronet Callback
+(SCB) ngwill ask you for your phone number.  This rhMUSTng be the phone number that 
+your modem is connected to and it must contain all of the digits necessary for
+chSCBng to call you back (including a '1' and area code if necessary).
+
+	If you choose not to continue with the validation process, answer hNOng
+to the 'hContinue with verification?ng' question that chSCBng will ask you next.
+
+	After you have entered an hACCEPTABLEng phone number, chSCBng will display a
+short message, and call you back.  When your phone rings you will need to
+answer it with your modem.  You should see a 'hRINGng' appear on your screen
+(and you may also hear your phone ring if one is connected to the same line as
+your modem), when this happens, you will need to type 'hATAng' and press hENTERng.
+rhDO NOTng answer the phone by voice!
+
+	The modems will then connect, and you will be given several chances to
+enter your correct password.  Once you have entered the hCORRECTng password, chSCBng
+will display a short message notifying you of this, validate your account,
+and then disconnect.  If you enter your password hINCORRECTLYng, chSCBng will rhNOTng
+validate your account.
diff --git a/xtrn/scb/ld_time.msg b/xtrn/scb/ld_time.msg
new file mode 100644
index 0000000000000000000000000000000000000000..cee29fdbf3194488a1f51c29ad521b8857faf822
--- /dev/null
+++ b/xtrn/scb/ld_time.msg
@@ -0,0 +1 @@
+Please try again later. 
diff --git a/xtrn/scb/no_one.msg b/xtrn/scb/no_one.msg
new file mode 100644
index 0000000000000000000000000000000000000000..6d72f2a2f9abcdfbbf031510ba6d53b7e7681d67
--- /dev/null
+++ b/xtrn/scb/no_one.msg
@@ -0,0 +1 @@
+Phone numbers beginning with '1' are not allowed.
diff --git a/xtrn/scb/no_zero.msg b/xtrn/scb/no_zero.msg
new file mode 100644
index 0000000000000000000000000000000000000000..2905d1f464fbbed24d8457bc5833a9ab1099e70b
--- /dev/null
+++ b/xtrn/scb/no_zero.msg
@@ -0,0 +1,2 @@
+Phone numbers beginning with '0' are not allowed.
+ 
\ No newline at end of file
diff --git a/xtrn/scb/phonecan.msg b/xtrn/scb/phonecan.msg
new file mode 100644
index 0000000000000000000000000000000000000000..66ddf7f9f7586a07598b27fad9ff40229ee0f6bd
--- /dev/null
+++ b/xtrn/scb/phonecan.msg
@@ -0,0 +1,2 @@
+The number you entered was found in a list of invalid or previously verified
+numbers.
diff --git a/xtrn/scb/refused.msg b/xtrn/scb/refused.msg
new file mode 100644
index 0000000000000000000000000000000000000000..9b984fd684d8e67bc4012b31cfefb7a3c84506ca
--- /dev/null
+++ b/xtrn/scb/refused.msg
@@ -0,0 +1,7 @@
+
+	ngYou have chosen to refuse the callback verification process.  As a
+result, you may not be given access to the BBS.  If you intend to gain access
+to the system, you may wish to leave a note to the Sysop explaining that you
+refused the callback verification, and your reasons for doing so, as well as
+a number where you CAN be called at.
+
diff --git a/xtrn/scb/scb.cfg b/xtrn/scb/scb.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e8672699badc050c97e9fc19d81dc5ce2fb9879f
--- /dev/null
+++ b/xtrn/scb/scb.cfg
@@ -0,0 +1,32 @@
+5
+NYNNNNNN
+\SBBS\TEXT\PHONE.CAN
+\SBBS\TEXT\PHONE.CAN
+
+
+
+
+
+
+
+
+
+
+
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+7
+11
+
diff --git a/xtrn/scb/scb.doc b/xtrn/scb/scb.doc
new file mode 100644
index 0000000000000000000000000000000000000000..878b3e69839ee1341d9079a3956b4d5912dd11a2
--- /dev/null
+++ b/xtrn/scb/scb.doc
@@ -0,0 +1,329 @@
+	   Synchronet Callback v1.32   Copyright 1995 Digital Dynamics
+	   -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+      Callback Verification Program for Synchronet Multinode BBS Software
+
+
+Installation:
+============
+
+ 1. Create a directory called "SCB" off of your Synchronet XTRN directory.
+
+	Example:
+
+		MD C:\SBBS\XTRN\SCB
+
+ 2. Copy all SCB files into this new directory.
+
+	Example:
+
+		COPY *.* C:\SBBS\XTRN\SCB
+
+	Note, SCB files include:
+
+		SCB.DOC 	This file
+		ORDER.TXT	Purchase order form
+		SCB.EXE 	Callback verifier external program
+		SCBCFG.EXE	Sysop configuration program for SCB
+		ALLOWED.DAT	List of allowable number prefixes
+		LDPREFIX.DAT	List of same-area prefixes that are long dist
+		SCB.MSG 	The opening screen for SCB
+		REFUSED.MSG	Verification refused
+		INSTRUCT.MSG	Instructions
+		NO_ONE.MSG	Long distance starting with 1 not allowed
+		NO_ZERO.MSG	Long distance starting with 0 not allowed
+		NO_LD.MSG	Long distance in same area code not allowed
+		LD_TIME.MSG	Not within long distance callback time range
+		TOOSHORT.MSG	Phone number less than minimum number of digits
+		TOOLONG.MSG	Phone number more than maximum number of digits
+		PHONECAN.MSG	Phone number found in trash can file
+		VERIFIED.MSG	Displayed to users after verification
+
+ 3. Run SCFG from your NODE1 directory.
+
+ 4. Go to the External Programs->Online Programs menu.
+
+ 5. Hit the <INSERT> key and enter "Synchronet Callback".
+
+ 6. Hit <ENTER> to edit the configuration for this new program.
+
+ 7. Set the "Start-up Directory" to "..\XTRN\SCB".
+
+ 8. Set "Multiuser" to "Yes".
+
+ 9. Set "Modify Uesr Data" to "Yes".
+
+10. Set "BBS Drop File Type" to "Synchronet XTRN.DAT"
+
+11. If you wish to have SCB run automatically for new users or as a logon
+    event for unvalidated users, set the "Execute on Event" option to the
+    appropriate event type. Use "Access Requirements" if you wish SCB to be
+    only available to unvalidated users (i.e. "LEVEL NOT 20").
+
+Example SCFG screen:
+
+	�[�][?]���������������������������������������������������ͻ
+	�		     Synchronet Callback		   �
+	����������������������������������������������������������͹
+	� �Name 		      Synchronet Callback	   �
+	� �Internal Code	      SCB			   �
+	� �Start-up Directory	      ..\XTRN\SCB		   �
+	� �Command Line 	      scb			   �
+	� �Clean-up Command Line				   �
+	� �Cost in Credits	      0 			   �
+	� �Access Requirements	      LEVEL NOT 20		   �
+	� �Execution Requirements				   �
+	� �Multiple Concurrent Users  Yes			   �
+	� �Intercept I/O Interrupts   No			   �
+	� �Swap BBS out of Memory     No			   �
+	� �Modify User Data	      Yes			   �
+	� �Execute on Event	      Logon			   �
+	� �BBS Drop File Type	      Synchronet      XTRN.DAT	   �
+	� �Place Drop File In	      Node Directory		   �
+	����������������������������������������������������������ͼ
+
+
+Configuration:
+=============
+
+ 1. Go into your SCB directory and run SCBCFG.
+
+	Example:
+
+		CD \SBBS\XTRN\SCB
+		SCBCFG
+
+    You will see a menu similar to the following:
+
+	�[�][?]���������������������������������������������������ͻ
+	�	      Synchronet Callback Configuration 	   �
+	����������������������������������������������������������͹
+	� �Toggle Options...					   �
+	� �Validation Values... 				   �
+	� �Allowed Prefix List...				   �
+	� �Long Distance Prefix List... 			   �
+	� �Long Distance Calling Times...			   �
+	� �Phone Number Trash Can   \SBBS\TEXT\PHONE.CAN	   �
+	� �Validated Phone List     \SBBS\TEXT\PHONE.CAN	   �
+	� �Callback Attempts	    4				   �
+	� �Minimum Phone Length     7				   �
+	� �Maximum Phone Length     11				   �
+	� �BBS Area Code	    714 			   �
+	� �Send Message to Sysop    User #1			   �
+	� �Registration Number	    UNREGISTERED		   �
+	����������������������������������������������������������ͼ
+
+ 2. If your Synchronet text directory is in a directory or drive other
+    than "\SBBS\TEXT", change the Phone Can and Validated Phone List options
+    to reflect the correct location of your PHONE.CAN file.
+
+    If you want to allow duplicate validated phone numbers, change the
+    Validated Phone List to another path and/or filename. If left blank,
+    the validated phone numbers will not be written to a file at all
+    (other than the SCB.LOG and possibly the user data).
+
+    The phone number list(s) use the same syntax for trashcan files as
+    Synchronet BBS. Appending a '~' to a line specifies that the string
+    cannot be located anywhere within the phone number. Appending a '^'
+    indicates that the phone number cannot begin with the string.
+
+    The stock PHONE.CAN for Synchronet BBS contains the following:
+
+	0-^
+	1-^
+	000-~
+	111-~
+	123-^
+	222-^
+	333-^
+	444-^
+	555-~
+	666-^
+	777-^
+	888-^
+	999-^
+	900-^
+	411-~
+	911-~
+	976-~
+	411^
+	911^
+
+    Since SCB does not allow non-numeric characters in phone numbers, all
+    entries with the dash (-) character above are ignored.
+
+ 3. SCB uses the dial string specified in the modem configuration in SCFG
+    for each node for dialing the remote user's modem. The default dial
+    string is "ATDT".
+
+ 4. Use the "Toggle Options" sub-menu to configure your preferences.
+
+	�[�][?]����������������������������������������������ͻ
+	�		     Toggle Options		      �
+	�����������������������������������������������������͹
+	� �Validate if Unable to Verify 	   No	      �
+	� �Put Result in User Note		   Yes	      �
+	� �Long Distance if not an Allowed Prefix  No	      �
+	� �Allow Long Distance (Starting with 0)   No	      �
+	� �Allow Long Distance (Starting with 1)   Yes	      �
+	� �Allow Long Distance (Same Area Code)    Yes	      �
+	� �U.S. Style Phone Format (AAA-PPP-SSSS)  Yes	      �
+	� �Stay Connected After Callback	   No	      �
+	�����������������������������������������������������ͼ
+
+    If the "Validate if Unable to Verify" option is set to "Yes", users will
+    always be validated. Even if their number was not verified.
+
+    If the "Put Result in User Note" option is set to "Yes", the result of
+    the verification attempt will be placed in the user's note field
+    (in the Synchronet BBS user database).
+
+    If the "Long Distance if not an Allowed Prefix" option is set to "Yes",
+    then all numbers will be considered long distance unless the prefix
+    is specifically listed in the "Allowed Prefix List" off of the main
+    menu. If this option is set to "Yes", the "Long Distance Prefix List"
+    is not used.
+
+    If the "Allow Long Distance (starting with 0)" option is set to "Yes",
+    then numbers starting with '0' will be allowed. This option does not
+    effect numbers included in the "Allowed Prefix List".
+
+    If the "Allow Long Distance (starting with 1)" option is set to "Yes",
+    then numbers starting with '1' will be allowed. This option does not
+    effect numbers included in the "Allowed Prefix List".
+
+    If the "Allow Long Distance (same area code)" option is set to "Yes",
+    then numbers that begin with a prefix located in the "Long Distance
+    Prefix List" will not be allowed. This option does not effect numbers
+    included in the "Allowed Prefix List".
+
+    If the "U.S. Style Phone Format" option is set to "Yes", then SCB will
+    assume that phone numbers are in the AAA-PPP-SSSS format, and will
+    automatically strip the BBS area code from the prefix if the user
+    entered a number in the same area code as the BBS and included the
+    area code (both AAA and 1AAA will be stripped).
+
+    The "Stay Connected After Callback" option can be set to "Yes", "No",
+    or "Local Only". SCB will not disconnect after calling the user back
+    if this option is set to "Yes". If set to "Local Only", SCB will only
+    remain connected if the number verfied does not begin with a '1' or '0'.
+
+ 5. Use the "Validation Values" sub-menu to configure how you want validated
+    users' accounts to be modified.
+
+	�[�][?]�������������������������������������������������������ͻ
+	�			Validation Values		       �
+	��������������������������������������������������������������͹
+	� �Security Level		   23			       �
+	� �Flag Set 1			   CDLN 		       �
+	� �Flag Set 2						       �
+	� �Flag Set 3						       �
+	� �Flag Set 4			   -V			       �
+	� �Exemptions						       �
+	� �Restrictions 		   -CX			       �
+	� �Days to Extend Expiration	   0			       �
+	� �Credits to Add					       �
+	� �Minutes to Add					       �
+	��������������������������������������������������������������ͼ
+
+    Note: The DEMO version will only set new security level and add credits.
+	  The other validation options are only available in the registered
+	  version.
+
+ 6. Use the "Allowed Prefix List" sub-menu to set which prefixes will NOT be
+    affected by long distance restrictions (such as long distance
+    calling times, and numbers starting with 0 or 1). This option is useful
+    for specifying local numbers in other area codes. For example, in the
+    714 area code, 529 prefix, the following non-714 prefixes are local (*):
+
+	1310690 1310691 1310694 1310697 1310902 1310905 1310943 1310947
+	1818330 1818333 1818336 1818369 1818810 1818854 1818855 1818912
+	1818913 1818937 1818961 1818964 1818965 1818968 1909396 1909468
+	1909594 1909595 1909598 1909860 1909861 1909869
+
+	* Multiple entries per line to conserve documentation space
+
+    If the "Long Distance if not an Allowed Prefix" toggle option is set to
+    "Yes", then any number that is NOT included in this list will be
+    considered long distance.
+
+ 7. Use the "Long Distance Prefix List" sub-menu to set which SAME-AREA
+    prefixes will be considered long distance. If the "Long Distance if not
+    an Allowed Prefix" toggle option is set to "Yes", then this list is not
+    used. For example, in the 714 area code, 529 prefix, the following
+    714 prefixes are long distance (*):
+
+	241 258 259 265 285 418 432 433 434 435 436 437 438 479 480
+	505 513 531 540 541 542 543 544 545 546 547 549 550 554 556
+	557 558 560 564 565 566 567 568 569 571 573 641 647 648 662
+	664 665 667 668 669 691 708 730 731 751 754 755 775 832 834
+	835 836 838 839 850 953 954 957 966 967 972 973 977 979
+
+	* Multiple entries per line to conserve documentation space
+
+ 8. Use the "Long Distance Calling Times" sub-menu to set the times on each day
+    when SCB is allowed to place long distance calls. All times are entered
+    in 24-hour format and ranges crossing midnight (e.g. 18:00 to 09:00) are
+    valid. Numbers included in the "Allowed Prefix List" will not be limited
+    to these calling times.
+
+ 9. If you are a registered owner of SCB, be sure to enter your registration
+    number on the main menu.
+
+10. Hit ESC from the main SCB configuration menu and select "Yes" to save
+    the configuration file.
+
+
+Notes:
+=====
+
+All SCB activity is logged to the file SCB.LOG in the SCB directory in
+the format:
+
+	Node 7	     : Mon Sep 27 16:44:31 1993
+	User Name    : Digital Man
+	Voice Number : 714-529-6328
+	Modem Number : 5295313
+	Result	     : Verified
+
+Results 	Description
+------- 	-----------
+Hung up 	User hung up before or during validation process
+Refused 	User refused validation
+Long Dist	User is a long distance call and long distance is disallowed
+Invalid #	User entered a number that is in the trashcan file, less than
+		    minimum length, or begins with a disallowed number (0 or 1)
+No Init 	Modem could not be initialized
+No Connect	Modem could not connect to user's modem (possibly wrong #)
+Verified	User was called, modem connected, and password verified
+Bad Pass	User was called, modem connected, and incorrect password
+
+
+Customizations:
+==============
+
+The following files can be modified (optionally using Ctrl-A codes for color):
+
+SCB.MSG 	The opening screen for SCB
+REFUSED.MSG	Verification refused
+INSTRUCT.MSG	Instructions
+NO_ONE.MSG	Long distance numbers starting with 1 not allowed
+NO_ZERO.MSG	Long distance numbers starting with 0 not allowed
+LD_TIME.MSG	Not within allowed long distance callback time range
+TOOSHORT.MSG	Phone number is less than configured minimum number of digits
+PHONECAN.MSG	Phone number found in trashcan file
+VERIFIED.MSG	Displayed to users after verification
+
+
+Registration:
+============
+
+If you wish to order a registered copy of SCB at the same time as Synchronet,
+use the Synchronet ORDER.TXT file (in the SBBS\DOCS directory).
+
+If registering SCB after already purchasing a copy of Synchronet, call
+Digital Dynamics voice at 714-529-6328, Support BBS at 714-529-9525 (and use
+Domain Shopper), or complete the ORDER.TXT file in the SCB directory and mail
+or fax it to Digital Dynamics.
+
+/* End of SCB.DOC */
diff --git a/xtrn/scb/scb.msg b/xtrn/scb/scb.msg
new file mode 100644
index 0000000000000000000000000000000000000000..617338c75d3cab422a30073429c9ce822744c7de
--- /dev/null
+++ b/xtrn/scb/scb.msg
@@ -0,0 +1,8 @@
+ng    Synchronet Callback can be used to verify your modem phone number.
+If your modem phone number is verified, your access on the BBS may be 
+immediately upgraded or the sysop will be notified of the verification and your
+access may be upgraded later.
+
+    Some BBSs may not allow long distance verification calls.  If you are 
+not a local call from this BBS and it doesn't allow long distance calls,
+you will be notified of this when you start the verification procedure.
diff --git a/xtrn/scb/toolong.msg b/xtrn/scb/toolong.msg
new file mode 100644
index 0000000000000000000000000000000000000000..079411abdeee7b5559aa0c98c49ee77f864f5980
--- /dev/null
+++ b/xtrn/scb/toolong.msg
@@ -0,0 +1,2 @@
+Your phone number is too long to be valid.
+
diff --git a/xtrn/scb/tooshort.msg b/xtrn/scb/tooshort.msg
new file mode 100644
index 0000000000000000000000000000000000000000..e31e216680ed482057ce281bce6cd55d9e3195ed
--- /dev/null
+++ b/xtrn/scb/tooshort.msg
@@ -0,0 +1 @@
+Your phone number is too short to be valid.
diff --git a/xtrn/scb/verified.msg b/xtrn/scb/verified.msg
new file mode 100644
index 0000000000000000000000000000000000000000..ae5b1c8f293eec75d0eee41bee30520749295c83
--- /dev/null
+++ b/xtrn/scb/verified.msg
@@ -0,0 +1,5 @@
+
+ghYour number has been verified and your access upgraded. Please call back
+to use your upgraded security!
+
+p
\ No newline at end of file
diff --git a/xtrn/scb/whats.new b/xtrn/scb/whats.new
new file mode 100644
index 0000000000000000000000000000000000000000..2945099c7fe7c0d220110fea7f2dc96da2239f09
--- /dev/null
+++ b/xtrn/scb/whats.new
@@ -0,0 +1,21 @@
+What's new in SCB v1.20
+=======================
+
+ o	Supports new communications routines, supporting UART, FOSSIL,
+	BIOS (PC BIOS), EBIOS (PS/2 BIOS), and DigiBoard support.
+
+If installing over existing SCB install, just update SCB*.EXE and SCB.DOC.
+
+
+What's new in SCB v1.12
+=======================
+
+ o	Updated and more comprehensive documentation.
+ o	New "Long Distance if not an Allowed Prefix" toggle option.
+ o	If the user's expiration date is already set to the future, it will be
+	extended by the number of days in SCBCFG. Previously, expiration dates
+	would always be set to the current date plus the number of days set in
+	SCBCFG.
+
+If installing over existing SCB install, just update SCB*.EXE and SCB.DOC.
+
diff --git a/xtrn/sdk/xsdk.c b/xtrn/sdk/xsdk.c
new file mode 100644
index 0000000000000000000000000000000000000000..34ebaa8abb1ad5d98269507dc292afff520a068f
--- /dev/null
+++ b/xtrn/sdk/xsdk.c
@@ -0,0 +1,2476 @@
+/* XSDK.C */
+
+/****************************************************************************/
+/*			Synchronet External Program Software Development Kit			*/
+/*							1995 Digital Dynamics							*/
+/****************************************************************************/
+
+/****************************************************************************/
+/* This source code file is public domain and may be modified, compiled 	*/
+/* distributed, or used in any way, in part or whole for any purposes		*/
+/* without the consent or notification of Digital Dynamics. 				*/
+/*																			*/
+/* We only request that you display to the user, at some point, in your 	*/
+/* program the character "XSDK" and the version number.                     */
+/* example: bprintf("XSDK v%s",xsdk_ver);                                   */
+/****************************************************************************/
+
+/****************************************************************************/
+/* The source code for two external programs developed by Digital Dynamics	*/
+/* using XSDK (Synchronet Blackjack [SBJ] and Synchronet BBS List [SBL])	*/
+/* are available to the public domain as examples of how to implement the	*/
+/* functions and variables included in this software development kit.		*/
+/****************************************************************************/
+
+/****************************************************/
+/* For use with Borland/Turbo C and C++ compilers.	*/
+/* Tabstop set to 4.								*/
+/****************************************************/
+
+/***************************** Revision History *****************************\
+
+			Initial version for use with Synchronet v1a r6
+	1.0�	
+			Added bgotoxy() macro
+			Added mnehigh and mnelow vars for control of the mnemonic colors
+			Added sys_nodes and node_num variables to xtrn_sdk.c
+			Added MAX_NODES to xtrn_sdk.h
+			Added printfile() function to xtrn_sdk.c
+			Added rputs() (Raw put string)
+			Added getstr() (Get string)
+			Added redrwstr() (Redraw string)
+			Added stripattr() (String attributes)
+			Added sys_op var and the reading from the xtrn.dat
+			Removed user_min and the reading from the xtrn.dat
+			Changed read_xtrn_dat() to initdata()
+			Added ctrl-break handler to xtrn_sdk
+			Changed xtrn.dat format (again), adding system operator,
+				guru name, user ml, tl, birthdate and sex.
+			Added username() function
+			Renamed xtrn_sdk.* to xsdk.* and xtrnvars.c to xsdkvars.c
+				and xtrndefs.h to xsdkdefs.h
+			Added fexist() function
+	1.0
+			Ctrl-p is now switched into ctrl-^ by SBBS
+			Fixed relative data_dir bug in username()
+	1.01
+			Added flength() function and lowered disk activity
+			Lowered MAX_CARDS from 20 to 10 and made the re-shuffling happen
+				less often.
+	1.02
+			Fixed bug in attr() for monochrome users
+	1.03
+			Made warning and timeout times variables (sec_warn and sec_timeout)
+			Added SYSOP macro
+			Made it so sysop won't get timeout
+			Added user's phone number to XTRN.DAT
+			Added modem and com port information to XTRN.DAT
+			Added ahtoul function
+			Changed getstr commands Ctrl-L to Ctrl-R and Ctrl-P to Ctrl-\
+	1.04
+			Added intercommunication between the external programs and users
+			on the BBS or other external programs written with XSDK.
+			Added rprintf() function
+			Made many changes to getstr() function
+	2.00
+			Added DESQview awareness
+			Removed difftime() function calls completely
+			Added ungetkey() function
+			Added memory address of last modem status register for com routines
+				so we can track DCD incase user hangs up.
+			Added checkline function that does the checking of DCD.
+			Added new bug-free fdelay() routine to replace TC's delay() that
+				crashes multi-taskers such as DESQview and Windows
+	2.01
+			Added external program name access for user listings.
+			Added last node to send message to remembering and defaulting.
+			Added MALLOC and FREE macros for memory model non-specific memory
+				allocation.
+	2.02
+			Added INTRSBBS.DAT support for Synchronet shrinking to run programs
+				written with XSDK (new with v1b rev 01).
+			Added user's main flags, transfer flags, exemptions, and
+				restrictions to XTRN.DAT
+			Added support for the NODE_PAGE action (paging for private chat)
+				when listing nodes (printnodedat()).
+			Added user expiration date support to XTRN.DAT
+	2.03
+			Fixed bug with com_base variable being messed up.
+			New messaging system supported (for v1b r2 and higher)
+				(putnmsg and getnmsg functions).
+	2.10
+			Added support for file retrieving node status display.
+			NOPEN collision notice only appears after 25 retries.
+	2.11
+			Changed getnmsg function to not use IXB files.
+			Changed getsmsg function to not re-open for truncate.
+			Added user address, location, and zip/postal code suppport.
+			Added support for local arrow keys, home, end, ins, and del.
+			Remote keys ^] (back one space) and ^BkSpc (del) supported now.
+			Added support for high-bit Ctrl-A codes (for cursor positioning)
+			Removed file locking check - slowed down initialization sometimes.
+			Change user_ml to user_level, removed user_tl, changed user_mf to
+				user_flags1, changed user_tf to user_flags2, and added
+				user_flags3 and user_flags4.
+			cls() now updates lncntr like it should have.
+			Added ctrl-break handler so that users can abort printfile()
+			If a ctrl-c is received by inkey, the aborted flag is set.
+			Removed fdelay from XSDK and replaced with mswait for better
+				multitasker performance
+	2.20
+			New mswait that support OS2/Windows, DOS Idle, and non-DV modes.
+			XTRN.DAT passes mode of mswait configured in node setup.
+	2.21
+			Added user's real name/company name (user_realname) to XTRN.DAT
+	2.22
+			Added usernumber() function to get user number from a user name.
+	2.23
+			DTE rate (com_rate) now a ulong (instead of uint) to allow 115.2K
+	2.24
+			New K_AUTODEL mode for getstr() function, to be used in conjunction
+				with the K_EDIT mode. Makes overwriting existing strings very
+				easy for users.
+			Supports intelligent timeslice APIs while waiting for a key with
+				getkey() and with getstr() if K_LOWPRIO mode is used.
+			Hitting Ctrl-C sets the 'aborted' variable to 1.
+			Time zone and daylight savings automatically initialized to 0.
+			Modem strings up to 63 chars in XTRN.DAT now supported.
+			Fixed 10 character zip code bug in XSDKVARS.C.
+			Node directories (node_dir) up to 127 chars now supported.
+			nopen() can now open DENYNONE if passed access O_DENYNONE.
+	2.30
+			Added support for the following Ctrl-A codes: ;,.<>[]A
+			Changed definitions of TAB, SP, etc. to hex
+	2.31
+			C restriction disallows users to use Ctrl-P.
+			T exemption disables "Time's up" message.
+			Added center() function for outputting centered lines of text.
+			Added auto-pause to cls() and outchar() functions when clearing
+				the screen and lncntr is greater than 1
+			Changed bstrlen() to not count control characters (CR,LF,TAB,etc)
+			XSDK is now Watcom C++ compatible (although SBJ.C and SBL.C aren't)
+			XSDK.H is now *.CPP compatible
+			Added support for Ctrl-AQ (reset pause) and Ctrl-AZ (premature EOF)
+	2.32
+			Change bstrlen(char *str) to bstrlen(uchar *str)
+			Fixed bug in getstr() when word-wrapping a line that contains
+				ctrl-a codes and the input string did not begin at column 1.
+			Added user_dce variable (initialized by initdata from XTRN.DAT)
+			Fixed printnodedat() to not show Waiting for call (M)
+			Fixed typo in C restriction Ctrl-P message.
+			Moved call of checkline() in getkey() to immediately abort when
+				user hangs-up, even when keys are in the input buffer.
+			Added setmode() call to initdata() to set stderr to binary mode
+			Changed putchar() to write() in outchar() to elminate LF to CRLF
+				expansion
+	2.33
+			Improved cls() routine for auto-pause feature.
+			Added get_term() automatic RIP and WIP terminal detection function.
+	2.34
+			Added exec_dir, text_dir, and temp_dir variables to XTRN.DAT
+				format and initdata() function.
+			Added _fullpath() calls to initdata() to fix dir paths.
+			Added sys_id (QWK ID) to XTRN.DAT format and initdat() func.
+			Added node_misc to XTRN.DAT format and initdata() function.
+			If NM_LOWPRIO (low priority string input) is toggled on in
+				node_misc, then time-slices are always given up during input.
+			XSDK is now Symantec C++ compatible
+	2.40
+			node_misc was being read as a decimal number (it's stored in
+				the XTRN.DAT as hex), thus causing time-slice APIs to not
+				function correctly.
+	2.41
+			Ctrl-T is now intercepted by inkey() and displays the time the
+				program was launched, the current time, time used, and time
+				left (similar to SBBS).
+			Users are now warned of their last 5 minutes available (like SBBS).
+	2.42
+
+\****************************************************************************/
+
+#include "xsdk.h"
+
+char *xsdk_ver="2.42";
+
+#ifdef __TURBOC__
+extern long timezone=0L;
+extern daylight=0;
+#endif
+
+#ifdef __SC__
+#include <disp.h>
+short wherey(void);
+void clrscr(void);
+#endif  
+
+
+/****************************************************************************/
+/* This allows users to abort the listing of text by using Ctrl-C           */
+/****************************************************************************/
+int cbreakh(void)	/* ctrl-break handler */
+{
+aborted=1;
+return(1);		/* 1 to continue, 0 to abort */
+}
+
+/****************************************************************************/
+/* Performs printf() using bbs bputs function								*/
+/****************************************************************************/
+int bprintf(char *fmt, ...)
+{
+	va_list argptr;
+	char sbuf[1024];
+	int chcount;
+
+va_start(argptr,fmt);
+chcount=vsprintf(sbuf,fmt,argptr);
+va_end(argptr);
+bputs(sbuf);
+return(chcount);
+}
+
+/****************************************************************************/
+/* Performs printf() using bbs rputs function								*/
+/****************************************************************************/
+int rprintf(char *fmt, ...)
+{
+	va_list argptr;
+	char sbuf[1024];
+	int chcount;
+
+va_start(argptr,fmt);
+chcount=vsprintf(sbuf,fmt,argptr);
+va_end(argptr);
+rputs(sbuf);
+return(chcount);
+}
+
+/****************************************************************************/
+/* Outputs a NULL terminated string locally and remotely (if applicable) 	*/
+/****************************************************************************/
+void bputs(char *str)
+{
+	ulong l=0;
+
+while(str[l] && !aborted) {
+	if(str[l]==1) {				/* ctrl-a */
+		ctrl_a(str[++l]);		/* skip the ctrl-a */
+		if(str[l]=='Z')         /* Ctrl-AZ marks premature end of file */
+			break;
+		l++; }					/* skip the attribute code */
+	else
+		outchar(str[l++]); }
+}
+
+/****************************************************************************/
+/* Outputs a NULL terminated string locally and remotely (if applicable) 	*/
+/* Does not process ctrl-a codes (raw output)								*/
+/* Max length of str is 64 kbytes											*/
+/****************************************************************************/
+void rputs(char *str)
+{
+	ulong l=0;
+
+while(str[l])
+	outchar(str[l++]);
+}
+
+/****************************************************************************/
+/* Returns the number of characters in 'str' not counting ctrl-ax codes		*/
+/* or the null terminator													*/
+/****************************************************************************/
+int bstrlen(uchar *str)
+{
+	int i=0;
+
+while(*str) {
+	if(*str<SP) {	/* ctrl char */
+		if(*str==1) /* ctrl-A */
+			str++;
+		else if(*str!=CR && *str!=LF && *str!=FF)
+			i++; }
+	else
+		i++;
+	if(!(*str))
+		break;
+	str++; }
+return(i);
+}
+
+/****************************************************************************/
+/* Outputs the string 'str' centered for an 80 column display               */
+/* Automatically appends "\r\n" to output                                   */
+/****************************************************************************/
+void center(char *str)
+{
+	 int i,j;
+
+j=bstrlen(str);
+for(i=0;i<(80-j)/2;i++)
+	outchar(SP);
+bputs(str);
+}
+
+/****************************************************************************/
+/* Outputs one character to the screen. Handles, pause, saving and			*/
+/* restoring lines, etc.													*/
+/****************************************************************************/
+void outchar(char ch)
+{
+
+write(fileno(con_fp),&ch,1);
+
+if(ch==LF) {
+	lncntr++;
+	lbuflen=0;
+	tos=0; }
+else if(ch==FF) {
+	if(lncntr>1) {
+		lncntr=0;
+		CRLF;
+		pause(); }
+	lncntr=0;
+	lbuflen=0;
+	tos=1; }
+else if(ch==BS) {
+	if(lbuflen)
+		lbuflen--; }
+else {
+	if(!lbuflen)
+		latr=curatr;
+	if(lbuflen>=LINE_BUFSIZE) lbuflen=0;
+	lbuf[lbuflen++]=ch; }
+if(lncntr==user_rows-1) {
+	lncntr=0;
+    pause(); }
+}
+
+/****************************************************************************/
+/* Prints PAUSE message and waits for a key stoke							*/
+/****************************************************************************/
+void pause(void)
+{
+	uchar tempattrs=curatr,*msg="\1_\1r\1h[Hit a key] ";
+	int i,j;
+
+lncntr=0;
+bputs(msg);
+j=bstrlen(msg);
+getkey(0);
+for(i=0;i<j;i++)
+	bputs("\b \b");
+attr(tempattrs);
+}
+
+/****************************************************************************/
+/* Prompts user for Y or N (yes or no) and CR is interpreted as a Y			*/
+/* Returns 1 for Y or 0 for N												*/
+/* Called from quite a few places											*/
+/****************************************************************************/
+char yesno(char *str)
+{
+	char ch;
+
+bprintf("\1_\1b\1h%s (Y/n) ? \1w",str);
+while(1) {
+	ch=getkey(K_UPPER);
+	if(ch=='Y' || ch==CR) {
+		bputs("Yes\r\n");
+		return(1); }
+	if(ch=='N' || aborted) {
+		bputs("No\r\n");
+		return(0); } }
+}
+
+/****************************************************************************/
+/* Prompts user for N or Y (no or yes) and CR is interpreted as a N			*/
+/* Returns 1 for N or 0 for Y												*/
+/* Called from quite a few places											*/
+/****************************************************************************/
+char noyes(char *str)
+{
+	char ch;
+
+bprintf("\1_\1b\1h%s (y/N) ? \1w",str);
+while(1) {
+	ch=getkey(K_UPPER);
+	if(ch=='N' || ch==CR || aborted) {
+		bputs("No\r\n");
+		return(1); }
+	if(ch=='Y') {
+		bputs("Yes\r\n");
+		return(0); } }
+}
+
+/****************************************************************************/
+/* Outputs a string highlighting characters preceeded by a tilde with the	*/
+/* color specified in mnehigh and the rest of the line is in color mnelow.	*/
+/* If the user doesn't have ANSI, it puts the character following the tilde */
+/* in parenthesis.															*/
+/****************************************************************************/
+void mnemonics(char *str)
+{
+	long l;
+
+attr(mnelow);
+l=0L;
+while(str[l]) {
+	if(str[l]=='~' && str[l+1]) {
+		if(!(user_misc&ANSI))
+			outchar('(');
+		l++;
+		attr(mnehigh);
+		outchar(str[l]);
+		l++;
+		if(!(user_misc&ANSI))
+			outchar(')');
+		attr(mnelow); }
+	else
+		outchar(str[l++]); }
+attr(LIGHTGRAY);
+}
+
+/****************************************************************************/
+/* If a key has been pressed, the ASCII code is returned. If not, 0 is		*/
+/* returned. Ctrl-P and Ctrl-U are intercepted here.						*/
+/****************************************************************************/
+char inkey(int mode)
+{
+	static in_ctrl_p;
+	uchar ch=0,hour,min,sec;
+	ushort tleft;
+	uint i;
+	time_t now;
+
+if(keybufbot!=keybuftop) {
+	ch=keybuf[keybufbot++];
+	if(keybufbot==KEY_BUFSIZE)
+		keybufbot=0; }
+else if(_bios_keybrd(1)) {
+	i=_bios_keybrd(0);
+	if(i&0xff)
+		ch=i&0xff;
+	else {					/* Local Alt or Function key hit */
+		i>>=8;
+		switch(i) {
+			case 0x47:	/* Home - Same as Ctrl-B */
+				return(2);	/* ctrl-b beginning of line */
+			case 0x4b:		/* Left Arrow - same as ctrl-] */
+				return(0x1d);
+			case 0x4d:		/* Right Arrow - same as ctrl-f */
+				return(6);
+			case 0x48:		/* Up arrow - same as ctrl-^ */
+				return(0x1e);
+			case 0x50:		/* Down arrow - same as CR */
+				return(CR);
+			case 0x4f:	  /* End	  - same as Ctrl-E */
+                return(5);  /* ctrl-e - end of line */
+			case 0x52:	/* Insert */
+				return(0x1f);	/* ctrl-minus - insert mode */
+			case 0x53:	/* Delete */
+                return(0x7f);   /* ctrl-bkspc - del cur char */
+			}
+		return(0); } }
+
+if(ch==0x10 || ch==0x1e) {	/* Ctrl-P or Ctrl-^ */
+	if(in_ctrl_p || !ctrl_dir[0])	/* keep from being recursive */
+		return(0);
+	in_ctrl_p=1;
+	SAVELINE;
+	CRLF;
+	nodemsg();
+	CRLF;
+	RESTORELINE;
+	lncntr=0;
+	in_ctrl_p=0;
+	return(0); }
+
+if(ch==20) { /* Ctrl-T Time left online */
+	SAVELINE;
+	attr(LIGHTGRAY);
+	now=time(NULL);
+	checktimeleft();
+	CRLF;
+	bprintf("\r\nStart     : %.24s",ctime(&starttime));
+	bprintf("\r\nNow       : %.24s",ctime(&now));
+	i=now-starttime;
+	hour=(i/60)/60;
+	min=(i/60)-(hour*60);
+	sec=i-((min+(hour*60))*60);
+	bprintf("\r\nTime Used : %02u:%02u:%02u",hour,min,sec);
+	tleft=timeleft-(now-starttime);
+	hour=(tleft/60)/60;
+	min=(tleft/60)-(hour*60);
+	sec=tleft-((min+(hour*60))*60);
+	bprintf("\r\nTime Left : %02u:%02u:%02u\r\n\r\n",hour,min,sec);
+	RESTORELINE;
+	lncntr=0;
+	return(0); }
+
+if(ch==21) { /* Ctrl-U Users online */
+	if(!ctrl_dir[0])
+		return(0);
+	SAVELINE;
+	CRLF;
+	whos_online(1);
+	CRLF;
+	RESTORELINE;
+	lncntr=0;
+    return(0); }
+
+if(ch==3)
+	aborted=1;
+else if(aborted)
+	ch=3;
+
+if(!ch && (!(mode&K_GETSTR) || mode&K_LOWPRIO|| node_misc&NM_LOWPRIO))
+	mswait(0);
+return(ch);
+}
+
+/****************************************************************************/
+/* Waits for remote or local user to hit a key. Inactivity timer is checked */
+/* and hangs up if inactive for 4 minutes. Returns key hit, or uppercase of */
+/* key hit if mode&K_UPPER or key out of KEY BUFFER. Does not print key.	*/
+/****************************************************************************/
+char getkey(int mode)
+{
+	char ch,warn=0;
+	ushort tleft;
+	time_t timeout,now;
+
+aborted=lncntr=0;
+timeout=time(NULL);
+do {
+	checkline();
+	ch=inkey(mode);
+	now=time(NULL);
+	if(ch) {
+		if(mode&K_NUMBER && isprint(ch) && !isdigit(ch))
+			continue;
+		if(mode&K_ALPHA && isprint(ch) && !isalpha(ch))
+			continue;
+		if(ch==LF) continue;
+		if(mode&K_UPPER)
+			return(toupper(ch));
+		return(ch); }
+	checktimeleft();
+
+	tleft=timeleft-(now-starttime);
+	if((tleft/60)<(5-timeleft_warn)) {	/* Running out of time warning */
+		timeleft_warn=5-(tleft/60);
+        SAVELINE;
+		bprintf("nh\r\n\7\r\nYou only have ri%unh minute%s "
+			"left.\r\n\r\n"
+			,((ushort)tleft/60)+1,(tleft/60) ? "s" : "");
+        RESTORELINE; }
+
+	if(now-timeout>=sec_warn && !warn)		/* Inactivity warning */
+		for(warn=0;warn<5;warn++)
+			outchar(7);
+	} while(now-timeout<sec_timeout);
+bputs("\r\nInactive too long.\r\n");
+exit(0);
+return(0);	/* never gets here, but makes compiler happy */
+}
+
+/****************************************************************************/
+/* If remote user, checks DCD to see if user has hung up or not.			*/
+/****************************************************************************/
+void checkline(void)
+{
+if(com_port && !((*msr)&DCD)) exit(0);
+}
+
+/****************************************************************************/
+/* Waits for remote or local user to hit a key that is contained inside str.*/
+/* 'str' should contain uppercase characters only. When a valid key is hit, */
+/* it is echoed (upper case) and is the return value.						*/
+/* If max is non-zero and a number is hit that is not in str, it will be	*/
+/* returned with the high bit set. If the return of this function has the	*/
+/* high bit set (&0x8000), just flip the bit (^0x8000) to get the number.	*/
+/****************************************************************************/
+int getkeys(char *str,int max)
+{
+	uchar ch,n=0;
+	int i=0;
+
+strupr(str);
+while(!aborted) {
+	ch=getkey(K_UPPER);
+	if(max && ch>0x7f)	/* extended ascii chars are digits to isdigit() */
+		continue;
+	if(ch && !n && (strchr(str,ch))) { 	/* return character if in string */
+		outchar(ch);
+		attr(LIGHTGRAY);
+		CRLF;
+		return(ch); }
+	if(ch==CR && max) {             /* return 0 if no number */
+		attr(LIGHTGRAY);
+		CRLF;
+		if(n)
+			return(i|0x8000);		/* return number plus high bit */
+		return(0); }
+	if(ch==BS && n) {
+		bputs("\b \b");
+		i/=10;
+		n--; }
+	else if(max && isdigit(ch) && (i*10)+(ch&0xf)<=max && (ch!='0' || n)) {
+		i*=10;
+		n++;
+		i+=ch&0xf;
+		outchar(ch);
+		if(i*10>max) {
+			attr(LIGHTGRAY);
+			CRLF;
+			return(i|0x8000); } } }
+return(0);
+}
+
+/****************************************************************************/
+/* Hot keyed number input routine.											*/
+/****************************************************************************/
+int getnum(int max)
+{
+	uchar ch,n=0;
+	int i=0;
+
+while(1) {
+	ch=getkey(K_UPPER);
+	if(ch>0x7f)
+		continue;
+	if(ch=='Q') {
+		outchar('Q');
+		CRLF;
+		return(-1); }
+	else if(ch==3) {		/* ctrl-c */
+		CRLF;
+		return(-1); }
+	else if(ch==CR) {
+		CRLF;
+		return(i); }
+	else if(ch==BS && n) {
+		bputs("\b \b");
+		i/=10;
+		n--; }
+	else if(isdigit(ch) && (i*10)+(ch&0xf)<=max && (ch!='0' || n)) {
+		i*=10;
+		n++;
+		i+=ch&0xf;
+		outchar(ch);
+		if(i*10>max) {
+			CRLF;
+			return(i); } } }
+return(-1);
+}
+
+/****************************************************************************/
+/* Waits for remote or local user to input a CR terminated string. 'length' */
+/* is the maximum number of characters that getstr will allow the user to 	*/
+/* input into the string. 'mode' specifies upper case characters are echoed */
+/* or wordwrap or if in message input (^A sequences allowed). ^W backspaces */
+/* a word, ^X backspaces a line, ^Gs, BSs, TABs are processed, LFs ignored. */
+/* ^N non-destructive BS, ^V center line. Valid keys are echoed.			*/
+/****************************************************************************/
+int getstr(char *strout, int maxlen, int mode)
+{
+	int i,l,x,z;	/* i=current position, l=length, j=printed chars */
+					/* x&z=misc */
+	uchar ch,str1[256],str2[256],ins=0,atr;
+
+if(mode&K_LINE && user_misc&ANSI) {
+	attr(LIGHTGRAY|HIGH|(BLUE<<4));  /* white on blue */
+	for(i=0;i<maxlen;i++)
+		outchar(SP);
+	bprintf("\x1b[%dD",maxlen); }
+i=l=0;	/* i=total number of chars, j=number of printable chars */
+if(wordwrap[0]) {
+	strcpy(str1,wordwrap);
+	wordwrap[0]=0; }
+else str1[0]=0;
+if(mode&K_EDIT)
+	strcat(str1,strout);
+if(strlen(str1)>maxlen)
+	str1[maxlen]=0;
+atr=curatr;
+if(mode&K_AUTODEL && str1[0])
+	attr(BLUE|(LIGHTGRAY<<4));
+rputs(str1);
+if(mode&K_EDIT && !(mode&(K_LINE|K_AUTODEL)) && user_misc&ANSI)
+	bputs("\x1b[K");  /* destroy to eol */
+i=l=strlen(str1);
+
+if(mode&K_AUTODEL && str1[0]) {
+	ch=getkey(mode);
+    attr(atr);
+    if(isprint(ch) || ch==0x7f) {
+        for(i=0;i<l;i++)
+            bputs("\b \b");
+        i=l=0; }
+    else {
+        for(i=0;i<l;i++)
+            outchar(BS);
+        rputs(str1);
+        i=l; }
+	if(ch!=SP && ch!=TAB)
+        ungetkey(ch); }
+
+while((ch=getkey(mode|K_GETSTR))!=CR && !aborted) {
+	switch(ch) {
+		case 1:	/* Ctrl-A for ANSI */
+			if(!(mode&K_MSG) || i>maxlen-3)
+				break;
+            if(ins) {
+				if(l<maxlen)
+                    l++;
+				for(x=l;x>i;x--)
+					str1[x]=str1[x-1];
+				rprintf("%.*s",l-i,str1+i);
+				rprintf("\x1b[%dD",l-i);
+				if(i==maxlen-1)
+					ins=0; }
+			outchar(str1[i++]=1);
+			break;
+		case 2:	/* Ctrl-B Beginning of Line */
+			if(user_misc&ANSI && i) {
+				bprintf("\x1b[%dD",i);
+				i=0; }
+			break;
+		case 4:	/* Ctrl-D Delete word right */
+        	if(i<l) {
+				x=i;
+				while(x<l && str1[x]!=SP) {
+					outchar(SP);
+					x++; }
+				while(x<l && str1[x]==SP) {
+					outchar(SP);
+					x++; }
+				bprintf("\x1b[%dD",x-i);   /* move cursor back */
+				z=i;
+				while(z<l-(x-i))  {             /* move chars in string */
+					outchar(str1[z]=str1[z+(x-i)]);
+					z++; }
+				while(z<l) {					/* write over extra chars */
+					outchar(SP);
+					z++; }
+				bprintf("\x1b[%dD",z-i);
+				l-=x-i; }						/* l=new length */
+			break;
+		case 5:	/* Ctrl-E End of line */
+			if(user_misc&ANSI && i<l) {
+				bprintf("\x1b[%dC",l-i);  /* move cursor right one */
+				i=l; }
+			break;
+		case 6:	/* Ctrl-F move cursor forewards */
+			if(i<l && (user_misc&ANSI)) {
+				bputs("\x1b[C");   /* move cursor right one */
+				i++; }
+			break;
+		case 7:
+			if(!(mode&K_MSG))
+				break;
+             if(ins) {
+				if(l<maxlen)
+                    l++;
+				for(x=l;x>i;x--)
+					str1[x]=str1[x-1];
+				if(i==maxlen-1)
+					ins=0; }
+			 if(i<maxlen) {
+				str1[i++]=7;
+				outchar(7); }
+			 break;
+		case 14:	/* Ctrl-N Next word */
+			if(i<l && (user_misc&ANSI)) {
+				x=i;
+				while(str1[i]!=SP && i<l)
+					i++;
+				while(str1[i]==SP && i<l)
+					i++;
+				bprintf("\x1b[%dC",i-x); }
+			break;
+		case 0x1c:	  /* Ctrl-\ Previous word */
+			if(i && (user_misc&ANSI)) {
+				x=i;
+				while(str1[i-1]==SP && i)
+					i--;
+				while(str1[i-1]!=SP && i)
+					i--;
+				bprintf("\x1b[%dD",x-i); }
+			break;
+		case 18:	/* Ctrl-R Redraw Line */
+            redrwstr(str1,i,l,0);
+            break;
+		case TAB:
+			if(!(i%TABSIZE)) {
+            	if(ins) {
+					if(l<maxlen)
+                        l++;
+					for(x=l;x>i;x--)
+						str1[x]=str1[x-1];
+					if(i==maxlen-1)
+						ins=0; }
+				str1[i++]=SP;
+				outchar(SP); }
+			while(i<maxlen && i%TABSIZE) {
+            	if(ins) {
+					if(l<maxlen)
+                        l++;
+					for(x=l;x>i;x--)
+						str1[x]=str1[x-1];
+					if(i==maxlen-1)
+						ins=0; }
+				str1[i++]=SP;
+				outchar(SP); }
+			if(ins)
+				redrwstr(str1,i,l,0);
+			break;
+		case BS:
+			if(!i)
+				break;
+			i--;
+			l--;
+			if(i!=l) {				/* Deleting char in middle of line */
+				outchar(BS);
+				z=i;
+				while(z<l)	{		/* move the characters in the line */
+					outchar(str1[z]=str1[z+1]);
+					z++; }
+				outchar(SP);		/* write over the last char */
+				bprintf("\x1b[%dD",(l-i)+1); }
+			else
+				bputs("\b \b");
+			break;
+		case 22:	/* Ctrl-V 	Center line */
+			str1[l]=0;
+			l=bstrlen(str1);
+			for(x=0;x<(maxlen-l)/2;x++)
+				str2[x]=SP;
+			str2[x]=0;
+			strcat(str2,str1);
+			strcpy(strout,str2);
+			l=strlen(strout);
+			if(mode&K_MSG)
+				redrwstr(strout,i,l,K_MSG);
+			else {
+				while(i--)
+					bputs("\b");
+				bputs(strout);
+				if(mode&K_LINE)
+					attr(LIGHTGRAY); }
+			CRLF;
+			return(l);
+		case 23:	/* Ctrl-W   Delete word left */
+			if(i<l) {
+				x=i;							/* x=original offset */
+				while(i && str1[i-1]==SP) {
+					outchar(BS);
+					i--; }
+				while(i && str1[i-1]!=SP) {
+					outchar(BS);
+					i--; }
+				z=i;                            /* i=z=new offset */
+				while(z<l-(x-i))  {             /* move chars in string */
+					outchar(str1[z]=str1[z+(x-i)]);
+					z++; }
+				while(z<l) {					/* write over extra chars */
+					outchar(SP);
+					z++; }
+				bprintf("\x1b[%dD",z-i);        /* back to new x corridnant */
+				l-=x-i; }						/* l=new length */
+			else {
+            	while(i && str1[i-1]==SP) {
+					i--;
+					l--;
+					bputs("\b \b"); }
+                while(i && str1[i-1]!=SP) {
+					i--;
+					l--;
+					bputs("\b \b"); } }
+			break;
+		case 24:	/* Ctrl-X   Delete entire line */
+			while(i<l) {
+				outchar(SP);
+				i++; }
+			while(l) {
+				l--;
+				bputs("\b \b"); }
+			i=0;
+			break;
+		case 25:	/* Ctrl-Y	Delete to end of line */
+			if(user_misc&ANSI) {
+				bputs("\x1b[s\x1b[K\x1b[u");
+				l=i; }
+			break;
+		case 31:	/* Ctrl-Minus		Toggles Insert/Overwrite */
+			if(!(user_misc&ANSI))
+				break;
+			if(ins) {
+				ins=0;
+				redrwstr(str1,i,l,0); }
+			else if(i<l) {
+				ins=1;
+				bprintf("\x1b[s\x1b[%dC",80-i);         /* save pos  */
+				z=curatr;								/* and got to EOL */
+                attr(z|BLINK|HIGH);
+				outchar('�');
+				attr(z);
+				bputs("\x1b[u"); }  /* restore pos */
+			break;
+		case 0x1d:	/* Ctrl-]  Reverse Cursor Movement */
+			if(i && (user_misc&ANSI)) {
+				bputs("\x1b[D");   /* move cursor left one */
+				i--; }
+			break;
+		case 0x7f:	/* Ctrl-BkSpc (DEL) Delete current char */
+			if(i==l)
+				break;
+			l--;
+			z=i;
+			while(z<l)	{		/* move the characters in the line */
+				outchar(str1[z]=str1[z+1]);
+				z++; }
+			outchar(SP);		/* write over the last char */
+			bprintf("\x1b[%dD",(l-i)+1);
+            break;
+		case ESC:
+			if(!(user_misc&ANSI))
+				break;
+			if((ch=getkey(0x8000))!='[') {
+				ungetch(ch);
+				break; }
+			if((ch=getkey(0x8000))=='C') {
+				if(i<l) {
+					bputs("\x1b[C");   /* move cursor right one */
+					i++; } }
+			else if(ch=='D') {
+				if(i) {
+					bputs("\x1b[D");   /* move cursor left one */
+					i--; } }
+			else {
+				while(isdigit(ch) || ch==';' || isalpha(ch)) {
+					if(isalpha(ch)) {
+						ch=getkey(0);
+						break; }
+					ch=getkey(0); }
+				ungetch(ch); }
+			break;
+		default:
+			if(mode&K_WRAP && i==maxlen && ch>=SP && !ins) {
+				str1[i]=0;
+				if(ch==SP) {	/* don't wrap a space as last char */
+					strcpy(strout,str1);
+					if(stripattr(strout))
+						redrwstr(strout,i,l,K_MSG);
+					CRLF;
+					return(i); }
+				x=i-1;
+				z=1;
+				wordwrap[0]=ch;
+				while(str1[x]!=SP && x)
+					wordwrap[z++]=str1[x--];
+				if(x<(maxlen/2)) {
+					wordwrap[1]=0;	/* only wrap one character */
+					strcpy(strout,str1);
+					if(stripattr(strout))
+						redrwstr(strout,i,l,K_MSG);
+					CRLF;
+					return(i); }
+				wordwrap[z]=0;
+				while(z--) {
+					i--;
+					bputs("\b \b"); }
+				strrev(wordwrap);
+				str1[x]=0;
+				strcpy(strout,str1);
+				if(stripattr(strout))
+					redrwstr(strout,i,x,mode);
+				CRLF;
+				return(x); }
+			if(i<maxlen && ch>=SP) {
+				if(mode&K_UPRLWR)
+					if(!i || (i && (str1[i-1]==SP || str1[i-1]=='-'
+						|| str1[i-1]=='.' || str1[i-1]=='_')))
+						ch=toupper(ch);
+					else
+						ch=tolower(ch);
+				if(ins) {
+					if(l<maxlen)	/* l<maxlen */
+                        l++;
+					for(x=l;x>i;x--)
+						str1[x]=str1[x-1];
+					rprintf("%.*s",l-i,str1+i);
+					rprintf("\x1b[%dD",l-i);
+					if(i==maxlen-1) {
+						bputs("  \b\b");
+						ins=0; } }
+				str1[i++]=ch;
+				outchar(ch); } }
+    if(i>l)
+		l=i;
+	if(mode&K_CHAT && !l)
+		return(0); }
+if(i>l)
+	l=i;
+str1[l]=0;
+if(!aborted) {
+    strcpy(strout,str1);
+    if(stripattr(strout) || ins)
+        redrwstr(strout,i,l,K_MSG); }
+else
+    l=0;
+if(mode&K_LINE) attr(LIGHTGRAY);
+if(!(mode&K_NOCRLF)) {
+	outchar(CR);
+	if(!(mode&K_MSG && aborted))
+		outchar(LF); }
+return(l);
+}
+
+/****************************************************************************/
+/* Redraws str using i as current cursor position and l as length           */
+/****************************************************************************/
+void redrwstr(char *strin, int i, int l, char mode)
+{
+	char str[256],c;
+
+sprintf(str,"%-*.*s",l,l,strin);
+c=i;
+while(c--)
+	outchar(BS);
+if(mode&K_MSG)
+	bputs(str);
+else
+	rputs(str);
+if(user_misc&ANSI) {
+	bputs("\x1b[K");
+	if(i<l)
+		bprintf("\x1b[%dD",l-i); }
+else {
+	while(c<79)	{ /* clear to end of line */
+		outchar(SP);
+		c++; }
+	while(c>l) { /* back space to end of string */
+		outchar(BS);
+		c--; } }
+}
+
+/****************************************************************************/
+/* Strips invalid Ctrl-Ax sequences from str                                */
+/* Returns number of ^A's in line                                           */
+/****************************************************************************/
+char stripattr(char *strin)
+{
+	uchar str[81];
+	uchar a,c,d,e;
+
+e=strlen(strin);
+for(a=c=d=0;c<e;c++) {
+	if(strin[c]==1) {
+		a++;
+		switch(toupper(strin[c+1])) {
+			case '-':	/* clear 		*/
+			case '_':	/* clear		*/
+			case 'B':	/* blue 	fg 	*/
+			case 'C':	/* cyan 	fg 	*/
+			case 'G':	/* green	fg 	*/
+			case 'H':	/* high 	fg 	*/
+			case 'I':	/* blink 	   	*/
+			case 'K':	/* black 	fg 	*/
+			case 'L':	/* cls         	*/
+			case 'M':	/* magenta  fg 	*/
+			case 'N':	/* normal      	*/
+			case 'P':	/* pause       	*/
+			case 'Q':   /* pause reset  */
+			case 'R':	/* red      fg 	*/
+			case 'W':	/* white    fg 	*/
+			case 'Y':	/* yellow   fg 	*/
+			case '0':	/* black 	bg 	*/
+			case '1':	/* red   	bg 	*/
+			case '2':	/* green 	bg 	*/
+			case '3':   /* brown	bg 	*/
+			case '4':	/* blue  	bg 	*/
+			case '5':   /* magenta 	bg 	*/
+			case '6':	/* cyan    	bg 	*/
+			case '7':	/* white   	bg 	*/
+				break;
+			default:
+				c++;
+				continue; } }
+	str[d++]=strin[c]; }
+str[d]=0;
+strcpy(strin,str);
+return(a);
+}
+
+/***************************************************************************/
+/* Changes local and remote text attributes accounting for monochrome      */
+/***************************************************************************/
+void attr(char atr)
+{
+
+if(!(user_misc&ANSI) || aborted)
+	return;
+if(!(user_misc&COLOR)) {  /* eliminate colors if user doesn't have them */
+	if(atr&LIGHTGRAY)		/* if any bits set, set all */
+		atr|=LIGHTGRAY;
+	if(atr&(LIGHTGRAY<<4))
+		atr|=(LIGHTGRAY<<4);
+	if(atr&LIGHTGRAY && atr&(LIGHTGRAY<<4))
+		atr&=~LIGHTGRAY; }	/* if background is solid, forground is black */
+if(curatr==atr) /* attribute hasn't changed. don't send codes */
+	return;
+
+if((!(atr&HIGH) && curatr&HIGH)	|| (!(atr&BLINK) && curatr&BLINK)
+	|| atr==LIGHTGRAY) {
+	bprintf("\x1b[0m");
+	curatr=LIGHTGRAY; }
+
+if(atr==LIGHTGRAY) {				 /* no attributes */
+	curatr=atr;
+	return; }
+
+if(atr&BLINK) {						/* special attributes */
+	if(!(curatr&BLINK))
+		bprintf("\x1b[5m"); }
+if(atr&HIGH) {
+	if(!(curatr&HIGH))
+		bprintf("\x1b[1m"); }
+
+if((atr&0x7)==BLACK) {				/* foreground colors */
+	if((curatr&0x7)!=BLACK)
+		bprintf("\x1b[30m"); }
+else if((atr&0x7)==RED) {
+	if((curatr&0x7)!=RED)
+		bprintf("\x1b[31m"); }
+else if((atr&0x7)==GREEN) {
+	if((curatr&0x7)!=GREEN)
+		bprintf("\x1b[32m"); }
+else if((atr&0x7)==BROWN) {
+	if((curatr&0x7)!=BROWN)
+		bprintf("\x1b[33m"); }
+else if((atr&0x7)==BLUE) {
+	if((curatr&0x7)!=BLUE)
+		bprintf("\x1b[34m"); }
+else if((atr&0x7)==MAGENTA) {
+	if((curatr&0x7)!=MAGENTA)
+		bprintf("\x1b[35m"); }
+else if((atr&0x7)==CYAN) {
+	if((curatr&0x7)!=CYAN)
+		bprintf("\x1b[36m"); }
+else if((atr&0x7)==LIGHTGRAY) {
+	if((curatr&0x7)!=LIGHTGRAY)
+		bprintf("\x1b[37m"); }
+
+if((atr&0x70)==(BLACK<<4)) {		/* background colors */
+	if((curatr&0x70)!=(BLACK<<4))
+		bprintf("\x1b[40m"); }
+else if((atr&0x70)==(RED<<4)) {
+	if((curatr&0x70)!=(RED<<4))
+		bprintf("\x1b[41m"); }
+else if((atr&0x70)==(GREEN<<4)) {
+	if((curatr&0x70)!=(GREEN<<4))
+		bprintf("\x1b[42m"); }
+else if((atr&0x70)==(BROWN<<4)) {
+	if((curatr&0x70)!=(BROWN<<4))
+		bprintf("\x1b[43m"); }
+else if((atr&0x70)==(BLUE<<4)) {
+	if((curatr&0x70)!=(BLUE<<4))
+		bprintf("\x1b[44m"); }
+else if((atr&0x70)==(MAGENTA<<4)) {
+	if((curatr&0x70)!=(MAGENTA<<4))
+		bprintf("\x1b[45m"); }
+else if((atr&0x70)==(CYAN<<4)) {
+	if((curatr&0x70)!=(CYAN<<4))
+		bprintf("\x1b[46m"); }
+else if((atr&0x70)==(LIGHTGRAY<<4)) {
+	if((curatr&0x70)!=(LIGHTGRAY<<4))
+		bprintf("\x1b[47m"); }
+
+curatr=atr;
+}
+
+/****************************************************************************/
+/* Peform clear screen														*/
+/****************************************************************************/
+void cls(void)
+{
+	int i;
+
+if(lncntr>1 && !tos) {
+	lncntr=0;
+	CRLF;
+	pause();
+	while(lncntr && !aborted)
+		pause(); }
+
+if(user_misc&ANSI)
+	bprintf("\x1b[2J");
+else {
+	outchar(FF);
+	clrscr(); }
+tos=1;
+lncntr=0;
+}
+
+#ifdef __WATCOMC__
+
+short wherey(void)
+{
+	struct rccoord rc;
+
+rc=_gettextposition();
+return(rc.col);
+}
+
+void clrscr(void)
+{
+_clearscreen(_GCLEARSCREEN);
+}
+
+#endif
+
+/****************************************************************************/
+/* performs the correct attribute modifications for the Ctrl-A code			*/
+/****************************************************************************/
+void ctrl_a(char x)
+{
+	char atr=curatr;
+	int i,j;
+
+if((uchar)x>=0x7f) {
+	if(user_misc&ANSI)
+		bprintf("\x1b[%uC",(uchar)x-0x7f);
+	else
+		for(i=0;i<(uchar)x-0x7f;i++)
+			outchar(SP);
+    return; }
+
+switch(toupper(x)) {
+	case '-':								/* turn off all attributes if */
+		if(atr&(HIGH|BLINK|(LIGHTGRAY<<4)))	/* high intensity, blink or */
+			attr(LIGHTGRAY);				/* background bits are set */
+		break;
+	case '_':								/* turn off all attributes if */
+		if(atr&(BLINK|(LIGHTGRAY<<4)))		/* blink or background is set */
+			attr(LIGHTGRAY);
+		break;
+	case ',':   /* Delay 1/10 sec */
+		mswait(100);
+		break;
+	case ';':   /* Delay 1/2 sec */
+		mswait(500);
+		break;
+	case '.':   /* Delay 2 secs */
+		mswait(2000);
+        break;
+	case 'P':	/* Pause */
+		pause();
+		break;
+	case 'Q':   /* Pause reset */
+		lncntr=0;
+		break;
+	case 'L':	/* CLS (form feed) */
+		cls();
+		break;
+	case '>':   /* CLREOL */
+		if(user_misc&ANSI)
+			bputs("\x1b[K");
+		else {
+			i=j=wherey();
+			while(i++<80)
+				outchar(SP);
+			while(j++<80)
+				outchar(BS); }
+		break;
+	case '<':   /* Non-destructive backspace */
+		outchar(BS);
+		break;
+	case '[':   /* Carriage return */
+		outchar(CR);
+		break;
+	case ']':   /* Line feed */
+		outchar(LF);
+		break;
+	case 'A':   /* Ctrl-A */
+		outchar(1);
+        break;
+	case 'H': 	/* High intensity */
+		atr|=HIGH;
+		attr(atr);
+		break;
+	case 'I':	/* Blink */
+		atr|=BLINK;
+		attr(atr);
+		break;
+	case 'N': 	/* Normal */
+		attr(LIGHTGRAY);
+		break;
+	case 'R':
+		atr=(atr&0xf8)|RED;
+		attr(atr);
+		break;
+	case 'S':
+		nodesync();
+		break;
+	case 'G':
+		atr=(atr&0xf8)|GREEN;
+		attr(atr);
+		break;
+	case 'B':
+		atr=(atr&0xf8)|BLUE;
+		attr(atr);
+		break;
+    case 'W':	/* White */
+		atr=(atr&0xf8)|LIGHTGRAY;
+		attr(atr);
+		break;
+    case 'C':
+		atr=(atr&0xf8)|CYAN;
+		attr(atr);
+		break;
+	case 'M':
+		atr=(atr&0xf8)|MAGENTA;
+		attr(atr);
+		break;
+	case 'Y':
+		atr=(atr&0xf8)|BROWN;
+		attr(atr);
+		break;
+    case 'K':	/* Black */
+		atr=(atr&0xf8)|BLACK;
+		attr(atr);
+		break;
+    case '0':	/* Black Background */
+		atr=(atr&0x8f)|(BLACK<<4);
+		attr(atr);
+		break;
+	case '1':	/* Red Background */
+		atr=(atr&0x8f)|(RED<<4);
+		attr(atr);
+		break;
+	case '2':	/* Green Background */
+		atr=(atr&0x8f)|(GREEN<<4);
+		attr(atr);
+		break;
+	case '3':	/* Yellow Background */
+		atr=(atr&0x8f)|(BROWN<<4);
+		attr(atr);
+		break;
+	case '4':	/* Blue Background */
+		atr=(atr&0x8f)|(BLUE<<4);
+		attr(atr);
+		break;
+	case '5':	/* Magenta Background */
+		atr=(atr&0x8f)|(MAGENTA<<4);
+		attr(atr);
+		break;
+	case '6':	/* Cyan Background */
+		atr=(atr&0x8f)|(CYAN<<4);
+		attr(atr);
+		break;
+	case '7':	/* White Background */
+		atr=(atr&0x8f)|(LIGHTGRAY<<4);
+		attr(atr);
+		break; }
+}
+
+/****************************************************************************/
+/* Network open function. Opens all files DENYALL and retries LOOP_NOPEN    */
+/* number of times if the attempted file is already open or denying access  */
+/* for some other reason.	All files are opened in BINARY mode.			*/
+/****************************************************************************/
+int nopen(char *str, int access)
+{
+	char count=0;
+	int file,share;
+
+if(access&SH_DENYNO) share=SH_DENYNO;
+else if(access==O_RDONLY) share=SH_DENYWR;
+else share=SH_DENYRW;
+while(((file=sopen(str,O_BINARY|access,share,S_IWRITE))==-1)
+	&& errno==EACCES && count++<LOOP_NOPEN)
+	if(count>10)
+		mswait(50);
+if(count>(LOOP_NOPEN/2) && count<=LOOP_NOPEN)
+	bprintf("\r\nNOPEN COLLISION - File: %s Count: %d\r\n"
+		,str,count);
+if(file==-1 && errno==EACCES)
+	bputs("\7\r\nNOPEN: ACCESS DENIED\r\n\7");
+return(file);
+}
+
+/****************************************************************************/
+/* Reads data from XTRN.DAT in the node directory and fills the appropriate */
+/* global variables.														*/
+/* Initializes starttime variable with current time.						*/
+/****************************************************************************/
+void initdata(void)
+{
+	char str[256],tmp[256];
+	int i;
+	FILE *stream;
+
+#if defined(__TURBOC__) || defined(__SC__)	/* Borland or Symantec */
+	ctrlbrk(cbreakh);
+#endif
+
+#ifdef __WATCOMC__
+	putenv("TZ=UCT0");
+	setvbuf(stdout,NULL,_IONBF,0);
+	setvbuf(stderr,NULL,_IONBF,0);
+#endif
+
+#ifdef __SC__
+    setvbuf(stdout,NULL,_IONBF,0);
+	con_fp=stdout;
+#else
+	con_fp=stderr;
+#endif
+
+if(setmode(fileno(con_fp),O_BINARY)==-1) {	 /* eliminate LF expansion */
+	printf("Can't set console output to BINARY\n");
+	exit(1); }
+
+sprintf(str,"%sXTRN.DAT",node_dir);
+if((stream=fopen(str,"rt"))==NULL) {
+	printf("Can't open %s\r\n",str);
+	exit(1); }
+fgets(str,81,stream);			/* username */
+sprintf(user_name,"%.25s",str);
+truncsp(user_name);
+fgets(str,81,stream);			/* system name */
+sprintf(sys_name,"%.40s",str);
+truncsp(sys_name);
+fgets(str,81,stream);			/* system operator */
+sprintf(sys_op,"%.40s",str);
+truncsp(sys_op);
+fgets(str,81,stream);			/* system guru */
+sprintf(sys_guru,"%.40s",str);
+truncsp(sys_guru);
+
+fgets(str,81,stream);			/* ctrl dir */
+str[50]=0;
+if(str[0]=='.')
+	sprintf(ctrl_dir,"%s%s",node_dir,str);
+else
+	strcpy(ctrl_dir,str);
+truncsp(ctrl_dir);
+if(_fullpath(str,ctrl_dir,50))
+	strcpy(ctrl_dir,str);
+backslash(ctrl_dir);
+
+fgets(str,81,stream);			/* data dir */
+if(str[0]=='.')
+	sprintf(data_dir,"%s%s",node_dir,str);
+else
+	sprintf(data_dir,"%.40s",str);
+truncsp(data_dir);
+if(_fullpath(str,data_dir,50))
+	strcpy(data_dir,str);
+backslash(data_dir);
+
+fgets(str,81,stream);			/* total nodes */
+sys_nodes=atoi(str);
+fgets(str,81,stream);			/* current node */
+node_num=atoi(str);
+fgets(str,81,stream);			/* time left */
+timeleft=atoi(str);
+fgets(str,81,stream);			/* ANSI? (Yes, Mono, or No) */
+user_misc=0;
+if(str[0]=='Y')
+	user_misc|=(ANSI|COLOR);
+else if(str[0]=='M')
+	user_misc|=ANSI;
+fgets(str,81,stream);			/* screen lines */
+user_rows=atoi(str);
+fgets(str,81,stream);			/* credits */
+user_cdt=atol(str);
+fgets(str,81,stream);			/* level */
+user_level=atoi(str);
+fgets(str,81,stream);			/* was transfer level, left for compat. */
+fgets(str,81,stream);			/* birthdate */
+truncsp(str);
+sprintf(user_birth,"%.8s",str);
+fgets(str,81,stream);			/* sex */
+user_sex=str[0];
+fgets(str,81,stream);			/* user number */
+user_number=atoi(str);
+fgets(str,81,stream);			/* user phone number */
+sprintf(user_phone,"%.12s",str);
+truncsp(user_phone);
+fgets(str,81,stream);			/* com port (0 if local or no modem) */
+com_port=atoi(str);
+fgets(str,81,stream);			/* com (UART) irq */
+com_irq=atoi(str);
+fgets(str,81,stream);			/* com (UART) base address in hex */
+truncsp(str);
+com_base=(uint)ahtoul(str);
+fgets(str,81,stream);			/* com rate */
+com_rate=(ulong)atol(str);
+fgets(str,81,stream);			/* hardware flow control (Y/N) */
+if(toupper(str[0])=='Y')
+	mdm_misc|=MDM_FLOWCTRL;
+fgets(str,81,stream);			/* locked DTE rate (Y/N) */
+if(toupper(str[0])=='Y')
+	mdm_misc|=MDM_STAYHIGH;
+fgets(str,81,stream);			/* modem initialization string */
+sprintf(mdm_init,"%.63s",str);
+truncsp(mdm_init);
+fgets(str,81,stream);			/* modem special init string */
+sprintf(mdm_spec,"%.63s",str);
+truncsp(mdm_spec);
+fgets(str,81,stream);			/* modem terminal mode string */
+sprintf(mdm_term,"%.63s",str);
+truncsp(mdm_term);
+fgets(str,81,stream);			/* modem dial string */
+sprintf(mdm_dial,"%.63s",str);
+truncsp(mdm_dial);
+fgets(str,81,stream);			/* modem off-hook string */
+sprintf(mdm_offh,"%.63s",str);
+truncsp(mdm_offh);
+fgets(str,81,stream);			/* modem answer string */
+sprintf(mdm_answ,"%.63s",str);
+truncsp(mdm_answ);
+fgets(str,81,stream);			/* memory address of modem status register */
+msr=(uint far *)atol(str);
+if(!fgets(str,81,stream))		/* total number of external programs */
+	total_xtrns=0;
+else
+	total_xtrns=atoi(str);
+if(total_xtrns && (xtrn=(char **)MALLOC(sizeof(char *)*total_xtrns))==NULL) {
+	printf("Allocation error 1: %u\r\n",sizeof(char *)*total_xtrns);
+	exit(1); }
+for(i=0;i<total_xtrns;i++) {
+	fgets(str,81,stream);
+	truncsp(str);
+	if((xtrn[i]=(char *)MALLOC(strlen(str)+1))==NULL) {
+		printf("Allocation error 2 (%u): %u\r\n",i,strlen(str)+1);
+		exit(1); }
+	strcpy(xtrn[i],str); }
+fgets(str,81,stream);			/* user's main flags */
+sprintf(user_flags1,"%.26s",str);
+fgets(str,81,stream);			/* user's xfer flags */
+sprintf(user_flags2,"%.26s",str);
+fgets(str,81,stream);			/* user's exemptions */
+sprintf(user_exempt,"%.26s",str);
+fgets(str,81,stream);			/* user's restrictions */
+sprintf(user_rest,"%.26s",str);
+fgets(str,81,stream);			/* user's expiration date */
+truncsp(str);
+user_expire=ahtoul(str);
+str[0]=0;
+fgets(str,81,stream);			/* user's address */
+sprintf(user_address,"%.30s",str);
+truncsp(user_address);
+fgets(str,81,stream);			/* user's location (city, state) */
+sprintf(user_location,"%.30s",str);
+truncsp(user_location);
+fgets(str,81,stream);			/* user's zip/postal code */
+sprintf(user_zipcode,"%.10s",str);
+truncsp(user_zipcode);
+str[0]=0;
+fgets(str,81,stream);
+sprintf(user_flags3,"%.26s",str);
+fgets(str,81,stream);
+sprintf(user_flags4,"%.26s",str);
+if(fgets(str,81,stream))		/* Time-slice API type */
+	mswtyp=ahtoul(str);
+str[0]=0;
+fgets(str,81,stream);
+truncsp(str);
+sprintf(user_realname,"%.25s",str);
+str[0]=0;
+fgets(str,81,stream);
+user_dce=atol(str);
+
+str[0]=0;
+fgets(str,81,stream);			/* exec dir */
+if(!str[0])
+	sprintf(exec_dir,"%s..\\EXEC\\",ctrl_dir);
+else {
+	if(str[0]=='.')
+		sprintf(exec_dir,"%s%s",node_dir,str);
+	else
+		sprintf(exec_dir,"%.50s",str); }
+truncsp(exec_dir);
+if(_fullpath(str,exec_dir,50))
+	strcpy(exec_dir,str);
+backslash(exec_dir);
+
+str[0]=0;
+fgets(str,81,stream);			/* text dir */
+if(!str[0])
+	sprintf(text_dir,"%s..\\TEXT\\",ctrl_dir);
+else {
+	if(str[0]=='.')
+		sprintf(text_dir,"%s%s",node_dir,str);
+	else
+		sprintf(text_dir,"%.50s",str); }
+truncsp(text_dir);
+if(_fullpath(str,text_dir,50))
+	strcpy(text_dir,str);
+backslash(text_dir);
+
+str[0]=0;
+fgets(str,81,stream);			/* temp dir */
+if(!str[0])
+	sprintf(temp_dir,"%sTEMP\\",node_dir);
+else {
+	if(str[0]!='\\' && str[1]!=':')
+		sprintf(temp_dir,"%s%s",node_dir,str);
+	else
+		sprintf(temp_dir,"%.50s",str); }
+truncsp(temp_dir);
+if(_fullpath(str,temp_dir,50))
+	strcpy(temp_dir,str);
+backslash(temp_dir);
+
+str[0]=0;
+fgets(str,81,stream);
+sprintf(sys_id,"%.8s",str);
+
+str[0]=0;
+fgets(str,81,stream);
+truncsp(str);
+if(str[0])
+	node_misc=(uint)ahtoul(str);
+else
+	node_misc=NM_LOWPRIO;
+
+fclose(stream);
+
+sprintf(str,"%sINTRSBBS.DAT",node_dir);     /* Shrank to run! */
+if(fexist(str)) {
+	if((stream=fopen(str,"rt"))==NULL) {
+		printf("Can't open %s\n",str);
+		exit(1); }
+	fgets(tmp,81,stream);					/* so get MSR address from file */
+	msr=(uint far *)atol(tmp);
+	fclose(stream);
+	remove(str); }
+
+starttime=time(NULL);			/* initialize start time stamp */
+wordwrap[0]=0;					/* set wordwrap to null */
+attr(LIGHTGRAY);				/* initialize color and curatr to plain */
+mnehigh=LIGHTGRAY|HIGH; 		/* mnemonics highlight color */
+mnelow=GREEN;					/* mnemonics normal text color */
+sec_warn=180;					/* seconds till inactivity warning */
+sec_timeout=300;				/* seconds till inactivity timeout */
+tos=lncntr=0;					/* init topofscreen and linecounter to 0 */
+lastnodemsg=0;					/* Last node to send message to */
+aborted=0;                      /* Ctrl-C hit flag */
+sysop_level=90; 				/* Minimum level to be considered sysop */
+timeleft_warn=0;				/* Running out of time warning */
+
+sprintf(str,"%s%s",ctrl_dir,"NODE.DAB");
+if((nodefile=sopen(str,O_BINARY|O_RDWR,SH_DENYNO))==-1) {
+	bprintf("\r\n\7Error opening %s\r\n",str);
+	exit(1); }
+
+sprintf(str,"%sUSER\\NAME.DAT",data_dir);
+if((i=nopen(str,O_RDONLY))==-1) {
+	printf("\r\n\7Error opening %s\r\n",str);
+	exit(1); }
+memset(str,0,30);
+read(i,str,26);
+close(i);
+if(str[25]==CR) 	/* Version 1b */
+	name_len=25;
+else				/* Version 1a */
+	name_len=30;
+}
+
+/****************************************************************************/
+/* Automatic RIP & WIP terminal detection function. Sets RIP and WIP bits	*/
+/* in user_misc variable. Must be called AFTER initdat(), not before.		*/
+/****************************************************************************/
+void get_term(void)
+{
+	char str[128],ch;
+	int i;
+
+bputs("\r\x1b[!_\x1b[0t_\r        \r");
+mswait(500);
+for(i=0;i<120;i++) {
+	ch=inkey(0);
+	if(!ch)
+		break;
+	mswait(1);
+	str[i]=ch; }
+str[i]=0;
+if(strstr(str,"RIPSCRIP"))
+	user_misc|=RIP;
+if(strstr(str,"DC-TERM"))
+	user_misc|=WIP;
+}
+
+/****************************************************************************/
+/* Truncates white-space chars off end of 'str' and terminates at first tab */
+/****************************************************************************/
+void truncsp(uchar *str)
+{
+	char c;
+
+str[strcspn(str,"\t")]=0;
+c=strlen(str);
+while(c && (uchar)str[c-1]<=SP) c--;
+str[c]=0;
+}
+
+/****************************************************************************/
+/* Puts a backslash on path strings 										*/
+/****************************************************************************/
+void backslash(char *str)
+{
+    int i;
+
+i=strlen(str);
+if(i && str[i-1]!='\\') {
+    str[i]='\\'; str[i+1]=0; }
+}
+
+
+/****************************************************************************/
+/* Checks the amount of time inside the external program against the amount */
+/* of time the user had left online before running the external program and */
+/* prints a message and exits the program if time has run out.				*/
+/****************************************************************************/
+void checktimeleft(void)
+{
+if(!SYSOP && !strchr(user_exempt,'T') && time(NULL)-starttime>timeleft) {
+	bputs("\1_\n\1r\1hTime's up.\n");
+	exit(0);  }
+}
+
+/****************************************************************************/
+/* Prints a file remotely and locally, interpreting ^A sequences.			*/
+/* 'str' is the path of the file to print                                   */
+/****************************************************************************/
+void printfile(char *str)
+{
+	char *buf;
+	int file;
+	ulong length;
+
+strupr(str);
+if(!tos)
+	CRLF;
+if((file=nopen(str,O_RDONLY))==-1) {
+	bprintf("File not Found: %s\r\n",str);
+	return; }
+length=filelength(file);
+if((buf=MALLOC(length+1L))==NULL) {
+	close(file);
+	bprintf("\7\r\nPRINTFILE: Error allocating %lu bytes of memory for %s.\r\n"
+		,length+1L,str);
+	return; }
+buf[read(file,buf,length)]=0;
+close(file);
+bputs(buf);
+aborted=0;
+FREE(buf);
+}
+
+/****************************************************************************/
+/* Returns a char pointer to the name of the user that corresponds to		*/
+/* usernumber. Takes value directly from database.							*/
+/****************************************************************************/
+char *username(uint usernumber)
+{
+	static	char name[26];
+	char	str[128];
+	int 	i,file;
+
+strcpy(name,"UNKNOWN USER");
+if(!data_dir[0])
+	return(name);
+if(!usernumber) {
+	bputs("\7username: called with zero usernumber\r\n");
+	return(name); }
+sprintf(str,"%sUSER\\NAME.DAT",data_dir);
+if((file=nopen(str,O_RDONLY))==-1) {
+	bprintf("\7username: couldn't open %s\r\n",str);
+	return(name); }
+if(filelength(file)<(long)(usernumber-1)*((long)name_len+2L)) {
+	close(file);
+	return(name); }
+lseek(file,(long)(usernumber-1)*((long)name_len+2L),SEEK_SET);
+read(file,name,25);
+close(file);
+for(i=0;i<25;i++)
+	if(name[i]==3)
+		break;
+name[i]=0;
+if(!name[0])
+	strcpy(name,"DELETED USER");
+return(name);
+}
+
+/****************************************************************************/
+/* Returns the number of the user 'username' from the NAME.DAT file.        */
+/* If the username is not found, the function returns 0.					*/
+/****************************************************************************/
+uint usernumber(char *username)
+{
+	char str[128];
+	int i,file;
+	FILE *stream;
+
+if(!data_dir[0])
+	return(0);
+sprintf(str,"%sUSER\\NAME.DAT",data_dir);
+if((file=nopen(str,O_RDONLY))==-1 || (stream=fdopen(file,"rb"))==NULL) {
+	if(file!=-1)
+		close(file);
+	bprintf("\7usernumber: couldn't open %s\r\n",str);
+	return(0); }
+for(i=1;!feof(stream);i++) {
+	if(!fread(str,27,1,stream))
+		break;
+	str[25]=0;
+	truncsp(str);	/* chop of trailing EOTs and spaces */
+	if(!stricmp(str,username)) {
+		fclose(stream);
+		return(i); } }
+fclose(stream);
+return(0);
+}
+
+
+/****************************************************************************/
+/* Checks the disk drive for the existance of a file. Returns 1 if it 		*/
+/* exists, 0 if it doesn't.													*/
+/* Called from upload														*/
+/****************************************************************************/
+char fexist(char *filespec)
+{
+#ifdef __SC__	/* Symantec */
+if(findfirst(filespec,0)==NULL)
+	return(0);
+return(1);
+#else			/* Not Symantec */
+	struct ffblk f;
+
+if(findfirst(filespec,&f,0)==NULL)
+	return(1);
+return(0);
+#endif			/* !__SC__ */
+}
+
+/****************************************************************************/
+/* Returns the length of the first file found that matches 'filespec'       */
+/* -1 if the file doesn't exist.                                            */
+/****************************************************************************/
+long flength(char *filespec)
+{
+#ifdef __SC__		/* Symantec */
+	struct FILE *f;
+
+if((f=findfirst(filespec,0))==NULL)
+	return(-1);
+return(f->size);
+
+#else				/* Not Symantec */
+
+	struct ffblk f;
+
+if(findfirst(filespec,&f,0)==NULL)
+#ifdef __TURBOC__	/* Borland */
+	return(f.ff_fsize);
+#else				/* Other (Watcom) */
+	return(f.size);
+#endif
+return(-1L);
+#endif				/* !__SC__ */
+}
+
+/****************************************************************************/
+/* Returns in 'string' a character representation of the number in l with   */
+/* commas. Maximum value of l is 4 gigabytes.								*/
+/****************************************************************************/
+char *ultoac(ulong l, char *string)
+{
+	char str[81];
+	char i,j,k;
+
+ultoa(l,str,10);
+i=strlen(str)-1;
+j=i/3+1+i;
+string[j--]=0;
+for(k=1;i>-1;k++) {
+	string[j--]=str[i--];
+	if(j>0 && !(k%3))
+		string[j--]=','; }
+return(string);
+}
+
+/****************************************************************************/
+/* Converts an ASCII Hex string into a ulong								*/
+/****************************************************************************/
+ulong ahtoul(char *str)
+{
+	ulong l,val=0;
+
+while((l=(*str++)|0x20)!=0x20)
+	val=(l&0xf)+(l>>6&1)*9+val*16;
+return(val);
+}
+
+/****************************************************************************/
+/* Reads the data for node number 'number' into the structure 'node'        */
+/* from NODE.DAB															*/
+/* if lockit is non-zero, locks this node's record. putnodedat() unlocks it */
+/****************************************************************************/
+void getnodedat(uchar number, node_t *node, char lockit)
+{
+	char str[256];
+	int count=0;
+
+if(nodefile<0)
+	return;
+number--;	/* make zero based */
+while(count<LOOP_NODEDAB) {
+	lseek(nodefile,(long)number*sizeof(node_t),SEEK_SET);
+	if(lockit
+		&& lock(nodefile,(long)number*sizeof(node_t),sizeof(node_t))==-1) {
+		count++;
+		continue; }
+	if(read(nodefile,node,sizeof(node_t))==sizeof(node_t))
+		break;
+	count++; }
+if(count==LOOP_NODEDAB)
+	bprintf("\7Error unlocking and reading NODE.DAB\r\n");
+}
+
+/****************************************************************************/
+/* Write the data from the structure 'node' into NODE.DAB  					*/
+/* getnodedat(num,&node,1); must have been called before calling this func  */
+/*          NOTE: ------^   the indicates the node record has been locked   */
+/****************************************************************************/
+void putnodedat(uchar number, node_t node)
+{
+	char str[256];
+	int count;
+
+if(nodefile<0)
+	return;
+number--;	/* make zero based */
+lseek(nodefile,(long)number*sizeof(node_t),SEEK_SET);
+if(write(nodefile,&node,sizeof(node_t))!=sizeof(node_t)) {
+	unlock(nodefile,(long)number*sizeof(node_t),sizeof(node_t));
+	bprintf("\7Error writing NODE.DAB for node %u\r\n",number+1);
+	return; }
+unlock(nodefile,(long)number*sizeof(node_t),sizeof(node_t));
+}
+
+/****************************************************************************/
+/* Checks for messages waiting for this node or interruption.				*/
+/****************************************************************************/
+void nodesync(void)
+{
+	node_t node;
+
+if(!ctrl_dir[0])
+	return;
+getnodedat(node_num,&node,0);
+
+if(node.misc&NODE_MSGW)
+	getsmsg(user_number);		/* getsmsg clears MSGW flag */
+
+if(node.misc&NODE_NMSG) 		/* getnmsg clears NMSG flag */
+	getnmsg();
+
+if(node.misc&NODE_INTR)
+	exit(0);
+
+}
+
+/****************************************************************************/
+/* Displays the information for node number 'number' contained in 'node'    */
+/****************************************************************************/
+void printnodedat(uchar number, node_t node)
+{
+	char hour,mer[3],tmp[256];
+	int i;
+
+attr(LIGHTGRAY|HIGH);
+bprintf("Node %2d: ",number);
+attr(GREEN);
+switch(node.status) {
+    case NODE_WFC:
+        bputs("Waiting for call");
+        break;
+    case NODE_OFFLINE:
+        bputs("Offline");
+        break;
+    case NODE_NETTING:
+        bputs("Networking");
+        break;
+    case NODE_LOGON:
+        bputs("At logon prompt");
+        break;
+    case NODE_EVENT_WAITING:
+        bputs("Waiting for all nodes to become inactive");
+        break;
+    case NODE_EVENT_LIMBO:
+        bprintf("Waiting for node %d to finish external event",node.aux);
+        break;
+    case NODE_EVENT_RUNNING:
+        bputs("Running external event");
+        break;
+    case NODE_NEWUSER:
+		attr(GREEN|HIGH);
+        bputs("New user");
+		attr(GREEN);
+        bputs(" applying for access ");
+        if(!node.connection)
+            bputs("Locally");
+        else
+            bprintf("at %ubps",node.connection);
+        break;
+    case NODE_QUIET:
+        if(!SYSOP) {
+            bputs("Waiting for call");
+            break; }
+    case NODE_INUSE:
+		attr(GREEN|HIGH);
+        if(node.misc&NODE_ANON && !SYSOP)
+            bputs("UNKNOWN USER");
+        else
+			bputs(username(node.useron));
+		attr(GREEN);
+        bputs(" ");
+        switch(node.action) {
+            case NODE_MAIN:
+                bputs("at main menu");
+                break;
+            case NODE_RMSG:
+                bputs("reading messages");
+                break;
+            case NODE_RMAL:
+                bputs("reading mail");
+                break;
+            case NODE_RSML:
+                bputs("reading sent mail");
+                break;
+            case NODE_RTXT:
+                bputs("reading text files");
+                break;
+            case NODE_PMSG:
+                bputs("posting message");
+                break;
+            case NODE_SMAL:
+                bputs("sending mail");
+                break;
+            case NODE_AMSG:
+                bputs("posting auto-message");
+                break;
+            case NODE_XTRN:
+                if(!node.aux)
+                    bputs("at external program menu");
+                else {
+					bputs("running ");
+					i=node.aux-1;
+					if(i>=total_xtrns || !xtrn[i][0])
+						bputs("external program");
+					else
+						bputs(xtrn[i]); }
+                break;
+            case NODE_DFLT:
+                bputs("changing defaults");
+                break;
+            case NODE_XFER:
+                bputs("at transfer menu");
+                break;
+			case NODE_RFSD:
+				bprintf("retrieving from device #%d",node.aux);
+                break;
+            case NODE_DLNG:
+                bprintf("downloading");
+                break;
+            case NODE_ULNG:
+                bputs("uploading");
+                break;
+            case NODE_BXFR:
+				bputs("transferring bidirectional");
+                break;
+            case NODE_LFIL:
+                bputs("listing files");
+                break;
+            case NODE_LOGN:
+                bputs("logging on");
+                break;
+            case NODE_LCHT:
+                bprintf("in local chat with %s",sys_op);
+                break;
+            case NODE_MCHT:
+                if(node.aux) {
+                    bprintf("in multinode chat channel %d",node.aux&0xff);
+					if(node.aux&0x1f00)  /* password */
+						outchar('*'); }
+                else
+                    bputs("in multinode global chat channel");
+                break;
+			case NODE_PAGE:
+				bprintf("paging node %u for private chat",node.aux);
+				break;
+			case NODE_PCHT:
+				bprintf("in private chat with node %u",node.aux);
+				break;
+            case NODE_GCHT:
+                bprintf("chatting with %s",sys_guru);
+                break;
+            case NODE_CHAT:
+                bputs("in chat section");
+                break;
+            case NODE_TQWK:
+                bputs("transferring QWK packet");
+                break;
+            case NODE_SYSP:
+                bputs("performing sysop activities");
+                break;
+            default:
+                bputs(itoa(node.action,tmp,10));
+                break;  }
+        if(!node.connection)
+            bputs(" locally");
+        else
+            bprintf(" at %ubps",node.connection);
+        if(node.action==NODE_DLNG) {
+            if((node.aux/60)>12) {
+                hour=(node.aux/60)-12;
+                strcpy(mer,"pm"); }
+            else {
+                if((node.aux/60)==0)    /* 12 midnite */
+                    hour=12;
+                else hour=node.aux/60;
+                strcpy(mer,"am"); }
+            bprintf(" ETA %02d:%02d %s"
+				,hour,node.aux%60,mer); }
+        break; }
+i=NODE_LOCK;
+if(node.status==NODE_INUSE || SYSOP)
+	i|=NODE_POFF|NODE_AOFF|NODE_MSGW|NODE_NMSG;
+if(node.misc&i) {
+    bputs(" (");
+	if(node.misc&(i&NODE_AOFF))
+		outchar('A');
+    if(node.misc&NODE_LOCK)
+		outchar('L');
+	if(node.misc&(i&(NODE_MSGW|NODE_NMSG)))
+		outchar('M');
+	if(node.misc&(i&NODE_POFF))
+		outchar('P');
+	outchar(')'); }
+if(SYSOP && ((node.misc
+    &(NODE_ANON|NODE_UDAT|NODE_INTR|NODE_RRUN|NODE_EVENT|NODE_DOWN))
+    || node.status==NODE_QUIET)) {
+    bputs(" [");
+    if(node.misc&NODE_ANON)
+		outchar('A');
+    if(node.misc&NODE_INTR)
+		outchar('I');
+    if(node.misc&NODE_RRUN)
+		outchar('R');
+    if(node.misc&NODE_UDAT)
+		outchar('U');
+    if(node.status==NODE_QUIET)
+		outchar('Q');
+    if(node.misc&NODE_EVENT)
+		outchar('E');
+    if(node.misc&NODE_DOWN)
+		outchar('D');
+	outchar(']'); }
+if(node.errors && SYSOP) {
+	attr(RED|HIGH|BLINK);
+    bprintf(" %d error%c",node.errors, node.errors>1 ? 's' : '\0' ); }
+CRLF;
+}
+
+/****************************************************************************/
+/* Prints short messages waiting for 'usernumber', if any...				*/
+/* then deletes them.														*/
+/****************************************************************************/
+void getsmsg(int usernumber)
+{
+	char str[256], *buf;
+	int file;
+	long length;
+	node_t node;
+
+if(!data_dir[0])
+	return;
+sprintf(str,"%sMSGS\\%4.4u.MSG",data_dir,usernumber);
+if(flength(str)<1L) {
+	return; }
+if((file=nopen(str,O_RDWR))==-1) {
+	bprintf("\7Error opening %s for read/write access\r\n",str);
+	return; }
+length=filelength(file);
+if((buf=MALLOC(length+1))==NULL) {
+	close(file);
+	bprintf("\7Error allocating %u bytes of memory for %s\r\n",length+1,str);
+	return; }
+if(read(file,buf,length)!=length) {
+	close(file);
+	FREE(buf);
+	bprintf("\7Error reading %u bytes from %s\r\n",length,str);
+	return; }
+chsize(file,0L);
+close(file);
+buf[length]=0;
+getnodedat(node_num,&node,0);
+if(node.action==NODE_MAIN || node.action==NODE_XFER) {
+	CRLF; }
+if(node.misc&NODE_MSGW) {
+	getnodedat(node_num,&node,1);
+	node.misc&=~NODE_MSGW;
+	putnodedat(node_num,node); }
+bputs(buf);
+FREE(buf);
+}
+
+/****************************************************************************/
+/* Creates a short message for 'usernumber' than contains 'strin'			*/
+/****************************************************************************/
+void putsmsg(int usernumber, char *strin)
+{
+	char str[256];
+	int file,i;
+    node_t node;
+
+if(!data_dir[0])
+	return;
+sprintf(str,"%sMSGS\\%4.4u.MSG",data_dir,usernumber);
+if((file=nopen(str,O_WRONLY|O_CREAT|O_APPEND))==-1) {
+	bprintf("\7Error opening/creating %s for creat/append access\r\n",str);
+	return; }
+i=strlen(strin);
+if(write(file,strin,i)!=i) {
+	close(file);
+	bprintf("\7Error writing %u bytes to %s\r\n",i,str);
+	return; }
+close(file);
+for(i=1;i<=sys_nodes;i++) {		/* flag node if user on that msg waiting */
+	getnodedat(i,&node,0);
+	if(node.useron==usernumber
+		&& (node.status==NODE_INUSE || node.status==NODE_QUIET)
+		&& !(node.misc&NODE_MSGW)) {
+		getnodedat(i,&node,1);
+		node.misc|=NODE_MSGW;
+        putnodedat(i,node); } }
+}
+
+/****************************************************************************/
+/* Prints short messages waiting for this node, if any...					*/
+/****************************************************************************/
+void getnmsg(void)
+{
+	char str[256], *buf;
+	int file;
+	long length;
+	node_t thisnode;
+
+if(!data_dir[0])
+	return;
+getnodedat(node_num,&thisnode,1);
+thisnode.misc&=~NODE_NMSG;			/* clear the NMSG flag */
+putnodedat(node_num,thisnode);
+
+sprintf(str,"%sMSGS\\N%3.3u.MSG",data_dir,node_num);
+if(flength(str)<1L) {
+	return; }
+if((file=nopen(str,O_RDWR))==-1) {
+	printf("Couldn't open %s for read/write\r\n",str);
+	return; }
+length=filelength(file);
+if((buf=MALLOC(length+1))==NULL) {
+	close(file);
+	printf("Couldn't allocate %lu bytes for %s\r\n",length+1,str);
+	return; }
+if(read(file,buf,length)!=length) {
+	close(file);
+	FREE(buf);
+	printf("Couldn't read %lu bytes from %s\r\n",length,str);
+	return; }
+chsize(file,0L);
+close(file);
+buf[length]=0;
+
+bputs(buf);
+FREE(buf);
+}
+
+/****************************************************************************/
+/* Creates a short message for node 'num' than contains 'strin'             */
+/****************************************************************************/
+void putnmsg(int num, char *strin)
+{
+	char str[256];
+	int file,i;
+    node_t node;
+
+if(!data_dir[0])
+	return;
+sprintf(str,"%sMSGS\\N%3.3u.MSG",data_dir,num);
+if((file=nopen(str,O_WRONLY|O_CREAT|O_APPEND))==-1) {
+	printf("Couldn't open %s for append\r\n",str);
+	return; }
+i=strlen(strin);
+if(write(file,strin,i)!=i) {
+	close(file);
+	printf("Error writing %u bytes to %s\r\n",i,str);
+	return; }
+close(file);
+getnodedat(num,&node,0);
+if((node.status==NODE_INUSE || node.status==NODE_QUIET)
+	&& !(node.misc&NODE_NMSG)) {
+	getnodedat(num,&node,1);
+	node.misc|=NODE_NMSG;
+	putnodedat(num,node); }
+}
+
+/****************************************************************************/
+/* This function lists users that are online.								*/
+/* If listself is true, it will list the current node.						*/
+/* Returns number of active nodes (not including current node). 			*/
+/****************************************************************************/
+int whos_online(char listself)
+{
+	int i,j;
+	node_t node;
+
+if(!ctrl_dir[0])
+	return(0);
+CRLF;
+for(j=0,i=1;i<=sys_nodes;i++) {
+	getnodedat(i,&node,0);
+	if(i==node_num) {
+		if(listself)
+			printnodedat(i,node);
+		continue; }
+	if(node.status==NODE_INUSE || (SYSOP && node.status==NODE_QUIET)) {
+		printnodedat(i,node);
+		if(!lastnodemsg)
+			lastnodemsg=i;
+		j++; } }
+if(!j)
+	bputs("\1nNo other active nodes.\r\n");
+return(j);
+}
+
+/****************************************************************************/
+/* Sending single line messages between nodes                               */
+/****************************************************************************/
+void nodemsg(void)
+{
+	char	str[256],line[256],buf[512];
+	int 	i,j;
+	node_t	thisnode;
+	node_t 	node;
+
+if(!ctrl_dir[0])
+	return;
+if(strchr(user_rest,'C')) {
+	bputs("You cannot send messages.\r\n");
+	return; }
+getnodedat(node_num,&thisnode,0);
+wordwrap[0]=0;
+if(lastnodemsg) {
+	getnodedat(lastnodemsg,&node,0);
+	if(node.status!=NODE_INUSE)
+		lastnodemsg=0; }
+if(!whos_online(0))
+	return;
+bprintf("\r\nngNumber of node to send message to, whAngll, "
+	"or whQnguit [%u]: wh",lastnodemsg);
+i=getkeys("QA",sys_nodes);
+if(i==-1)
+	return;
+if(i&0x8000 || !i) {
+	if(!i)
+		i=lastnodemsg;
+	else {
+		i^=0x8000;
+		lastnodemsg=i; }
+	if(!i || i>sys_nodes)
+		return;
+	getnodedat(i,&node,0);
+	if(node.status!=NODE_INUSE && !SYSOP)
+		bprintf("\r\n_whNode %d is not in use.\r\n",i);
+	else if(i==node_num)
+		bputs("\r\nThere's no need to send a message to yourself.\r\n");
+	else if(node.misc&NODE_POFF && !SYSOP)
+		bprintf("\r\nrhiDon't bug %s.n\r\n"
+			,node.misc&NODE_ANON ? "UNKNOWN USER"
+			: username(node.useron));
+	else {
+		bputs("_yhMessage: ");
+		if(!getstr(line,70,K_LINE))
+			return;
+		sprintf(buf
+			,"\7_whNode %2d: g%sng sent you a message:\r\nwh4%sn\r\n"
+			,node_num
+			,thisnode.misc&NODE_ANON ? "UNKNOWN USER" : user_name,line);
+		putnmsg(i,buf); } }
+else if(i=='A') {
+	bputs("_yhMessage: ");
+	if(!getstr(line,70,K_LINE))
+		return;
+	sprintf(buf
+		,"\7_whNode %2d: g%sng sent all nodes a message:\r\n"
+			"wh4%sn\r\n"
+		,node_num
+		,thisnode.misc&NODE_ANON ? "UNKNOWN USER" : user_name,line);
+	for(i=1;i<=sys_nodes;i++) {
+		if(i==node_num)
+			continue;
+		getnodedat(i,&node,0);
+		if((node.status==NODE_INUSE || (SYSOP && node.status==NODE_QUIET))
+			&& (SYSOP || !(node.misc&NODE_POFF)))
+			putnmsg(i,buf); } }
+
+}
+
+/****************************************************************************/
+/* Puts a character into the input buffer									*/
+/****************************************************************************/
+void ungetkey(char ch)
+{
+
+keybuf[keybuftop++]=ch;
+if(keybuftop==KEY_BUFSIZE)
+	keybuftop=0;
+}
+
+#ifdef __SC__					/* Missing from Symantec RTL */
+void clrscr(void) 
+{
+        asm
+        {       mov ah,8        /*function # for "Get char with attr*/
+                xor bh,bh       /*page 0*/
+                int 10h         /*Call interrupt 10h (video)*/
+                
+                mov bh,ah       /*set "set attr" to "current attr"*/
+                mov ah,6        /*function # for "Scroll Window Up"*/
+                xor cx,cx       /*set upper row & column (0,0)*/
+                xor al,al       /*set "# lines to scroll" to 0*/
+                mov dh,119      /*set lowqer colum*/
+                int 10h         /*Call interrupt 10h*/
+                
+                mov ah,2        /*function # for "Set Cursor Position"*/
+                xor bh,bh       /*set page to 0*/
+                xor dx,dx       /*set row & colum to 0 (upper left)*/
+                int 10h         /*Call interrupt 10h*/
+        }
+        return;
+}
+
+short wherey(void)
+{
+        struct disp_t rc;
+        return(rc.cursorcol);
+}               
+#endif  /* __SC__ */
+
diff --git a/xtrn/sdk/xsdk.h b/xtrn/sdk/xsdk.h
new file mode 100644
index 0000000000000000000000000000000000000000..592d24c1cad9e13f2224b3c89ef1348282febbf2
--- /dev/null
+++ b/xtrn/sdk/xsdk.h
@@ -0,0 +1,273 @@
+/* XSDK.H */
+
+#ifndef _XSDK_H
+#define _XSDK_H
+
+/****************************************************************************/
+/*			Synchronet External Program Software Development Kit			*/
+/*							1993 Digital Dynamics							*/
+/****************************************************************************/
+
+/****************************************************************************/
+/* This source code file is public domain and may be modified, compiled 	*/
+/* distributed, or used in any way, in part or whole for any purposes		*/
+/* without the consent or notification of Digital Dynamics. 				*/
+/*																			*/
+/* We only request that you display to the user, at some point, in your 	*/
+/* program the character "XSDK" and the version number.                     */
+/* example: bprintf("XSDK v%s",xsdk_ver);                                   */
+/****************************************************************************/
+
+/****************************************************************************/
+/* The source code for two external programs developed by Digital Dynamics	*/
+/* using XSDK (Synchronet Blackjack [SBJ] and Synchronet BBS List [SBL])	*/
+/* are available to the public domain as examples of how to implement the	*/
+/* functions and variables included in this software development kit.		*/
+/****************************************************************************/
+
+
+/****************************************************/
+/* For use with Borland/Turbo C and C++ compilers.	*/
+/* Tabstop set to 4.								*/
+/****************************************************/
+
+/*********************************************/
+/* Standard Borland/Turbo C/C++ Header Files */
+/*********************************************/
+#include <io.h>
+#include <dos.h>
+#include <bios.h>
+#include <time.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <share.h>
+#include <conio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <malloc.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifdef __TURBOC__
+    #include <dir.h>
+#endif
+
+#ifdef __WATCOMC__
+	#include <graph.h>
+#endif
+
+#define GLOBAL extern	/* turns XSDKVAR.C into a header file */
+#include "xsdkvars.c"
+
+#ifdef __cplusplus
+	extern "C" {
+#endif
+
+extern char *xsdk_ver;	/* XSDK version number */
+extern int mswtyp;		/* MSwait type */
+
+/***********************/
+/* Function Prototypes */
+/***********************/
+
+/* Initialize Data
+	- Reads XTRN.DAT and initializes variables */
+void initdata(void);
+
+/* Get Terminal Type
+	- Detects RIP and WIP terminal compatibility */
+void get_term(void);
+
+/* BBS Print String
+	- Displays a string locally and remotely (with Ctrl-A codes) */
+void bputs(char *str);
+
+/* Raw Print String
+	- Oututs a string locally and remotely (verbatim) */
+void rputs(char *str);
+
+/* BBS Print Formatted
+	- Displays a formatted string locally and remotely (with Ctrl-A codes)
+	- printf() equivalent */
+int  bprintf(char *fmt, ...);
+
+/* Raw Print Formated
+	- Displays a formatted string locally and remotely (verbatim)
+	- printf() equivalent */
+int  rprintf(char *fmt, ...);
+
+/* BBS String Length
+	- Returns length of string, excluding Ctrl-A codes */
+int  bstrlen(uchar *str);
+
+/* Output character
+	- Displays a single character */
+void outchar(char ch);
+
+/* Mnemonics
+	- Display a string expanding ~letter combinations to command keys
+	- Automatically colorizes mnemonic letters or places them in parenthesis
+	  for non-ANSI users */
+void mnemonics(char *str);
+
+/* Pause prompt
+	- Displays [Hit a key] and waits for the user to hit a key */
+void pause(void);
+
+/* Yes/no Question
+	- Displays a string with (Y/n) ? appended and waits for the user to hit
+	  'Y', 'N' or enter
+	- Returns 1 if the user hit 'Y' or enter
+	- Automatic colorization */
+char yesno(char *str);
+
+/* No/yes Question
+	- Displays a string with (y/N) ? appended and waits for the user to hit
+	  'Y', 'N' or enter
+	- Returns 1 if the user hit 'N' or enter */
+char noyes(char *str);
+
+/* Inbound Keystroke
+	- If the local or remote user has struck a key, this function returns the
+	  key, otherwise it returns 0
+	- Does not wait for a keystroke */
+char inkey(int mode);
+
+/* Get a Key
+	- Waits for the local or remote user to hit a valid key
+	- See K_* constants in XSDKDEFS.H for possible values of mode */
+char getkey(int mode);
+
+/* Get One of these Keys or a Number
+	- Waits for the user to hit a valid key or if max is non-zero, then enter
+	  a number not greater than max
+	- Hot-keyed input, automatically displays the struck key (in upper case)
+	  followed by CRLF
+	- If the user entered a number, the number is ORed with 0x8000 and returned
+	  you must XOR (or not) this bit to get the correct number */
+int  getkeys(char *str, int max);
+
+/* Get a Number
+	- Waits for the user to enter a number from 0 to max, 'Q' or ENTER
+	- Returns -1 if the user hit 'Q' or Ctrl-C
+	- Returns 0 if the user just hit ENTER */
+int  getnum(int max);
+
+/* Change Attribute
+	- Sends ANSI escape sequences (if user supports ANSI) to change color
+	- Valid color bits are defined in INCLUDE\CONIO.H */
+void attr(char atr);
+
+/* Clear Screen
+	- Clears local and remote screen (using ANSI if appropriate)
+	- Automatically pauses before clearing if lncntr is >1 */
+void cls(void);
+
+/* Process Ctrl-A Code
+	- Changes color or performs special Ctrl-A function */
+void ctrl_a(char x);
+
+/* Network Open
+	- Opens a file in DENYNONE or DENYWRITE mode, automatically retrying */
+int  nopen(char *str, int access);
+
+/* Truncate Space
+	- Removes white space characters from the end of a string */
+void truncsp(uchar *str);
+
+/* Adds Backslash
+	- Adds backslash to end of string if it doesn't exist */
+void backslash(char *str);
+
+/* Check Time Left
+	- Checks the amount of time the user has left and sets the timeleft
+	  variable
+	- Automatically exits if user runs out of time */
+void checktimeleft(void);
+
+/* Print File
+	- Displays contents of file (expanding Ctrl-A characters if appropriate) */
+void printfile(char *str);
+
+/* Get String
+	- Waits for the user to enter a string
+	- maxlen is the maximum length of the string
+	- See K_* constants in XSDKDEFS.H for possible values of mode */
+int  getstr(char *str, int maxlen, int mode);
+
+/* Redraw String
+	- Redisplays a string, mainly called by getstr() */
+void redrwstr(char *strin, int i, int l, char mode);
+
+/* Strip Invalid Ctrl-A codes */
+char stripattr(char *strin);
+
+/* Returns the name of the user number passed */
+char *username(uint usernumber);
+
+/* Returns the number of the user name passed */
+uint usernumber(char *username);
+
+/* Returns 1 if the file exists, 0 otherwise */
+char fexist(char *filespec);
+
+/* Returns the length of the file */
+long flength(char *filespec);
+
+/* Convert unsigned long to an ASCII string with commas */
+char *ultoac(ulong l, char *string);
+
+/* Convert an ASCII string of hex digits into an unsigned long */
+ulong ahtoul(char *str);
+
+/* Display status of node */
+void printnodedat(uchar number, node_t node);
+
+/* Checks to see if this node has been interrupted or any messages waiting */
+void nodesync(void);
+
+/* Writes the node information to disk */
+void putnodedat(uchar number, node_t node);
+
+/* Reads the node information from disk */
+void getnodedat(uchar number, node_t *node, char lockit);
+
+/* Writes a short message (telegram) for specified user number */
+void putsmsg(int usernumber, char *strin);
+
+/* Reads and displays short message for specified user number */
+void getsmsg(int usernumber);
+
+/* Writes a node message for specified node */
+void putnmsg(int num, char *strin);
+
+/* Reads and displays node message for current node */
+void getnmsg(void);
+
+/* Displays users online */
+int whos_online(char listself);
+
+/* Send a node-message (ctrl-P) */
+void nodemsg(void);
+
+/* Put a key into the keyboard buffer */
+void ungetkey(char ch);
+
+/* Check to see if the user has hung-up */
+void checkline(void);
+
+/* Wait a specific number of milliseconds */
+void mswait(int ms);
+
+/* Display a line (with ctrl-A codes) centered on the screen */
+void center(char *str);
+
+#ifdef __cplusplus
+	}
+#endif
+
+
+#endif	/* Don't add anything after this #endif */
diff --git a/xtrn/sdk/xsdkdefs.h b/xtrn/sdk/xsdkdefs.h
new file mode 100644
index 0000000000000000000000000000000000000000..9f0cb632d2920e9e8f009472d26d4f610fc47d62
--- /dev/null
+++ b/xtrn/sdk/xsdkdefs.h
@@ -0,0 +1,249 @@
+/* XSDKDEFS.H */
+
+#ifndef _XSDKDEFS_H
+#define _XSDKDEFS_H
+
+/**********/
+/* Macros */
+/**********/
+									/* Control characters */
+#define STX 	0x02				/* Start of text			^B	*/
+#define ETX 	0x03				/* End of text				^C	*/
+#define BS		0x08				/* Back space				^H	*/
+#define TAB 	0x09				/* Horizontal tabulation	^I	*/
+#define LF		0x0a				/* Line feed				^J	*/
+#define FF		0x0c				/* Form feed				^L	*/
+#define CR		0x0d				/* Carriage return			^M	*/
+#define ESC 	0x1b				/* Escape					^[	*/
+#define SP      0x20                /* Space                        */
+
+
+#define DCD    0x8000		/* Data carrier detect bit in msr			*/
+
+#define TABSIZE 	4		/* Tabs will expand to 4 chars upon input	*/
+
+#define SAVE_LINES	 4		/* Maximum number of lines to save			*/
+#define LINE_BUFSIZE 512    /* Size of line output buffer               */
+
+#define HIGH 8				/* High intensity attribute bit */
+
+							/* user_misc bits */
+#define ANSI	(1<<0)		/* user has ANSI capability */
+#define COLOR	(1<<1)		/* user has monochrome ANSI display */
+#define RIP 	(1<<2)		/* RIP compatible terminal detected */
+#define WIP 	(1<<3)		/* WIP compatible terminal detected */
+
+#ifndef uchar				/* Short-hand for unsigned data types */
+#define uchar unsigned char
+#endif
+#ifndef uint
+#define uint unsigned int
+#endif
+#ifndef ushort
+#define ushort unsigned short
+#endif
+#ifndef ulong
+#define ulong unsigned long
+#endif
+
+							/* Bits in 'mode' for getkey and getstr     */
+#define K_UPPER		(1<<0)	/* Converts all letters to upper case 		*/
+#define K_UPRLWR    (1<<1)  /* Upper/Lower case automatically           */
+#define K_NUMBER    (1<<2)  /* Allow numbers only                       */
+#define K_WRAP	    (1<<3)  /* Allows word wrap							*/
+#define K_MSG		(1<<4)  /* Allows ANSI, ^N ^A ^G					*/
+#define K_SPIN      (1<<5)  /* Spinning cursor (same as SPIN)           */
+#define K_LINE      (1<<6)  /* Input line (inverse color)               */
+#define K_EDIT      (1<<7)  /* Edit string passed                       */
+#define K_CHAT		(1<<8)	/* In chat multi-chat 						*/
+#define K_NOCRLF    (1<<9)	/* Don't print CRLF after string input      */
+#define K_ALPHA     (1<<10) /* Only allow alphabetic characters         */
+#define K_AUTODEL	(1<<11) /* Auto-delete text (used with K_EDIT)		*/
+#define K_LOWPRIO	(1<<12) /* Low priority getstr() operation			*/
+#define K_GETSTR	(1<<13) /* getkey() called from getstr()			*/
+
+							/* Miscellaneous Modem Settings (mdm_misc)	*/
+#define MDM_FLOWCTRL (1<<0)	/* Use flow control with modem				*/
+#define MDM_STAYHIGH (1<<1) /* Stay at highest DTE rate                 */
+
+#define LOOP_NOPEN   50    	/* Retries before file access denied        */
+#define LOOP_NODEDAB 100	/* Retries on NODE.DAB locking/unlocking	*/
+
+#define MAX_NODES	250 	/* Maximum number of nodes					*/
+
+enum {								/* Node Status */
+	 NODE_WFC			        	/* Waiting for Call */
+	,NODE_LOGON                  	/* at logon prompt */
+	,NODE_NEWUSER         			/* New user applying */
+	,NODE_INUSE			 			/* In Use */
+	,NODE_QUIET			 			/* In Use - quiet mode */
+	,NODE_OFFLINE		 			/* Offline */
+	,NODE_NETTING		 			/* Networking */
+	,NODE_EVENT_WAITING				/* Waiting for all nodes to be inactive */
+	,NODE_EVENT_RUNNING				/* Running an external event */
+	,NODE_EVENT_LIMBO				/* Allowing another node to run an event */
+	};
+
+									/* Bit values for node.misc */
+#define	NODE_ANON	(1<<0)			/* Anonymous User */
+#define	NODE_LOCK	(1<<1)			/* Locked for sysops only */
+#define	NODE_INTR	(1<<2)			/* Interrupted - hang up */
+#define NODE_MSGW   (1<<3)			/* Message is waiting */
+#define NODE_POFF   (1<<4)        	/* Page disabled */
+#define NODE_AOFF	(1<<5)			/* Activity Alert disabled */
+#define NODE_UDAT	(1<<6)			/* User data has been updated */
+#define NODE_RRUN	(1<<7)			/* Re-run this node when log off */
+#define NODE_EVENT	(1<<8)			/* Must run node event after log off */
+#define NODE_DOWN	(1<<9)			/* Down this node after logoff */
+#define NODE_RPCHT	(1<<10) 		/* Reset private chat */
+#define NODE_NMSG   (1<<11)         /* Node message waiting (new way) */
+
+
+enum {								/* Node Action */
+	 NODE_MAIN						/* Main Prompt */
+	,NODE_RMSG			 			/* Reading Messages */
+	,NODE_RMAL						/* Reading Mail */
+	,NODE_SMAL			 			/* Sending Mail */
+	,NODE_RTXT			 			/* Reading G-Files */
+	,NODE_RSML			 			/* Reading Sent Mail */
+	,NODE_PMSG			 			/* Posting Message */
+	,NODE_AMSG			 			/* Auto-message */
+	,NODE_XTRN			  			/* Running External Program */
+	,NODE_DFLT			  			/* Main Defaults Section */
+	,NODE_XFER			   			/* Transfer Prompt */
+	,NODE_DLNG		    			/* Downloading File */
+	,NODE_ULNG			    		/* Uploading File */
+	,NODE_BXFR						/* Bidirectional Transfer */
+	,NODE_LFIL			    		/* Listing Files */
+	,NODE_LOGN			    		/* Logging on */
+	,NODE_LCHT			    		/* In Local Chat with Sysop */
+	,NODE_MCHT		     			/* In Multi-Chat with Other Nodes */
+	,NODE_GCHT			   			/* In Local Chat with Guru */
+	,NODE_CHAT			   			/* In Chat Section */
+	,NODE_SYSP		     			/* Sysop Activity */
+	,NODE_TQWK						/* Transferring QWK packet */
+	,NODE_PCHT						/* Private node-to-node chat */
+	,NODE_PAGE						/* Paging node for Private Chat */
+	,NODE_RFSD						/* Retrieving file from sequential dev */
+	};
+
+							/* Different bits in node_misc				*/
+#define NM_ANSALARM (1<<0)	/* Alarm locally on answer					*/
+#define NM_WFCSCRN  (1<<1)	/* Wait for call screen                     */
+#define NM_WFCMSGS	(1<<2)	/* Include total messages/files on WFC		*/
+#define NM_LCL_EDIT (1<<3)	/* Use local editor to create messages		*/
+#define NM_EMSOVL	(1<<4)	/* Use expanded memory of overlays			*/
+#define NM_WINOS2	(1<<5)	/* Use Windows/OS2 time slice API call		*/
+#define NM_INT28	(1<<6)	/* Make int 28 DOS idle calls				*/
+#define NM_NODV 	(1<<7)	/* Don't detect and use DESQview API        */
+#define NM_NO_NUM	(1<<8)	/* Don't allow logons by user number        */
+#define NM_LOGON_R	(1<<9)	/* Allow logons by user real name			*/
+#define NM_LOGON_P	(1<<10) /* Secure logons (always ask for password)	*/
+#define NM_NO_LKBRD (1<<11) /* No local keyboard (at all)				*/
+#define NM_SYSPW	(1<<12) /* Protect WFC keys and Alt keys with SY:	*/
+#define NM_NO_INACT (1<<13) /* No local inactivity alert/logoff 		*/
+#define NM_NOBEEP	(1<<14) /* Don't beep locally                       */
+#define NM_LOWPRIO	(1<<15) /* Always use low priority input			*/
+#define NM_7BITONLY (1L<<16) /* Except 7-bit input only (E71 terminals)  */
+
+#ifdef __WATCOMC__
+
+	#if !defined(__COLORS)
+	#define __COLORS
+
+	enum COLORS {
+		BLACK,			/* dark colors */
+		BLUE,
+		GREEN,
+		CYAN,
+		RED,
+		MAGENTA,
+		BROWN,
+		LIGHTGRAY,
+		DARKGRAY,		/* light colors */
+		LIGHTBLUE,
+		LIGHTGREEN,
+		LIGHTCYAN,
+		LIGHTRED,
+		LIGHTMAGENTA,
+		YELLOW,
+		WHITE
+	};
+	#endif
+
+	#define BLINK		128 /* blink bit */
+
+	#define ffblk find_t
+    #define findfirst(x,y,z) _dos_findfirst(x,z,y)
+	#define findnext(x) _dos_findnext(x)
+	#pragma aux mswait "_*"
+#endif
+
+/****************************************************************************/
+/* MALLOC/FREE Macros for various compilers and environments				*/
+/* MALLOC is used for allocations of 64k or less							*/
+/* FREE is used to free buffers allocated with MALLOC						*/
+/* LMALLOC is used for allocations of possibly larger than 64k				*/
+/* LFREE is used to free buffers allocated with LMALLOC 					*/
+/* REALLOC is used to re-size a previously MALLOCed or LMALLOCed buffer 	*/
+/****************************************************************************/
+#if defined(__COMPACT__) || defined(__LARGE__) || defined(__HUGE__)
+	#if defined(__TURBOC__)
+		#define REALLOC(x,y) farrealloc(x,y)
+		#define LMALLOC(x) farmalloc(x)
+		#define MALLOC(x) farmalloc(x)
+		#define LFREE(x) farfree(x)
+		#define FREE(x) farfree(x)
+	#elif defined(__WATCOMC__)
+		#define REALLOC realloc
+		#define LMALLOC(x) halloc(x,1)	/* far heap, but slow */
+		#define MALLOC malloc			/* far heap, but 64k max */
+		#define LFREE hfree
+		#define FREE free
+	#else	/* Other 16-bit Compiler */
+		#define REALLOC realloc
+		#define LMALLOC malloc
+		#define MALLOC malloc
+		#define LFREE free
+		#define FREE free
+	#endif
+#else		/* 32-bit Compiler or Small Memory Model */
+	#define REALLOC realloc
+	#define LMALLOC malloc
+	#define MALLOC malloc
+	#define LFREE free
+	#define FREE free
+#endif
+
+#define KEY_BUFSIZE 256
+
+#define CRLF  { outchar(CR); outchar(LF); }
+
+#define SYSOP (user_level>=sysop_level) /* Is current user sysop t/f macro	 */
+
+#define bgotoxy(x,y)	printf("\x1b[%d;%dH",y,x)   /* place the cursor at   */
+													/* location x,y via ANSI */
+
+#define SAVELINE		{ slatr[slcnt]=latr; \
+							sprintf(slbuf[slcnt<SAVE_LINES ? slcnt++ : slcnt] \
+							,"%.*s",lbuflen,lbuf); \
+							lbuflen=0; }
+#define RESTORELINE 	{ lbuflen=0; attr(slatr[--slcnt]); \
+							bputs(slbuf[slcnt]); }
+
+/************/
+/* Typedefs */
+/************/
+
+typedef struct {						/* Node information kept in NODE.DAB */
+	uchar	status,						/* Current Status of Node */
+			errors,						/* Number of Critical Errors */
+			action;						/* Action User is doing on Node */
+	uint	useron,						/* User on Node */
+			connection,					/* Connection rate of Node */
+			misc,						/* Miscellaneous bits for node */
+			aux;						/* Auxillary word for node */
+	ulong	extaux;						/* Extended aux dword for node */
+            } node_t;
+
+#endif	/* Don't add anything after this endif */
diff --git a/xtrn/sdk/xsdkvars.c b/xtrn/sdk/xsdkvars.c
new file mode 100644
index 0000000000000000000000000000000000000000..68182bdf756b1f92775fb781b830ca0b6f940d0d
--- /dev/null
+++ b/xtrn/sdk/xsdkvars.c
@@ -0,0 +1,94 @@
+/* XSDKVARS.C */
+
+#include <time.h>
+#include <stdio.h>
+#include "xsdkdefs.h"
+
+#ifndef GLOBAL
+#define GLOBAL
+#endif
+
+/*******************************/
+/* Global Variable Definitions */
+/*******************************/
+GLOBAL char 	lncntr; 			/* Line counter */
+GLOBAL char 	tos;				/* Top of screen */
+GLOBAL uchar	curatr; 			/* Current attribute */
+GLOBAL char 	mnehigh,			/* Colors for mnemonics */
+				mnelow;
+GLOBAL uchar	sys_nodes,			/* Number of nodes on system */
+				node_num;			/* Current node number */
+GLOBAL ulong	node_misc;			/* Misc. node toggles */
+GLOBAL char 	sys_name[41],		/* System name */
+				sys_id[9],			/* System QWK ID */
+				sys_op[41], 		/* System operator name */
+				sys_guru[41];		/* System guru name */
+GLOBAL ushort	timeleft;			/* Time left in seconds */
+GLOBAL char 	ctrl_dir[128],		/* SBBS dirs */
+				data_dir[128],
+				node_dir[128],
+				exec_dir[128],
+				text_dir[128],
+				temp_dir[128];
+GLOBAL uint 	user_number;		/* User's number */
+GLOBAL char 	user_name[45],		/* User's name/alias */
+				user_misc,			/* User's misc settings */
+				user_rows,			/* User's rows per screen */
+				user_level, 		/* User's main level */
+				user_flags1[27],	/* User's flag set #1 */
+				user_flags2[27],	/* User's flag set #2 */
+				user_flags3[27],	/* User's flag set #3 */
+				user_flags4[27],	/* User's flag set #4 */
+				user_exempt[27],	/* User's exemptions */
+				user_rest[27],		/* User's restrictions */
+				user_sex,			/* User's sex */
+				user_birth[9],		/* User's birthday */
+				user_phone[13], 	/* User's phone number */
+				user_address[31],	/* User's address */
+				user_location[31],	/* User's location */
+				user_zipcode[11],	/* User's zip/postal code */
+				user_realname[26];	/* User's real name or company name */
+GLOBAL ulong	user_cdt,			/* User's credits */
+				user_dce;			/* User's DCE rate */
+GLOBAL time_t	user_expire;		/* User's expiration date */
+
+									/* COM port registers: */
+GLOBAL uint 	com_base,			/* COM base address */
+				com_irq;			/* irq line number	   */
+GLOBAL ulong	com_rate;			/* DTE rate in bps	   */
+GLOBAL char  	com_port;			/* Number of COM port  */
+
+									/* Modem command strings */
+GLOBAL char 	mdm_init[64],		/* Initialization */
+				mdm_spec[64],		/* Special Initialization */
+				mdm_term[64],		/* Terminal Initialization String */
+				mdm_dial[64],		/* Dial */
+				mdm_offh[64],		/* Off hook */
+				mdm_answ[64],		/* Answer */
+                mdm_misc;           /* Misc bits used for flags */
+
+GLOBAL time_t	starttime;			/* Start time-stamp */
+GLOBAL char 	wordwrap[81];		/* Wordwrap buffer */
+GLOBAL uint 	sec_warn,			/* Seconds till inactivity warning */
+				sec_timeout;		/* Seconds till disconnect */
+GLOBAL char 	timeleft_warn;		/* Minutes left warning */
+
+GLOBAL int		nodefile;			/* File descriptor for NODE.DAB */
+
+GLOBAL char 	slbuf[SAVE_LINES][LINE_BUFSIZE+1]; /* Saved for redisplay */
+GLOBAL char 	slatr[SAVE_LINES];	/* Starting attribute of each line */
+GLOBAL char 	slcnt;			/* Number of lines currently saved */
+GLOBAL char 	lbuf[LINE_BUFSIZE];/* Temp storage for each line output */
+GLOBAL int		lbuflen;		/* Number of characters in line buffer */
+GLOBAL char     latr;           /* Starting attribute of line buffer */
+GLOBAL uint 	inDV;			/* DESQview version if running under DV */
+GLOBAL int		keybuftop,keybufbot;	/* Keyboard input buffer pointers */
+GLOBAL char     keybuf[KEY_BUFSIZE];    /* Keyboard input buffer */
+GLOBAL uint far *msr;			/* Last modem status register contents */
+GLOBAL char 	**xtrn; 		/* List of external program names */
+GLOBAL uint 	total_xtrns;	/* Total number of external programs */
+GLOBAL uchar	lastnodemsg;	/* Last node to send a message to */
+GLOBAL uchar	name_len;		/* Length of name field in NAME.DAT */
+GLOBAL char 	aborted;		/* Aborted flag - if ctrl-c hit */
+GLOBAL uint 	sysop_level;	/* Sysop Level */
+GLOBAL FILE 	*con_fp;		/* Console file pointer (stdout/stderr) */
diff --git a/xtrn/smm/adult.que b/xtrn/smm/adult.que
new file mode 100644
index 0000000000000000000000000000000000000000..dd10f2f4c89cbb22d1a74be1d85364ef45a134eb
--- /dev/null
+++ b/xtrn/smm/adult.que
@@ -0,0 +1,93 @@
+20
+How old were you when you lost your virginity?
+9
+1
+I still have it
+Younger than 12
+Between 12 and 14
+Between 15 and 16
+Between 17 and 18
+Between 19 and 20
+Between 21 and 25
+Between 25 and 30
+Older than 30
+How many sexual partners have you had?
+8
+1
+None
+Between 1 and 5
+Between 6 and 10
+Between 11 and 20
+Between 21 and 30
+Between 31 and 50
+Between 51 and 100
+More than 100
+When engaged in a relationship, how often do you prefer to have sex?
+8
+1
+Never
+At least once a day
+Between 4 and 6 times a week
+Between 1 and 3 times a week
+Between 1 and 3 times a month
+Between 6 and 11 times a year
+Between 1 and 5 times a year
+Less than once a year
+What form of sexually transmitted disease protection do you prefer?
+5
+1
+I don't know
+Abstinence
+No protection
+Male condoms
+Female condoms
+Do you enjoy oral sex?
+5
+1
+I don't know
+No
+Yes, giving
+Yes, receiving
+Yes, both giving and receiving
+Do you enjoy anal sex?
+5
+1
+I don't know
+No
+Yes, giving
+Yes, receiving
+Yes, both giving and receiving
+How often do you masturbate?
+8
+1
+At least once a day
+Between 4 and 6 times a week
+Between 1 and 3 times a week
+Between 1 and 3 times a month
+Between 6 and 11 times a year
+Between 1 and 5 times a year
+Less than once a year
+Never
+Do you reach orgasm (alone or with a partner)?
+5
+1
+I don't know
+Yes, always
+Yes, occasionally
+Yes, rarely
+No
+Have you had sexual partners of your same gender?
+6
+1
+I don't know
+Yes, always
+Yes, occasionally
+Yes, rarely
+No, but I'm curious
+No, never
+Do you shave your pubic area?
+3
+1
+Yes, all of it
+Yes, some of it
+No
diff --git a/xtrn/smm/appear.que b/xtrn/smm/appear.que
new file mode 100644
index 0000000000000000000000000000000000000000..b7b1b7ea02a51d80816883dc50d08d9f84bed505
--- /dev/null
+++ b/xtrn/smm/appear.que
@@ -0,0 +1,119 @@
+8
+Do you wear any of the following?
+16
+16
+Eye glasses
+Partial beard (goatee, five o'clock shadow, etc.)
+Full beard
+Mustache
+Side burns
+Earring(s)
+Make-up
+Body piercing (nose, tongue, naval, eyebrow, etc.)
+Visible tatoos
+Covered tatoos
+Unnaturally colored/bleached hair (blue, purple, pink, etc.)
+Brades or dreds
+Pony tail
+Long fingernails
+Painted fingernails
+None of the above
+Larger than average or smaller than average attributes?
+16
+8
+Small eyes
+Large eyes
+Small ears
+Large ears
+Small nose
+Large nose
+Small breasts
+Large breasts
+Large arms
+Large legs
+Large hips/buttocks
+Thin-lips
+Thick-lips
+Thin eyebrows
+Thick eyebrows
+None of the above
+Do you have a sun-tan?
+5
+1
+No, I'm naturally dark
+No, but I will this summer
+No, I don't try to tan
+No, I always burn
+Yes
+Your (natural) complexion?
+9
+3
+Very dark
+Medium dark (olive)
+Light dark
+White
+Whiter than white (alabaster)
+Sparse freckles
+Dense freckles
+Mild acne
+Cystic acne
+Your hair (on your head)?
+16
+5
+Gone (bald)
+Thin (balding)
+Thinner than average
+Average thickness
+Thicker than average
+Feathered
+All one length
+Curly
+Wavy
+Straight
+Partially shaved
+Completely shaved
+Short length
+Medium length
+Long length
+Very long length
+Your teeth?
+11
+4
+Very white
+Average whiteness
+Slightly stained (smoke, coffee, etc.)
+Very stained (smoke, coffee, etc.)
+Very straight
+Average straightness
+Slightly crooked
+Very crooked
+Visible missing teeth
+Visible caps (gold or silver)
+Visible braces
+Your build?
+6
+1
+Thin, wirey
+Thin, but toned
+Average
+Chunky
+Stocky
+Muscle-bound
+Physical disabilities/deformities?
+14
+5
+Missing fingers
+Gimpy or missing limbs
+Scarred face
+Scarred arms or hands
+Mongloidian eyes
+Lazy/cross/cock-eyed
+Cleft pallette
+Paraplegic
+Quadriplegic
+Blind
+Deaf
+Mute
+Other
+None
+
diff --git a/xtrn/smm/basic.que b/xtrn/smm/basic.que
new file mode 100644
index 0000000000000000000000000000000000000000..aaf3634eb02a3553e5e2569e92c06b52b12cd9f6
--- /dev/null
+++ b/xtrn/smm/basic.que
@@ -0,0 +1,179 @@
+17
+What is your religious belief?
+9
+1
+Christian
+Jewish
+Catholic
+Buddihst
+Muslim
+Mormon
+Jehovah's Witness
+Other
+None
+Do you participate in organized religion?
+3
+1
+Yes
+No
+Sometimes
+Do you smoke tobacco?
+5
+1
+Yes, daily
+Yes, occasionally
+Yes, I'm trying to quit
+No, I quit
+No, I never smoked tobacco regularly
+Do you use forms of nicotine other than tobacco (dip, snuff, etc)?
+5
+1
+Yes, daily
+Yes, occasionally
+Yes, I'm trying to quit
+No, I quit
+No, never
+Do you drink alcohol?
+6
+1
+Yes, daily
+Yes, occasionally
+Yes, but never enough to get drunk
+Yes, I'm trying to quit
+No, I quit
+No, I never drank
+Do you dance?
+7
+1
+Yes, professionally (exotic)
+Yes, professionally (tame)
+Yes, regularly
+Yes, occasionally
+Yes, rarely
+No, not recently
+No, not ever
+Favorite types of music?
+16
+16
+Easy Listening
+New Age
+Classical
+Classic Rock
+Heavy Metal
+Punk Rock
+Alternative
+Industrial
+Techno
+Pop
+Rap
+Disco
+Rhthym and Blues
+Jazz
+Folk
+Country and Western
+Completed education?
+7
+6
+Grade school
+High school
+Trade school
+College (Associate's)
+College (Bachelor's)
+College (Master's)
+None
+Are you currently enrolled in school?
+8
+1
+Yes, grade school
+Yes, high school
+Yes, trade school
+Yes, city college
+Yes, state college
+No, but I plan to return
+No, but I plan to start
+No, and I have no plans to
+Are you currently employed?
+4
+1
+Yes, full-time
+Yes, part-time
+No, but I'm seeking employment
+No, I don't need/want to work right now
+Are you financially independant?
+5
+1
+Yes
+No, I depend on my parent(s)
+No, I depend on other relative(s)
+No, I depend on my spouse
+No, I depend on a significant other
+Do you play sports?
+5
+1
+Yes, professionally
+Yes, frequently
+Yes, occasionally
+Yes, rarely
+No
+Do you do any recreational reading?
+4
+1
+Yes, frequently
+Yes, occasionally
+Yes, rarely
+No
+Favorite out-door activities?
+16
+16
+Water (swimming, skiing, surfing, etc.)
+Snow (skiing, boarding, ice skating, etc.)
+Hiking/camping/climbing
+Roller skating/blading
+Bicycling
+Running/jogging/walking
+Team sports (baseball, basketball, football, soccer, etc.)
+Racket sports (tennis, racquetball, badminton, etc.)
+Golf
+Shooting (firearms, archery, etc.)
+Off-road (motorcycles, ATVs, etc.)
+Sky diving/bungee jumping
+Gardening
+Fishing/hunting
+Horseback riding
+None of the above
+Favorite in-home activities?
+16
+16
+Painting/drawing/sculpting/etc.
+Sewing/weaving/etc.
+Cooking
+Writing
+Reading
+Playing a musical instrument/singing
+Playing video/computer games
+Playing card/board games
+Cleaning
+Computer programming/exploring
+BBS/E-mail/Internet communications
+CB/HAM/shortwave radio communications
+Electronics/chemistry/physics experiments
+Talking on the telephone
+Watching television
+Listening to music (CDs, records, tapes, radio, etc.)
+Do you have any children?
+6
+1
+Yes, three or more
+Yes, two
+Yes, one
+No, but definitely someday
+No, but possibly someday
+No, I don't ever want children
+What kind of relationship are you seeking?
+5
+5
+Platonic friendship (no intimacy)
+Dating (no commitment)
+Long term relationship (boyfriend/girlfriend/lover)
+Marriage
+None of the above
diff --git a/xtrn/smm/file_id.diz b/xtrn/smm/file_id.diz
new file mode 100644
index 0000000000000000000000000000000000000000..5b423d7a3f4db10074039113ceb646dd2842a419
--- /dev/null
+++ b/xtrn/smm/file_id.diz
@@ -0,0 +1,7 @@
+Synchronet Match Maker (BBS door) v2.10
+supports any DOOR.SYS or Synchronet BBS.
+Internationally networked profiles, 
+telegrams, wall, photographs, and more! 
+Developed by Digital Dynamics, makers of 
+Synchronet BBS Software. Perhaps the most 
+comprehensive match maker available.
diff --git a/xtrn/smm/list_hdr.asc b/xtrn/smm/list_hdr.asc
new file mode 100644
index 0000000000000000000000000000000000000000..182a1298dd2ff79ae8d05de74f2ee8b7a2c8784e
--- /dev/null
+++ b/xtrn/smm/list_hdr.asc
@@ -0,0 +1,5 @@
+nh���Marital Status or Orientation (Single, Married, Divorced, Widowed, etc)
+�ng��Race (White, Black, Hispanic, Indian, Middle Eastern, Asian, or Other)
+hw�ng�hc�Sex  m   �Eyes    y�Weight 
+w�ng�hc�bAge gHairm�  rHeighty�   ng�Location           cPhoto�hw�Name/Alias           mMatch�
+w�ng�hc� b�  g�   m�   r�    y�ng   �                        c�hw�m                          �
diff --git a/xtrn/smm/material.que b/xtrn/smm/material.que
new file mode 100644
index 0000000000000000000000000000000000000000..2de521f0150f61b7dd4025f43ca153597b8150ef
--- /dev/null
+++ b/xtrn/smm/material.que
@@ -0,0 +1,91 @@
+16
+Do you own a passenger vehicle?
+5
+1
+Yes, more than one
+Yes
+No, but I will soon
+No, and I don't know when/if I will
+No, I don't have a driver's license
+Do you own a recreational vehicle?
+2
+1
+Yes
+No
+Do you own a home (mortage counts)?
+4
+1
+Yes, more than one
+Yes
+No, I'm renting
+No
+Do you own furniture?
+3
+1
+Yes
+No, I'm renting
+No
+Do you own a computer?
+4
+1
+Yes, more than one
+Yes
+No, I'm renting
+No
+Do you have a savings account?
+2
+1
+Yes
+No
+Do you have a checking account?
+2
+1
+Yes
+No
+Do you have a credit card?
+3
+1
+Yes, more than one
+Yes
+No
+Do you have health insurance?
+2
+1
+Yes
+No
+Do you have life insurance?
+2
+1
+Yes
+No
+Do you have car insurance?
+2
+1
+Yes
+No
+Do you have an accountant?
+2
+1
+Yes
+No
+Do you have a maid?
+2
+1
+Yes
+No
+Do you own a swimming pool (i.e. in your backyard)?
+2
+1
+Yes
+No
+Do you own an acre or more of land?
+2
+1
+Yes
+No
+Do you own any stocks or bonds?
+2
+1
+Yes
+No
+
diff --git a/xtrn/smm/mb-e.asc b/xtrn/smm/mb-e.asc
new file mode 100644
index 0000000000000000000000000000000000000000..2b612f78b2f06864bce8dbc5667aa911a9904393
--- /dev/null
+++ b/xtrn/smm/mb-e.asc
@@ -0,0 +1,7 @@
+An Extrovert is energized by people. A strong extrovert feels very much alone
+without lots of people around. E's tend to talk first & think later, know a
+lot of people, don't mind having a conversation while the TV is on, are
+approachable, don't consider the telephone to be an interruption, tend to 
+think out loud and use people as a sounding board for their ideas, need
+affirmation from friends about  who I am & how do I look. 
+About 75% of the population are type E.
diff --git a/xtrn/smm/mb-enfj.asc b/xtrn/smm/mb-enfj.asc
new file mode 100644
index 0000000000000000000000000000000000000000..7dadc77fe75706a029dc3ce7ff44c9c4e806a044
--- /dev/null
+++ b/xtrn/smm/mb-enfj.asc
@@ -0,0 +1,134 @@
+ENFJs in short:
+
+Responsive and responsible.  Generally feel real concern for what
+others think or want, and try to handle things with due regard for
+other person's feelings.  Can present a proposal or lead a group
+discussion with ease and tact.  Sociable, popular, sympathetic.
+Responsive to praise and criticism.
+
+About 5% of the population.
+
+Details about ENFJs:
+
+ENFJs are outstanding leaders of groups, both task groups and
+growth groups.  They have the charming characteristic of seeming to
+take for granted that they will be followed, never doubting that
+people will want to do what they suggest.  And, more often than
+not, people do, because this type of has unusual charisma.  ENFJs
+place a high value on cooperation from others and are most willing
+to cooperate themselves.
+
+Found in only about 5 percent of the general population, ENFJs
+place people as being of highest importance and priority.  As a
+result, ENFJs may find themselves feeling responsible for the
+feelings of others to an extent which places a burden on the
+relationship.
+
+An ENFJ communicates caring, concern, and a willingness to be
+involved.  Thus people turn to ENFJs for nurture and support, which
+an ENFJ is usually able to deliver.  At times, however, these kinds
+of demands can overwhelm ENFJs, who find at this point that they
+lack the skills to dissociate.  ENFJs do not seem able to turn away
+from these demands even when they become unreasonable.  Or, if
+forced to let go of the burden through sheer unavailability of time
+or energy, ENFJs experience a guilt all out of proportion to the
+realities of the commitment to the relationship.
+
+ENFJs are vulnerable to idealize interpersonal relationships,
+raising these relationships to a plane which seldom can sustain the
+realities of human nature.  Because of this tendency to raise
+interpersonal relations to the ideal, ENFJs may unwittingly
+overpower their friends, who believe that they cannot possibly live
+up to an ENFJ's perception of them.  The fact is, ENFJs are
+extraordinarily tolerant of others, seldom critical, and always
+trustworthy.
+
+ENFJs take communication for granted and believe that they are
+understood and that their communications are accepted.  Just as
+they themselves are accepting, so do they assume that others are
+the same.  When ENFJs find that their position or beliefs were not
+comprehended or accepted, they are surprised, puzzled, and
+sometimes hurt.  Fortunately, this does not happen with high
+frequency, as ENFJs have a remarkable fluency with language,
+especially in speech; they are particularly adept when
+communicating face-to-face as opposed to communicating in writing.
+
+They are influential, therefore, in groups, having no hesitation
+about speaking out, no matter how large or small the group may be.
+
+ENFJs have an unusual ability to relate to others with empathy,
+taking into themselves the characteristics, emotions, and beliefs
+of others.  This can pose a danger for ENFJs, they can
+unconsciously over-identify with others and pick up their burdens
+as if they were their own.  In the process, ENFJs may risk their
+own sense of identity.
+
+They have a natural ability to mimic because of this highly
+developed ability to empathize by interjection.  They are likely to
+be very concerned about the problems of those close to them, but
+they also may get as deeply involved in the problems of those not
+so close and may find themselves overextended emotionally.
+
+ENFJs would do well to follow their hunches, for their intuition
+tends to be well developed.  Decisions made purely on the basis of
+logic may not be so sound, and checking with a person who has a
+strong T preference might be at times advisable for the ENFJ.  In
+the framework of values, however, the ENFJ is on certain ground.
+Generally, they know what they prefer and can read other people
+with outstanding accuracy.  Seldom is an ENFJ wrong about the
+motivations or intents of another, hidden or not.
+
+ENFJs are socially adept and make excellent companions and mates.
+They also are deeply devoted to their children, yet tend not to be
+domineering to either the children or a mate.  In fact, the ENFJ is
+so even-tempered that he or she can be victimized by a mate who
+might have become more than demanding.
+
+ENFJ mates always try to please and feel personally responsible
+when home life does not go smoothly.  They are tireless in their
+efforts to see that it does, providing generously from available
+income, time and energy.  This dedication often exists, however,
+side by side with an ENFJ's dream of the perfect relationship - a
+characteristic of all NFs, but one which is particularly strong in
+an ENFJ.  Thus an ENFJ has that longing for the ideal which results
+in a vague dissatisfaction with whatever is in the way of
+relationships, mating as well as friendship.
+
+This longing for the perfect carries over into the careers of
+ENFJs, who experience some degree of restlessness whatever their
+jobs.  And, as with ENFPs, ENFJs have a wide range of occupations
+which offer success.
+
+Being verbally adept, ENFJs contribute to an unusual level when
+dealing with people, particularly face-to-face: the media, the
+ministry, and the stage and screen are populated with successful
+ENFJs.  They make superior therapists, charismatic teachers,
+excellent executives, and personalized executives.  Areas that
+would not permit utilization of the interaction talents of the
+ENFJs, for example, accounting, should be avoided; otherwise,
+almost any people-to-people occupation where personal, sustained
+contact is involved capitalizes on the personality of an ENFJ.
+
+ENFJs like to have things organized and settled.  They prefer to
+plan both work and social engagements ahead and tend to be
+absolutely reliable in honoring these commitments.  ENFJs are very
+much at home in complex situations which require the juggling of
+much data.  At the same time they can handle people with charm and
+concern.
+
+ENFJs are usually popular wherever they are.  Their ability to be
+comfortable either leading or following makes them easy to have
+around, whatever the situation.  A well-developed ENFJ group leader
+can provide, almost endlessly, activities for groups to play.  In
+some, this can amount to genius which other types find hard to
+emulate.  In this ability to organize without planning there is a
+certain, similarity to the ESFJ, but the latter acts more as a
+master of ceremonies than as a leader of groups.  The ESFJ is more
+of a recreational leader, who insures that each member has fun at a
+party and that the right things are expressed at social occasions,
+especially institutional social occasions such as weddings,
+funerals, parties, and the like.
+
+ENFJs, just like ESFJs, value harmonious human relations above all
+else; but ENFJs are not so easily crushed by indifference as are
+ESFJs and are more independent of others' valuations.
diff --git a/xtrn/smm/mb-enfp.asc b/xtrn/smm/mb-enfp.asc
new file mode 100644
index 0000000000000000000000000000000000000000..b8bcfdb153f8419529a418c96e59c5959015c9c7
--- /dev/null
+++ b/xtrn/smm/mb-enfp.asc
@@ -0,0 +1,157 @@
+ENFPs in short:
+
+Warmly enthusiastic, high-spirited, ingenious, imaginative.  Able
+to do almost anything that interests them.  Quick with a solution
+for any difficulty and ready to help anyone with a problem.  Often
+rely on their ability to improvise instead of preparing in advance.
+Can usually find compelling reasons for whatever they want.
+
+About 5% of the population.
+
+Details on ENFPs:
+
+For ENFPs nothing occurs which does not have some significance, and
+they have an uncanny sense of the motivations of others.  This
+gives them a talent for seeing life as an exciting drama, pregnant
+with possibilities for both good and evil.  This type is found in
+only about 5 percent of the general population, but they have great
+influence because of their extraordinary impact on others.
+
+ENFPs strive toward the authentic, even when acting spontaneously,
+and this intent is usually communicated nonverbally to others, who
+find this characteristic attractive.  ENFPs, however, find their
+own efforts of authenticity and spontaneity always lacking, and
+tend to heap coals of fire on themselves, always berating
+themselves for being so conscious of self.
+
+ENFPs consider intense emotional experiences vital; when they have
+these, however, they are made uneasy by a sense of being there but
+with a part of themselves cut off.  They strive for congruency, but
+always see themselves in some danger of losing touch with their
+real feelings, which ENFPs possess in a wide range and variety.
+
+ENFPs exercise a continuous scanning of the external environment
+and nothing out of the ordinary is likely to escape their
+attention.  They are keen and penetrating observers and are capable
+of intense concentration on another individual while aware of what
+is going on about them.  Their attention is never passive or
+casual, never wandering, but always directed.
+
+At times, ENFPs find themselves interpreting events in terms of
+another's "hidden motive," giving special meaning to words or
+actions.  This interpretation tends to be negative and, more often
+than not, inaccurately negative.  In the process, an ENFP may find
+that he or she has introduced an unnecessary, toxic element into
+the relationship.
+
+While ENFPs are brilliantly perceptive, they can make serious
+mistakes in judgement, which works to their discomfort.  These
+mistakes derive from their tendency to focus on data which confirm
+their own biases.  They may be absolutely correct in their
+perceptions but wrong in their conclusions.
+
+Because they tend to be hypersensitive and hyperalert, they may
+suffer from muscle tension.  They live in readiness for
+emergencies; because they have this facility, they assume this is
+true for others.  They can become bored rather quickly with both
+situations and people, and resist repeating experiences.
+
+They enjoy the process of creating something - an idea or a project
+- but are not as interested in the follow-through.  They are
+typically enthusiastic, and this is contagious.  People get caught
+up and entranced by an ENFP.  Yet this type is marked with a fierce
+independence, repudiating any kind of subordination, either in
+themselves or in others in relation to them.
+
+They do tend to attribute more power to authority figures than is
+there and give over to these figures an ability to "see through"
+them - which also is not apt to be there.
+
+While ENFPs resist the notion of others becoming dependent or
+having power over them, their charisma draws followers who wish to
+be shown the way.  ENFPs constantly find themselves surrounded by
+others who look toward the ENFP for wisdom, inspiration, courage,
+leadership, and so on - an expectancy which, at times, weighs
+rather heavily on an ENFP.
+
+ENFPs are characteristically optimistic and are surprised when
+people and events do not turn out as anticipated.  Often their
+confidence in the innate goodness of fate and human nature is a
+self-fulfilling prophecy.
+
+ENFPs have a remarkable latitude in career choices and succeed in
+many fields.  As workers, they are warmly enthusiastic, high
+spirited, ingenious, imaginative, and can do almost anything that
+interests them.  They can solve most problems, particularly those
+dealing with people.  They are charming and at ease with
+colleagues; others enjoy their presence.
+
+ENFPs are outstanding in getting people together, and are good at
+initiating meetings and conferences, although not as talented at
+providing for the operational details of these events.  They enjoy
+inventing new ways of doing things, and their projects tend to
+become a cause, quickly becoming personalized.
+
+They are imaginative themselves, but can have difficulty picking up
+on ideas and projects initiated by others.  They must make these
+ideas and projects their own if ENFPs are to lend energy and
+interest.  Once people or projects become routine, ENFPs are likely
+to lose interest; what 'might be' is always more fascinating than
+'what is.'
+
+ENFPs make extensive use of their intuitive powers.  They usually
+have a wide range of personal and telephone contacts, expending
+energy in maintaining both career and personal relationships.
+
+ENFPs make excellent salespeople, advertising people, politicians,
+screen or play writers, and in general are attracted to the
+interpretive arts, particularly, character acting.
+People-to-people work is essential for ENFPs, who need the feedback
+of interaction with others.
+
+ENFPs may find it difficult to work within the constraints of an
+institution, especially in following rules, regulations, and
+standard operating procedures.  More frequently, institutional
+procedures and policies are targets to be challenged and bent by
+the will of an ENFP.  Colleagues and superiors sometimes find
+themselves in the position of having to accommodate and salvage.
+
+At times, ENFPs demonstrate impatience with others; they may get
+into difficulty in an organization by siding with its detractors,
+who find in an ENFP a sympathetic ear and a natural rescuer.  In
+occupational choice, ENFPs quickly become restless if the choice
+involves painstaking detail and follow-through over a period of
+time.  Variety in day-to-day operations and interactions best suits
+the talents of ENFPs, who need quite a bit of latitude in which to
+exercise their adaptive ingenuity.
+
+As mates, ENFPs tend to be charming, gentle, sympathetic, and
+nonconformist.  They are not likely to be interested in the
+less-inspired routines of daily maintenance and ever will be
+seeking new outlets for their inspirations.
+
+As parents, ENFPs are devoted to their children, shifting from a
+role of "friend in need rescuer" to stern authority figure.  They
+may not always be willing to enforce their impulsive
+pronouncements, but leave it to their mates to follow through.
+
+A mate of an ENFP can expect charming surprises: extravagant
+generosity punctuated by periods of frugality.  Independent actions
+regarding money on the part of an ENFP's mate are not ordinarily
+welcomed, and the mate may find him or herself in an embarrassing
+situation of having to return purchases.
+
+ENFPs generally are the ones in charge of the home, and a
+conflict-free home is desired, almost demanded.  When he or she is
+in charge of economic resources, the ENFPs may contain extravagant
+luxuries, while necessities may be missing.  They are not always
+interested in saving for the future and may be casual in giving
+consideration to such things as life insurance, savings accounts,
+and even a ready cash supply for mate and children.
+
+ENFPs are characteristic in their pursuit of the novel, their
+strong sense of the possible, and outstanding intuitive powers.  At
+the same time, they have warmth and fun with people and generally
+are unusually skilled in handling people.  Their extroverted role
+tends to be well developed, as is their capacity for the novel and
+the dramatic.
diff --git a/xtrn/smm/mb-entj.asc b/xtrn/smm/mb-entj.asc
new file mode 100644
index 0000000000000000000000000000000000000000..a37af1d7e69f038528b89029a41d5f8606f06317
--- /dev/null
+++ b/xtrn/smm/mb-entj.asc
@@ -0,0 +1,105 @@
+ENTJs in short:
+
+Hearty, frank, decisive, leaders in activities.  Usually good in
+anything that requires reasoning and intelligent talk, such as
+public speaking.  Are usually well-informed and enjoy adding to
+their fund of knowledge.  May sometimes be more positive and
+confident than their experience in an area warrants.
+
+About 5% of the population.
+
+Details about ENTJs:
+
+If one word were used to capture ENTJ's style, it would be
+commandant.  The basic driving force and need of ENTJs is to lead,
+and from an early age they can be observed taking over groups. This
+type is found in approximately 5 percent of the total population.
+
+ENTJs have a strong urge to give structure wherever they are - to
+harness people to distant goals.  Their empirical, objective, and
+extroverted thinking may be highly developed; if this is the case,
+they use classification, generalization, summarization, adduction
+of evidence, and demonstration with ease.
+
+They resemble ESTJs in their tendency to establish plans for a
+task, enterprise, or organization, but ENTJs search more for policy
+and goals than for regulations and procedures.
+
+An ENTJ's introverted thinking (analysis and conservation) may be
+less well developed than the extroverted thinking processes, and
+the ENTJ leader may turn to an ENTP or INTP to provide this kind of
+input.  ENTJs are similar to INTJs except that the former places
+greater trust in empirical thought than in intuition; it is the
+ENTJs own intuitive sense of coherence, however, that augments and
+supports their empirical thinking.
+
+Although ENTJs are tolerant of established procedures, they can
+abandon any procedure when it can be shown to be indifferent to the
+goal it seemingly serves.  Inefficiency is especially rejected by
+ENTJs, and repetition of error causes them to become impatient. For
+the ENTJ, there must always be a reason for doing anything, and
+people's feelings usually are not sufficient reason.
+
+When in charge of an organization, ENTJs more than any other type
+desire (and generally have the ability) to visualize where the
+organization is going and seem able to communicate that vision to
+others.  They are the natural organization builders, and they
+cannot not lead.  They find themselves in command and sometimes are
+mystified as to how this happened.
+
+As administrators, ENTJs organize their units into a smooth
+functioning system, planning in advance, keeping both short-term
+and long-range objectives well in mind.  They seek and can see
+efficiency and effectiveness in personnel.  They prefer decisions
+to be based on impersonal data, want to work from well-thought-out
+plans, and like to use engineered operations - and they prefer that
+others follow suit.  ENTJs will support the policy of the
+organization and will expect others to do so.
+
+ENTJs will usually rise to positions of responsibility and enjoy
+being executives.  They are tireless in their devotion to their
+jobs and can easily block out other areas of life for the sake of
+work.  They will be able to reduce inefficiency, ineffectiveness,
+and aimless confusion, being willing to dismiss employees who
+perpetuate such behaviors.
+
+ENTJs tend to work in organizational structures of some sort, tend
+to be in charge administratively, and rise to top levels of
+responsibility, whether in the military, business, education, or
+government.
+
+ENTJs take charge of the home.  When the ENTJ is present, there
+will be little doubt as to who is in command.  Because their work
+is so important to them, however, they can become increasingly
+absent, especially if male.
+
+Male or female, ENTJs expect a great deal of their mates, who need
+to possess a strong personality of their own, a well-developed
+autonomy, many and varied interests, and a healthy self-esteem.  A
+career wife, however, may not be appealing to an ENTJ male, who is
+apt to view his home and family as a part of his professional
+background, a resource, and adjunct to his own career development.
+
+As a parent, an ENTJ will be thoroughly in charge, and the children
+will know what is expected of them - and will be expected to obey.
+When this does not occur, an ENTJ parent is not apt to make a
+scene; rather, there is likely to be a low-key, firm issuance of
+reprimand and a taking-for-granted of immediate obedience.
+
+While both mating and parenting are roles of importance to the
+ENTJ, they are to some degree preempted by the ENTJ's strong career
+interest.  The romantic dream and the quest for the ideal mate is
+usually not a characteristic of this type.
+
+ENTJs generally do, however, expect a home to be attractive,
+well-ordered, with meals served punctually and maintenance
+accomplished on schedule - all these in the service of the larger
+goal of creating a family system where children can be reared to be
+productive and healthy and establishing a devoted, harmonious
+relationship between man and woman.
+
+An ENTJ male might expect his mate to be active in civic and
+community affairs, to be socially sophisticated, and to be as
+well-educated as he.  The ENTJ female may find it difficult to
+select a mate who is not overwhelmed by her strong personality and
+will.
diff --git a/xtrn/smm/mb-entp.asc b/xtrn/smm/mb-entp.asc
new file mode 100644
index 0000000000000000000000000000000000000000..4817854f1d243baddc38eb1a8359470a2327143d
--- /dev/null
+++ b/xtrn/smm/mb-entp.asc
@@ -0,0 +1,145 @@
+ENTPs in short:
+
+Quick, ingenious, good at many things.  Stimulating company, alert
+and outspoken.  May argue for fun on either side of a question.
+Resourceful in solving new and challenging problems, but may
+neglect routine assignments.  Apt to turn to one new interest after
+another.  Skillful in finding logical reasons for what they want.
+
+About 5% of the population.
+
+Details about ENTPs:
+
+ENTPs wish to exercise their ingenuity in the world of people and
+things.  Found in about five out of every hundred people, ENTPs
+extrovert intuition; thus they deal imaginatively with social
+relationships as well as physical and mechanical relations.  They
+are very alert to what is apt to occur next, and always sensitive
+to possibilities.
+
+ENTPs are good at analysis, especially functional analysis, and
+have both a tolerance for and enjoyment of the complex.  Usually
+enthusiastic, ENTPs are apt to express interest in everything, and
+thus are a source of inspiration to others, who find themselves
+caught up by the ENTP's enthusiasm.  This type is delighted over
+many things and so is easy to please, often showing the
+effervescence of their NF counterpart, the ENFP.
+
+The ENTP is the most reluctant of all the types to do things in a
+particular manner just because that is the way things have always
+been done.  They characteristically have an eye for a better way,
+always on the lookout for new projects, new activities, new
+procedures.
+
+ENTPs are confident in the value of their pursuits and display a
+charming capacity to ignore the standard, the traditional, and the
+authoritative.  As a result of this open attitude, they often bring
+a fresh, new approach to their work and their lives.  The ENTP is a
+keen judge of the pragmatics of both the social and the mechanical,
+and may become expert at directing relationships between means and
+ends.
+
+Where the introverted NTP sees design as an end in itself, the
+extroverted NTP sees design as a 'means'; the end is the invention
+that works, the prototype that is replicatable.  Ideas are valuable
+when and only when they make possible actions and objects.  "It
+can't be done" is a challenge to an ENFP and elicits a reaction of
+"I can do it."
+
+They are not, however, the movers of mountains as are the INTJs.
+Rather, the faith of the ENTPs is in their ability to improvise
+something, and they display an unusual talent for rising to the
+expectancy of a situation.
+
+Superficially, ENTPs resemble ESTPs in their derring-do.  But the
+focus of the ENTP is on competency and the sense of power this
+gives, rather than on the feeling of freedom of action experienced
+by the ESTP.
+
+ENTPs can be fascinating conversationalists, able as they are to
+follow the complex verbalizations of others.  They may deliberately
+employ debate tactics to the disadvantage of their opponents, even
+when the "opponents" are close associates and valued friends.
+
+ENTPs are the most able of all types to maintain a one-up position
+with others.  They value adaptability and innovation and thus
+respond quickly and adeptly to another's shifting position.  They
+may even be several jumps ahead.
+
+The ENTP, talkative and motivating, is often the life of an
+enterprise.  The ENTP can be an entrepreneur and cleverly makes do
+with whatever or whoever is at hand, counting on ingenuity to solve
+problems as they arise, rather than carefully generating a detailed
+blueprint in advance.  A rough draft is all an ENTP needs to feel
+confident and ready to proceed into action, counting on the ability
+to improvise as a situation develops.
+
+Because of this tendency to depend on ingenuity and improvisation,
+they may neglect very necessary preparation at times.  After
+repeated failures in situations where improvising has met with
+defeat, the ENTP may develop ways of avoiding such situations as a
+substitute to thorough preparation.
+
+ENTPs can succeed in a variety of occupations, as long as the job
+does not involve too much humdrum routine.  At this point, they
+become restless.  If a project in which they are engaged is no
+longer challenging, they tend to lose interest in that project and
+fail to follow through - often to the discomfort of colleagues.
+
+Seldom are ENTPs conformists.  ENTPs enjoy outwitting the system
+and use rules and regulations within the system to win the game -
+whatever it may be.  They understand well the politics of
+institutions and deal with these realities very well, always aiming
+to understand the people within the system rather than to judge
+them.
+
+ENTPs are good at innovative projects and can administer them well
+if dull routine is not involved.  They usually are outstanding
+teachers, continuously devising new participative ways to make
+learning exciting for the students.
+
+As an employee, an ENTP may work against the system just for the
+joy of being one-up.  For ENTPs, to be taken-in, to be manipulated
+by another, is humiliating; this offends their joy in being masters
+of the art of one-upmanship.
+
+ENTPs are the natural engineers of human relationships and human
+systems.  Their good humor and optimistic outlook tend to be
+contagious, and people seek out their company.
+
+As mates, ENTPs tend to create a lively living environment.  They
+are gregarious, laugh easily and often, and are typically in good
+humor.
+
+Orderliness in the routines of daily living is not apt to inspire
+them; they usually solve this problem by mobilizing those around
+them.  Tom Sawyer illustrated this talent when he solved the
+problem of getting his Aunt Polly's fence whitewashed.  Life with
+ENTPs is likely to be a daring adventure; they can lead families
+into physical and economic dangers.  ENTPs improvise to remain
+unaware that they do not have the necessary knowledge of the
+situation to ward off such dangers.
+
+If the mate of an ENTP is not competitive, he or she is likely to
+find the one-up/one-down transactions somewhat wearing.  If the
+mate is competitive, the result might be conflict.
+
+Although usually good providers of economic necessities, ENTPs at
+times engage in brinkmanship with their careers, placing them in
+jeopardy and behaving as if unaware of the consequences; they may
+thus offer unnecessary challenges to those who have power over
+their professional success.  When challenges elicit negative
+responses from superiors, ENTPs are apt to react with delight at
+having an opportunity to improvise a solution to the crisis - and,
+more often than not, they succeed in doing so.
+
+ENTPs are likely to have all sorts of hobbies and to be experts in
+unexpected areas, but they are not apt to share these hobbies with
+their mates or children in the sense of teaching them.  In fact,
+ENTPs may be very inconsistent in the attention given to offspring.
+
+Usually, it is feast or famine.  ENTPs have a lively circle of
+friends and are interested in their ideas and activities.  They are
+usually easygoing, seldom critical or nagging.  At their worst,
+they can show undependable, fickle characteristics and may be
+easily discouraged.
diff --git a/xtrn/smm/mb-esfj.asc b/xtrn/smm/mb-esfj.asc
new file mode 100644
index 0000000000000000000000000000000000000000..c3801a37f6f07bf4b3b308a40146e9ee33cf69b9
--- /dev/null
+++ b/xtrn/smm/mb-esfj.asc
@@ -0,0 +1,118 @@
+ESFJs in short:
+
+Warmhearted, talkative, popular, conscientious, born cooperators,
+active committee members.  Need harmony and may be good at creating
+it.  Always doing something nice for someone.  Work best with
+encouragement and praise.  Little interest in abstract thinking or
+technical subjects.  Main interest is in things that directly and
+visibly affect people's lives.
+
+About 13% of the population.
+
+Details about ESFJs:
+
+ESFJs, the most sociable of all the types, are energized by
+interactions with people, tending to idealize whatever or whoever
+they admire.  'Harmony' is a key to this type, which is represented
+in about 13 percent of the general population.
+
+ESFJs are the great nurturers of established institutions such as
+the home, the school, the church, and civic groups.  Wherever they
+go, they promote harmony and harmonious relationships.  They are
+outstanding hosts or hostesses, able to call people by name,
+usually after one introduction.  At a social gathering they can be
+observed attending to the needs of others, trying to insure that
+all are comfortable and involved.
+
+Social ties matter to the ESFJs, and their conversations often
+drift to the nostalgic recounting of past memories.  Traditions are
+developed, supported, and carefully observed by the ESFJ.
+
+ESFJs are hurt by indifference and need to be appreciated both for
+themselves and for the abundance, typically in the form of
+services, they give to others.  They are conscious of appearances
+and take the opinions of others regarding social standards very
+seriously.  Values in an ESFJ may take the form of 'shoulds' and
+'should nots' and may be freely expressed.  Conscientious and
+orderly, ESFJs may become restless when isolated from people.
+
+Career selection by ESFJs may lean toward service occupations.
+They have such outgoing personalities that they are outstanding at
+selling, being an invariable winner in sales contests.  They are
+apt to have seniority in any sales group within an organization.
+Observation of ESFJs at work in a sales transaction will
+demonstrate how this type personalizes the sale: The customer is
+not buying the product; he or she is buying personally from the
+ESFJ.
+
+This same characteristic causes ESFJs to be good in teaching,
+preaching, supervision, administration, coaching, and, in general,
+people-to-people jobs.  They seldom become a source of irritation
+to their superiors, for they respect and obey the rules and
+regulations, are duty and service oriented.  They are loyal to
+their bosses.
+
+ESFJs are likely to be aware of and enjoy discussing events and
+problems in the lives of their colleagues; but when conversations
+turn to abstractions of philosophy or science, the ESFJ may become
+restive.  Analysis of the complex - for example, an attempt to find
+an explanation of events through an analysis of principles - does
+not excite their interest, as it does the NTs.
+
+ESFJ mates have a set of values which contain clear 'shoulds' and
+'should-nots', and they expect their family to abide by these.
+They are conscientious about home responsibilities, are orderly
+about the home, and prefer that other occupants be the same.  They
+enjoy socializing and entertaining.
+
+ESFJs want family decisions settled efficiently and quickly and
+want family living routinized, scheduled, and correctly executed.
+They do not rebel against routine operations, and are devoted to
+the traditional values of home and hearth, respect their marriage
+vows, and are the most sympathetic of all types.
+
+They tend to be dependent on their mates and may marry to insure
+that they have a proper place in the social strata.  They enjoy the
+rituals in connected with serving of good food and beverages,
+thrive on festive occasions, respect and accumulate a goodly store
+of material possessions.  They take their role in the community
+seriously and are sensitive to the acknowledged, official
+decision-makers and identify with them.  They are aware of status,
+and often depend on higher authority as the source of opinions and
+attitudes.
+
+ESFJs wear their hearts on their sleeves and are outgoing in their
+emotional reactions.  They need to be needed, loved, and
+appreciated and may spend much energy reassuring themselves that
+this is the case.  They can become melancholy and depressed and
+even suicidal if they take the blame for whatever might be wrong in
+their institution or their personal relationships - as they are
+prone to do.
+
+ESFJs usually respect and revere their parents, and as children
+were responsive and obedient pupils.  They seem able to express the
+right feeling for a given situation.
+
+They are softhearted, sentimental, and usually observe with gusto
+and a flourish birthdays, anniversaries, and the like, making of
+the event a delightful, important occasion.  At the same time,
+however, ESFJs can cause others undue tension by expressing
+anticipations of gloom and doom, exhibiting a bent toward the
+pessimistic that can be contagious.  They need to control their
+fears that the worst is sure to happen and suppress their tendency
+toward crepe-hanging and anticipating disasters.
+
+The children of an ESFJ are seen as an extension of the family, and
+all they do reflects on the ESFJ.  If things do not go well, the
+SFJ may be critical, even carping toward his or her mate and
+children.
+
+This type may marry alcoholics or others who are particularly
+needy.  If a female ESFJ is married to a mate who is not a good
+provider, she can become nagging and brood over a comparison of her
+possessions and status with that of others.
+
+ESFJs, male or female, live in terms of people and things rather
+than in terms of ideas and principles.  They enjoy the process of
+decision-making, particularly when focus is on the 'usefulness' of
+things and people.
diff --git a/xtrn/smm/mb-esfp.asc b/xtrn/smm/mb-esfp.asc
new file mode 100644
index 0000000000000000000000000000000000000000..633ade2c9b416ce9e7b14a820623ffb6516b9c96
--- /dev/null
+++ b/xtrn/smm/mb-esfp.asc
@@ -0,0 +1,89 @@
+ESFPs in short:
+
+Outgoing, easygoing, accepting, friendly, enjoy everything and make
+things more fun others by their enjoyment.  Like sports and making
+things.  Know what's going and join in eagerly.  Find remembering
+facts easier than mastering theories.  Are best in situations that
+need sound common sense and practical ability with people as well
+as with things.
+
+About 13% of the population.
+
+Details about ESFPs:
+
+ESFPs radiate attractive warmth and optimism.  Smooth, witty,
+charming, clever, voluble, and open to the environment - this
+describes ESFPs who, like ESTPs, represent about 13 percent of the
+general population.  They are great fun to be with and are the most
+generous of all the types.  'Performer' would be the word which
+best describes an ESFP.
+
+ESFPs will avoid being alone and seek the company of others
+whenever possible.  ESFPs easily find company, for others are
+usually highly entertained by the presence of an ESFP.  ESFPs love
+excitement and create it wherever they are.  Their joy of living is
+contagious and generally they wear happy faces.  Often outstanding
+conversationalists, their flowing banter is amusing in its wit.
+
+ESFPs have an air of sophistication and are likely to be dressed in
+the latest fashion, displaying an enjoyment of all the good things
+of life: dress, food, physical comfort, and happy times.  ESFPs
+create a mood of "eat, drink, and be merry" wherever they go, and
+around them life can have a continual party-like atmosphere of
+gaiety.
+
+ESFPs make exciting, if somewhat unpredictable mates, which may
+give quieter type mates some anxiety and tension from living on the
+edge of adventure.  The home of an ESFP is likely to be filled with
+people all having a good time.  Problems will not be allowed to
+make their appearance.  The ESFP accomplishes this by taking an
+attitude of "walking by the graveyard whistling," refusing to
+recognize doom and gloom.
+
+ESFPs can be generous to a fault.  What is theirs is yours, and
+what is yours is yours still.  They give assistance to one and all
+without expectation of a return, just as they love freely without
+expecting something in return.  ESFPs seem to view life as an
+eternal cornucopia from which flows an endless supply of pleasures
+that require no effort on their part to insure.
+
+ESFPs' talent for enjoying life can make them more subject to
+temptations than are other types.  They are inclined to be
+impulsive, and thus both male and female ESFPs are vulnerable to
+psychological seduction, if not physical seduction, with an ESFP
+giving in easily and agreeably to the demands of others.  As a
+parent, the ESFP will be entertaining, a friend, and a source of
+fun and excitement.  When there is sickness, or trouble, however,
+ESFPs may become impatient and may want to absent themselves.
+
+ESFPs' tolerance for anxiety is the lowest of all types.  Anxiety
+is avoided by ignoring the dark side of a situation as long as
+possible.  They are inclined to be somewhat self-indulgent, but,
+rather than make an outward show of resistance or make waves, ESFPs
+will give apparent compliance - and then go their own way to what
+they enjoy.
+
+ESFPs prefer active jobs and should not be given lonely, solitary
+assignments.  Outstanding in public relations, they love working
+with people.  Decisions are made with personal warmth, based on
+personal reference or reference to significant others.  This type
+relies on their personal experiences and generally show good common
+sense.
+
+The gregarious sociability and adaptability of ESFPs make them a
+source of warmth to others.  They do not mind telephone or personal
+interruptions and are verbally facile in both situations.  They can
+be counted on to have accurate data about the people around them,
+gaining these data through effortless and continuous observations.
+
+ESFPs are not deeply interested in scholastic pursuits, wanting
+knowledge only for immediate utility.  They avoid science and
+engineering, gravitate toward business, and are adept at selling,
+particularly selling tangibles.
+
+They can be effective in education, especially elementary school
+teaching, and can enjoy nursing for its drama.  They are good at
+working with people in crisis, a facility, which often leads ESFPs
+into social work.  They also enjoy entertaining people and are thus
+drawn to the performing arts, thriving on the excitement of being
+in the limelight.
diff --git a/xtrn/smm/mb-estj.asc b/xtrn/smm/mb-estj.asc
new file mode 100644
index 0000000000000000000000000000000000000000..a58f235e23972f61f097e6ed7999204b4af43eae
--- /dev/null
+++ b/xtrn/smm/mb-estj.asc
@@ -0,0 +1,70 @@
+ESTJs in short:
+
+Practical, realistic, matter-of-fact, with a natural head for
+business or mechanics.  Not interested in subjects they see no use
+for, but can apply themselves when necessary.  Like to organize and
+run activities.  May make good administrators, especially if they
+remember to consider others' feelings and points of view.
+
+About 13% of the population.
+
+Details about ESTJs:
+
+ESTJs are very much in touch with the external environment.  They
+know their community and usually are pillars of strength.  The best
+adjective to describe ESTJs would be 'responsible.'  They represent
+about 13 percent of the general population.
+
+ESTJs are outstanding at organizing procedures and in detailing
+rules and regulations.  They like to see things done correctly.
+They tend to be impatient with those who do not carry out
+procedures with sufficient attention to those details, prescribed
+by those with the most experience, that will get the job done
+right.
+
+ESTJs are comfortable in evaluating others and tend to judge how a
+person is doing in terms of standard operating procedures.  They
+may, at times, be abrupt with those who do not follow the rules
+correctly.  ESTJs are realistic, matter-of-fact, and more curious
+about new devices and processes than about new principles and
+theories.
+
+ESTJs generally are loyal to their institutions, work, and
+community and make excellent, faithful mates and parents.  They see
+where their duty lies and are not likely to shirk the doing of that
+duty, even when this requires considerable sacrifice on their part.
+
+They frequently rise to positions of responsibility in their jobs,
+in the community, and in their religious affiliations.  They very
+often belong to several civic clubs and support them both through
+steady attendance and through their spoken attitudes.  ESTJs
+themselves are punctual and expect others to be also.
+
+ESTJs may not always be responsive to points of view and emotions
+of others and may have a tendency to jump to conclusions too
+quickly at times.  They may not always be willing to listen
+patiently to opposing views; they are especially vulnerable to this
+tendency when in positions of authority.  They may need to make
+special effort to remain open to input from others who are
+dependent on them - their children, spouses, and employees.
+
+ESTJs are so in tune with the established, time-honored
+institutions and ways of behaving within those institutions that
+they cannot understand those who might wish to abandon or radically
+change those institutions.  They follow routines well at home and
+at work, tending to have a place for everything and wanting
+everything in its place.  They are usually neat and orderly at work
+and at play.
+
+They approach human relations through traditions and rituals,
+promoting harmony and contentment in their relationships through
+creating well-worked-out routines and procedures.  Family
+traditions have meaning for ESTJs, and they willingly participate
+in observing these.  They enjoy opportunities to see friends,
+former colleagues, and relatives at functions such as retirement
+dinners, annual picnics, Thanksgiving gatherings, and weddings.
+
+ESTJs are relatively easy to get to know; they do not tend to
+confuse people by sending double messages.  They are dependable and
+consistent, and what they seem to be is what they are.
+
diff --git a/xtrn/smm/mb-estp.asc b/xtrn/smm/mb-estp.asc
new file mode 100644
index 0000000000000000000000000000000000000000..e8c8c956fe9f53f729444e80eff0fbb5a5567459
--- /dev/null
+++ b/xtrn/smm/mb-estp.asc
@@ -0,0 +1,119 @@
+ESTPs in short:
+
+Matter-of-fact, do not worry or hurry, enjoy whatever comes along.
+Tend to like mechanical things and sports, with friends on the
+side.  May be a bit blunt or insensitive.  Adaptable, tolerant,
+generally conservative in values.  Dislike long explanations.  Are
+best with real things that can be worked, handled, taken apart or
+put together.
+
+About 13% of the population.
+
+Details about ESTPs:
+
+ESTPs are men and women of action.  When someone of this
+personality is present, things begin to happen.  The lights come
+on, the music plays, the game begins.  And a game it is for the
+ESTP, The outstanding entrepreneur, the international diplomat, the
+conciliator, and the negotiator 'par excellence.'
+
+Approximately 13 percent of the general population are of this
+extroverted, sensing, thinking, perceiving type, and if only one
+adjective could be used to describe ESTPs 'resourceful' would be an
+apt choice.
+
+Life is never dull around ESTPs.  Their attractive, friendly style
+has a theatrical flourish which makes even the most routine,
+mundane event seem exciting.  ESTPs usually know the location of
+the best restaurants, and headwaiters are likely to call them by
+name.  ESTPs are socially sophisticated, suave, and urbane and are
+master manipulators of the external environment.
+
+ESTPs are uncanny at observing people's motivations, somehow
+hypersensitive to minimal nonverbal cues which other types might
+miss.  And they are masters at using these observations to "sell"
+the "client."  The eye of the ESTP is ever on the eye of the
+beholder, and all actions are directed toward this audience.
+
+Witty, clever, and fun, ESTPs seem to possess an unusual amount of
+empathy, when in fact this is not the case; rather, they are so
+acutely aware of minimal signals from others that they are usually
+several jumps ahead in anticipation of another's position.  And
+ESTPs can use information gained to the ends they have in mind -
+apparently with nerves of steel, engaging in what seems to others
+to be suicidal brinkmanship.  Other types may find this exhausting,
+but ESTPs are exhilarated by working close to the edge of disaster.
+
+ESTPs are ruthless pragmatists and often offer the ends as
+justification for whatever means they see as necessary -
+regrettable, perhaps, but necessary.  Usually, however, ESTPs do
+not care to justify actions, but prefer instead to get on to the
+next action.
+
+ESTPs are outstanding as initiators of enterprises that bring
+people together to negotiate.  They make invaluable itinerant
+administrators who can pull troubled companies or institutions out
+of the red very quickly, and with style!
+
+They can sell an idea or project in a way no other type can, but
+won't follow through on the tedious administrative details of a
+project.  This characteristic often causes ESTPs to be
+unappreciated for the extraordinary talents they have, for people
+lose sight of the idea contributed and focus on the details left
+undone, becoming critical of ESTPs' weaknesses rather than
+appreciating their strength.
+
+Few enterprises which are institutionally based use ESTPs as they
+should be used.  When they strike out on their own, however, they
+do not always succeed, for their unwillingness to bother with
+follow-up details may cause an otherwise excellent project to fail.
+ESTPs need to be sure they have someone who will take care of
+follow-up if at all possible.
+
+If the promotional, entrepreneurial capabilities of ESTPs are used
+to constructive ends, an institution is fortunate for their
+presence.
+
+If their desire for excitement is not met constructively, however,
+these energies may be channeled into destructive, antisocial
+activities such as those of the confidence rackets -
+counterfeiting, bad-check artistry, safe-cracking, and swindling. A
+movie of the early 1970's which caught this use of the ESTP's
+talent was The Sting.
+
+ESTPs live in the immediate moment and as mates lend excitement -
+and unpredictability - to the relationship.  The ESTP mate is
+usually extremely attentive in public and smooth in social rituals.
+They carry on amusing repartee, and laughter surrounds them as they
+recount from their endless supply of clever jokes and stories.
+
+Charm radiates from ESTPs.  Nothing is too good for their friends,
+although family responsibilities may, at times, be given second
+priority.  The ESTP's mate may in time come to feel like an object
+- the female a chattel and the male a negotiable commodity.  Deep
+commitments do not always occur in the lives of ESTPs, although
+they are always popular and know many, many people by name.
+
+Relationships usually are conditional, and the condition is the
+consideration of what the ESTP has to gain  from the relationship.
+Anything gained, however, is shared freely and generously with the
+mate.  The unexpected gift, the impulsive trip to Paris, the
+extravagant surprise at Christmas - all these an ESTP brings to a
+mate.  Fun, excitement, laughter, and that element of
+unpredictability are characteristic of their relationship.
+
+The ESTPs have a low tolerance for anxiety and are apt to avoid or
+leave situations that are consistently filled with interpersonal
+tensions.  ESTPs are usually somewhat of a mystery to their mates
+and to others.  Few people comprehend this unique personality.
+
+ESTPs themselves understand well the maxim, "He who travels
+fastest, travels alone."  Still, ESTPs are not likely to be lonely
+for long.
+
+ESTPs meet life with a hearty appetite for the good things of the
+world, searching out excitement, perhaps as a warrior, an athlete,
+an adventurer, or as a professional gambler, but always seeking the
+thrill of courting Lady Luck in one fashion or another.  A theme of
+seeking excitement through taking of risks runs through the lives
+of ESTPs.
diff --git a/xtrn/smm/mb-f.asc b/xtrn/smm/mb-f.asc
new file mode 100644
index 0000000000000000000000000000000000000000..49cec2c04674a129e55f92a69445efb9fd231c59
--- /dev/null
+++ b/xtrn/smm/mb-f.asc
@@ -0,0 +1,7 @@
+The Feeler tends toward subjective & social values, personal feelings, 
+harmony, intimacy, sympathy and devotion. The F overextends himself to meet
+others needs, enjoys serving people, tends to retract statements that offend
+others, asks himself 'how will this affect the people involved'.
+The feeler feels the thinker has ice in his veins.
+The population is equally divided between Ts and Fs, although more men tend
+to be Thinkers & more women tend to be Feelers.
diff --git a/xtrn/smm/mb-fp.asc b/xtrn/smm/mb-fp.asc
new file mode 100644
index 0000000000000000000000000000000000000000..cdc07cb81e34bdf64acbbb1968a2db2161de9bce
--- /dev/null
+++ b/xtrn/smm/mb-fp.asc
@@ -0,0 +1 @@
+You should never have a party without an FP.
diff --git a/xtrn/smm/mb-i.asc b/xtrn/smm/mb-i.asc
new file mode 100644
index 0000000000000000000000000000000000000000..8ff52b2d3e61ef668e0123b8e7e6f0e0bf8c6e62
--- /dev/null
+++ b/xtrn/smm/mb-i.asc
@@ -0,0 +1,9 @@
+An Introvert gets his energy from inside himself and needs his own space for 
+recharging his batteries. An Introvert is drained by being around people.
+I is perceived as a good listener, tends to be shy, likes to share occasions
+with a few close friends, doesn't like interruptions, is suspicious if people 
+are too complimentary, gets irritated if people repeat what has already been
+said. 'I' tends to consider - then speak, An 'I' is reluctant to reveal himself
+and may resist taking this test for fear of being exposed, but can complete 
+the test in a short time without carefully considering & weighing each 
+question. About 25% of the population are type I.
diff --git a/xtrn/smm/mb-infj.asc b/xtrn/smm/mb-infj.asc
new file mode 100644
index 0000000000000000000000000000000000000000..73e1893f97ed90c324ce71ca53b6cea9830a5e98
--- /dev/null
+++ b/xtrn/smm/mb-infj.asc
@@ -0,0 +1,144 @@
+INFJs in short:
+
+Succeed by perseverance, originality and desire to do whatever is
+needed or wanted.  Put their best efforts into their work.  Quietly
+forceful, conscientious, concerned for others.  Respected for their
+firm principles.  Likely to be honored and followed for their clear
+convictions as to how best to serve the common good.
+
+About 1% of the population.
+
+Details about INFJs:
+
+INFJs focus on possibilities, think in terms of values, and come
+easily to decisions.  The small number of this type (1 percent) is
+regrettable, since INFJs have an unusually strong drive to
+contribute to the welfare of others and genuinely enjoy helping
+their fellow men.  This type has great depth of personality; they
+are themselves complicated, and can understand and deal with
+complex issues and people.
+
+It is an INFJ who is likely to have visions of human events past,
+present, or future.  If a person demonstrates an ability to
+understand psychic phenomenon better than most others, this person
+is likely to be an INFJ.
+
+Characteristically, INFJs have strong empathetic abilities and can
+be aware of another's emotions or intents even before that person
+is conscious of these.  This can take the form of feeling the
+distress or illnesses of others to an extent which is difficult for
+other types.  INFJs can intuit good and evil in others, although
+they seldom can tell how they came to know.  Subsequent events tend
+to bear them out, however.
+
+INFJs are usually good students, achievers who exhibit an
+unostentatious creativity.  They take their work seriously and
+enjoy academic activity.  They can exhibit qualities of
+over-perfectionism and put more into a task than perhaps is
+justified by the nature of the task.  They generally will not be
+visible leaders, but will quietly exert influence behind the
+scenes.
+
+INFJs are hard to get to know.  They have an unusually rich inner
+life, but they are reserved and tend not to share their reactions
+with those they trust.  Because of their vulnerability through a
+strong facility to interject, INFJs can be hurt rather easily by
+others, which, perhaps, is at least one reason they tend to be
+private people.  People who have known an INFJ for years may find
+sides emerging which come as a surprise.  Not that INFJs are
+inconsistent; they are very consistent and value integrity.  But
+they have convoluted, complex personalities which sometimes puzzle
+even them.
+
+INFJs like to please others and tend to contribute their best
+efforts in all situations.  They prefer and enjoy agreeing with
+others, and find conflict disagreeable and destructive.  What is
+known as ESP is likely found in an INFJ.
+
+INFJs have vivid imaginations exercised both as memory and
+intuition, and this can amount to genius, resulting at times in an
+INFJs being seen as mystical.  This unfettered imagination often
+will enable this person to compose complex and often aesthetic
+works of arts such as music, mathematical systems, poems, plays,
+and novels.  In a sense the INFJ is the most poetic of all the
+types.  INFJs can have uncanny communications with certain
+individuals at a distance.
+
+INFJs often select liberal arts as a college major and opt for
+occupations which involve interacting with people, but on a
+one-to-one basis.  For example, the general practitioner in
+medicine might be an INFJ, or the psychiatrist or psychologist.  As
+with all NFs, the ministry holds attraction, although the INFJ must
+develop an extroverted role here which requires a great deal of
+energy.
+
+INFJs may be attracted to writing as a profession, and often they
+use language which contains an unusual degree of imagery.  They are
+masters of the metaphor, and both their verbal and written
+communications tend to be elegant and complex.  Their great talent
+for language usually is directed toward people, describing people
+and writing to communicate with people in a personalized way. INFJs
+who write comment often that they write with a particular person in
+mind; writing to a faceless abstract audience leaves them
+uninspired.
+
+INFJs make outstanding individual therapists who have the ability
+to get in touch with the archetypes of their patients in a way some
+other types do not.  The INFJs are also the most vulnerable of all
+types to the eruptions of their own archetypal material.  As
+therapists, INFJs may choose counseling, clinical psychology, or
+psychiatry, or may choose to teach in these fields.  Writing about
+these professions often intrigues an INFJ.
+
+Whatever their choice, they generally are successful in these
+fields because of their great personal warmth, their enthusiasm,
+their insight, their depth of concentration, their originality, and
+their organizational skills can all be brought into play.
+
+At work as well as socially, INFJs are highly sensitive in their
+handling of others and tend to work well in an organizational
+structure.  They have a capacity for working at jobs which require
+solitude and concentration, but also do well when in contact with
+people, providing the human interaction is not superficial.
+
+INFJs enjoy problem-solving and can understand and use human
+systems creatively and humanistically.  As employees and employers,
+INFJs are concerned with people's feelings and are able to provide
+in themselves a barometer of the feelings of individuals and groups
+within the organization.  INFJs listen well and are  willing and
+able to consult and cooperate with others.  Once a decision is
+made, they work to implement it.
+
+INFJs are usually good at public relations and themselves have good
+interpersonal relations.  They value staff harmony and want an
+organization to run smoothly and pleasantly, themselves making
+every effort to contribute to that end.
+
+They are crushed by too much criticism and can have their feelings
+hurt rather easily.  They respond to praise and use approval as a
+means of motivating others, just as they, the INFJs, are motivated
+by approval.  If they are subject to a hostile, unfriendly working
+condition or to constant criticism, they tend to lose confidence,
+become unhappy and immobilized, and finally become physically ill.
+
+As mates INFJs are devoted to their spouses, but may not always be
+open to physical approaches.  They tend to be physically
+demonstrative at times, but wish to choose when, which is when they
+are in the mood.  This may be quite confusing to an extroverted
+mate.
+
+Often an INFJs expressions of affection will be subtle, taking a
+humorous, unexpected turn.  INFJs need and want harmony in their
+home and find constant conflict, overt or covert, extremely
+destructive to their psyches.  Their friendship circle is likely to
+be small, deep, and longstanding.
+
+As parents, INFJs usually are fiercely devoted.  A female INFJ,
+particularly, is linked to her children in a way different from the
+other types: with almost a psychic symbiosis.  This deep bond can
+create an overdependency that can be unhealthy for both mother and
+child.  At the same time, INFJs tend to be good friends with their
+children, while firm in discipline.  They usually are concerned
+about the comfort of a home and most especially the comfort,
+physical health, and emotional well-being of both mates and
+children.
diff --git a/xtrn/smm/mb-infp.asc b/xtrn/smm/mb-infp.asc
new file mode 100644
index 0000000000000000000000000000000000000000..f99ff2b82f182c82526befe1da89b7e53bbebdba
--- /dev/null
+++ b/xtrn/smm/mb-infp.asc
@@ -0,0 +1,118 @@
+INFPs in short:
+
+Full of enthusiasms and loyalties, but seldom talk of these until
+they know you well.  Care about learning, ideas, language, and
+independent projects of their own.  Tend to undertake too much,
+then somehow get it done.  Friendly, but often too absorbed in what
+they are doing to be sociable.  Little concerned with possessions
+or physical surroundings.
+
+About 1% of the population.
+
+Details about INFPs:
+
+INFPs present a calm, pleasant face to the world and are seen as
+reticent and even shy.  Although they demonstrate a cool reserve
+toward others, inside they are anything but distant.  They have a
+capacity for caring which is not always found in other types.  They
+care deeply - indeed passionately - about a few special persons or
+cause.  One word that captures this type is 'idealistic'.  At
+times, this characteristic leaves them feeling isolated, especially
+since INFPs are found in only 1 percent of the general population.
+
+INFPs have a profound sense of honor derived from internal values.
+The INFP is the Prince or Princess of mythology, the King's
+champion, Defender of the Faith, and guardian of the castle.  Sir
+Galahad and Joan of Arc are male and female prototypes of an INFP.
+To understand INFPs their cause must be understood, for they are
+willing to make unusual sacrifices for someone or something
+believed in.
+
+INFPs seek unity in their lives, unity of body and mind, emotions
+and intellect.  They often have a subtle tragic motif running
+through their lives, but others seldom detect this inner minor key.
+The deep commitment of INFPs to the positive and the good causes
+them to be alert to the negative and the evil, which can take the
+form of a fascination with the profane.  Thus INFPs may live a
+paradox, drawn toward purity and unity but looking over the
+shoulder toward the sullied and desecrated.  When INFPs believe
+that they have yielded to an impure temptation, they may be given
+to acts of self-sacrifice in atonement.  The atonement, however, is
+within the INFP, who does not feel compelled to make public the
+issue.
+
+INFPs prefer the valuing process over the purely logical.  They
+respond to the beautiful versus the ugly, the good versus the bad,
+and the moral versus the immoral.  Impressions are gained in a
+fluid, global, diffused way.  Metaphors and similes come naturally
+but may be strained.
+
+INFPs have a gift of interpreting symbols, as well as creating
+them, and thus often write in lyric fashion.  They may demonstrate
+a tendency to take deliberate liberties with logic.  Unlike the NT,
+they see logic as something optional.
+
+INFPs also may, at times, assume an unwarranted familiarity with a
+domain, because their global, impressionistic way of dealing with
+reality may have failed to register a sufficient number of details
+for mastery.  INFPs may have difficulty thinking in terms of a
+conditional framework; they see things as either real or fancied,
+and are impatient with the hypothetical.
+
+At work, INFPs are adaptable, welcome new ideas and new
+information, are well aware of people and their feelings, and
+relate well to most, albeit with some psychological distance.
+INFPs dislike telephone interruptions and work well alone, as well
+as with others.  They are patient with complicated situations, but
+impatient with routine details.  They can make errors of fact, but
+seldom of values.  Their career choices may be toward the ministry,
+missionary work, college teaching, psychiatry, architecture,
+psychology - and away from business.
+
+They seem willing and usually are able to apply themselves
+scholastically to gain the necessary training for professional
+work, often doing better in college than in high school.  They have
+a natural interest in scholarly activities and demonstrate, as do
+other NFs, a remarkable facility for languages.  Often they hear a
+calling to go forth into the world to help others; they seem
+willing to make the necessary personal sacrifices involved in
+responding to that call, even if it means asking others to do
+likewise.
+
+INFPs can make outstanding novelists and character actors, for they
+able to efface their own personalities in their portrayal of a
+character in a way other types cannot.
+
+As mates, INFPs have a deep commitment to their pledges.  They like
+to live in harmony and may go to great lengths to avoid constant
+conflict.  They are sensitive to the feelings of others and enjoy
+pleasing those they care for.  They may find it difficult to
+reconcile a romantic, idealized concept of conjugal life with the
+realities of everyday living with another person.
+
+At times, INFPs may seem fearful of exuberant attainment, afraid
+that current advances may have to be paid for with later
+sacrifices.  The devil is sure to get his due if the INFP
+experiences too freely of success, or beauty, or health, or wealth,
+or knowledge.  And thus, INFPs guard against giving way to relaxing
+in the happiness of mating.  They may have difficulty in expressing
+affection directly, but communicate interest and affection
+indirectly.
+
+For INFPs, their home is their castle.  As parents, they are fierce
+in protection of their home and family and are devoted to the
+welfare of family members.  They have a strong capacity for
+devotion, sympathy, and adaptability in their relationships, and
+thus are easy to live with.  They are loyal to their family and,
+although they may dream of greener pastures, they soon locate the
+nettles.  The almost preconscious conviction that pleasure must be
+paid for with pain can cause a sense of uneasiness in the family
+system of an INFP, who may transmit an air of being ever-vigilant
+against invasion.
+
+In the routine rituals of daily living, INFPs tend to be compliant
+and may even prefer having decisions made on their behalf - until
+their value system is violated!  Then INFPs dig in their heels and
+will not budge from ideals.  Life with an INFP will go gently along
+for long periods, until an ideal is struck and violated.  Then an
+INFP will resist and insist.
diff --git a/xtrn/smm/mb-intj.asc b/xtrn/smm/mb-intj.asc
new file mode 100644
index 0000000000000000000000000000000000000000..18d1d03349d4806f6721f8a2e97282ff4ea8eb89
--- /dev/null
+++ b/xtrn/smm/mb-intj.asc
@@ -0,0 +1,169 @@
+INTJs in short:
+
+Usually have original minds and great drive for their own ideas and
+purposes.  In fields that appeal to them, they have a fine power to
+organize a job and carry it through with or without help.
+Skeptical, critical, independent, determined, often stubborn.  Must
+learn to yield less important points in order to win the most
+important.
+
+About 1% of the population.
+
+Details about INTJs:
+
+INTJs are the most self-confident of all the types, having
+"self-power" awareness.  Found in about 1 percent of the general
+population, the INTJs live in an introspective reality,  focusing
+on possibilities, using thinking in the form of empirical logic,
+and preferring that events and people serve some positive use.
+Decisions come naturally to the INTJs; once a decision is made,
+INTJs are at rest.  INTJs look to the future rather than the past,
+and a word which captures the essence of INTJs is 'builder' - a
+builder of systems and the applier of theoretical models.
+
+To INTJs, authority based on position, rank, title, or publication
+has absolutely no force.  This type is not likely to succumb to the
+magic of slogans, watchwords, or shibboleths.  If an idea or
+position makes sense to an INTJ, it will be adopted; if it doesn't,
+it won't, regardless of who took the position or generated the
+idea.  As with the INTP, authority per se does not impress the
+INTJ.
+
+INTJs do, however, tend to conform to rules if they are useful, not
+because they believe in them, or because they make sense, but
+because of their unique view of reality.  They are the supreme
+pragmatists, who see reality as something which is quite arbitrary
+and made up.  Thus it can be used as a tool - or ignored.
+
+Reality is quite malleable and can be changed, conquered, or
+brought to a heel.  Reality is a crucible for the refining of
+ideas, and in this sense, INTJs are the most theoretical of all the
+types. An INTJ sees reality as the pawn of ideas: No idea is too
+farfetched to be entertained.  INTJs are natural brainstormers,
+always open to new concepts, and, in fact, aggressively seeking
+them.
+
+INTJs manipulate the world of theory as if on a gigantic chess
+board, always seeking strategies and tactics that have high payoff.
+In their penchant for logic, the INTJs resemble the INTPs.  The
+logic of an INTJ, however, is not confined to the expressibly
+logical.  Unlike INTPs, INTJs need only to have a vague, intuitive
+impression of the unexpressed logic of a system to continue surely
+on their way.  Things need only 'seem' logical; this is entirely
+sufficient.
+
+Moreover, they always have a keen eye for the consequences of the
+application of new ideas or positions.  They can be quite ruthless
+in the implementation of systems, seldom counting personal cost in
+terms of time and energy.  Theories which cannot be made to work
+are quickly discarded by the INTJs.
+
+To understand INTJs, their way of dealing with reality rather than
+their way of dealing with ideas should be observed closely.  Their
+conscious thought is extroverted and empirical.  Hence, they are
+better at generalizing, classifying, summarizing, adducing
+evidence, proving and demonstrating than are the INTPs.
+
+The INTJs are somewhat less at home with pure reason, that is,
+systemic logic, where principles are explicit.  In this respect
+they resemble the ENTJs.  The INTJs, rather than using deductive
+logic, use their intuition to grasp coherence.
+
+INTJs have a drive to completion, always with an eye to long-term
+consequences.  Ideas seem to carry their own force for INTJs,
+although they subject every idea to the test of usefulness.
+
+Difficulties are highly stimulating to INTJs, who love responding
+to a challenge that requires creativity.  Those personality traits
+lead INTJs to occupations where theoretical models can be
+translated into actuality.  They build data and human systems
+wherever they work if given even a slight opportunity.  They can be
+outstanding in scientific research and also outstanding as
+executives who generate a plethora of implementations of ideas.
+
+Teamed with an INTP who is the architect of systems, the INTJ
+provides a dimension to an organization which insures that the work
+of the INTP does not gather dust on library shelves.
+
+INTJs can be very single-minded at times; this can be either a
+weakness or a strength in their careers, for they can ignore the
+points of view and wishes of others.  INTJs usually rise to
+positions of responsibility, for they work long and hard and steady
+in their pursuit of goals, sparing neither time nor effort on their
+part or that of their colleagues and employees.
+
+INTJs live to see systems translated into substance; an INTP, by
+way of contrast, is content to design the system.  In both, these
+types, however, coherence is the master.  Both internal and
+external consistency are important, and if an INTJ finds that he or
+she is in a working situation where overlapping functions,
+duplication of effort, inefficient paper flow, and waste of human
+and material resources abound, the INTJ cannot rest until an effort
+is made to correct the situation.  Cost-effectiveness is a concept
+which has a strong imperative for INTJs, who frequently select
+occupations in engineering, particularly human engineering.
+
+They also can be found in the physical sciences, in roles which
+require development, such as curriculum building, and, in general,
+any job which requires the creation and application of technology
+to complex areas.
+
+Fellow workers of INTJs often feel as if the INTJ can see right
+through them, and often believe that the INTJ finds them wanting.
+This tendency of people to feel transparent in the presence of the
+INTJ often results in relationships which have psychological
+distance.  Thus colleagues find the INTJ apparently unemotional
+and, at times, cold and dispassionate.  Because of their tendency
+to drive others as hard as themselves, INTJs often seem demanding
+and difficult to satisfy.
+
+INTJs are high achievers in school and on the job.  On the job,
+they take the goals of the institution seriously and continually
+strive to respond to these goals.  They make dedicated, loyal
+employees whose loyalties are directed toward the system, rather
+than toward individuals within the system.  So as the people of an
+institution come and go, the INTJs have little difficulty - unlike
+the NFs, who have their loyalties involved more with persons than
+offices.
+
+INTJs ordinarily tend to verbalize the positive and eschew comments
+of a negative nature; they are more interested in moving an
+institution forward than commiserating about mistakes of the past.
+
+As mates, INTJs want harmony and order in the home and in
+relationships.  They are the most independent of all the types.
+They will trust their intuitions about others when making choices
+of friends and mates, even in the face of contradictory evidence
+and pressures applied by others.
+
+The emotions of an INTJ are hard to read, and neither male nor
+female INTJ is apt to express emotional reactions.  At times, both
+will seem cold, reserved, and unresponsive, while in fact INTJs are
+almost hypersensitive to signals of rejection from those for whom
+they care.
+
+In social situations, INTJs may also be unresponsive and may
+neglect to observe small rituals designed to put others at their
+ease.  For example, INTJs may communicate that time is wasted if
+used for idle dialogue, and thus people receive a sense of hurry
+from an INTJ which is not always intended.  In their interpersonal
+relationships, INTJs are usually better in a working situation than
+in recreational situations.  They do not enjoy physical contact
+except with a chosen few.
+
+As parents, INTJs are dedicated and single-minded in their
+devotion: Their children are a major focus in life.  They are
+supportive of their children and tend to allow them to develop in
+directions of their own choosing.  INTJs usually are firm and
+consistent in their discipline and rarely care to repeat directions
+given to children - or others.
+
+Being the most independent of all the types, they have a strong
+need for autonomy; indifference or criticism from people in general
+does not particularly bother INTJs, if they believe that they are
+right.  They also have a strong need for privacy.
+
+The most important preference of an INTJ is 'intuition', but this
+is seldom seen.  Rather, the function of 'thinking' is used to deal
+with the world and with people.  INTJs are vulnerable in the
+emotional area and may make serious mistakes here.
diff --git a/xtrn/smm/mb-intp.asc b/xtrn/smm/mb-intp.asc
new file mode 100644
index 0000000000000000000000000000000000000000..97594584b51cc919a9a4f7e051f459220aa97e14
--- /dev/null
+++ b/xtrn/smm/mb-intp.asc
@@ -0,0 +1,118 @@
+INTPs in short:
+
+Quiet, reserved, impersonal.  Enjoy especially theoretical or
+scientific subjects.  Logical to the point of hairsplitting.
+Usually interested mainly in ideas, with little liking for parties
+or small talk.  Tend to have sharply defined interests.  Need
+careers where some strong interest can be used and useful.
+
+About 1% of the population.
+
+Details on INTPs:
+
+INTPs exhibit the greatest precision in thought and language of all
+the types; they tend to see the distinctions and inconsistencies in
+thought and language instantaneously.  The one word which captures
+the unique style of INTPs is 'architect' - the architect of ideas
+and systems as well as the architect of edifices.  This type is
+found in only 1 percent of the population and therefore is not
+encountered as frequently as some of the other types.
+
+INTPs detect contradictions in statements no matter how distant in
+space or time the contradictory statements were produced.  The
+intellectual scanning of INTPs has a principled quality; that is,
+INTPs search for whatever is relevant and pertinent to the issue at
+hand.  Consequently, INTPs can concentrate better than any other
+type.
+
+Authority derived from office, position, or wide acceptance does
+not impress INTPs.  External authority per se is irrelevant.  INTPs
+abhor redundancy and incoherence.
+
+Possessing a desire to understand the universe, an INTP is
+constantly looking for natural law.  Curiosity concerning these
+keys to the universe is a driving force in this type.
+
+INTPs prize intelligence in themselves and in others, but can
+become intellectual dilettantes as a result of their need to amass
+ideas, principles, or understanding of behavior.  And once they
+know something, it is remembered.
+
+INTPs can become obsessed with analysis.  Once caught up in a
+thought process that thought process seems to have a will of its
+own for INTPs, and they persevere until the issue is comprehended
+in all its complexity.  They can be intellectual snobs and may show
+impatience at times with others less endowed intellectually.  This
+quality, INTPs find, generates hostility and defensive behaviors on
+the part of others, who may describe an INTP as arrogant.
+
+For INTPs, the world exists primarily to be understood.  Reality is
+trivial, a mere arena for proving ideas.  It is essential that the
+universe is understood and that whatever is stated about the
+universe is stated correctly, with coherence and without
+redundancy.  This is the INTP's final purpose.  It matters not
+whether others understand or accept his or her truths.
+
+The INTP is the logician, the mathematician, the philosopher, the
+scientist; any pursuit requiring architecture of ideas intrigues
+this type.
+
+INTPs should not, however, be asked to work out the implementation
+of application of their models to the real world.  The INTP is the
+architect of a system and leaves it to others to be the builder and
+the applicator.  Very often, therefore, the INTP's work is not
+credited to him or her.  The builder and the applier gains fame and
+fortune, while the INTP's name remains obscure.  Appreciation of an
+INTP's theoretical work frequently comes posthumously - or the work
+may never be removed from library shelves at all and thus lost.
+
+INTPs tend not to be writers or to go into sales work.  They are,
+however, often excellent teachers, particularly for advanced
+students, although INTPs do not always enjoy much popularity, for
+they can be hard taskmasters.
+
+They are not good at clerical tasks and are impatient with routine
+details.  They prefer to work quietly, without interruption, and
+often alone.  If an organization is to use the talents of an INTP
+appropriately, the INTP must be given an efficient support staff
+who can capture ideas as they emerge and before the INTP loses
+interest and turns to another idea.
+
+INTPs take their mating relationship seriously and usually are
+faithful and devoted - albeit preoccupied at times.  They are not
+likely to welcome constant social activity or disorganization in
+the home.
+
+In all probability, the mate of an INTP will initiate and manage
+the social life.  If left to his or her own devices, the INTP mate
+will retreat into the world of books and emerge only when physical
+needs become imperative.
+
+INTPs are, however, willing, compliant, and easy to live with,
+although somewhat forgetful of appointments, anniversaries, and the
+rituals of daily living - unless reminded.  They may have
+difficulty expressing their emotions verbally, and the mate of an
+INTP may believe that he/she is somewhat taken for granted.
+
+As a parent, the INTP is devoted; they enjoy children and are
+serious about their upbringing.  The home of an INTP parent is
+usually calm, low-key in discipline, but well run and ordered.
+
+INTPs deal with the environment primarily through intuition, and
+their strongest quality, the thinking function, remains relatively
+hidden except in close associations.  Therefore, INTPs are often
+misunderstood, seen as difficult to know, and seldom perceived at
+their true level of competency.
+
+They are inclined to be shy except with close friends, and their
+reserve is difficult to penetrate.  They are very adaptable until
+one of their principles is violated.  Then INTPs are not adaptable
+at all!
+
+They may have difficulty in being understood by others because they
+tend to think in a complicated fashion and want to be precise,
+never redundant in their communications.
+
+Because their feeling qualities may be underdeveloped, they may be
+insensitive to the wants and wishes of others, often quiet unaware
+of the existence of these wants and wishes.
diff --git a/xtrn/smm/mb-intro.asc b/xtrn/smm/mb-intro.asc
new file mode 100644
index 0000000000000000000000000000000000000000..bdd60da67d3464895750139400e8d345bc9425f1
--- /dev/null
+++ b/xtrn/smm/mb-intro.asc
@@ -0,0 +1,18 @@
+nyhThe Myers-Briggs Personality Test nc
+
+The test you are about to take is titled, "hThe Keirsey Temperament Sorternc".
+
+There are no "right" or "wrong" answers to the questions in this inventory.
+Your answers will help show how you like to look at things and how you like to
+go about deciding things.  Knowing your own preferences and learning about
+other people's can help you understand where your special strengths are, what
+kind of work you might enjoy and be successful doing, and how people with
+different preferences can relate to each other and be valuable to society.
+ 
+Read each question carefully and indicate your answer by typing 'A', 'B' or
+'C' for cannot decide.
+
+Do not think too long about any question.
+
+The test consists of 70 questions.
+
diff --git a/xtrn/smm/mb-isfj.asc b/xtrn/smm/mb-isfj.asc
new file mode 100644
index 0000000000000000000000000000000000000000..c59d1d4a1930ac18c7ee6a63731e95fd0fe8c72c
--- /dev/null
+++ b/xtrn/smm/mb-isfj.asc
@@ -0,0 +1,116 @@
+ISFJs in short:
+
+Quiet, friendly, responsible and conscientious.  Work devotedly to
+meet their obligations.  Lend stability to any project or group.
+Thorough, painstaking, accurate.  May need time to master technical
+subjects, as their interests are usually not technical.  Patient
+with detail and routine.  Loyal, considerate, concerned with how
+other people feel.
+
+About 6% of the population.
+
+Details about ISFJs:
+
+Six out of every hundred people are ISFJs.  Here the primary is to
+be of service and to minister to individual needs.  ISFJs carry a
+sense of history, a sense of continuity with past events and
+relationships.  Traditions and the conservation of resources are
+highly valued.
+
+The least hedonistic of all types, ISFJs believe work is good, play
+must be earned.  ISFJs are willing to work long, long hours.  When
+they undertake a task, it will be completed if at all humanly
+possible.
+
+Adhering to an established way of doing things and doing them well
+is valued and respected.  The efficiency and effectiveness of an
+established procedure is not often questioned.  Procedures dictated
+by handbooks are law.  If others violate or ignore these standard
+operating procedures, ISFJs are annoyed and irritated, although
+they may not always display this reaction.  Usually, such
+irritation is turned inward and may be experienced as fatigue and
+muscle tension.
+
+ISFJs are super-dependable and seldom are happy working in
+situations where rules are constantly changing.  Their major need
+to be of service to others leads them into occupations such as
+nursing, teaching, secretarial work, medical practice (especially
+general practice), librarian work, and middle-management
+administrative jobs.
+
+They relate well to people who need them, for example, the sick,
+the ignorant, students, and the "boss."  Much satisfaction comes to
+them when they are taking care of the needs of another and they
+render the service gently and helpfully.  When the recipient is no
+longer in need, the relationship may change its character, the ISFJ
+becoming disinterested.
+
+They enjoy assisting the downtrodden and can handle better than
+other types servility of others.  If a situation calls for such
+behavior on their part, they will show "due respect."
+
+ISFJs have an extraordinary sense of responsibility and an
+outstanding talent for executing routines which call for repeated,
+sequential procedures; for example, ISFJs make extraordinary
+secretaries, highly efficient nurses, and dedicated teachers.
+Speculation and theory do not intrigue ISFJs, who would rather
+leave the less practical matters to others while remaining
+themselves practical and down-to-earth.
+
+ISFJs tend to be devoted and loyal to a boss and tend to identify
+personally rather than institutionally.  They expect others,
+including the boss, to follow procedures and are distressed and
+embarrassed when people do not behave as they are supposed to
+behave.
+
+ISFJs often seem to feel personally responsible for seeing to it
+that people in an institution or business carry out established
+rules and routines.  They often are aware of status given by
+titles, environment, offices, and the like and can use this to
+advantage.
+
+They are aware of the value of material resources and abhor the
+squandering or misuse of these resources.  To save, to put
+something aside against an unpredictable future, to prepare for
+emergencies - these are important actions.
+
+ISFJs may experience some discomfort when placed in positions of
+authority over others and may tend to try to do everything
+themselves rather than insist that others do their jobs.  As a
+result, ISFJs are frequently overworked.
+
+ISFJs are devoted to mate and family and usually are excellent
+homemakers.  The home of an ISFJ is likely to be well kept inside
+and out.  Interior and exterior are meticulously maintained and
+appointed in the traditional manner.
+
+As a parent, the ISFJ expects children to conform to the rules of
+society and has a feeling of personal responsibility to see to it
+that these rules are honored.
+
+An ISFJ is apt to find the putting on of airs as offensive and
+tends to prefer modest, quiet friends rather than more boisterous
+ones.  For the ISFJ, people should behave according to their
+position in life, and the ISFJ may be annoyed by others who act
+either above or below their social or economic station.
+
+The ISFJ female often displays a flair for making the interior of
+the home attractive in a time-honored style, provides attractive,
+nourishing meals, and maintains the environment in a neat and
+orderly state.  To the ISFJ male and female, the home territory is
+important to own and to preserve.
+
+While ISFJs are super-dependable, they may be fascinated by and
+attracted to the irresponsible, the lush, the glutton.  Many ISFJs
+marry alcoholics and then proceed to conduct a rescue-rejection
+game without end, with the rescuing phase taking the guise of an
+attempt to reform.  Occasionally an ISFJ mother may reveal a
+tendency to find humor in the "waywardness" of a son, while raising
+her daughters to respect traditions and to do the Right Thing at
+the Right Time - always.
+
+ISFJs are frequently misunderstood and undervalued.  Their
+contributions often are taken for granted, and the ISFJ as well is
+often taken for granted.  This can cause an ISFJ to harbor feelings
+of resentment, and this bottled up emotion can gnaw inwardly,
+causing the ISFJ much undeserved suffering.
diff --git a/xtrn/smm/mb-isfp.asc b/xtrn/smm/mb-isfp.asc
new file mode 100644
index 0000000000000000000000000000000000000000..4b9b4d322b01c9f64fde08c362edddc48c7c3784
--- /dev/null
+++ b/xtrn/smm/mb-isfp.asc
@@ -0,0 +1,166 @@
+ISFPs in short:
+
+Retiring, quietly friendly, sensitive, kind, modest about their
+abilities.  Shun disagreements, do not force their opinions or
+values on others.  Usually do not care to lead but are often loyal
+followers.  Often relaxed about getting things done, because they
+enjoy the present moment and do not want to spoil it by undue haste
+or exertion.
+
+About 5% of the population.
+
+Details about ISFPs:
+
+Although all SPs (Sensuous Performers) are artisans in their
+nature, they usually do not pursue their artistry with the same
+devotion and adornment as the ISFP.  For whatever reason, the ISFP
+seems more inclined to the "fine arts" than the other SPs; so when
+an especially gifted composer, painter, or dancer shows up, he or
+she, more frequently than not, possesses the character of an ISFP.
+Beethoven, Toscanini, Rembrandt, and Nijinski, as shown by
+typohistorical research, were clear-cut ISFPs.
+
+But the ISFP temperament is very difficult to observe, even in the
+great artists, and so ISFP is probably the most misunderstood of
+all the types.
+
+A major source of misunderstanding is the tendency of ISFPs not to
+express themselves directly, but through action.  If they find a
+medium of expression, some art form, then the character is
+expressed in some degree in the medium.  If not, it simply doesn't
+come out, and no one knows them, this social reticence making the
+character all but invisible.
+
+Of course, in those rare cases where remarkable skill is achieved,
+such as in the virtuoso, ISFPs become celebrities, but their nature
+is still far from visible.  Harpo Marx, a brilliant comedic actor,
+may well be seen as a prototype, in his simultaneous celebrity and
+mute invisibility.
+
+On close observation, these relatively infrequent SPs (5 percent of
+the population is ISFP, as compared to 15 percent ESFP) are just as
+hedonistic and impulsive as the other SPs.  Here is no NF search
+for significance, nor for that matter any fascination with science
+(NT) or commerce (SJ).
+
+ISFPs live Epicurean lives in the here and now, and as gracefully
+as possible.  They do not plan and prepare.  Submergence in their
+artistry is not preparation for something later; rather they
+experience intensely, now.  ISFPs do not wait, for to wait is to
+see their impulse wither and die; they want and value their
+impulses and see them as the center of their lives.
+
+Nor are ISFPs devoted or committed to artful play; rather they are
+caught, as by a magnet or a whirlwind.  So then the long hours of
+"practice" the virtuoso "gives" to artistry is not practice at all
+and it is not given; it is doing and it is taken from the (willing)
+ISFP by the performance itself.  The act is ISFP's master, not the
+reverse, so we must abandon any notion of ISFPs as careful and
+devoted planners and of dutiful preparation and rehearsal.  They
+paint, or sing, or toot, or dance, or run, or skate, or pot, or
+whatever, simply because must: the mountain is climbed 'because it
+is there'.
+
+Because the ISFP is always caught up, so to speak, in whatever
+actions are underway, rather than practicing toward some distant
+goal, there is no question of the ISFP noticing fatigue, pain, or
+danger.  They are usually quite oblivious to the accompaniments of
+many of their favorite activities.  It is not that ISFPs are inured
+to them as much as it is that, wholly engaged by an action, they
+simply do not notice them.  In this ISFP is similar to other SPs
+and different from all other types.
+
+ISFP, like other SPs, has a special kind of intelligence.  Please
+recall that intelligence is defined in this book as doing things
+well under varying circumstances.  This particular category of
+intelligence might be called "artisan concretized."  Such talent
+differs from that possessed by NFs, NTs, and SJs (granting of
+course, that they too have their own unique and inherent
+abilities).  This artisan concretization somehow keeps the ISFP
+more closely in touch with the very real.
+
+While the ISTP is attuned to the tool, so to speak, the ISFP is
+attuned to color, line, texture, shading - touch, motion, seeing,
+and hearing in harmony.  The senses of the ISFP seem more keenly
+tuned than those of others.  Rembrandt could almost taste colors so
+great was his discrimination.  Toscanini could hear a single false
+note in the most complex operatic-orchestral score, and Hemingway's
+words tasted and smelled and felt the waves.  This extreme
+concreteness and specificity seems to come naturally to the ISFP
+and is embedded "in the warp and woof of the man's make."
+
+The social side of the ISFP character must not be eclipsed by the
+more spectacular performances some of this group are capable of.
+The ISFP has to be the kindest of all the types with no near
+competitors.  This kindness is unconditional.  Here is sympathy, of
+which we are all capable, carried to its most extreme form.  The
+ISFP is especially sensitive to the pain and suffering of others
+and, like St.  Francis of Assisi, with sympathetic impulsivity
+gives freely to the sufferer.
+
+ISFP is usually not interested in developing facility in speaking,
+writing, or conversation.  Speech, after all, is abstract, not
+concrete, ISFPs preferring to have their fingers on the pulse of
+life.  That pulse must be felt - by touch, in the muscles, in the
+eyes, in the ears.
+
+This insistence on the senses being so closely attuned to reality
+can, in some ISFPs, occasion a breach with language, and language
+becomes a barrier to smooth interpersonal relations.  So ISFPs are
+sometimes seen by others as reserved and private, tending to give
+up rather easily in their attempts to express themselves verbally.
+
+But this reluctant speech is not so much a lack of ability as it is
+disinterest.  Hemingway broke that barrier, a splendid instance of
+an ISFP entering the world of words and making apparent
+inarticulateness into art, changing the face of 20th Century
+literature.
+
+The number of great artisans who, upon investigation, were found
+clearly to have been ISFPs, is truly awesome.  The other SPs seem
+to have contributed far fewer masters to the fine arts.  Gaugin and
+Puccini, both ESTPs, were in this sense exceptional.  Music and the
+dance seems almost the province of ISFP, and surely investigation
+will show many of the great athletes come from this group.
+
+Of course, all ISFPs have not been and need not be artisans in the
+narrow sense of the word.  Art, broadly conceived, is any action
+the next move of which is a free variable, and it is art thus
+conceived that is the forte of SPs in general and the ISFP in
+particular.
+
+Thus ISFPs have a lot of leeway in choice of occupation, especially
+if they do not drop out of school early (most SPs do, since the
+school offers little that is of interest to them or that challenges
+their special brand of intelligence).  It is a sad day indeed when
+the ISFP chooses work wherein the operations are fixed by rule or
+necessity and not free.  To be happy and productive the ISFP must
+choose variable actions and be rewarded for doing them.
+
+Finally, in many ISFPs may be found an instinctive longing for the
+natural, the pastoral, the bucolic.  They are quite at home in the
+wilds, and nature seems to welcome them.  Some have a remarkable
+way with animals, even wild animals, almost as if there were a bond
+of mutual sympathy and trust.  In some instances a similar bond may
+be seen between the ISFP and young children, instant and unstudied.
+
+Perhaps the most important thing to understand about ISFPs is that
+they are SPs, with much in common with ESFPs especially, often
+resembling ISTPs, and even sharing some traits with the seemingly
+very different ESTP.
+
+To summarize this commonality with other SPs, ISFPs may be seen as
+optimistic and cheerful; egalitarian, fraternal, and insubordinate;
+tending to ward off obligation, duty, confinement, and fetters; a
+lover of freedom, easily bored, wanting excitement, risk, chance,
+and tests of luck; uncomplicated in motivation, trusting,
+receptive, generous, and in every sense of the word a spender
+rather than a saver.
+
+ISFPs are misunderstood not only because they are retiring,
+reserved, and even self-effacing, but because the Jungians have
+cast them as "introverted feeling types," and therefore very much
+like the INFPs.  Watch a few thoroughgoing ISFPs and you'll find
+they have very little in common with INFPs.  Other types are
+reminded to guard against the natural tendency to project their own
+traits of character onto the silent ISFP.
diff --git a/xtrn/smm/mb-istj.asc b/xtrn/smm/mb-istj.asc
new file mode 100644
index 0000000000000000000000000000000000000000..399af67a1bde56e24327c2d24847a2ff999eba25
--- /dev/null
+++ b/xtrn/smm/mb-istj.asc
@@ -0,0 +1,121 @@
+ISTJs in short:
+
+Serious, quiet, earn success by concentration and thoroughness.
+Practical, orderly, matter-of-fact, logical, realistic, and
+dependable.  See to it that everything is well organized.  Take
+responsibility.  Make up their own minds as to what should be
+accomplished and work toward it steadily, regardless of protests or
+distractions.
+
+About 6% of the population.
+
+Details about ISTJs:
+
+ISTJs are characterized by decisiveness in practical affairs, are
+the guardians of time-honored institutions, and, if only one
+adjective could be selected, 'dependable' would best describe this
+type which represents about 6 percent of the general population.
+The word of ISTJs is their bond, and they experience great
+uneasiness by thoughts of a bankrupt nation, state, institution, or
+family.
+
+Whether at home or at work, this type is rather quiet and serious.
+ISTJs are extraordinary persevering and dependable.  The thought of
+dishonoring a contract would appall a person of this type.  When
+they give their word, they give their honor.
+
+ISTJs can be counted on to conserve the resources of the
+institution they serve and bring to their work a practical point of
+view.  They perform their duties without flourish or fanfare;
+therefore, the dedication they bring to their work can go unnoticed
+and unappreciated.
+
+ISTJ's interest in thoroughness, details, justice, practical
+procedures, and smooth flow of personnel and material leads this
+type to occupations where these preferences are useful.
+
+For example, ISTJs make excellent bank examiners, auditors,
+accountants, or tax examiners.  Investments in securities are
+likely to interest this type, particularly investments in blue chip
+securities, ISTJs are not likely to take chances with their own or
+others' money.
+
+ISTJs can handle difficult, detailed figures and make sense of
+them.  They communicate a message of reliability and stability,
+which often makes them excellent supervisors of, for example, a
+ward of a hospital, a library, or a business operation.
+
+They would be capable of handling the duties of a mortician, a
+legal secretary, or a law researcher.  High school teachers of
+business, home economics, physical education, and the physical
+sciences, are ISTJs, as are top-ranking officers of the Women's
+Army Corps.  Often this type seem to have ice in their veins, for
+people fail to see an ISTJ's vulnerability to criticism.
+
+ISTJs are patient with their work and with procedures within an
+institution, although not always patient with the individual goals
+of people in that institution.  ISTJs will see to it that resources
+are delivered when and where they are supposed to be; materiel will
+be in the right place at the right time.  And ISTJs would prefer
+that this be the case with people, too.
+
+As a husband or wife, the ISTJ is a pillar of strength.  Just as
+this type honors business contracts, so do they honor the marriage
+contract.  Loyal and faithful mates, they take responsibilities to
+children and mate seriously, giving lifelong commitment to these.
+'Duty' is a word the ISTJ understands.
+
+The male ISTJ sees himself as the breadwinner of a family, although
+he can accept a working wife - as long as responsibilities to
+children are not shirked.  The male ISTJ's concept of masculinity
+is patriarchal, and both female and male ISTJs make steady,
+dependable partners.  The female ISTJ may abandon the frivolous for
+the sensible and may not always deepen her sensuality.
+
+As parents, ISTJs are consistent in handling children, and the
+rules of family are made clear.  A rebellious, nonconformist child
+may have a difficult time, however, with an ISTJ parent - and vice
+versa.  As a child, the ISTJ is apt to be obedient and a source of
+pleasure to parents and teachers.
+
+Although ISTJs are outstandingly practical and sensible, they can
+marry people who are thoroughly irresponsible, with the marriage
+developing into a relationship more parent-to-child than
+adult-to-adult.
+
+The ISTJ fluctuates from being rescuer to reformer of the wayward
+mate.  The marriage then becomes a lifelong game: On one side,
+there is Irresponsibility, Promise of Reform, Brief Period of
+Reform, and Irresponsibility again;  on the ISTJ's part, the cycle
+is Disapproval, Rescue, Scolding, Forgiveness, Acceptance of
+Promise To Do Better, and on and on.
+
+This pattern often is seen when an ISTJ marries an alcoholic and
+enters a life of care taking punctuated by periods of anger and
+rejection.  Somehow, although ISTJs can accept periodic fickleness
+and selfishness in significant others, they do not see this kind of
+behavior as acceptable in themselves.
+
+ISTJs have a distaste for and distrust of fanciness in speech,
+dress, or home.  The ostentatious is abhorred, and a neat, orderly,
+and functional home and work environment is preferred.  Durability
+of furnishings are of primary concern, esthetics given slim
+consideration.  The clothes of an ISTJ tend to be practical and
+durable rather than in the latest style or luxurious.  "No
+nonsense" in both food and clothes seem characteristic of this type
+who tend not to be attracted by exotic foods, beverages, or places.
+
+The male ISTJ may enjoy stag, men-only parties and use a different
+sort of language when only men are present.  The yearly hunting or
+fishing trip as a male ritual is often a part of recreation for an
+ISTJ.  More than the female, the ISTJ male is apt to be involved in
+community service organizations that transmit traditional values to
+the young, such as Boy Scouting.  They understand and appreciate
+the contributions these groups make in preserving the national
+heritage.
+
+Along with the SJs, the ISTJ takes particular delight in festive
+occasions held in the context of rituals, for example, weddings,
+holiday feasts, and birthdays.  At work, the ISTJ is apt to see the
+holiday office party as a necessary nuisance and would be likely to
+participate and enjoy these events.
diff --git a/xtrn/smm/mb-istp.asc b/xtrn/smm/mb-istp.asc
new file mode 100644
index 0000000000000000000000000000000000000000..6341a59e448c4715865140cc49099481e64b4b54
--- /dev/null
+++ b/xtrn/smm/mb-istp.asc
@@ -0,0 +1,191 @@
+ISTPs in short:
+
+Cool-onlookers - quiet, reserved, observing and analyzing life with
+detached curiosity and unexpected flashes of original humor.
+Usually interested in impersonal principles, cause and effect, how
+and why mechanical things work.  Exert themselves no more than they
+think necessary, because any waste of energy would be inefficient.
+
+About 13% of the population.
+
+Details about ISTPs:
+
+Just as impulsive as other SPs, the ISTP's life is artful action -
+and action is end in itself.  Action for the ISTP is more
+gratifying if it is born of impulse rather than purpose.  If the
+action is in the service of an end or aim, let the aim look out for
+itself; it cannot be allowed to influence execution.  The act is
+self-directed, self-leading, containing its own imperatives which
+cannot be suborned to mere rules, regulations, or laws.
+
+ISTPs are egalitarian and can be fiercely loyal to "brothers." They
+can also be fiercely insubordinate seeing hierarchy and authority
+as unnecessary and even superfluous.  It is not so much a matter of
+going against regulations as it is simply ignoring them.
+
+The ISTP must do his or her own thing, free to vary each next move.
+And ISTPs are, or want to be, proud of their ability to make the
+next move skillfully.
+
+ISTPs are often fearless, risking themselves more than other types,
+despite (even frequent) injury.  Of all the types, ISTPs are most
+likely to pit themselves, or their technique, against chance, odds,
+or fate.  They thrive on excitement; they crave some excitement
+each day, in the form of fast motion - racing, sky diving, or
+surfing, for instance.  This hunger for action makes them much more
+subject to boredom than any other type, their urge driving them to
+a faster pace.  Strangely, however, they are not bored while doing
+their thing, even though there may be long stretches when nothing
+happens, as during travel, surfing, hunting, or fishing.
+
+The ISTP nature is most easily seen in their mastery of tools,
+tools of any kind, from microscopic drill to supersonic jet.  From
+an early age, they are drawn to tools as to a magnet; they must
+manipulate them, and tools fall into their hands demanding use.
+Many pilots knew by the age of five that they were going to be
+pilots.
+
+ISTPs tend to take up activities that allow them to use tools;
+driving, steering, operating.  And if given a tool, whether scalpel
+or earth mover, is operated with a precision that defies belief,
+that operator is likely an ISTP.  Others use tools, of course, but
+not with the virtuosity of the ISTP.
+
+Indeed, we must call ISTP's the tool artisans, for they above all
+others command the tool and bend it to their impulse.  But again,
+ISTPs - personified in Michaelangelo and Leonardo - work (or
+better, play) with their tools on personal impulse and not on
+schedule.  If an externally imposed schedule coincides with
+impulse, fine; if not, so much the worse for the schedule.
+
+One tool especially attractive to the ISTP is the weapon.  Should
+ISTPs turn against society (for whatever reason), they wield their
+weapons with lethal genius to support their rejection.  The hit man
+of today, the gunslinger of the American West, and the duelist of
+18th Century Europe, may be seen as virtuosos of precision
+homicide.
+
+Hit man, gunslinger, and duelist alike took pride in their prowess.
+Fortunately, they face their own kind in battle, the good warriors
+of the land: soldier, marshal, police, intelligence agent.  This is
+not to say that all warriors, good or bad, are ISTPs, or that ISTPs
+are all weapons experts; rather that the weapon virtuoso is more
+frequently ISTP than not.
+
+ISTPs also play on impulse, taking off at any time just because
+they "feel like it."  (We are advised not to try to stop the ISTP
+who "feels like" doing something.)  The neurosurgeon does crop
+dusting on the side and rides a motorcycle to the airport, and the
+financier goes on a hunting trip in the middle of an audit (i.e.,
+SJ scrutiny).  There can be no end to the ways ISTPs seek thrills
+in play.
+
+Although they may have the appearance of loners in their work, they
+nonetheless hang around their own kind in play.  The climbers,
+racers, flyers, hunters, and in general, movers flock together. The
+companionship is mediated through the tool, and conversation is
+sparse and terse.
+
+Like the ISFPs, ISTPs communicate through action, and show little
+interest in developing verbal skills.  Indeed, this lack of
+interest in communication may be mistaken for what well meaning but
+misguided medics and educators call "learning disability" or
+"dyslexia," both preposterous notions when meant as explanations.
+Let ISFPs get near a tool of any complexity and power and see how
+fast they pass up everybody in 'learning' to use it and how precise
+their 'lexicon' in talking of its features.
+
+Despite their egalitarianism, insubordination, and love of freedom,
+they can be leaders, even great ones.  But they must be "up front,"
+sword in hand, leading the charge.  That is to say, ISTPs can be
+very successful as battle leaders, for instance, no matter how
+large or small the force under their command.  Their supreme
+realism, timing, and sense of expediency allows them to seize the
+moment and fully exploit whatever resources can be gotten (theirs
+or others) and capitalize on deficits and mistakes of their
+opponent.
+
+Theirs is an expediency or exploitative leadership, based on a
+special kind of intelligence which may be called artistic
+concreteness.  Yes, for the ISTP battle leader, combat is an art,
+an intellectual game, not in the sense of strategy (that is for
+NTs), but rather using whatever is at hand to defeat the other with
+the least injury.
+
+Battle leaders are duellists.  Patton was such a leader, and we
+must credit Marshall (an NTJ strategist) for seeing beneath that
+flamboyant, impulsive, insubordinate, and reckless exterior a
+peerless warrior.  The same credit goes to Grant (another NTJ) for
+selecting Sheridan (STP), and to Hitler (ENFJ) for selecting Rommel
+(ISTP).  Patton, Sheridan, and Rommel were cut from the same cloth
+and showed the same artistic espionage and rapier-like tactics.
+
+Glory is a pre-20th Century concept better understood by the ISTP
+than by others.  Or at least the ISTP is more interested in it than
+most others.  In battle there is glory, for it is in battle that
+one can exercise one's lethal skills with positive sanction.
+
+The Seven Samurai were glorified and so have been duellists down
+through the ages.  Foss, Boyington, Fonck, and von Richtoffen, all
+virtuosos of the winged machine gun, are still glorified heroes.
+But there are hundreds of warriors just like them in nature.  One
+can test one's mettle in lethal duel, there's glory in it, as the
+film The Great Waldo Pepper showed most poetically.
+
+The education and intelligence of the ISTP is worth special
+comment.  Possessed of artisan intelligence, ISTP is not in the
+least interested in the clerical, interpretive, and "science"
+curricula that abound in the 20th Century school.  The other SPs,
+equally bored by the school, will at least act as if they're trying
+to learn, but not ISTP.  ISTP will stare coldly into the eyes of
+the teacher and not even say no.  No amount of cajoling, bribing,
+rewarding, punishing, or threat will get them to do their school
+work.
+
+School work, quite apart from being irrelevant to the talents of
+SPs, is, after all, mere preparation for something the ISTPs figure
+they're never going to do anyway.  SPs do not wish to prepare - for
+anything - and ISTPs are careful to make this clear to their
+would-be instructors.
+
+What is there to 'do', 'now', that is 'worthwhile'?  ISTP will not
+sit still (literally) for the trivial fare dished out
+(sanctimoniously, in the eyes of the ISTP).  Most seem to agree
+that ISTPs "should" do their school work.  But why? The arguments
+are piddling and incoherent, warranting the scorn they get from the
+unshakable ISTP.
+
+ISTPs are not "minimally brain damaged," or "hyperactive," or
+"dyslexic"; they are active, and they are stubbornly insistent upon
+getting to do, in school, some things that allow them to test their
+intelligence and their mettle.  Name-calling and pill-pushing won't
+change them, other than destroying their self confidence and
+perhaps creating a stimulant addict now and then.  Give them a
+tool-centered curriculum and watch their speed.
+
+Behaviorally the ISTP is more like the ESTP than any other type,
+and the older they get, the greater the resemblance.  When young,
+ISTPs may look very much like ISFPs, but as their confidence and
+pride increase this resemblance recedes.  Jungians think ISTPs are
+just like INTPs with only minor differences, but this is based on
+the definition of ISTPs as "introverted thinking types."
+
+INTPs are logicians, philologists, and architects in the way they
+think, but ISTPs are completely disinterested in these pursuits.
+Even a cursory observation of a few clear-cut ISTPs will show how
+striking the contrast, and how trivial the resemblance.
+
+Still, the most important thing about the ISTPs is their
+commonality with other SPs.  We might think that there would be
+some resemblance to the ISTJ, having as they do, "IST" in common.
+but no, their behavior is antithetical in almost every dimension of
+comparison.  One is pessimistic while the other optimistic; one is
+parental, the other, fraternal; one saves, the other spends; one
+believes in rules, the other is instinctual insubordinate and
+recalcitrant to rules; and so on.
+
+ISTPs have infinitely more in common with the very different ESFP
+than they do with any NT or SJ; besides the above, their mood is
+one of good cheer, they are loyal to their equals, they want no
+obligations, duties, or confining promises, are uncomplicated in
+their desires, and are trusting, receptive, and generous.
diff --git a/xtrn/smm/mb-j.asc b/xtrn/smm/mb-j.asc
new file mode 100644
index 0000000000000000000000000000000000000000..92c1cb6cbfb6de7c2ba943db5b7873a9b221b40f
--- /dev/null
+++ b/xtrn/smm/mb-j.asc
@@ -0,0 +1,11 @@
+The Judgement type tends toward planning, goal setting, deciding, and
+getting-things-done. Once the J makes a decision he tends to be fixed and
+inflexible. The J finds himself always waiting for others - who are always
+late; thinks that if everyone would do what they are supposed to, the world
+would be a better place, has a personal schedule for the day, and gets 
+frustrated when the schedule is interrupted, doesn't like surprises, keeps
+lists, thrives on order, likes to finish-the-job.
+J creates an environment that is structured, scheduled, ordered, planned,
+and controlled; is decisive, deliberate.
+J's plan their work and work their plan.
+The population is equally divided between Js and Ps.
diff --git a/xtrn/smm/mb-n.asc b/xtrn/smm/mb-n.asc
new file mode 100644
index 0000000000000000000000000000000000000000..3347e8fb90085b5856e368ae99d31c076274ae2d
--- /dev/null
+++ b/xtrn/smm/mb-n.asc
@@ -0,0 +1,7 @@
+The iNtuitive type gets his information from within himself, tends to favor
+hunches, bases his decisions on possibilities, is innovative, imaginative. 
+An N tends to think about several things at once, finds future possibilities
+more intriguing than frightening, thinks 'boring details' is a redundancy,
+believes time is relative, likes to figure out how things work, is prone to
+puns & word games, thinks in terms of general directions rather than specific
+steps. About 25% of the population are type N.
diff --git a/xtrn/smm/mb-nf.asc b/xtrn/smm/mb-nf.asc
new file mode 100644
index 0000000000000000000000000000000000000000..988b822688f39d23feb96c153e37df7762741872
--- /dev/null
+++ b/xtrn/smm/mb-nf.asc
@@ -0,0 +1,22 @@
+The purpose of life for an NF is to find his purpose in life. NF is self in
+search of self & has difficulty understanding what seem to be the false goals
+of others. Search for identity, meaning, & actualization. Believes his real
+self is somewhat less than what it should be. Desires to be genuine,
+transparent; without masks or game-playing. Life is a drama; each encounter
+is pregnant with becoming. Very sensitive to non-verbal communication.
+Relationships can fall into a pattern of generous investment of energy followed
+by disappointment in what-could-have-been. Tend to be writers, poets, musicians
+teachers, counselors, actors; excellent communicators; gift of helping people 
+grow; strong empathy. Good at appearing to be what the beholder wants to see.
+Seeks greater intensity in relationships, seeks elusive intimacy. At once both
+the audience and the actor. Often has difficulty putting limits on time & 
+energy spent at work. Seeks perfection; and is often disappointed. Tends to
+romanticize experience. Hunger for relationships.
+NF strengths are a capacity for working with people & drawing out their best,
+being articulate & persuasive, a strong desire to help others, and the ability
+to affirm others freely & easily.
+As a manager, NF has difficulty being firm, tends to give workers too much
+leeway. As mate, NF has a deep need to give & receive affection and avoid 
+conflict. As a teacher, NF makes each student feel important & cared about.
+Desire to please & comfortable with theory makes NF an excellent student.
+About 12% of the population.
diff --git a/xtrn/smm/mb-nt.asc b/xtrn/smm/mb-nt.asc
new file mode 100644
index 0000000000000000000000000000000000000000..0b6ebfbf443cf8cabaf91305b0be804a4e9a5ac7
--- /dev/null
+++ b/xtrn/smm/mb-nt.asc
@@ -0,0 +1,32 @@
+NT often feel like aliens in a world of SPs and SJs. The NT has a hunger for
+understanding, control & power; not over people as much as over things. 
+Basic drives are for competence, abilities, skills, ingenuity. Loves doing
+things well. Addicted to storing up wisdom & acquiring intelligence. Has
+more interest in figuring things out than actually doing them. Self critical
+and self doubt to the point of paralysis. Perfectionist, tense, compulsive,
+fear-of-failure. Doesn't accept the conventional wisdom. Tries to lift others
+to his own exaggerated levels of competence and can be arrogant with those
+who choose not to see the complexity of his chosen area. His arrogance causes
+others to withhold their reactions for fear of being labeled 'stupid' and
+isolates him from the people around him. His speech tends to be terse, 
+compact, logical, precise. Wastes few words. Doesn't like to state the obvious.
+Passion for knowing. Tends to seek complicated & exacting profession; engineer,
+mathematics, philosophy. Enjoys building systems, developing models, exploring
+ideas. Tends to be straightforward in dealing with others; others find him
+cold, remote, detached & enigmatic. Vulnerable to all-work-and-no-play.
+Enjoys playing with words, paradox, bad puns. Tends to focus on the future;
+the past is dead & gone. Is humiliated when others witness his errors.
+Not sensitive to emotions of others, capable of biting sarcasm.
+NT strengths are the ability to see the big picture, a talent for systems
+planning, insight into the internal logic & principles of systems, and the
+ability to speak & write clearly.
+As managers, NT is the strategic planner; as mate NT is more interested in
+intellectualizing feeling & emotions than in experiencing them; As parent
+NT provides a role model that is impossible to live up to; as teacher, NT
+clarity & precision can be both exciting & intimidating to students, but 
+they can bore their students by working a point to death; in finances, NT
+probably has a plan, but at the same time is comfortable with high risk
+ventures.
+NT chooses clothes for comfort & utility, doesn't pay much attention to
+fashion.
+About 12% of the population.
diff --git a/xtrn/smm/mb-p.asc b/xtrn/smm/mb-p.asc
new file mode 100644
index 0000000000000000000000000000000000000000..2cccc090a00033d51af214107930556be35cbcf3
--- /dev/null
+++ b/xtrn/smm/mb-p.asc
@@ -0,0 +1,9 @@
+The Perceptive type tends toward plan-as-you-go, flexibility, open-ended
+tentative agreements, and letting-life-unfold. P is easily distracted, loves
+to explore the unknown, doesn't plan tasks, depends on last-minute-spurts to
+meet deadlines, doesn't believe neatness counts, enjoys creativity, trys to turn
+work into play, doesn't like to be pinned down, tends toward non-judgemental
+statements. 'P' must generate alternatives for everything.
+Likes to keep his options open, but not always - it all depends.
+
+The population is equally divided between Js and Ps.
diff --git a/xtrn/smm/mb-s.asc b/xtrn/smm/mb-s.asc
new file mode 100644
index 0000000000000000000000000000000000000000..ef399f5634c69606dcc546f40685a54234a7538c
--- /dev/null
+++ b/xtrn/smm/mb-s.asc
@@ -0,0 +1,11 @@
+The Sensor gets his information from external events; tends to be practical,
+base his decisions on experience, is down to earth, and notices details.
+An S prefers specific answers to specific questions, would rather DO something
+rather than think about it, likes to concentrate on the task at hand, likes
+tasks with tangible results, prefers facts to theories, thinks 'fantasy' is
+a dirty word, prefers clear/complete plans to vague guidelines, are very
+literal in use of words, very much a 'show me' person. An S may resist
+taking this test & quickly get bored with it because it is not practical
+and doesn't have an immediate application. S will take a long time to take
+this test, reading every question carefully.
+About 75% of the population are type S.
diff --git a/xtrn/smm/mb-sj.asc b/xtrn/smm/mb-sj.asc
new file mode 100644
index 0000000000000000000000000000000000000000..78fc2553399dba0e0faa9a13f6739138c38a5405
--- /dev/null
+++ b/xtrn/smm/mb-sj.asc
@@ -0,0 +1,16 @@
+The SJ exists to belong and be useful to his social group. A giver, provider,
+caretaker, parent; feels uncomfortable in a dependent role. Duty oriented,
+desires to serve, very aware of what-should-be-done. Is comfortable with
+obligations. Has a strong work ethic & desire for hierarchy & rule & order.
+Pessimistic, spends much energy in preparing for the future. Tradition &
+heritage are important. Has a keen sense for detecting ingratitude & lack of
+appreciation. Drawn to institutions; banking, teaching, government, service.
+Strong desire to conserve & store-up-for-the-future. Obligation to serve
+often overrides the fact that he is already overburdened. Backbone of society.
+SJ strengths are administration, dependability, ability to take charge.
+Home & hearth are cornerstones of SJ marriages, ritual & tradition is 
+important. SJ makes very clear who is the parent & what is expected of 
+children. As teacher SJ places high importance on neatness & punctuality.
+SJ's are the money - and moneyed - people of the world. Prefers classic,
+durable clothes. 60% of public school teachers are SJ.
+About 38% of the population.
diff --git a/xtrn/smm/mb-sp.asc b/xtrn/smm/mb-sp.asc
new file mode 100644
index 0000000000000000000000000000000000000000..76882d4822516e5c46a7b13af950fcb6b25aca05
--- /dev/null
+++ b/xtrn/smm/mb-sp.asc
@@ -0,0 +1,20 @@
+The SP must be free & will not be tied down with responsibility. Eat, drink &
+be merry, for tomorrow we die. In Aesop's fable of the ant & the grasshopper,
+the SP is the grasshopper. SP has the urge for impulsive action-for-right-now 
+rather than practicing for some future event. Waiting is the same as death.
+Likes diversity & trying new things. Easy come easy go. Not goal oriented.
+Yet the SP shows long endurance; He wouldn't climb a mountain to get to the
+top, but for the thrill of every moment. Gravitates toward jobs where action
+is involved. Tends to be fraternal & loyal to his equals. Master of the grand
+gesture, but not of the small important acts or words. Capable of abruptly
+abandoning his path & striking out in a completely different direction.
+Optimistic, libertarian, social equality. Compulsion to perform.
+SP strengths are practicality, adept problem solving skills, resourcefulness.
+As manager, SP is excellent in a crisis - and is not above creating a crisis
+to give him a sense of purpose. As mate, enjoys intensity & a thrill a minute.
+As teachers, are best at practical-vocational, tend to shy away from 
+theoretical or abstract. Not good at lesson plans. Dealing with money, SP is
+the original high roller. Not good at academics, poor spellers, least likely
+of all the types to receive a college degree.
+Chooses clothes for IMPACT.
+About 38% of the population.
diff --git a/xtrn/smm/mb-t.asc b/xtrn/smm/mb-t.asc
new file mode 100644
index 0000000000000000000000000000000000000000..b6d430d8b14021b716077a1097b23fc8210ec6cd
--- /dev/null
+++ b/xtrn/smm/mb-t.asc
@@ -0,0 +1,7 @@
+The Thinker tends toward logic, reason, laws, policy, principals, justice,
+and impersonal decisions. The T tends to favor critical analysis, stay calm
+& objective when others panic, enjoy proving their point, would rather be
+right than liked, remember facts better than faces.
+The thinker thinks the feeler is fuzzybrained.
+The population is equally divided between Ts and Fs, although more men tend
+to be Thinkers & more women tend to be Feelers.
diff --git a/xtrn/smm/mb-tj.asc b/xtrn/smm/mb-tj.asc
new file mode 100644
index 0000000000000000000000000000000000000000..a5c3dbeca2f99c0b95b65a0702a632ad7cb587b5
--- /dev/null
+++ b/xtrn/smm/mb-tj.asc
@@ -0,0 +1 @@
+60 % of the world's managers are TJ.
diff --git a/xtrn/smm/mb-type.asc b/xtrn/smm/mb-type.asc
new file mode 100644
index 0000000000000000000000000000000000000000..efd263b45fe7c01c4eea68ee4a6e40cfa1efe6d4
--- /dev/null
+++ b/xtrn/smm/mb-type.asc
@@ -0,0 +1,20 @@
+yhMyers-Briggs Personality Types:nc
+
+The first letter of the personality type indicates how you are energized
+(hEncxtroversion or hIncntroversion).
+
+The second letter of the personality type indicates how you obtain information
+for your decisions (hSncensation or ihNnctuition).
+
+The third letter of the personality type measures your preference to operate
+from your head to your heart (hTnchinking or hFnceeling).
+
+The fourth letter of the personality type indicates how you like to order your
+life (hJncudging or hPncerception).
+
+An asterisk (h*nc) in any of your letter positions indicates you are equally
+divided between the two personality tendancies.
+
+From here you can read definitions of letter combinations (types):
+hEnc, hInc, hSnc, hNnc, hTnc, hFnc, hJnc, hPnc, hTJnc, hSPnc, hSJnc, hNTnc, hNFnc, hENFPnc, hENFJnc, hENTPnc, hENTJnc, hESFPnc, hESFJnc,
+hESTPnc, hESTJnc, hINFPnc, hINFJnc, hINTPnc, hINTJnc, hISFPnc, hISFJnc, hISTPnc, and hISTJnc.
diff --git a/xtrn/smm/mb-type.que b/xtrn/smm/mb-type.que
new file mode 100644
index 0000000000000000000000000000000000000000..987d79d569596870372afd7bab47c819ab32259e
Binary files /dev/null and b/xtrn/smm/mb-type.que differ
diff --git a/xtrn/smm/purity-1.asc b/xtrn/smm/purity-1.asc
new file mode 100644
index 0000000000000000000000000000000000000000..773f53d0a268533ae5d004bc9ae8940b4dc8aad3
--- /dev/null
+++ b/xtrn/smm/purity-1.asc
@@ -0,0 +1,16 @@
+Section 1:  Platonic Relations.  19 Questions.
+
+For this section, if you are mostly a:
+
+         - heterosexual, then your partner in deed, often referred to
+                         by the word "someone" or "partner", is to be someone
+                         of the OPPOSITE gender.
+
+         - homosexual, then your partner in deed, often referred to by the
+                         word "someone" or "partner", is to be someone of
+                         YOUR OWN gender.
+
+         - 50-50 confirmed bisexual, then your partner in deed, often
+                         referred to by the word "someone" or "partner", is to
+                         be someone of the OPPOSITE gender.
+ 
diff --git a/xtrn/smm/purity-2.asc b/xtrn/smm/purity-2.asc
new file mode 100644
index 0000000000000000000000000000000000000000..b69f67e1b615f499890f2a8a87ef1beca4e19c8d
--- /dev/null
+++ b/xtrn/smm/purity-2.asc
@@ -0,0 +1,7 @@
+Section 2:  Auto-erotica and Mono-sexualism.  51 Questions.
+
+     Although this section is termed Auto-erotica and mono-sexualism, the
+     events herein still count even if you are with someone else at the time.
+     It was so named because these activities, like the harp, (and the
+     porcelain goddess) are predominantly solo events.
+ 
diff --git a/xtrn/smm/purity-3.asc b/xtrn/smm/purity-3.asc
new file mode 100644
index 0000000000000000000000000000000000000000..5c33111569d7280eac84a367aba80fe63c414e57
--- /dev/null
+++ b/xtrn/smm/purity-3.asc
@@ -0,0 +1,2 @@
+Section 3:  Legislative Misfits and Other Ethical Matters.  61 Questions.
+ 
diff --git a/xtrn/smm/purity-4.asc b/xtrn/smm/purity-4.asc
new file mode 100644
index 0000000000000000000000000000000000000000..37289ae749fe509a0af279ca5d89a1ba95f27f36
--- /dev/null
+++ b/xtrn/smm/purity-4.asc
@@ -0,0 +1,2 @@
+Section 4:  Drugs.  43 Questions.
+ 
diff --git a/xtrn/smm/purity-5.asc b/xtrn/smm/purity-5.asc
new file mode 100644
index 0000000000000000000000000000000000000000..14d44dda2ceaa5d8f6d2c288d9d7d740a1ff3818
--- /dev/null
+++ b/xtrn/smm/purity-5.asc
@@ -0,0 +1,16 @@
+Section 5:  Non Platonic Relations.  63 Questions.
+
+For this section, it you are mostly a:
+
+         - heterosexual, then your partner in deed, often referred to by
+                         the word "someone" or "partner", is someone of the
+                         OPPOSITE gender.
+
+         - homosexual, then your partner in deed, often referred to by the
+                         word "someone" or "partner", is to be someone of
+                         your OWN gender.
+
+         - 50-50 confirmed bisexual, then your partner in deed, often referred
+                         to by the word "someone" or "partner", is to be
+                         someone of the OPPOSITE gender.
+ 
diff --git a/xtrn/smm/purity-6.asc b/xtrn/smm/purity-6.asc
new file mode 100644
index 0000000000000000000000000000000000000000..3531a8ef778a37285eadbc69629b43a49bd368d3
--- /dev/null
+++ b/xtrn/smm/purity-6.asc
@@ -0,0 +1,18 @@
+Section 6:  Non Primary Choice Relations.  39 questions.
+
+      This section of the test deals with whether you have done things with
+      people whom you may not be altogether comfortable, therefore in this
+      section of the test, if you are mostly a:
+
+         - heterosexual, then your partner in deed, often referred to by the
+                         word "someone" or "partner", is to be someone of your
+                         OWN gender.
+
+         - homosexual, then your partner in deed, often referred to by the
+                         word "someone" or "partner", is someone of the
+                         OPPOSITE gender.
+
+         - 50-50 confirmed bisexual, then your partner in deed, often
+                         referred to by the word "someone" or "partner", is to
+                         be someone of your OWN gender.
+ 
diff --git a/xtrn/smm/purity-7.asc b/xtrn/smm/purity-7.asc
new file mode 100644
index 0000000000000000000000000000000000000000..4cf23745bc29143dfd1c9ccc8977cc76b99a5e02
--- /dev/null
+++ b/xtrn/smm/purity-7.asc
@@ -0,0 +1,8 @@
+Section 7:  Alternate Choices.  26 questions.
+
+      For any of the questions in this section, a yes answer is in order if it
+      is something that you do as an alternative to other sexual gratifications
+      or as an aid and/or in conjunction with other means of sexual
+      gratification.  In other words, have you done it in a serious basis?
+      Trying it a few times to see what it's like does not count.
+ 
diff --git a/xtrn/smm/purity-8.asc b/xtrn/smm/purity-8.asc
new file mode 100644
index 0000000000000000000000000000000000000000..bad4ef2b31a91350ab10dcbbefd9966e1c76dc6e
--- /dev/null
+++ b/xtrn/smm/purity-8.asc
@@ -0,0 +1,7 @@
+Section 8:  Group Sexual Relations.  27 questions.
+
+      This section relates to what you have or have not done.  Accordingly, the
+      group of people of which we are speaking are of both genders.  In
+      questions where groups of people are concerned, there must be at least
+      one person in the group who is of the opposite gender.
+ 
diff --git a/xtrn/smm/purity-9.asc b/xtrn/smm/purity-9.asc
new file mode 100644
index 0000000000000000000000000000000000000000..85a257e743f874233e8ec6a35211d1d1e8ed1041
--- /dev/null
+++ b/xtrn/smm/purity-9.asc
@@ -0,0 +1,6 @@
+Section 9:  Non sentient objects.  62 questions.
+
+      This section measures your kinkiness.  Therefore, the questions apply to
+      actions and events which occurred while you were alone, as well as those
+      which occurred while you were with someone else.
+ 
diff --git a/xtrn/smm/purity-a.asc b/xtrn/smm/purity-a.asc
new file mode 100644
index 0000000000000000000000000000000000000000..3218f28900d925e24603aaf83b5ac584c7cc0c5d
--- /dev/null
+++ b/xtrn/smm/purity-a.asc
@@ -0,0 +1,5 @@
+Section 10:  Locality.  33 questions.
+
+This  section  tries  to  figure  out how many places you have done it. It
+applies only to those situations in which you were with someone else.
+ 
diff --git a/xtrn/smm/purity-b.asc b/xtrn/smm/purity-b.asc
new file mode 100644
index 0000000000000000000000000000000000000000..ad61bbf6c6ad4f79cbc523232493cc16824e89e5
--- /dev/null
+++ b/xtrn/smm/purity-b.asc
@@ -0,0 +1,2 @@
+Section 11:  Style.  76 questions.
+ 
diff --git a/xtrn/smm/purity.100 b/xtrn/smm/purity.100
new file mode 100644
index 0000000000000000000000000000000000000000..c97b56bea11a7bd40fdf3adfd7c99c4de4744912
--- /dev/null
+++ b/xtrn/smm/purity.100
@@ -0,0 +1,100 @@
+Had a date
+Been out on a date past 4 am
+Had a blind date
+Kissed a MOS
+Been french kissed
+Kissed a MOS in the horizontal position
+French kissed three or more MOS's in 24 hours
+Kissed a MOS in the last three months
+Necked for more than 2 hours consecutively
+Slow danced cheek-to-cheek
+Had an alcoholic drink
+Been drunk
+Driven while under the influence of alcohol or drugs
+Had a lapse of memory due to drinking or drugs
+Used alcohol or drugs to lower a MOS's resistance
+Smoked tobacco
+Smoked pot or hashish
+Used a stronger drug
+Taken 4 or more "recreational" drugs within 24 hours
+Read a pornographic book or magazine
+Seen a pornographic movie
+Seen a stripper
+Been arrested
+Been convicted of a crime
+Had an erection/clitoral erection
+Had an orgasm
+Had an orgasm in a dream
+Fondled a MOS's ass
+Caressed a MOS's thigh
+Fondled a breast or had your breast fondled
+Wrestled with a MOS
+Showered, bathed, jacuzzied, or saunaed with a MOS
+Gone coed skinny-dipping
+Gone through the motions of intercourse while fully clothed
+Spent the night in a MOS's room
+Slept in the same bed with a MOS
+Seen a naked post-pubescent MOS
+Been seen naked by a MOS after puberty
+Undressed or been undressed by a MOS
+Kissed a MOS on the breast or been kissed on the breast
+Fondled a MOS's genitals or had your genitals fondled by a MOS
+Had an orgasm due to manipulation by a MOS
+Kissed a MOS on the thigh
+Engaged in cunnilingus
+Engaged in fellatio
+Had sexual intercourse at a parents house while they were home
+Engaged in definitely sexual activity on the first date
+Masturbated
+Masturbated to a picture
+Masturbated with another person in the room
+Watched another person masturbate
+Been caught masturbating
+Simulated intercourse with an inanimate object
+Committed an act of voyeurism
+Committed an act of exhibitionism
+Massaged or been massaged by a MOS
+Unintentionally interrupted a couple in a significant state of undress
+Participated in a tickle orgy, gross out, truth or dare, etc.
+Experimented sexually before puberty
+Purchased contraceptives in a drug store
+Had sexual intercourse
+Had sexual intercourse more than 10 times
+Had sexual intercourse continuously for 1/2 hour
+Had sexual intercourse within the last 3 months
+Had sexual intercourse 3 or more times in 1 night
+Had sexual intercourse in 3 or more positions
+Had sexual intercourse in a car
+Had sexual intercourse using a condom
+Done sixty-nine
+Had sexual intercourse outdoors
+Had sexual intercourse with 3 different people
+Had sexual intercourse with a virgin
+Had sexual intercourse during menstruation
+Had sexual intercourse without using birth control
+Had sexual intercourse with 2 MOSs in 24 hours
+Had sexual intercourse in a public place
+Described a sexual experience to a separate party
+Committed statutory rape
+Been the object of travel or traveled 100 miles or more for sexual activity
+Impregnated a woman or been pregnant
+Arranged or had an abortion
+Displaced a roommate by staying with a MOS for 1 or more nights
+Shacked up with a MOS for a month or more
+Tasted semen
+Been propositioned by a prostitute or pimp
+Accepted
+Had anal intercourse
+Been tested for V.D. due to reasonable suspicion
+Had V.D.
+Picked up a strange MOS for sexual purposes
+Engaged in group sex
+Engaged in sadism or masochism for sexual enjoyment
+Been propositioned by a member of the same sex
+Accepted a proposition by a member of the same sex
+Been masturbated by a member of the same sex
+Been orally stimulated by a member of the same sex
+Committed incest
+Fondled a pre-pubescent MOS
+Engaged in transvestitism for sexual enjoyment
+Committed bestiality
diff --git a/xtrn/smm/purity.500 b/xtrn/smm/purity.500
new file mode 100644
index 0000000000000000000000000000000000000000..5f389c7ea827353afce460ace6278b9e0af58297
--- /dev/null
+++ b/xtrn/smm/purity.500
@@ -0,0 +1,511 @@
+*PURITY-1.ASC
+Kissed a friend or stranger as a friendly gesture
+Held hands with someone
+Had a date
+Had a date past 1 a.m.
+Dated someone on a regular basis
+Picked someone up
+Been picked up
+Gone steady
+Slow danced
+Had the symptoms of Russian fingers (rushin' fingers)
+Had the symptoms of Roman hands (roamin' hands)
+Shared a bed with someone without anything steamy happening
+Given a back or neck rub or massage with no ulterior motive
+Used tickling as a pick-up get-to-know-you-better routine
+Directly asked someone if they were a virgin
+Used physical strength as a get-to-know-you-better routine
+Secretly lusted after someone without that person knowing
+Dropped subtle hints to someone hoping they would pick up on it
+Written anonymous 'love letters' to someone
+*PURITY-2.ASC
+Had an arousing dream  (Wet dreams and the like.)
+Been sexually aroused
+Uttered/muttered/yelled/screamed obscenities
+Fantasized about your instructor or teacher
+Fantasized about your lawyer, doctor, nurse, psychiatrist
+Fantasized about someone you know personally but not closely
+Fantasized about anyone and masturbated at the same time
+Read or bought pornographic periodicals
+Ever had a subscription to pornographic periodicals
+Read sexually explicit literature
+Gone skinny dipping alone
+Made obscene phone calls
+Phoned up any recorded phone sex numbers
+Phoned up any live phone sex numbers
+Stuffed your bra or stuffed your pants
+Shaved your genital pubic hair
+Shaved your genital pubic hair on a fairly regular basis
+Colored or bleached your genital pubic hair
+Shaved or shaped your genital pubic hair in a particular design
+Masturbated
+Masturbated at least five times in one twenty-four hour period
+Masturbated on a fairly regular basis of no less than once a week
+Masturbated where you could have been discovered
+Masturbated out in the wilds or in nature
+Masturbated to orgasm
+Masturbated while reading sexually explicit materials
+Masturbated while driving a *moving* land vehicle
+Masturbated while on the phone
+Masturbated while in a bathroom of the opposite sex
+Masturbated while watching an R or X-rated show
+Seen any burlesque show (Rocky Horror counts)
+Been to a peep show
+Been to a private showing of a pornographic movie
+Seen a pornographic movie in a theater
+Walked around in your room/apartment/house/habitation in nude
+Walked around in a public or semi-public area bottomless
+Bought blatant sexual objects
+Owned any erotic art pieces
+Written your own fantasies for masturbatory purposes
+Sculpted erotic/obscene artworks in food
+Eaten any erotic food items  (Chocolate tits, banana dicks)
+Sculpted erotic/obscene artworks in soap, wood, etc
+Made an X- or R-rated snowman or snowwoman
+Tasted your own orgasmic liquids
+Inserted your finger into your rectum
+Used ben-wa balls or anal beads
+Performed oral sex on yourself
+Willingly urinated on your garments while wearing them
+Willingly urinated on any part of your body
+Willingly defecated on your garments while wearing them
+Willingly defecated on yourself
+*PURITY-3.ASC
+Administered a whole Purity Test or in the process
+Taken Purity Tests of any versions more than 5 times
+Lied on any previous Purity Tests
+Exaggerated about any sexual experiences
+Gone to (or escorted someone to) a Planned Parenthood Clinic
+Broken your word, promise, or vow
+Lied to someone at someone else's request
+Lied about your sexual preference in order to avoid a date
+Written graffitti
+Plagiarized
+Shoplifted
+Stolen
+Made out a check that bounced
+Used someone else's credit card without their knowledge
+Committed breaking and entering
+Seen a snuff film
+Read someone else's diary without their knowledge or consent
+Searched someone's room without their knowledge or consent
+Told someone that you loved them when you did not
+Told someone that you loved them just for sex
+Fantasized about someone else while having sex
+Intentionally listened in on other people having sex
+Used alcohol to lower someone else's inhibitions for sex
+Used drugs to lower someone else's inhibitions for sex
+Drugged someone without their knowledge and/or consent
+Forced someone into having intimate physical relations with you
+Had dates with more than one person in the same night
+Gone steady with two or more people at the same time(secret)
+Gone steady with two or more people at the same time(known)
+Urinated on someone else intentionally	(Piss fights!)
+Urinated in object that was not designed for such purpose
+Urinated anywhere other than commonly accepted piss hole
+Urinated from higher than the fifth floor or off a bridge
+Defecated anywhere other accepted shit hole
+Stored any excretia in a refrigerator/oven
+Stored evacuated excretia in your room/apartment
+Entered a bathroom of the opposite sex- unaccompanied
+Mooned or flashed someone from the front
+Farted (audibly) in mixed company
+Streaked/flashed or exposed yourself in public
+Streaked/flashed or exposed yourself at a formal gathering
+Been arrested  (Picture taken and all)
+Received money to have sex or mutual masturbation with someone
+Given money to have sex mutual masturbation with someone
+Thought you might be or might have caused someoneto be pregnant
+Bought a home pregnancy test
+Bought condoms
+Borrowed/stolen/taken birth control devices from someone else
+Had or given someone an unwanted pregnancy
+Lied about being pregnant or about having made someone pregnant
+Had sex while either you or your partner were under age
+Thought you had VD
+Had VD of any sort (VD/STD's/the clap/crabs/herpes/etc.)
+Had an AIDS test due to reasonable suspicion or imagination
+Given a sympathy fuck
+Initiated sex with someone for the sake of sex only
+Willingly committed incest
+Committed adultery
+Bought lingerie/undergarments of the opposite sex
+Stolen the underwear of someone you knew not for joke
+Intentionally taken a stranger's underwear
+*PURITY-4.ASC
+Had an alcoholic drink
+Been intoxicated
+Thrown up from having drunk too much alcohol
+Passed out due to having drunk too much alcohol
+Forgotten events that occurred while you were drunk
+Smoked tobacco	(cigarette/pipe/cigar/hookah)
+Chewed tobacco	(snuff)
+Bought controlled/illicit drugs in violation of the law
+Sold or re-sold controlled/illicit substances
+Taken stimulants
+Taken depressants excluding alcohol
+Inhaled nitrous oxide while not visiting a dentist
+Inhaled anything containing butyl nitrate
+Used a commercial aphrodisiac
+Taken valium
+Smoked marijuana/sensemilia
+Smoked marijuana/sensemilia more than four times
+Eaten marijuana/sensemilia
+Eaten marijuana or sensemilia more than four times
+Taken opiate in any form
+Taken opiate in any form more than twice
+Used cocaine
+Used cocaine more than four times
+Injected any drugs into your body other than medical
+Injected any illegal drug into your body more than twice
+Taken Ecstasy/X
+Taken PCP
+Taken PCP more than twice
+Taken LSD-25, peyote or psilocybin
+Taken LSD/peyote or psilocybin more than twice
+Taken LSD more than six times
+Deliberately injured yourself so as to obtain medication
+Played any games in which drugs were the prize
+Gone to class or work while under the influence of drugs
+Mixed drugs (alcohol counts)
+Sold possessions in order to obtain drugs
+Had sex while under the influence of nitrous
+Had an orgasm while under the influence of nitrous
+Had sex while under the influence of cocaine
+Had sex while under the influence of marijuana/sensemilia
+Had sex while under the influence of Ecstasy/X
+Had sex while under the influence of LSD/peyote or psilocybin
+Had sex while under the influence with a unknowing partner
+*PURITY-5.ASC
+Propositioned someone for necking or petting
+Propositioned someone for sex/oral sex or mutual masturbation
+Pinched or patted someone else's buttocks
+French kissed
+Been kissed below the neck but not including arms or hands
+Kissed someone else below the neck not including arms or hands
+Necked
+Petted above the waist
+Petted below the waist
+Kissed on the first date
+Necked on the first date
+Petted above the waist on the first date
+Petted below the waist on the first date
+Given a hickey
+Received a hickey
+Worn specific clothes for the purpose of hiding hickeys
+Fondled or stroked someone else's clothed legs
+Fondled or stroked someone else's bare legs
+Fondled or stroked someone else's frontal chest/torso region
+Had your frontal chest/torso region fondled or stroked
+Been involved with pelvic thrusting while fully clothed
+Had your fingers licked or sucked
+Had your ear or ear region licked/sucked, or nibbled
+Licked, breathed onto, sucked, or nibbled an ear or ear region
+Licked or sucked someone else's finger(s)
+Fondled someone who was asleep
+Given a back massage with ulterior motives
+Given a back massage that led to something steamier
+Seen someone completely nude when under good light conditions
+Been seen nude by someone else under good lighting conditions
+Been in someone's company while both were completely nude
+Bathed or showered with someone
+Let someone else wash you when you were capable of doing it
+Gone skinny dipping in mixed company
+Been involved with the fondling of a woman's clothed breast
+Been involved with the fondling of a woman's bare breast
+Licked, sucked, or nibbled on someone else's nipple
+Had your nipple licked, sucked, or nibbled upon
+Petted/fondled or otherwise handled someone's covered genitals
+Had your covered genitals petted/fondled or handled
+Petted/fondled or otherwise handled someone's bare genitals
+Had your bare genitals petted/fondled or otherwise handled
+Had an orgasm while petting
+Given your partner an orgasm while petting
+Given finger scratch marks
+Received finger scratch marks
+Drawn blood by scratching during sex
+Drawn blood by biting during sex
+Given or received *scars* from scratches or bites during sex
+Performed oral sex
+Received oral sex
+Swallowed semen, or licked female liquids off of fingers
+Done sixty-nine
+Performed mutual masturbation
+Had sex  (No need for orgasm; penetration counts.)
+Had sex on the first date
+Had sex without the use of birth control devices
+Had sex with a virgin  (Not yourself.)
+Had sex with someone whose name you did not know
+Had sex with someone whose face you never saw
+Had sex with someone with a 20 year age difference
+Had sex with someone not of your own race
+Had sex with a religious officiary
+*PURITY-6.ASC
+Held hands or otherwise displayed public affection
+Kissed someone on the lips
+French kissed someone
+Necked
+Petted
+Received manual sex
+Given manual sex
+Received oral sex
+Given oral sex
+Had sex
+Had sex with a virgin  (not yourself.)
+Had sex with someone with age difference of more than 20 years
+Had anal sex
+Been involved in fist-fucking
+Done 69  (simultaneous oral sex.)
+Propositioned someone for sex, oral sex, or mutual masturbation
+Yielded willingly to a proposition from someone for sex/oral sex
+Had sex with someone whose name you didn't know
+Had sex with someone whose face you never saw
+Been a participant in a who's-physically-better-equipped contest
+Been a judge in a contest such as above
+Gave money or favors for sex, oral sex, or mutual masturbation
+Received money or favors for sex/oral sex or mutual masturbation
+Fondled someone who was asleep
+Attempted to seduce someone
+Allowed yourself to be seduced
+Had an orgasm while petting
+Gave your partner an orgasm while petting
+Had an orgasm at all
+Had sex/oral sex or mutual masturbation with more than 10 people
+Stroked or fondled the clothed legs of someone
+Stroked or fondled the bare legs of someone
+Stroked or fondled the clothed chest/torso region of someone
+Stroked or fondled the bare chest/torso region of someone
+Fondled or handled the clothed genitals of someone else
+Stroked/fondled or handled the bare genitals of someone else
+Had sex/mutual masturbation with someone not of your own race
+Been in a menage-a-trois of people of the same sex
+Been involved in group sex/with all participants of the same sex
+*PURITY-7.ASC
+Been decidedly heterosexual
+Been decidedly homosexual
+Been decidedly bisexual
+Practiced bestiality (avec les animaux)
+Practiced transvesticism
+Practiced sadism
+Practiced masochism
+Practiced bondage
+Practiced domination
+Practiced submission
+Practiced sodomy (anal intercourse)
+Practiced cocrophilia (a marked interest in excrement)
+Practiced frotteurism
+Practiced infantilism(a dependency on diapers)
+Practiced klismaphilia (a dependency on being given an enema)
+Practiced necrophilia (copulation with a corpse)
+Practiced mysophilia (a dependency on something soiled)
+Practiced scoptophilia ( as in voyeurism)
+Practiced urophilia (being responsive to urine )
+Practiced role-playing
+Owned an underwear collection of underwear not belonging to you
+Been a foot fetishist to any degree
+Been a leather fetishist to any degree
+Been a rubber/latex fetishist to any degree
+Been a voyeur
+Been an exhibitionist
+Listened to dirty jokes in mixed company
+*PURITY-8.ASC
+Told dirty jokes in mixed company
+Discussed masturbation
+Watched a porn movie in mixed company
+Watched a porn movie with your own or someone else's parents
+Played a game which may require you or others to disrobe
+Played a game which may require you to perform action on others
+Been in contact with more than one person while all were nude
+Had oral sex with more than 10 people (not nec. at one time)
+Had sex with more than 10 people  (not necessarily at one time)
+Had sex with more than 1 person in a 24 hour period
+Had sex with both genders in a 24 hour period
+Been in a menage-a-trois (MFM OR FMF)
+Walked in on others having sex (oops) and then joined in
+Had sex or oral sex with a person and his/her parent
+Had sex/mutual masturbation, with a person and his/her sibling
+Been involved in a two (or more) in one
+Been involved in a gang bang
+Been in a circle of fuck
+Been in a 69 circle
+Been to a (cooking, baby, Wesson) oil party
+Played naked Twister [tm] (with or without oil)
+Participated in a hetro orgy or been involved in group sex
+Participated in a bisexual orgy or group sex
+Propositioned a person or group of people for group sex
+Been propositioned for group sex
+Participated in a fuck-a-thon
+*PURITY-9.ASC
+Used a foreign object or something for masturbation
+Masturbated using the aid of food
+Eaten the food used in masturbation after masturbation
+Eaten a lab dissection
+Inserted food into your or someone else's anus
+Eaten food after it was extracted from your or someone's anus
+Received an enema for a purpose other than medical
+Received an enema consisting on a non-normal enema solution
+Actually measured your own or someone else's penis
+Used a mechanical device in aiding or replacing masturbation
+Used a feather or other device for the purpose of tickling
+Used tickling as a form of *arousal*
+Used ice for sexual purposes
+Used ice for something frozen as a dildo
+Used a strap-on dildo or male extension sheath
+Used whipped cream for sexual purposes
+Used hot/melted wax for sexual purposes
+Had sex in front of or under a mirror
+Put food on your partner's body, and then eaten it
+Used household syrup/etc or any like substance for sex purposes
+Used ropes, cuffs or any other such device for bondage purposes
+Used a whip, cat-o-nine-tails, or something similar for pain
+Worn edible underwear/lingerie
+Eaten edible underwear/lingerie off of someone
+Worn a leather or rubber suit
+Worn diapers for a sexual or masturbatory purpose
+Been diapered by someone else for a sex or masturbatory purpose
+Used a ball gag or other manufactured gag
+Worn a collar and/or leash
+Been completely tied down (spreadeagled, hogtied, etc.)
+Tied someone down completely
+Had sex while you or your partner was tied up
+Used nipple clips (clothespins count)
+Pierced a part of your body other than your ears or nose
+Found a prepubescent child sexually attractive/arousing
+Had sexual contact of any kind with a prepubescent child
+Used an inflatable doll
+Humped an inanimate object like a pillow, banana, etc
+Had sex or oral sex with a dead person
+Inserted a small animal or creature into your rectum
+Had sex or oral sex with your (dead) dinner animal
+Watched animals having sex
+Been aroused by the sight of animals having sex
+Attempted to have sex with a live animal, but failed
+Had sex/oral sex or (mutual) masturbation with smallish animal
+Had sex/oral sex or masturbation with domesticated farm animal
+Mutilated or killed any living animal for your pleasures
+Had sex with a live animal  (Any size.)
+Received oral sex from a live animal  (Any size.)
+Gave oral sex to a live animal	(Any size.)
+Had sex or (mutual) masturbation with a animal more than once
+Had sex or masturbation with an animal in presence of others
+Cross dressed in the *undergarments* of the opposite sex
+Cross dressed in presence of others
+Stuffed your pants or bra while you were cross-dressed
+Had your head inserted into a urinal or toilet bowl willingly
+Worn groinal underwear on your head  (Panties/jock-strap/etc.)
+Inserted a  piece of groinal underwear into your mouth
+Deliberately sniffed or smelled a piece of groinal underwear
+Been gagged with someone's underwear  (not your own)
+Played in or with shit
+Worn or used a used condom or feminine hygiene contraption
+*PURITY-A.ASC
+Necked or petted in a place of religion
+Had sex or been involved in a place of religion
+Necked or petted in a place of the dead (mortuary)
+Had sex or been involved in oral sex in a place of the dead
+Necked or petted in a contraption of the dead (hearse/etc)
+Had sex in a contraption of the dead
+Had sex in/on a construction site (house, office, etc.)
+Necked or petted in a vehicle of LESS  THAN 30,000 pounds net
+Had sex in a vehicle of LESS THAN 30,000 pounds net
+Necked or petted in a vehicle of MORE THAN 30,000 pounds net
+Had sex in a vehicle of MORE THAN 30,000 pounds net
+Had sex in a land-based, non road dependant vehicle
+Necked in a water, manual powered vehicular transport medium
+Petted in a water/wind or prop driven transport medium
+Had sex in a water/wind driven transport MORE THAN 80 feet
+Had sex or oral sex in an aircraft
+Had sex in a household room other than a bedroom
+Had sex on the floor (but not the roof)
+Had sex on any furniture that is indoors but not a bed or table
+Had sex telephone booth, voting booth/etc
+Had sex in an elevator, people-mover, escalator, dumbwaiter
+Had sex up a tree but not in a tree house or similar structure
+Had sex in a suspension device of some kind (hammock)
+Had sex on the roof of a building in excess of 5 floors
+Had sex within the confines of a hedge, bush/etc
+Had sex, oral sex, or mutual masturbation in the snow
+Had sex in a place with the ambient temperature below freezing
+Had sex in a place where you could have been discovered
+Had sex in a water-filled bathtub, hot tub/etc
+Had sex, oral sex, or mutual masturbation in a body of water
+Had sex on the beach
+Had sex or mutual masturbation in a bathroom of the opposite sex
+Gone to a motel for the sole purpose of having sex
+*PURITY-B.ASC
+Watched while someone else masturbated
+Been watched while masturbating
+Orgasmed on somebody
+Orgasmed in somebody (male) or in you (female)
+Had more than one person orgasm on you at once
+Used  a penis as a leash or bludgeoning device
+Been involved in oxygen deprivation for sexual enhancement
+Willingly made video tapes or  pictures taken while having sex
+Physically watched others having sex
+Watched your partner of choice having sex with someone else
+Taken pictures of your partner having sex with someone else
+Willingly made audio recordings while having sex
+Talked dirty while having sex, oral sex, or mutual masturbation
+Intentionally made more noise than necessary while having sex
+Intentionally made 'animal' noises during sex
+Had a pet walk over you or your partner while during sex
+Had a general emergency arise while you were steeped in sex
+Had your sexual technique/style/skill openly praised by someone
+Taken nude pictures of someone else
+Had nude pictures of you taken
+Placed a personal ad
+Answered a personal ad
+Been involved in breast fucking  (The Hawaiian Muscle Fuck)
+Participated in fist-fucking  (see 'Caligula')
+Shaved someone's pubic hair (or had yours) as part of sex
+Had sex for more than three hours in a single session of sex
+Been bruised during sex, oral sex, or mutual masturbation
+Bruised someone else during sex, oral sex, or mutual masturbation
+Been injured during sex, oral sex, or mutual masturbation
+Orgasmed more than three times in one session of sex
+Had sex so many times or for so long that one involved runs dry
+Disturbed other people by making excessive noise during sex
+Had sex or received oral sex while you were driving
+Had sex doggie fashion
+Had sex in the female superior position
+Had sex sitting up
+Had sex standing up
+Had sex upside-down
+Gone through several sexual positions without need for re-entry
+Fallen asleep during sex
+Woken up to someone having sex with you
+Had sex while one person was passed out or unconscious
+Given or received a hickey on your upper inner thigh
+Been on the receiving of anal sex
+Been on the ramming end of anal sex (a dildo counts)
+Had sex more than 10 times with 1 person
+Had sex more than 5 times in a 24 hour period
+Had sex more than 10 times in a 24 hour period
+Had sex while someone other than your partner was watching
+Had sex while one or both were playing a musical instrument
+Performed oral sex after intercourse without washing or douching
+Kissed your partner on the lips after oral sex without rinsing
+Inflicted pain during sex
+Been involved in cunnilingus during the woman's period
+Had sex during the woman's period
+Foot masturbated someone
+Been foot masturbated
+Tongue bathed someone
+Been tongue bathed
+Licked or sucked on someone else's feet and/or toes
+Had your feet and/or toes licked or sucked by someone else
+Licked someone's anus
+Licked someone's anus while they were defecating
+Performed oral sex while the person was urinating
+Drank your own urine
+Tasted or drank someone else's urine
+Drank/drained an entire bladder-full of someone else's urine
+Drank human blood
+Tasted someone else's nasal mucous
+Been involved in a golden shower
+Swallowed your partner's orgasmic secretions
+Used the Purity Test as a checklist of things you could do
+Ever done something for purpose of lowering your Purity score
+Bought/read books to enhance sexual technique
+Participated in Purity Testing with an ulterior motive
+Become interested in person after hearing their Purity score
diff --git a/xtrn/smm/purity.asc b/xtrn/smm/purity.asc
new file mode 100644
index 0000000000000000000000000000000000000000..3aae420c0007303289cce4c9a9ae8ef84d580b1d
--- /dev/null
+++ b/xtrn/smm/purity.asc
@@ -0,0 +1,31 @@
+nch                            THE UNISEX, OMNISEXUAL
+
+y                            P U R I T Y    T E S T
+nc_______________________________________________________________________________
+
+                   rhiWARNINGnrh: THIS TEST MAY EASILY OFFEND YOU
+
+chInstructions for Use:
+nc
+This is a fairly long test consisting of hone hundred questionsnc.  It starts out
+tame and gets progressively worse (or better, depending on your viewpoint).
+
+hDefinitions
+nc
+Any references to hMOSnc are references to a hMncember of the hOncpposite hSncex.
+
+All questions in this test pertain to events that have happened to you
+subsequent to your weaning and babyhood/infancy.  Anything that may have
+happened before that time is considered not standing and void.
+
+The term mutual masturbation refers to someone masturbating you AND/OR you
+masturbating someone else, not exclusively both at the same time.
+
+We would also like to define having sex in the homosexual case; homosexual sex
+has occurred when both partners are of the same sex and one of the partners has
+an orgasm while there is some contact between the genitals of both partners.
+
+We would now like to bring to your attention that there is no passing nor
+failing score.  Therefore, one really shouldn't worry too much about getting a
+high score...  even if you do get giggled at for the rest of your life.
+
diff --git a/xtrn/smm/purity.que b/xtrn/smm/purity.que
new file mode 100644
index 0000000000000000000000000000000000000000..c97b56bea11a7bd40fdf3adfd7c99c4de4744912
--- /dev/null
+++ b/xtrn/smm/purity.que
@@ -0,0 +1,100 @@
+Had a date
+Been out on a date past 4 am
+Had a blind date
+Kissed a MOS
+Been french kissed
+Kissed a MOS in the horizontal position
+French kissed three or more MOS's in 24 hours
+Kissed a MOS in the last three months
+Necked for more than 2 hours consecutively
+Slow danced cheek-to-cheek
+Had an alcoholic drink
+Been drunk
+Driven while under the influence of alcohol or drugs
+Had a lapse of memory due to drinking or drugs
+Used alcohol or drugs to lower a MOS's resistance
+Smoked tobacco
+Smoked pot or hashish
+Used a stronger drug
+Taken 4 or more "recreational" drugs within 24 hours
+Read a pornographic book or magazine
+Seen a pornographic movie
+Seen a stripper
+Been arrested
+Been convicted of a crime
+Had an erection/clitoral erection
+Had an orgasm
+Had an orgasm in a dream
+Fondled a MOS's ass
+Caressed a MOS's thigh
+Fondled a breast or had your breast fondled
+Wrestled with a MOS
+Showered, bathed, jacuzzied, or saunaed with a MOS
+Gone coed skinny-dipping
+Gone through the motions of intercourse while fully clothed
+Spent the night in a MOS's room
+Slept in the same bed with a MOS
+Seen a naked post-pubescent MOS
+Been seen naked by a MOS after puberty
+Undressed or been undressed by a MOS
+Kissed a MOS on the breast or been kissed on the breast
+Fondled a MOS's genitals or had your genitals fondled by a MOS
+Had an orgasm due to manipulation by a MOS
+Kissed a MOS on the thigh
+Engaged in cunnilingus
+Engaged in fellatio
+Had sexual intercourse at a parents house while they were home
+Engaged in definitely sexual activity on the first date
+Masturbated
+Masturbated to a picture
+Masturbated with another person in the room
+Watched another person masturbate
+Been caught masturbating
+Simulated intercourse with an inanimate object
+Committed an act of voyeurism
+Committed an act of exhibitionism
+Massaged or been massaged by a MOS
+Unintentionally interrupted a couple in a significant state of undress
+Participated in a tickle orgy, gross out, truth or dare, etc.
+Experimented sexually before puberty
+Purchased contraceptives in a drug store
+Had sexual intercourse
+Had sexual intercourse more than 10 times
+Had sexual intercourse continuously for 1/2 hour
+Had sexual intercourse within the last 3 months
+Had sexual intercourse 3 or more times in 1 night
+Had sexual intercourse in 3 or more positions
+Had sexual intercourse in a car
+Had sexual intercourse using a condom
+Done sixty-nine
+Had sexual intercourse outdoors
+Had sexual intercourse with 3 different people
+Had sexual intercourse with a virgin
+Had sexual intercourse during menstruation
+Had sexual intercourse without using birth control
+Had sexual intercourse with 2 MOSs in 24 hours
+Had sexual intercourse in a public place
+Described a sexual experience to a separate party
+Committed statutory rape
+Been the object of travel or traveled 100 miles or more for sexual activity
+Impregnated a woman or been pregnant
+Arranged or had an abortion
+Displaced a roommate by staying with a MOS for 1 or more nights
+Shacked up with a MOS for a month or more
+Tasted semen
+Been propositioned by a prostitute or pimp
+Accepted
+Had anal intercourse
+Been tested for V.D. due to reasonable suspicion
+Had V.D.
+Picked up a strange MOS for sexual purposes
+Engaged in group sex
+Engaged in sadism or masochism for sexual enjoyment
+Been propositioned by a member of the same sex
+Accepted a proposition by a member of the same sex
+Been masturbated by a member of the same sex
+Been orally stimulated by a member of the same sex
+Committed incest
+Fondled a pre-pubescent MOS
+Engaged in transvestitism for sexual enjoyment
+Committed bestiality
diff --git a/xtrn/smm/que.lst b/xtrn/smm/que.lst
new file mode 100644
index 0000000000000000000000000000000000000000..95018ef15d5c40f4492698eb705092733f0d5aa3
--- /dev/null
+++ b/xtrn/smm/que.lst
@@ -0,0 +1,12 @@
+BASIC
+Basic Personality
+0
+APPEAR
+Appearance
+0
+MATERIAL
+Material
+0
+ADULT
+Adult Sexuality
+18
diff --git a/xtrn/smm/smm.can b/xtrn/smm/smm.can
new file mode 100644
index 0000000000000000000000000000000000000000..503ab7da423f090b526c1ca28cef26a0e5dcc34c
--- /dev/null
+++ b/xtrn/smm/smm.can
@@ -0,0 +1,4 @@
+l
+p
+digital man
+fuck
diff --git a/xtrn/smm/smm.cfg b/xtrn/smm/smm.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..334011d05ec08c4f055ec6ce0adfe6da12461542
--- /dev/null
+++ b/xtrn/smm/smm.cfg
@@ -0,0 +1,22 @@
+18
+15
+0
+
+
+
+
+0
+0
+30
+1
+
+0
+0
+0
+0
+0
+%!dsz portx %u,%i sz %f
+0
+
+
+90
diff --git a/xtrn/smm/smm.doc b/xtrn/smm/smm.doc
new file mode 100644
index 0000000000000000000000000000000000000000..37939ac6251347c2c5170880f9ab1ec4995ef654
--- /dev/null
+++ b/xtrn/smm/smm.doc
@@ -0,0 +1,594 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+     Synchronet Match Maker  Version 2.10  Copyright 1996 Digital Dynamics
+     -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+	       "Perhaps the most comprehensive Match Maker ever."
+
+
+Synchronet Installation (if you are using Synchronet BBS Software)
+==================================================================
+
+Synchronet Match Maker (SMM) comes pre-installed in Synchronet BBS v2.1 and
+later. If match maker has been pre-installed in your system, see the file
+UPGRADE.DOC for details on installing a newer version over an older version
+(upgrading).
+
+Create the following sub-directory off of your main Synchronet directory
+(e.g. C:\SBBS):
+
+XTRN\SMM	(The XTRN directory should already exist)
+
+(example: C:\SBBS\XTRN\SMM)
+
+Copy the files included in the archive or on the floppy disk into this
+directory. The DEMO and registered versions of this program are identical.
+The registration number must be obtained from Digital Dynamics and entered
+into the Match Maker configuration program (SMMCFG) to convert the DEMO into
+a registered copy.
+
+Licenses for registered use of Synchronet Match Maker are sold separately from
+Synchronet BBS Software. Once a registration number has been issued, no refunds
+or returns of Synchronet Match Maker will be accepted.
+
+Run SCFG (Synchronet Configuration Program) and add the Match Maker to one
+of your External Program Sections and configure as follows:
+
+	����������������������������������������������������������ͻ
+	�		   Synchronet Match Maker		   �
+	����������������������������������������������������������͹
+	� �Name 		      Synchronet Match Maker	   �
+	� �Internal Code	      SMM			   �
+	� �Start-up Directory	      ..\XTRN\SMM		   �
+	� �Command Line 	      smm			   �
+	� �Clean-up Command Line				   �
+	� �Multiple Concurrent Users  Yes			   �
+	� �Intercept I/O Interrupts   No  <-- Important 	   �
+	� �Swap BBS out of Memory     Yes			   �
+	� �Modify User Data	      Yes			   �
+	� �Execute on Event	      No			   �
+	� �BBS Drop File Type	      Synchronet      XTRN.DAT	   �
+	� �Place Drop File In	      Node Directory		   �
+	����������������������������������������������������������ͼ
+
+If you wish to have old profile entries and wall writings automatically deleted
+from your match maker, you will need to run the SMMUTIL program periodically to
+remove profiles that haven't been updated in a certain number of days
+(specified on the SMMUTIL command line). To set up a timed event in SCFG->
+External Programs->Timed Events for SMMUTIL:
+
+    ��������������������������������������������������������������������ͻ
+    �			      SMMUTIL Timed Event			 �
+    ��������������������������������������������������������������������͹
+    � �Internal Code		       SMMUTIL				 �
+    � �Start-up Directory	       ..\XTRN\SMM			 �
+    � �Command Line		       smmutil 90 7 > SMMSTATS.TXT	 �
+    � �Execution Node		       1				 �
+    � �Execution Days		       Sun Mon Tue Wed Thu Fri Sat	 �
+    � �Execution Time		       00:00				 �
+    � �Requires Exclusive Execution    Yes				 �
+    � �Force Users Off-line For Event  No				 �
+    ��������������������������������������������������������������������ͼ
+
+This would have SMMUTIL remove all profiles that haven't been updated in the
+past 90 days and all wall writings older than 7 days.
+
+It will also create the file SMMSTATS.TXT in your XTRN\SMM directory with
+detailed statistics on the demographics of the current SMM profile database.
+You may want to make this file available for download or as a viewable bulletin
+on your BBS.
+
+If you are currently on DOVE-Net or FidoNet and you carry the "Synchronet Data"
+(SYNCDATA) message area and wish to link your match maker database with other
+BBSs through this message area, set up the following two Timed Events in SCFG:
+
+    ��������������������������������������������������������������������ͻ
+    �			      SMB2SMM Timed Event			 �
+    ��������������������������������������������������������������������͹
+    � �Internal Code		       SMB2SMM				 �
+    � �Start-up Directory	       ..\XTRN\SMM			 �
+    � �Command Line		       smb2smm %jsubs\syncdata smm.dab	 �
+    � �Execution Node		       1				 �
+    � �Execution Days		       Sun Mon Tue Wed Thu Fri Sat	 �
+    � �Execution Time		       00:00				 �
+    � �Requires Exclusive Execution    No				 �
+    � �Force Users Off-line For Event  No				 �
+    ��������������������������������������������������������������������ͼ
+
+    ��������������������������������������������������������������������ͻ
+    �			      SMM2SMB Timed Event			 �
+    ��������������������������������������������������������������������͹
+    � �Internal Code		       SMM2SMB				 �
+    � �Start-up Directory	       ..\XTRN\SMM			 �
+    � �Command Line		       smm2smb smm.dab %jsubs\syncdata	 �
+    � �Execution Node		       1				 �
+    � �Execution Days		       Sun Mon Tue Wed Thu Fri Sat	 �
+    � �Execution Time		       00:00				 �
+    � �Requires Exclusive Execution    No				 �
+    � �Force Users Off-line For Event  No				 �
+    ��������������������������������������������������������������������ͼ
+
+Change "syncdata" in the above examples to the internal code you have used
+for the "Synchronet Data" echo. Also, if you are not running v2.1+ of
+Synchronet, you must specify the full path to the DATA directory instead of
+using the %j. If your "Synchronet Data" echo is stored on a different
+drive or in a different directory than SBBS, then you must specify that drive
+and path on the above command lines.
+
+To get your match maker database active immediately, you may want to reset
+your message pointers for the "Synchronet Data" echo on your hub BBS. If
+using QWKnet, you would accomplish this by posting the message in the
+"Synchronet Data" echo on your BBS to "SBBS" with a title of "RESET". The
+message body does not matter.
+
+Swapping
+--------
+If you get memory allocation errors during the execution of any of the above
+event command lines, add that program name to your Global Swap List in SCFG.
+
+DOOR.SYS BBS Installation (if you are using BBS software OTHER than Synchronet)
+===============================================================================
+
+Your BBS software must be able to create the industry standard DOOR.SYS drop
+file (35 lines or more). If you wish to charge credits for specific match maker
+functions (adding profile, sending telegrams, etc) your BBS software must be
+able to READ BACK in the modified DOOR.SYS drop file (e.g. GAP BBS software).
+Credits are read from line 31 of the DOOR.SYS file (in K) and credits used
+while in SMM are written to line 30 of the DOOR.SYS (in K) and total credits
+after adjustment (in K) are written back to line 31 of the DOOR.SYS. Since the
+DOOR.SYS format only supports one set of flags (line 23), flag set 1 will be
+the only valid flag set to use for the security options in SMMCFG (i.e. flag
+sets 2-4 will not be used).
+
+Create a directory for Synchronet Match Maker (SMM) and copy all the files
+into it (example: C:\BBS\DOORS\SMM).
+
+Copy the files included in the archive or on the floppy disk into this
+directory. The DEMO and registered versions of this program are identical.
+The registration number must be obtained from Digital Dynamics and entered
+into the Match Maker configuration program (SMMCFG) to convert the DEMO into
+a registered copy.
+
+Once a registration number has been issued, no refunds or returns of Synchronet
+Match Maker will be accepted.
+
+If you are running a multinode BBS, you will need to have the DOOR.SYS created
+in a separate directory for each node (i.e. NOT the directory where SMM has
+been installed). This will prevent drop file collisions if two users enter SMM
+at the same time. The complete path to the DOOR.SYS must be specified on the
+command line, so each node must have a different path specified. Example:
+
+Node 1 Command Line:	SMM C:\BBS\NODE1\DOOR.SYS
+Node 2 Command Line:	SMM C:\BBS\NODE2\DOOR.SYS
+
+SHARE must be loaded in your CONFIG.SYS or AUTOEXEC.BAT for SMM to function
+(in single node or multinode environments).
+
+If non-standard IRQs or I/O addresses are used, they must be specified on the
+SMM command line using the Command Line Options. IRQs 2-15 are fully supported.
+Shared IRQs are not supported in UART mode.
+
+16550 FIFO buffered UARTs are detected and enabled.
+
+DTE rates up to 115200 baud are supported (higher with proprietary hardware).
+
+Command Line Options:
+====================
+
+Command line options override the values (if present) in the DOOR.SYS file.
+No command line options are required when used with Synchronet BBS software.
+
+	/P#	Set the COM port (0=local only) (normally not necessary)
+
+		Example: /P1 to specify COM1
+			 /P0 to specify no COM port (local use)
+
+	/C#	Set the COM port type or I/O (D:DigiBoard, F:FOSSIL, E:EBIOS)
+
+		Example: /CF to specify FOSSIL
+			 /C3e8 to specify I/O address 3e8 (hexadecimal)
+
+	/I#	Set the COM port IRQ or channel (only required if non-standard)
+
+		Example: /I5 to specify IRQ 5
+			 /I0 to specify FOSSIL/Digi Channel 0
+
+	/R#	Set the COM port DTE rate (normally not necessary)
+
+		Example: /R57600 to specify a DTE rate of 57600 baud
+
+	/T#	Set time-slice APIs supported (default: DESQview if detected)
+
+		Example: /T1 to use INT28 time-slice API
+			 /T2 to use Win/OS2 time-slice API
+			 /T4 to disable DESQview detection and time-slice API
+
+If a user name is specified on the command line, that user's profile will be
+automatically looked up and displayed.
+
+	Example (Synchronet): SMM JOHN DOE
+
+	Example (non-Synchronet): SMM C:\BBS\NODE1\DOOR.SYS JOHN DOE
+
+The current directory MUST be the directory where SMM has been installed
+(e.g. C:\BBS\DOORS\SMM) when SMM is executed. Some non-Synchronet BBS packages
+may require a batch file to change drive and/or directory before running SMM
+and may require the same batch file re-load the BBS software. Example:
+
+	@ECHO OFF
+	CD \BBS\DOORS\SMM
+	SMM \BBS\NODE1\DOOR.SYS
+	REM The following may not be necessary with some BBS packages
+	CD \BBS\NODE1
+	BBS
+
+Configuration
+=============
+
+SMMCFG
+------
+SMMCFG.EXE is used to modify your SMM.CFG (Configuration) file. Hitting the F1
+key will display a help page on the currently selected menu or option.
+
+	����������������������������������������������������������ͻ
+	�	    Synchronet Match Maker Configuration	   �
+	����������������������������������������������������������͹
+	� �System Name				    MY BBS	   �
+	� �Wall Security...					   �
+	� �Profile Database Security... 			   �
+	� �Credit Cost for Adding Profile	    0k		   �
+	� �Credit Cost for Sending Telegram	    0k		   �
+	� �Credit Cost for Writing on the Wall	    0k		   �
+	� �Credit Cost for Reading Questionnaire    0k		   �
+	� �Minimum Level to Send Telegrams	    0		   �
+	� �Minimum Level to Read Questionnaires     0		   �
+	� �Sysop Level				    90		   �
+	� �Minor Segregation (Protection) Age	    Disabled	   �
+	� �Auto-Update Profiles 		    30		   �
+	� �Notify User of Activity		    1		   �
+	� �Use Metric System			    No		   �
+	� �Zmodem Send Command			    %!dsz portx %u,�
+	� �Local Photo Viewer			    dvpeg %f	   �
+	� �Registration Number			    UNREGISTERED   �
+	����������������������������������������������������������ͼ
+
+System Name
+-----------
+The most important option in this program is your System Name (BBS Name). If
+you are running Synchronet BBS Software, this option does not have to be set
+here (it is automatically passed to SMM in the XTRN.DAT drop file from
+Synchronet). If it is set, it will override the name specified in SCFG->System
+->BBS Name.
+
+If you have registered SMM for non-Synchronet BBS Software, this option must be
+set to the exact BBS name you registered SMM for (not case sensitive).
+
+Once you have setup SMM and your BBS users have entered their profiles, it is
+important that you DO NOT change your BBS name, or you will lose those
+profiles (and likely upset your users). If you are networking your match maker
+database with other BBSs, it is important that you do not have the same System
+Name as another BBS in the network. Only the first 25 characters of the BBS
+name are used and every BBS in an SMM network must have a unique BBS name. Your
+System Name (configured in SMMCFG) does not have to match your actual BBS name
+(as configured in your BBS software). This allows you to change your System
+Name for match maker networks where your real BBS name is already in use.
+
+Profile Database Security
+-------------------------
+	�����������������������������������������������ͻ
+	�	    Profile Database Security		�
+	�����������������������������������������������͹
+	� �Minimum User Age to Add Profile	     15 �
+	� �Minimum User Age to Take Purity Test      18 �
+	� �Minimum Security Level to Add Profile     30 �
+	� �Required Flags (Set 1) to Add Profile	�
+	� �Required Flags (Set 2) to Add Profile	�
+	� �Required Flags (Set 3) to Add Profile	�
+	� �Required Flags (Set 4) to Add Profile	�
+	�����������������������������������������������ͼ
+
+The Profile Database Security sub-menu allows you to define who can add
+profiles by security level, age, and flags as well as the minimum age for
+the purity test. Flag sets 2-4 are used only with Synchronet BBS software.
+
+Minimum Levels
+--------------
+From the SMMCFG main menu you can change the minimum levels required to
+send telegrams, write on the wall, read questionnaires, and perform sysop
+functions.
+
+Minor Segregation (Protection) Age
+----------------------------------
+This option separates all users into Minors (those users below the specified
+age) and Adults (those users at or above the specified age). When this option
+is used, adults cannot see minors' profiles or send them telegrams (and vice
+versa). This feature does not keep adult users on other non-segregated
+networked BBSs from sending telegrams to minors on your BBS (or minors on other
+BBSs from sending telegrams to adults on your BBS) - this can lead to confusion
+among users receiving telegrams from users that don't appear to be in the
+database (due to segregation on your system). This option is disabled by
+default.
+
+Auto-Update Profiles
+--------------------
+This option allows you to change the number of days between automatic
+profile updates across a match maker network. Normally a user's profile will
+not be sent out across the network unless the user makes a change to their
+profile. This option allows you to have their profile automatically sent
+every so many days as long as the user is active in the match maker (enters
+the match maker and uses it). This is useful for keeping profile databases
+up-to-date across the network as new systems come online. Setting this option
+to 0 disables this feature. The default value (30) should be fine for most
+configurations.
+
+Notify User of Activity
+-----------------------
+This is the number of the user who will be notified of match maker activity
+(profiles added, telegrams sent, wall writings, etc). Normally this option will
+be set to 1 (for user #1, the sysop). Setting this option to 0 disables this
+feature.
+
+Use Metric System
+-----------------
+If you want all heights and weights to be displayed in centimeters and
+kilograms instead of inches and pounds, set this option to "Yes". Since all
+measurements are internally stored in inches and pounds, those (English)
+measurements are the most accurate. Some measurements may change slightly
+when converting from metric to English and back.
+
+Zmodem Send Command
+-------------------
+This is the command line to execute to send a file via Zmodem to the remote
+user. The default command line (%!dsz portx %u,%i sz %f) should work for most
+system configurations (assuming DSZ is in your Synchronet EXEC directory or
+DOS search path and using UART serial ports on all nodes). The percent codes
+in the command line are called "Command Line Specifiers" and are explained
+later in this document.
+
+Local Photo Viewer
+------------------
+This is the command line to execute to view photos when running SMM locally.
+The percent codes in the command line are called "Command Line Specifiers" and
+are explained later in this document.
+
+Command Line Specifiers
+-----------------------
+%A	User name
+%B	Baud (DTE) Rate
+%C	Connect (DCE) Rate
+%F	File path
+%G	Temp directory (blank if non-SBBS)
+%I	UART IRQ Line
+%J	DATA directory (blank if non-SBBS)
+%K	CTRL directory (blank if non-SBBS)
+%N	Node Directory (blank if non-SBBS)
+%O	Sysop's name
+%P	COM Port
+%Q	System QWK ID (blank if non-SBBS)
+%R	Users Screen Rows
+%S	File Spec
+%T	Time left in seconds
+%U	UART I/O Address (in hex)
+%W	Time-slice API type
+%Z	TEXT Directory (blank if non-SBBS)
+%!	EXEC Directory (blank if non-SBBS)
+%#	Node number
+%*	Node number (zero padded to three digits)
+%$	Credits
+%%	%% for percent sign
+%1	User number
+%2	User number (zero padded to two digits)
+%3	User number (zero padded to three digits)
+%4	etc..
+
+SMM.CAN
+-------
+
+The trash can file (SMM.CAN) can be used to specify a list of words or word
+combinations (one word or word combination per line) that will not be allowed
+in user information and wall entries.
+
+QUE.LST
+-------
+
+The questionnaire list file (QUE.LST) can be edited with any ASCII text editor.
+The following is the stock QUE.LST containing the four stock questionnaires.
+
+BASIC
+Basic Personality
+0
+APPEAR
+Appearance
+0
+MATERIAL
+Material
+0
+ADULT
+Adult Sexuality
+18
+
+For each questionnaire, a code (up to eight valid DOS filename chars),
+a description (up to 25 chars), and minimum age are specified. A maximum
+of five questionnaires can be configured. If you are linked to the Synchronet
+Data echo, it is suggested you leave the four stock questionnaires intact
+(unmodified). If you change any of these questionnaires, you MUST change the
+code to something other than "BASIC, APPEAR, MATERIAL, or ADULT" as these codes
+are reserved specifically for the stock questionnaires. If you are not linked
+to the Synchronet Data echo, you can modify and remove any questionnaires you
+wish and use whatever code you wish except "PURITY" and "MB-TYPE" as these
+questionnaire names are specifically reserved by the Match Maker.
+
+The questionnaires are stored in files with the code for the name and .QUE
+as the file extension (e.g. BASIC.QUE, APPEAR.QUE, MATERIAL.QUE, etc).
+
+*.QUE Format
+------------
+
+A: First line is total number of questions (max: 20).
+   All lines following are question and answers groups.
+
+B: Each group starts with the actual question (max: 79 characters).
+
+C: The next line in the group consists of the total number of answers
+   (max: 16).
+
+D: The next line consists of the maximum number of answers this user can select
+   for this question.
+
+E: A single line for each answer (total answers from line C).
+
+F: Repeat lines B-E for each question (total questions from line A).
+
+Special Questionnaire Files
+---------------------------
+
+Two questionnaire files (MB-TYPE.QUE and PURITY.QUE) are unique in that they
+don't follow the above format at all and should need no modification.
+
+The MB-TYPE.QUE is used for the Myers-Briggs personality test and is a pretty
+standard questionnaire. The files MB-*.ASC are descriptions of the various
+personality types. The file MB-INTRO.ASC is displayed before the test and
+MB-TYPE.ASC is displayed when seeking descriptions of the various types.
+
+The PURITY.QUE is used for the purity test and the format is simply one
+"Have you ever" question per line. The default PURITY.QUE is a 100 question
+test of an adult nature. An optional 500 question purity test is also included
+as PURITY.500. To use this test in place of the 100 question version, copy
+PURITY.500 to PURITY.QUE. The 100 question version is also included as
+PURITY.100 as a back-up, in case you want to return to the 100 question
+version. The PURITY*.ASC files are displayed before and during the testing
+procedure.
+
+Sysop Commands
+==============
+
+From the Match Maker main menu, the sysop (any user with sysop security level)
+can use the (D)elete command to remove any profile from the database. Profiles
+can be undeleted with the '*' command (available only to sysops).
+
+When viewing profiles, sysops are shown the user name, number, and system
+name at the top of the profile. Normal users are not shown this information
+to maintain the anonymity of the users in the database.
+
+Hitting '!' will display a list of all networked BBSs that have contributed to
+the current profile database. If you see your BBS name in the list, either
+someone else is using the same BBS name as you (which is bad), or something has
+gone wrong in your network and you should contact Digital Dynamics to remedy
+the situation immediately. If someone else is using the exact same BBS name
+(not considering upper/lower case differences), one of you must change your BBS
+name by at least one character or you will definitely have problems with your
+users' Match Maker profiles and telegrams.
+
+Hitting '\' will rebuild the database index. If your database is corrupted,
+you should down all your nodes and use SMMUTIL instead of this command. If
+your SMM.IXB file disappears or becomes out-of-sync with your SMM.DAB file
+(which is unlikely to occur), you can use this command to quickly recreate
+it.
+
+Photographs
+===========
+
+You may use the included ADDPHOTO.EXE utility to import photographs into your
+local match maker database. The current directory must be the directory where
+SMM is installed when ADDPHOTO is run. The usage is:
+
+ADDPHOTO filename.ext user_number system_name
+
+where filename.ext is the path, filename, and extension of the photograph file
+to import (preferably, but not necessarily, a JPEG file with a .JPG extension).
+User_number is the user's number and system_name is the name of the system the
+user created their profile on.
+
+ADDPHOTO will create a directory called PHOTO off of the directory where SMM
+is installed (e.g. C:\SBBS\XTRN\SMM\PHOTO or C:\BBS\DOORS\SMM\PHOTO) and move
+the photograph file into this directory (changing the name, but leaving the
+extension (e.g. JPG, GIF, TIF, etc) intact.
+
+Example:
+
+		ADDPHOTO JAMIEFAR.JPG 1 M.A.S.H BBS
+
+The DELPHOTO.EXE utility is used to remove photographs from your local match
+maker database. The usage is:
+
+DELPHOTO user_number system_name
+
+Example:
+		DELPHOTO 1 M.A.S.H BBS
+
+The LSTPHOTO.EXE utility may be used to create a list of all users with photos
+in your local match maker database.
+
+Example:
+		LSTPHOTO > PHOTO.LST
+
+PHOTO.ZIP
+---------
+You can optionally, zip (using PKZIP) all the files in the PHOTO directory
+into a file called PHOTO.ZIP (in the SMM directory) and delete them from the
+PHOTO directory (to save disk space). When the photo is viewed or downloaded
+from SMM, it will be automatically extracted from the PHOTO.ZIP into the PHOTO
+directory (PKUNZIP.EXE must be in your DOS search path or your Synchronet
+EXEC directory). The ADDPHOTO utility will not automatically add new images to
+the PHOTO.ZIP (if you wish to use that storage method). Also, the files will
+not be automatically deleted from the PHOTO directory after download/viewing
+in SMM. This is so that popular image files will not have to be extracted each
+time. You may wish to periodically re-zip and delete the PHOTO directory
+(perhaps with an event) to preserve disk space.
+
+SYNCDATA Echo
+-------------
+If your match maker is linked (via DOVE-Net or FidoNet) to the SYNCDATA echo,
+you do not need a scanner or the ADDPHOTO utility to get your users' photos
+into the database. Simply have your users send their photos along with your
+full and correct BBS name, their user number, and a xerox-copy of a current
+formal photo-ID (to verify identity only, IDs will not be scanned and all
+ID information will remain completely confidential) to Digital Dynamics.
+
+The photos must have a 2 inch square drawn around the area to be scanned. The
+photos will be scanned and distributed in JPEG format (with monthly
+redistribution) on the SYNCDATA echo by Digital Dynamics. This service is
+available to all users of SMM free of charge (this free offer _may_ expire
+on Feb 14th, 1996).
+
+Digital Dynamics reserves the right to refuse to scan and distribute any
+photos for any reason. Nudity or vulgarities will not be allowed.
+
+Photos and IDs included with a self-addressed _stamped_ enveloped will be
+returned.
+
+DEMO Version
+============
+
+The unregistered "DEMO" version of Synchronet Match Maker will not allow
+users to send Telegrams to other users in the database (through the
+message network), write on the Match Maker wall, or read other users'
+questionnaires.
+
+To register your copy of Synchronet Match Maker, complete the ORDER.TXT form
+included in this archive/directory and send it to Digital Dynamics, or call
+714-529-6328 (voice) or 714-529-9525 (BBS) to order with a credit card for
+faster processing.
+
+/* End of SMM.DOC */
diff --git a/xtrn/smm/smm_help.asc b/xtrn/smm/smm_help.asc
new file mode 100644
index 0000000000000000000000000000000000000000..c969ab434c583892813e19e505d1801208d2a3b4
--- /dev/null
+++ b/xtrn/smm/smm_help.asc
@@ -0,0 +1,27 @@
+yhOverall Match (General Explanation):nc
+
+The overal match is a highly refined algorithm used to compute a percentage of 
+preference match between a user and a prospective mate. The user's preferences
+are weighed more heavily than those of the prospective mate. More specific
+preferences will result in a higher match percentage potential and a wider
+range of match percentages. More vague preferences will result in a rather
+narrow range of match percentages, so it is best to keep your "hideal matenc" 
+in mind when completing the preferred mate sections of your profile and
+questionnaires.
+
+yhGlobal Database:nc
+
+If your sysop has taken the steps to link this profile database with the 
+international hSyncDatanc network, your profile will be automatically included
+in this global database for users on other BBSs around the world to view. The 
+profiles you see in this database may very well be users in other counties, 
+states, or countries and you can view their information here and communicate
+with them via hTelegramnc using this program.
+
+yhCredits:nc
+
+hPurity Test                     ncOriginal version from MIT's Baker House.
+
+hMyers-Briggs Type Indicator     ncAdapted from the book "Please Understand Me"
+				by David Keirsey and Marilyn Bates
+
diff --git a/xtrn/smm/smm_logo.asc b/xtrn/smm/smm_logo.asc
new file mode 100644
index 0000000000000000000000000000000000000000..c60c944dfc74384bdf6b04a08c09052ee0472738
--- /dev/null
+++ b/xtrn/smm/smm_logo.asc
@@ -0,0 +1,3 @@
+              nhrn h7������������������������������������������������n hr
+              n k7nk7 nk7Find your mate or just a date, electronically! n hr
+              nhrn hk7������������������������������������������������n hrn
diff --git a/xtrn/smm/smm_main.asc b/xtrn/smm/smm_main.asc
new file mode 100644
index 0000000000000000000000000000000000000000..b453ada7f35f8c3dd8a0d094f263283b686a7c32
--- /dev/null
+++ b/xtrn/smm/smm_main.asc
@@ -0,0 +1,9 @@
+� nk7������������������������������������������������������������������0
+� 7  b[hRnb7] Read Profiles               [hHnb7] Help/Info                   hk�0
+� 7  nb7[hLnb7] List Profiles               [hMnb7] Myers-Briggs Types          hk�0
+� 7  nb7[hUnb7] Update Your Profile         [hPnb7] Toggle Screen Pause         hk�0
+� 7  nb7[hDnb7] Delete Your Profile         [hTnb7] Read Telegrams              hk�0
+� 7  nb7[hNnb7] Scan for New Profiles       [hSnb7] Send Telegrams              hk�0
+� 7  nb7[hGnb7] Go to a Specific Profile    [hVnb7] Visit the Wall              hk�0
+� 7  nb7[hFnb7] Find Text in Profiles       [hOnb7] Log-off (Hang-up)           hk�0
+� nk7�h������������������������������������������������������������������n
diff --git a/xtrn/smm/upgrade.doc b/xtrn/smm/upgrade.doc
new file mode 100644
index 0000000000000000000000000000000000000000..be87c35f6229bc64df1869b8e140df213115ec87
--- /dev/null
+++ b/xtrn/smm/upgrade.doc
@@ -0,0 +1,241 @@
+Upgrading from Synchronet Match Maker 
+-------------------------------------
+
+If you are a registered owner of SMM and upgrading from v1.10 or earlier,
+you'll need to optain your registration number from Digital Dynamics by calling
+voice at 714-529-6328. You will need to have your Synchronet registration
+number and control code on hand when you call.
+
+The most important change (in upgrading to v2.0) is to change "Intercept
+I/O Interrupts" in SCFG from "Yes" to "No". Example:
+
+	����������������������������������������������������������ͻ
+	�		   Synchronet Match Maker		   �
+	����������������������������������������������������������͹
+	� �Name 		      Synchronet Match Maker	   �
+	� �Internal Code	      SMM			   �
+	� �Start-up Directory	      ..\XTRN\SMM		   �
+	� �Command Line 	      smm			   �
+	� �Clean-up Command Line				   �
+	� �Multiple Concurrent Users  Yes			   �
+	� �Intercept I/O Interrupts   No  <-- Important 	   �
+	� �Swap BBS out of Memory     Yes			   �
+	� �Modify User Data	      Yes			   �
+	� �Execute on Event	      No			   �
+	� �BBS Drop File Type	      Synchronet      XTRN.DAT	   �
+	� �Place Drop File In	      Node Directory		   �
+        ����������������������������������������������������������ͼ
+
+The only files you need to update when converting from v2.01/2.02 to v2.10 are:
+
+SMM.DOC 	Sysop Documentation (v2.10)
+SMM.EXE 	Match Maker Program (v2.10)
+SMMCFG.EXE	Match Maker Configuration Program (v2.10)
+SMMUTIL.EXE	Repair/Maintenance Program (v2.01)
+
+The only files you need to update when converting from v2.00 to v2.10 are:
+
+SMM.DOC 	Sysop Documentation (v2.10)
+SMMCFG.EXE	Match Maker Configuration Program (v2.10)
+SMMUTIL.EXE	Repair/Maintenance Program (v2.01)
+SMB2SMM.EXE	Import Program (v2.01)
+SMM2SMB.EXE	Export Program (v2.01)
+ADDPHOTO.EXE	Import Photographs locally (v1.01)
+DELPHOTO.EXE	Delete Photographs locally (v1.00)
+LSTPHOTO.EXE	List Profiles with Photographs (v1.00)
+
+The only files you need to update when converting from v1.x to v2.0 are:
+
+*.EXE		Executables
+*.DOC		Documentation
+LIST_HDR.ASC	Condensed Listing Header
+
+If upgrading from v1.00 or v1.10, you will also need to update the following
+files:
+
+SMM_MAIN.ASC	Main Menu
+SMM_HELP.ASC	Help Screen (if upgrading from v1.00)
+PURITY.ASC	Purity Explanation (if upgrading from v1.00)
+
+New in version 2.10
+~~~~~~~~~~~~~~~~~~~
+o Minor segregation (protection) now supported. This option separates all users
+  into Adults and Minors (configurable separation age), disallowing adults to
+  see minors' profiles or send them telegrams (and vice versa)
+o Minimum Age to access the wall now configurable in SMMCFG
+o XSDK v2.42 feature: Ctrl-T now displays current time, time used, and time
+  left (similar to SBBS)
+o XSDK v2.42 feature: User is now warned on each of their last 5 minutes left
+
+New in version 2.02a
+~~~~~~~~~~~~~~~~~~~~
+o Fixed bug in SMM.EXE when using DOOR.SYS (non-Synchronet BBSs)
+
+New in version 2.02
+~~~~~~~~~~~~~~~~~~~
+o Low priority input now correctly supported (for giving up multitasker time
+  slices even while accepting string input from the user) - this was an
+  indirect bug fix in XSDK v2.41 most notably effecting OS/2 systems
+o "Searching..." text now displayed before possibly long searches to indicate
+  to the user the system is not crashed
+o Sysop text searches now search user real names, system names and user numbers
+  in addition to the normal user-viewable text fields
+
+New in version 2.01
+~~~~~~~~~~~~~~~~~~~
+o New DELPHOTO.EXE utility to delete photos from the database
+o New LSTPHOTO.EXE utility to create a list of all profiles with attached
+  photos
+o SMB2SMM/SMM2SMB updated to detect and not import/export corrupted profiles
+o ADDPHOTO 1.01 now copies (instead of renames/moves) photo image files
+o SMB2SMM no longer deletes existing photo if CRC error detected on new photo
+o Wall security level (configured in SMMCFG) now correctly keeps users below
+  that level from writing on the wall
+o FOSSIL channel number automatically determined by subtracting 1 from the
+  COM port number (e.g. COM 1 is FOSSIL/Digi channel 0)
+  /I command line switch may be used to set non-standard channel
+o New SMM, SMMCFG, and SMMUTIL fix 25 character BBS name limit
+o SMMUTIL (v2.00) now removes old photos (tallys photos and networked profiles)
+
+New in version 2.00
+~~~~~~~~~~~~~~~~~~~
+o Optionally reads from and writes to DOOR.SYS drop file
+  (for compatibility with other [non-Synchronet] BBS programs)
+o MSG, UTI, and possibly JAM message base import/export utilities will be
+  available soon (for match maker networking with non-SMB compatible BBSs)
+o Optional command line switches for com port configuration and time-slice APIs
+  (for non-standard COM ports with other BBS programs)
+o Photographs (in any size or format) can be added to the database locally
+  (with Zmodem download and local viewing)
+o Photographs are automatically imported from the SYNCDATA echo with SMB2SMM
+  (other match maker networks will need to use their own distribution methods)
+o System name separately configurable in SMMCFG (to work around duplicate BBS
+  names in a match maker network)
+o Metric measurements (centimeters and kilograms) can be enabled in SMMCFG
+o Sysop level defined in SMMCFG (no longer hard-coded at level 90)
+o Low priority input supported (based on SCFG Node toggle option if Synchronet,
+  automatically enabled with other BBS programs)
+o Trailing spaces from user names are automatically removed
+
+New in version 1.31
+~~~~~~~~~~~~~~~~~~~
+o Ability to specify a user to search for on the command line
+  displays extended profile on the user and then exits (just "SMM user name")
+o Messages can now be sent from Digital Dynamics to you (sysops, users, or
+  both) inside SMM (not telegrams, but similar) to inform sysops and users of
+  Match Maker progressions/news. All messages have pre-determined expiration
+  dates and will automatically disappear when the date is reached. Sysop-only
+  messages can be deleted immediately after they are read if the sysop wishes.
+  Special ONE-time messages are displayed to users only once, while others
+  may be displayed each time they enter SMM. (SMB2SMM v1.30 required)
+o New SMMUTIL (v1.15) fixes 4095 user limitation.
+
+New in version 1.30
+~~~~~~~~~~~~~~~~~~~
+o Ability to send telegrams directly from condensed user listings
+o Wall new-scans now correctly scan by date/time imported instead of the
+  date/time written (must use SMB2SMM 1.22, SMM2SMB 1.12, and SMMUTIL 1.14)
+o Condensed Wall listings now clear the screen between each screen-full
+  (faster screen draws) with a prompt allowing the user to read a profile,
+  send a telegram, move backwards through the wall writings, or quit at the
+  bottom of each screen-full (more intuitive and convenient)
+o Extended Wall listings now display wall writing in magenta if the author
+  is female, green if the author is male (instant gender recognition)
+  wall writings who can't be found in the database
+o Extended Wall listings allow sending telegrams directly (without reading
+  profile first) and moving backwards through the wall writings
+o Extended Wall listings stay on current wall writing after reading profile
+  or sending telegram
+o XSDK v2.33 fixes problem with flashing screen loops when a user hangs up
+  with keys in the modem buffer
+o XSDK v2.33 fixes problem with sending double carriage returns at the end
+  of each line (making captures difficult)
+o Fixed problem with selecting races above E
+o If the sysop hits '!' from the main menu, all systems connected to the
+  database are listed
+o When reading a user's questionnaire, (P)revious and (Q)uit commands are now
+  available on the pause prompt
+o Updated sysop documentation
+
+New in version 1.22
+~~~~~~~~~~~~~~~~~~~
+o New option for those seeking "any-sex non-romantic friendly matches" allows
+  matches with both romantic/intimate mates and non-romantic mates (friends)
+o When displaying long user information, preference ranges (age, weight, etc)
+  and specifics (hair color, marital status, etc) are high intensity (bright
+  colors) if the preferencees are considered requirements by the user
+o The Wall was revamped to allow new-scans for new writings since a specific
+  date only and optional display of extended user information allowing reading
+  of questionnaires and sending of telegrams
+o Wall writings will now disallow any text in SMM.CAN (same as user aliases
+  profiles, etc.)
+
+New in version 1.21
+~~~~~~~~~~~~~~~~~~~
+o Credits can be charged or given for reading questionnaires
+o New WALL inside Match Maker for public discussion
+o Credits can be charged or given for writing on the wall
+o Minimum level can be specified to allow writing on the wall
+o SMM2SMB v1.11 now creates unique profile messages that won't be deleted by
+  duplicate message checking
+o SMM2SMB v1.11 and SMB2SMM v1.21 read/write wall writings from message base
+o SMMUTIL v1.11 now allows command line specification of maximum wall writing
+  age before auto-deletion
+o Telegrams and Profile Personal Text no longer word wrap on last line (bugfix)
+o Telegrams now ask to Save (Y/n) when completed to allow last minute abort
+
+New in version 1.20
+~~~~~~~~~~~~~~~~~~~
+o DEMO and registered versions use the same executable files, unlocked with
+  registration number obtained from Digital Dynamics (entered in SMMCFG)
+o Minimum level can be specified to allow the sending of telegrams
+o Minimum level can be specified to allow the reading of questionnaires
+o Sysops deleting their own profile, will not have to exit and re-enter
+  before SMM will report their profile does not exist
+o Match Maker telegrams are stored in SMM and users are notified via BBS
+  telegram of newly received SMM telegrams
+o Users can read awaiting telegrams and send telegrams from the SMM main menu
+o New customizable main menu (SMM_MAIN.ASC)
+o (W)rite Profile command changed to (U)pdate Profile, although 'W' can still
+  be used
+o When sending telegrams, the destination user's detail is displayed to prevent
+  users from sending telegrams to a wrong user (with the same name)
+o Automatically corrects user sex when not set to (M)ale or (F)emale
+o Questionnaire answers are aligned so they can be visually compared easily
+o SMMUTIL v1.10 generates statistics, run "SMMUTIL > stats.txt" to create file
+  for display on your BBS
+
+New in version 1.10
+~~~~~~~~~~~~~~~~~~~
+o New configuration program (SMMCFG) instead of editing SMM.CFG with an
+  ASCII editor.
+o Optional trash can file for keeping users from using profanity in profiles
+  and users names.
+o SMB2SMM will not import profiles if the user age is less than you have
+  specified in SMMCFG for minimum user age.
+o SMB2SMM will not import profiles that contains words found in your TRASH.CAN
+  file (if it exists).
+o When listing profiles in SMM, you can go backwards to previous screens and
+  read profiles directly from the listing. The (R)ead profile key automatically
+  puts the user with the highest percentage on the current screen in the
+  user name field.
+o User's can enter "Non-disclosed" for their yearly income, by hitting ENTER
+  when asked.
+o User's can specify if they REQUIRE their matches to fall within certain
+  ranges or REQUIRE specific features.
+o Profile screens were condensed to get all information on one page whenever
+  possible.
+o SMMUTIL will scan for duplicate profiles or if specified, delete profiles
+  that haven't been updated in a certain number of days. SMMUTIL also
+  automatically condenses the data file, removing any deleted profiles and
+  rebuilds the index file.
+o Users can be charged credits or given credits for adding profiles and
+  sending telegrams. If a user adds a profile and then deletes it, the credit
+  charges are reversed.
+o Auto-update feature will automatically re-hatch a users entry into the
+  Global Database after a certain number of days, if the user is active in
+  the match maker but hasn't made any profile changes.
+o Sysop can be notified automatically of added profiles and sent telegrams
+  from the match maker
+
+/* End of UPGRADE.DOC */