xsdk.c 73 KB
Newer Older
1 2 3 4
/* xsdk.c */

/* Synchronet External Program Software Development Kit	*/

5
/* $Id: xsdk.c,v 1.41 2020/09/20 18:57:00 rswindell Exp $ */
6 7 8 9 10

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
11
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
 *																			*
 * This library is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU Lesser General Public License		*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU Lesser General Public License for more details: lgpl.txt or	*
 * http://www.fsf.org/copyleft/lesser.html									*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout xtrn	*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195

/***************************** 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
196 197 198 199 200 201 202 203 204 205 206
			Added Microsoft/Borland 32-bit compiler compatibility
			Added socket (Telnet) compatibility
			Changed Ctrl-Minus (Insert key) to Ctrl-V
			Changed Ctrl-V (Center text key) to Ctrl-L
			Added support for telnet nodes (connection=0xffff)
	3.00
			Fixed problem with clear screen (form feed) in node messages.
			checkline() now exits when the remote user disconnects on 32-bit 
				programs. Use atexit() to add cleanup code.
	3.01
			Eliminated warnings in ctrl_a() when compiled with VC++ 6.0.
207 208 209 210 211 212
			Added Linux/GCC support (xsdkwrap.c, xsdkwrap.h, and xsdkinet.h)
	3.10	
			Added COMPILER_DESC and PLATFORM_DESC macros to xsdkwrap.h
			Added support for no local console (XSDK_MODE_NOCONSOLE)
				- This is now the default mode when building 32-bit programs
			Eliminated use of ungetch() in favor of ungetkey() - more secure
213 214 215
	3.11
			Added support for stdio (non-socket) communications in 32-bit builds.
	3.20
216 217 218 219 220

\****************************************************************************/

#include "xsdk.h"

221 222 223 224
#ifdef _WINSOCKAPI_
WSADATA WSAData;		// WinSock data
#endif

225
char *xsdk_ver="3.21";
rswindell's avatar
rswindell committed
226
ulong xsdk_mode=XSDK_MODE_NOCONSOLE;
227

228 229 230 231 232
/****************************************************************************/
/* This allows users to abort the listing of text by using Ctrl-C           */
/****************************************************************************/
int cbreakh(void)	/* ctrl-break handler */
{
233 234
	aborted=1;
	return(1);		/* 1 to continue, 0 to abort */
235 236 237 238 239 240 241 242 243 244 245
}

/****************************************************************************/
/* Performs printf() using bbs bputs function								*/
/****************************************************************************/
int bprintf(char *fmt, ...)
{
	va_list argptr;
	char sbuf[1024];
	int chcount;

246 247 248 249 250
	va_start(argptr,fmt);
	chcount=vsprintf(sbuf,fmt,argptr);
	va_end(argptr);
	bputs(sbuf);
	return(chcount);
251 252 253 254 255 256 257 258 259 260 261
}

/****************************************************************************/
/* Performs printf() using bbs rputs function								*/
/****************************************************************************/
int rprintf(char *fmt, ...)
{
	va_list argptr;
	char sbuf[1024];
	int chcount;

262 263 264 265 266
	va_start(argptr,fmt);
	chcount=vsprintf(sbuf,fmt,argptr);
	va_end(argptr);
	rputs(sbuf);
	return(chcount);
267 268 269 270 271 272 273 274 275
}

/****************************************************************************/
/* Outputs a NULL terminated string locally and remotely (if applicable) 	*/
/****************************************************************************/
void bputs(char *str)
{
	ulong l=0;

276 277 278 279 280 281 282 283
	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++]); }
284 285 286 287 288 289 290 291 292 293 294
}

/****************************************************************************/
/* 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;

295 296
	while(str[l])
		outchar(str[l++]);
297 298 299 300 301 302
}

/****************************************************************************/
/* Returns the number of characters in 'str' not counting ctrl-ax codes		*/
/* or the null terminator													*/
/****************************************************************************/
303
int bstrlen(char *str)
304 305 306
{
	int i=0;

307
	while(*str) {
308
		if(*str >= 0 && *str<' ') {	/* ctrl char */
309 310 311 312 313 314 315 316 317 318
			if(*str==1) /* ctrl-A */
				str++;
			else if(*str!=CR && *str!=LF && *str!=FF)
				i++; }
		else
			i++;
		if(!(*str))
			break;
		str++; }
	return(i);
319 320 321 322 323 324 325 326 327 328
}

/****************************************************************************/
/* Outputs the string 'str' centered for an 80 column display               */
/* Automatically appends "\r\n" to output                                   */
/****************************************************************************/
void center(char *str)
{
	 int i,j;

329 330
	j=bstrlen(str);
	for(i=0;i<(80-j)/2;i++)
331
		outchar(' ');
332
	bputs(str);
333 334
}

335 336 337 338 339
#ifndef __16BIT__

char	outbuf[5000];
ulong	outbufbot=0;
ulong	outbuftop=0;
340
sem_t	output_sem;
341 342 343 344 345 346

void output_thread(void* arg)
{
	int		i,len;
	char	str[256];

347 348
	sem_init(&output_sem,0,0);

349 350
	while(client_socket!=INVALID_SOCKET) {
		if(outbufbot==outbuftop) {
351 352
			sem_init(&output_sem,0,0);
			sem_wait(&output_sem);
353 354
			continue; 
		}
355 356 357 358 359 360 361 362

		if(outbuftop>outbufbot)
			len=outbuftop-outbufbot;
		else
			len=sizeof(outbuf)-outbufbot;
		i=send(client_socket,outbuf+outbufbot,len,0);
		if(i!=len) {
			sprintf(str,"!XSDK Error %d (%d) sending on socket %d\n"
363 364
				,i,ERROR_VALUE,client_socket);
#ifdef _WIN32
365
			OutputDebugString(str);
366 367 368
#else
			fprintf(stderr,"%s",str);
#endif
369 370 371 372 373 374 375 376
		}
		outbufbot+=len;
		if(outbufbot>=sizeof(outbuf))
			outbufbot=0;
	}
}
#endif

377 378 379 380 381 382
/****************************************************************************/
/* Outputs one character to the screen. Handles, pause, saving and			*/
/* restoring lines, etc.													*/
/****************************************************************************/
void outchar(char ch)
{
383
	static char lastch;
384

385 386 387 388
	/* Fix for unix-formatted text, expand sole LF to CRLF */
	if(ch=='\n' && lastch!='\r')
		outchar('\r');

389
#ifndef __16BIT__
390 391 392 393 394 395 396 397 398 399 400
	if(client_socket!=INVALID_SOCKET) {
		ulong	top=outbuftop+1;

		if(top==sizeof(outbuf))
			top=0;
		if(top!=outbufbot) {
			outbuf[outbuftop++]=ch;
			if(outbuftop==sizeof(outbuf))
				outbuftop=0;
			sem_post(&output_sem);
		}
401 402
	}
#endif
403

rswindell's avatar
rswindell committed
404 405
	if(con_fp!=NULL)
		write(fileno(con_fp),&ch,1);
406 407 408 409 410 411 412 413 414 415

	if(ch==LF) {
		lncntr++;
		lbuflen=0;
		tos=0; 
	}
	else if(ch==FF) {
		if(lncntr>1) {
			lncntr=0;
			CRLF;
416
			bpause(); 
417
		}
418
		lncntr=0;
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
		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;
434
		bpause(); 
435
	}
436
	lastch=ch;
437 438
}

439 440 441 442 443 444 445 446 447
void flushoutput(void)
{
#ifndef __16BIT__
	int i;
	for(i=0;i<10000 && outbuftop!=outbufbot;i++)
		mswait(1);
#endif
}

448 449 450
/****************************************************************************/
/* Prints PAUSE message and waits for a key stoke							*/
/****************************************************************************/
451
void bpause(void)
452
{
453 454 455
	char	ch;
	uchar	tempattrs=curatr,*msg="\1_\1r\1h[Hit a key] ";
	int		i,j;
456

457 458 459 460 461 462 463 464 465
	lncntr=0;
	bputs(msg);
	j=bstrlen(msg);
	ch=getkey(K_UPPER);
	for(i=0;i<j;i++)
		bputs("\b \b");
	attr(tempattrs);
	if(ch=='N' || ch=='Q')
		aborted=1;
466 467 468 469 470 471 472 473 474 475 476
}

/****************************************************************************/
/* 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;

477 478 479 480 481 482 483 484 485 486 487
	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); 
		} 
	}
488 489 490 491 492 493 494 495 496 497 498
}

/****************************************************************************/
/* 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;

499 500 501 502 503 504 505 506 507 508 509
	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); 
		} 
	}
510 511 512 513 514 515 516 517 518 519 520 521
}

/****************************************************************************/
/* 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;

522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
	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);
540 541
}

542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561
#ifdef _WIN32
int stdin_kbhit(void)
{
	int ch;

	ch=getc(stdin);
	if(ch==EOF)
		return(0);
	ungetc(ch,stdin);
	return(ch);
}
int stdin_getch(void)
{
	return(getc(stdin));
}
#else
	#define stdin_kbhit kbhit
	#define stdin_getch getch
#endif

562 563 564
int keyhit()
{
#ifndef __16BIT__
565 566 567 568 569 570 571
	ulong cnt=0;
	if(client_socket!=INVALID_SOCKET) {
		if(ioctlsocket(client_socket,FIONREAD,&cnt))
	    	return(0);
	    return(cnt);
	}
	return(0);
572
#else
573
	return(stdin_kbhit());
574 575 576
#endif
}

577 578 579 580
/****************************************************************************/
/* If a key has been pressed, the ASCII code is returned. If not, 0 is		*/
/* returned. Ctrl-P and Ctrl-U are intercepted here.						*/
/****************************************************************************/
581
char inkey(long mode)
582
{
583 584
	static int in_ctrl_p;
	static int ansi_len;
585
	uchar ch=0,hour,min,sec;
586 587
	long tleft;
	int i=0;
588 589
	time_t now;

590 591 592 593 594 595 596 597
#ifndef __16BIT__
	char	str[256];
	ulong	cnt=0;

	if(client_socket!=INVALID_SOCKET) {
		i=ioctlsocket(client_socket,FIONREAD,&cnt);
		if(i) {
			sprintf(str,"!XSDK Error %d (%d) checking readcnt on socket %d\n"
598 599
				,i,ERROR_VALUE,client_socket);
#ifdef _WIN32
600
			OutputDebugString(str);
601 602 603
#else
			fprintf(stderr,"%s",str);
#endif
604 605 606 607 608 609 610
		}
	}

	if(i==0 && cnt) 
		recv(client_socket,&ch,1,0);
	else
#endif
611

612 613 614 615
	if(keybufbot!=keybuftop) {
		ch=keybuf[keybufbot++];
		if(keybufbot==KEY_BUFSIZE)
			keybufbot=0; }
616 617
	else if(!(xsdk_mode&XSDK_MODE_NOCONSOLE) && stdin_kbhit()) {
		i=stdin_getch();
618 619 620
#ifdef __unix__
		if(i==LF) i=CR;	/* Enter key returns Ctrl-J on Unix! (ohmygod) */
#endif
621
		if(i==0 || i==0xE0) {			/* Local Alt or Function key hit */
622
			i=stdin_getch();
623 624
			switch(i) {
				case 0x47:	/* Home - Same as Ctrl-B */
625
					return TERM_KEY_HOME;	/* ctrl-b beginning of line */
626
				case 0x4b:		/* Left Arrow - same as ctrl-] */
627
					return TERM_KEY_LEFT;
628
				case 0x4d:		/* Right Arrow - same as ctrl-f */
629
					return TERM_KEY_RIGHT;
630
				case 0x48:		/* Up arrow - same as ctrl-^ */
631
					return TERM_KEY_UP;
632
				case 0x50:		/* Down arrow - same as CR */
633
					return TERM_KEY_DOWN;
634
				case 0x4f:	  /* End	  - same as Ctrl-E */
635
					return TERM_KEY_END;
636
				case 0x52:	/* Insert */
637
					return TERM_KEY_INSERT;	/* ctrl-minus - insert mode */
638 639 640 641 642 643 644
				case 0x53:	/* Delete */
					return(0x7f);   /* ctrl-bkspc - del cur char */
				}
			return(0); } 
		ch=i;
	}

645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682
	if(ch == ESC) {
		ansi_len = !ansi_len;
	}
	else if(ansi_len == 1) {
		if(ch == '[')
			ansi_len++;
		else
			ansi_len = 0;
	}
	else if(ansi_len == 2) {
		ansi_len =0 ;
		switch(ch) {
			case 'A':
				return TERM_KEY_UP;
			case 'B':
				return TERM_KEY_DOWN;
			case 'C':
				return TERM_KEY_RIGHT;
			case 'D':
				return TERM_KEY_LEFT;
			case 'H':
				return TERM_KEY_HOME;
			case 'V':
				return TERM_KEY_PAGEUP;
			case 'U':
				return TERM_KEY_PAGEDN;
			case 'F':
			case 'K':
				return TERM_KEY_END;
			case '@':
				return TERM_KEY_INSERT;
			default:
				return 0;
		}
	}
	if(ansi_len)
		return 0;

683 684 685 686 687 688 689 690 691 692 693 694
	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); }
695

696 697 698 699 700 701 702 703 704 705 706 707 708 709
	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);
710 711
		if(tleft<0)
			tleft=0;
712 713 714 715 716 717 718
		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); }
719

720 721 722 723 724 725 726 727 728 729
	if(ch==21) { /* Ctrl-U Users online */
		if(!ctrl_dir[0])
			return(0);
		SAVELINE;
		CRLF;
		whos_online(1);
		CRLF;
		RESTORELINE;
		lncntr=0;
		return(0); }
730

rswindell's avatar
rswindell committed
731 732 733 734 735
#ifndef __16BIT__
	if(ch==LF) 
		ch=0;		/* Ignore LF of Telnet CR/LF sequence */
#endif

736 737 738 739
	if(ch==3)
		aborted=1;
	else if(aborted)
		ch=3;
740

741 742 743
	if(!ch && (!(mode&K_GETSTR) || mode&K_LOWPRIO|| node_misc&NM_LOWPRIO))
		mswait(1);
	return(ch);
744 745 746 747 748 749 750
}

/****************************************************************************/
/* 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.	*/
/****************************************************************************/
751
char getkey(long mode)
752
{
753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774
	char	ch,warn=0;
	long	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();

775 776 777 778 779 780 781 782 783 784 785 786
		if(!strchr(user_exempt,'T')) {
			tleft=timeleft-(now-starttime);
			if(tleft<0)
				tleft=0;
			if((tleft/60)<(5-timeleft_warn)) {	/* Running out of time warning */
				timeleft_warn=5-(tleft/60);
				SAVELINE;
				bprintf("\1n\1h\r\n\7\r\nYou only have \1r\1i%u\1n\1h minute%s "
					"left.\r\n\r\n"
					,((ushort)tleft/60)+1,(tleft/60) ? "s" : "");
				RESTORELINE; 
			}
787 788 789 790 791 792 793 794 795 796
		}

		if(now-timeout>=(time_t)sec_warn && !warn)		/* Inactivity warning */
			for(warn=0;warn<5;warn++)
				outchar(7);
	} while(now-timeout<(time_t)sec_timeout);

	bputs("\r\nInactive too long.\r\n");
	exit(0);
	return(0);	/* never gets here, but makes compiler happy */
797 798
}

799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836
/****************************************************************************/
/* If remote user, checks DCD to see if user has hung up or not.			*/
/****************************************************************************/
int isconnected(void)
{
#ifdef __16BIT__
	if(com_port && !((*msr)&DCD)) return(0);
#else
	char	str[256];
	char	ch;
	int		i;
	fd_set	socket_set;
	struct timeval timeout;

	if(client_socket!=INVALID_SOCKET) {
		FD_ZERO(&socket_set);
		FD_SET(client_socket,&socket_set);
		timeout.tv_sec=0;
		timeout.tv_usec=100;

		if((i=select(client_socket+1,&socket_set,NULL,NULL,&timeout))>0) {
			if((i=recv(client_socket,&ch,1,MSG_PEEK))!=1) {
				sprintf(str,"!XSDK Error %d (%d) checking state of socket %d\n"
					,i,ERROR_VALUE,client_socket);
	#ifdef _WIN32
				OutputDebugString(str);
	#else
				fprintf(stderr,"%s",str);
				fflush(stderr);
	#endif
				return(0);
			}
		}
	}
#endif
	return(1);
}

837 838 839 840 841
/****************************************************************************/
/* If remote user, checks DCD to see if user has hung up or not.			*/
/****************************************************************************/
void checkline(void)
{
842 843
	if(!isconnected())
		exit(0);
844 845 846 847 848 849 850 851 852 853
}

/****************************************************************************/
/* 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.	*/
/****************************************************************************/
rswindell's avatar
rswindell committed
854
int getkeys(char *instr,int max)
855
{
rswindell's avatar
rswindell committed
856 857 858
	char	str[256];
	uchar	ch,n=0;
	int		i=0;
859

rswindell's avatar
rswindell committed
860
	sprintf(str,"%.*s",sizeof(str)-1,instr);
861 862 863 864 865 866 867 868 869 870 871 872
	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 */
873 874
			attr(LIGHTGRAY);
			CRLF;
875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896
			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);
897 898 899 900 901 902 903 904 905 906
}

/****************************************************************************/
/* Hot keyed number input routine.											*/
/****************************************************************************/
int getnum(int max)
{
	uchar ch,n=0;
	int i=0;

907 908 909 910 911 912 913 914 915 916
	while(1) {
		ch=getkey(K_UPPER);
		if(ch>0x7f)
			continue;
		if(ch=='Q') {
			outchar('Q');
			CRLF;
			break;
		}
		else if(ch==3) {		/* ctrl-c */
917
			CRLF;
918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940
			break;
		}
		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);
941 942 943 944 945 946 947 948 949 950
}

/****************************************************************************/
/* 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.			*/
/****************************************************************************/
951
int getstr(char *strout, size_t maxlen, long mode)
952
{
953
	size_t i,l,x,z;	/* i=current position, l=length, j=printed chars */
954 955 956
					/* x&z=misc */
	uchar ch,str1[256],str2[256],ins=0,atr;

957 958 959
	if(mode&K_LINE && user_misc&ANSI) {
		attr(LIGHTGRAY|HIGH|(BLUE<<4));  /* white on blue */
		for(i=0;i<maxlen;i++)
960
			outchar(' ');
961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994
		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; 
		}
995
		if(ch!=' ' && ch!=TAB)
996 997 998 999 1000 1001 1002 1003 1004
			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) {
1005
					if(l<maxlen)
1006
						l++;
1007 1008
					for(x=l;x>i;x--)
						str1[x]=str1[x-1];
1009 1010
					rprintf("%.*s",l-i,str1+i);
					rprintf("\x1b[%dD",l-i);
1011
					if(i==maxlen-1)
1012 1013 1014 1015
						ins=0; 
				}
				outchar(str1[i++]=1);
				break;
1016
			case TERM_KEY_HOME:	/* Ctrl-B Beginning of Line */
1017 1018 1019 1020 1021 1022 1023 1024
				if(user_misc&ANSI && i) {
					bprintf("\x1b[%dD",i);
					i=0; 
				}
				break;
			case 4:	/* Ctrl-D Delete word right */
        		if(i<l) {
					x=i;
1025 1026
					while(x<l && str1[x]!=' ') {
						outchar(' ');
1027 1028
						x++; 
					}
1029 1030
					while(x<l && str1[x]==' ') {
						outchar(' ');
1031 1032 1033 1034 1035 1036 1037 1038 1039
						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 */
1040
						outchar(' ');
1041 1042 1043 1044 1045 1046
						z++; 
					}
					bprintf("\x1b[%dD",z-i);
					l-=x-i; 						/* l=new length */
				}
				break;
1047
			case TERM_KEY_END:	/* Ctrl-E End of line */
1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062
				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) {
1063
					if(l<maxlen)
1064
						l++;
1065 1066 1067
					for(x=l;x>i;x--)
						str1[x]=str1[x-1];
					if(i==maxlen-1)
1068 1069 1070 1071 1072 1073 1074 1075 1076 1077
						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;
1078
					while(str1[i]!=' ' && i<l)
1079
						i++;
1080
					while(str1[i]==' ' && i<l)
1081 1082 1083 1084 1085 1086 1087
						i++;
					bprintf("\x1b[%dC",i-x); 
				}
				break;
			case 0x1c:	  /* Ctrl-\ Previous word */
				if(i && (user_misc&ANSI)) {
					x=i;
1088
					while(str1[i-1]==' ' && i)
1089
						i--;
1090
					while(str1[i-1]!=' ' && i)
1091 1092 1093 1094 1095
						i--;
					bprintf("\x1b[%dD",x-i); 
				}
				break;
			case 18:	/* Ctrl-R Redraw Line */
1096 1097
				redrwstr(str1,i,l,0);
				break;
1098 1099 1100 1101 1102 1103 1104 1105 1106 1107
			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; 
					}
1108 1109
					str1[i++]=' ';
					outchar(' '); 
1110 1111 1112 1113 1114 1115 1116 1117 1118 1119
				}
				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; 
					}
1120 1121
					str1[i++]=' ';
					outchar(' '); 
1122 1123 1124 1125 1126 1127 1128 1129 1130 1131
				}
				if(ins)
					redrwstr(str1,i,l,0);
				break;
			case BS:
				if(!i)
					break;
				i--;
				l--;
				if(i!=l) {				/* Deleting char in middle of line */
1132
					outchar(BS);
1133 1134 1135 1136 1137
					z=i;
					while(z<l)	{		/* move the characters in the line */
						outchar(str1[z]=str1[z+1]);
						z++; 
					}
1138
					outchar(' ');		/* write over the last char */
1139 1140 1141 1142 1143 1144 1145 1146 1147
					bprintf("\x1b[%dD",(l-i)+1); 
				}
				else
					bputs("\b \b");
				break;
			case 12:	/* Ctrl-L 	Center line (used to be Ctrl-V) */
				str1[l]=0;
				l=bstrlen(str1);
				for(x=0;x<(maxlen-l)/2;x++)
1148
					str2[x]=' ';
1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166
				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 */
1167
					while(i && str1[i-1]==' ') {
1168 1169 1170
						outchar(BS);
						i--; 
					}
1171
					while(i && str1[i-1]!=' ') {
1172 1173 1174 1175 1176 1177 1178 1179 1180
						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 */
1181
						outchar(' ');
1182 1183 1184 1185 1186 1187
						z++; 
					}
					bprintf("\x1b[%dD",z-i);        /* back to new x corridnant */
					l-=x-i; 						/* l=new length */
				}
				else {
1188
            		while(i && str1[i-1]==' ') {
1189 1190 1191 1192
						i--;
						l--;
						bputs("\b \b"); 
					}
1193
					while(i && str1[i-1]!=' ') {
1194 1195 1196 1197 1198 1199 1200 1201
						i--;
						l--;
						bputs("\b \b"); 
					} 
				}
				break;
			case 24:	/* Ctrl-X   Delete entire line */
				while(i<l) {
1202
					outchar(' ');
1203 1204 1205
					i++; 
				}
				while(l) {
1206
					l--;
1207 1208 1209
					bputs("\b \b"); 
				}
				i=0;
1210
				break;
1211 1212 1213 1214 1215
			case 25:	/* Ctrl-Y	Delete to end of line */
				if(user_misc&ANSI) {
					bputs("\x1b[s\x1b[K\x1b[u");
					l=i; 
				}
1216
				break;
1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232
			case 22:	/* Ctrl-V		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 */
				}
1233
				break;
1234 1235
			case 0x1d:	/* Ctrl-]  Reverse Cursor Movement */
				if(i && (user_misc&ANSI)) {
1236
					bputs("\x1b[D");   /* move cursor left one */
1237 1238 1239 1240
					i--; 
				}
				break;
			case 0x7f:	/* Ctrl-BkSpc (DEL) Delete current char */
1241 1242 1243 1244
				if(i==l) {	/* Backspace if end of line */
					if(i) {
						i--;
						l--;
1245
						bputs("\b \b");
1246
					}
1247
					break;
1248
				}
1249 1250 1251 1252 1253 1254
				l--;
				z=i;
				while(z<l)	{		/* move the characters in the line */
					outchar(str1[z]=str1[z+1]);
					z++; 
				}
1255
				outchar(' ');		/* write over the last char */
1256 1257 1258 1259 1260 1261
				bprintf("\x1b[%dD",(l-i)+1);
				break;
			case ESC:
				if(!(user_misc&ANSI))
					break;
				if((ch=getkey(0x8000))!='[') {
1262
					ungetkey(ch);
1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284
					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); 
					}
1285
					ungetkey(ch); 
1286 1287 1288
				}
				break;
			default:
1289
				if(mode&K_WRAP && i==maxlen && ch>=' ' && !ins) {
1290
					str1[i]=0;
1291
					if(ch==' ') {	/* don't wrap a space as last char */
1292 1293 1294 1295 1296 1297 1298 1299 1300
						strcpy(strout,str1);
						if(stripattr(strout))
							redrwstr(strout,i,l,K_MSG);
						CRLF;
						return(i); 
					}
					x=i-1;
					z=1;
					wordwrap[0]=ch;
1301
					while(str1[x]!=' ' && x)
1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317
						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;
1318 1319
					strcpy(strout,str1);
					if(stripattr(strout))
1320
						redrwstr(strout,i,x,mode);
1321
					CRLF;
1322 1323
					return(x); 
				}
1324
				if(i<maxlen && ch>=' ') {
1325
					if(mode&K_UPRLWR)
1326
						if(!i || (i && (str1[i-1]==' ' || str1[i-1]=='-'
1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352
							|| 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); 
				} 
			} /* switch */
		if(i>l)