Skip to content
Snippets Groups Projects
term.c 81.6 KiB
Newer Older
deuce's avatar
deuce committed
/* Copyright (C), 2007 by Stephen Hurd */

/* $Id: term.c,v 1.387 2020/06/27 00:04:50 deuce Exp $ */
deuce's avatar
deuce committed
#include <ciolib.h>
deuce's avatar
deuce committed
#include <cterm.h>
#include <vidmodes.h>
#include "threadwrap.h"
#include "filewrap.h"
deuce's avatar
deuce committed
#include "xpbeep.h"
#include "xpendian.h"
#include "conn.h"
deuce's avatar
deuce committed
#include "window.h"
#include "filepick.h"
#include "dirwrap.h"
#include "sexyz.h"
#include "zmodem.h"
#include "xmodem.h"
#include "saucedefs.h"
deuce's avatar
deuce committed
#ifndef WITHOUT_OOII
#include "ooii.h"
#endif
#include "base64.h"
#include "md5.h"
deuce's avatar
deuce committed

#define	ANSI_REPLY_BUFSIZE	2048
static char ansi_replybuf[2048];
deuce's avatar
deuce committed

#ifndef MIN
#define MIN(a,b)	((a) < (b) ? (a) : (b))
#endif

#define TRANSFER_WIN_WIDTH	66
#define TRANSFER_WIN_HEIGHT	18
static struct vmem_cell winbuf[(TRANSFER_WIN_WIDTH + 2) * (TRANSFER_WIN_HEIGHT + 1) * 2];	/* Save buffer for transfer window */
static struct text_info	trans_ti;
static struct text_info	log_ti;

void get_cterm_size(int* cols, int* rows, int ns)
{
	*cols = 80;
	*rows = 24;
	if(cterm != NULL) {
		*cols = cterm->width;
		*rows = cterm->height;
	}
	else {
		get_term_win_size(cols, rows, &ns);
	}
	MM_X10 = 9,
	MM_NORMAL_TRACKING = 1000,
	MM_HIGHLIGHT_TRACKING = 1001,
	MM_BUTTON_EVENT_TRACKING = 1002,
	MM_ANY_EVENT_TRACKING = 1003
};

struct mouse_state {
	uint32_t flags;
#define MS_FLAGS_SGR	(1<<0)
#define MS_SGR_SET	(1006)
	enum mouse_modes mode;
};

void setup_mouse_events(struct mouse_state *ms)
{
	ciomouse_setevents(0);
			case MM_RIP:
				ciomouse_addevent(CIOLIB_BUTTON_1_PRESS);
				ciomouse_addevent(CIOLIB_BUTTON_1_RELEASE);
				ciomouse_addevent(CIOLIB_BUTTON_2_PRESS);
				ciomouse_addevent(CIOLIB_BUTTON_2_RELEASE);
				ciomouse_addevent(CIOLIB_BUTTON_3_PRESS);
				ciomouse_addevent(CIOLIB_BUTTON_3_RELEASE);
				ciomouse_addevent(CIOLIB_BUTTON_4_PRESS);
				mousepointer(CIOLIB_MOUSEPTR_ARROW);
				return;
			case MM_X10:
				ciomouse_addevent(CIOLIB_BUTTON_1_PRESS);
				ciomouse_addevent(CIOLIB_BUTTON_1_CLICK);
				ciomouse_addevent(CIOLIB_BUTTON_1_DRAG_START);
				ciomouse_addevent(CIOLIB_BUTTON_1_DRAG_MOVE);
				ciomouse_addevent(CIOLIB_BUTTON_1_DRAG_END);
				ciomouse_addevent(CIOLIB_BUTTON_2_CLICK);
				ciomouse_addevent(CIOLIB_BUTTON_3_CLICK);
				ciomouse_addevent(CIOLIB_BUTTON_4_PRESS); // For scrollback...
				mousepointer(CIOLIB_MOUSEPTR_ARROW);
				return;
			case MM_NORMAL_TRACKING:
				ciomouse_addevent(CIOLIB_BUTTON_1_PRESS);
				ciomouse_addevent(CIOLIB_BUTTON_1_RELEASE);
				ciomouse_addevent(CIOLIB_BUTTON_2_PRESS);
				ciomouse_addevent(CIOLIB_BUTTON_2_RELEASE);
				ciomouse_addevent(CIOLIB_BUTTON_3_PRESS);
				ciomouse_addevent(CIOLIB_BUTTON_3_RELEASE);
				ciomouse_addevent(CIOLIB_BUTTON_4_PRESS);
				ciomouse_addevent(CIOLIB_BUTTON_5_PRESS);
				mousepointer(CIOLIB_MOUSEPTR_ARROW);
				return;
			case MM_BUTTON_EVENT_TRACKING:
				ciomouse_addevent(CIOLIB_BUTTON_1_PRESS);
				ciomouse_addevent(CIOLIB_BUTTON_1_RELEASE);
				ciomouse_addevent(CIOLIB_BUTTON_2_PRESS);
				ciomouse_addevent(CIOLIB_BUTTON_2_RELEASE);
				ciomouse_addevent(CIOLIB_BUTTON_3_PRESS);
				ciomouse_addevent(CIOLIB_BUTTON_3_RELEASE);
				ciomouse_addevent(CIOLIB_BUTTON_4_PRESS);
				ciomouse_addevent(CIOLIB_BUTTON_5_PRESS);
				ciomouse_addevent(CIOLIB_MOUSE_MOVE);
				mousepointer(CIOLIB_MOUSEPTR_ARROW);
				return;
			case MM_ANY_EVENT_TRACKING:
				ciomouse_addevent(CIOLIB_BUTTON_1_PRESS);
				ciomouse_addevent(CIOLIB_BUTTON_1_RELEASE);
				ciomouse_addevent(CIOLIB_BUTTON_2_PRESS);
				ciomouse_addevent(CIOLIB_BUTTON_2_RELEASE);
				ciomouse_addevent(CIOLIB_BUTTON_3_PRESS);
				ciomouse_addevent(CIOLIB_BUTTON_3_RELEASE);
				ciomouse_addevent(CIOLIB_BUTTON_4_PRESS);
				ciomouse_addevent(CIOLIB_BUTTON_5_PRESS);
				ciomouse_addevent(CIOLIB_MOUSE_MOVE);
				mousepointer(CIOLIB_MOUSEPTR_ARROW);
	ciomouse_addevent(CIOLIB_BUTTON_1_DRAG_START);
	ciomouse_addevent(CIOLIB_BUTTON_1_DRAG_MOVE);
	ciomouse_addevent(CIOLIB_BUTTON_1_DRAG_END);
	ciomouse_addevent(CIOLIB_BUTTON_2_CLICK);
	ciomouse_addevent(CIOLIB_BUTTON_3_CLICK);
	ciomouse_addevent(CIOLIB_BUTTON_4_PRESS);
	mousepointer(CIOLIB_MOUSEPTR_BAR);
#if defined(__BORLANDC__)
	#pragma argsused
#endif
void mousedrag(struct vmem_cell *scrollback)
deuce's avatar
deuce committed
	int sbufsize;
	int pos, startpos,endpos, lines;
	int outpos;
	char *copybuf=NULL;
	char *newcopybuf;
	struct ciolib_screen *savscrn;
	sbufsize=term.width*sizeof(*screen)*term.height;
	screen=malloc(sbufsize);
	sbuffer=malloc(sbufsize);
	tscreen=malloc(term.width*2*term.height);
	vmem_gettext(term.x-1,term.y-1,term.x+term.width-2,term.y+term.height-2,screen);
	gettext(term.x-1,term.y-1,term.x+term.width-2,term.y+term.height-2,tscreen);
	savscrn = savescreen();
	set_modepalette(palettes[COLOUR_PALETTE]);
			key|=getch()<<8;
		switch(key) {
			case CIO_KEY_MOUSE:
				getmouse(&mevent);
				startpos=((mevent.starty-1)*term.width)+(mevent.startx-1);
				endpos=((mevent.endy-1)*term.width)+(mevent.endx-1);
deuce's avatar
deuce committed
				if(startpos>=term.width*term.height)
					startpos=term.width*term.height-1;
				if(endpos>=term.width*term.height)
					endpos=term.width*term.height-1;
				if(endpos<startpos) {
					pos=endpos;
					endpos=startpos;
					startpos=pos;
				}
				switch(mevent.event) {
					case CIOLIB_BUTTON_1_DRAG_MOVE:
						memcpy(sbuffer,screen,sbufsize);
						for(pos=startpos;pos<=endpos;pos++) {
							if((sbuffer[pos].legacy_attr&0x70)!=0x10)
								sbuffer[pos].legacy_attr=(sbuffer[pos].legacy_attr&0x8F)|0x10;
								sbuffer[pos].legacy_attr=(sbuffer[pos].legacy_attr&0x8F)|0x60;
							if(((sbuffer[pos].legacy_attr&0x70)>>4) == (sbuffer[pos].legacy_attr&0x0F)) {
								sbuffer[pos].legacy_attr|=0x08;
							attr2palette(sbuffer[pos].legacy_attr, &sbuffer[pos].fg, &sbuffer[pos].bg);
						vmem_puttext(term.x-1,term.y-1,term.x+term.width-2,term.y+term.height-2,sbuffer);
						break;
					default:
						lines=abs(mevent.endy-mevent.starty)+1;
deuce's avatar
deuce committed
						newcopybuf=realloc(copybuf, (endpos-startpos+4+lines*2)*4);
						if (newcopybuf)
							copybuf = newcopybuf;
						else
							goto cleanup;
						for(pos=startpos;pos<=endpos;pos++) {
deuce's avatar
deuce committed
							size_t outlen;
							uint8_t *utf8str;

							utf8str = cp_to_utf8(conio_fontdata[screen[pos].font].cp, (char *)&screen[pos].ch, 1, &outlen);
							if (utf8str == NULL)
								continue;
							memcpy(copybuf + outpos, utf8str, outlen);
							outpos += outlen;
							if(screen[pos].ch != ' ' && screen[pos].ch)
								#ifdef _WIN32
									copybuf[outpos++]='\r';
								#endif
								lastchar=outpos;
							}
						}
						copybuf[outpos]=0;
						copytext(copybuf, strlen(copybuf));
						vmem_puttext(term.x-1,term.y-1,term.x+term.width-2,term.y+term.height-2,screen);
				vmem_puttext(term.x-1,term.y-1,term.x+term.width-2,term.y+term.height-2,screen);

cleanup:
	free(screen);
	free(sbuffer);
	free(tscreen);
	if(copybuf)
		free(copybuf);
	restorescreen(savscrn);
	freescreen(savscrn);
deuce's avatar
deuce committed
void update_status(struct bbslist *bbs, int speed, int ooii_mode)
deuce's avatar
deuce committed
{
	char nbuf[LIST_NAME_MAX+10+11+1];	/* Room for "Name (Logging) (115300)" and terminator */
						/* SAFE and Logging should me be possible. */
deuce's avatar
deuce committed
	int oldscroll;
deuce's avatar
deuce committed
	int olddmc=hold_update;
deuce's avatar
deuce committed
	struct	text_info txtinfo;
	int now;
	static int lastupd=0;
deuce's avatar
deuce committed
	int	timeon;
	int oldfont_norm;
	int oldfont_bright;

	oldfont_norm=getfont(1);
	oldfont_bright=getfont(2);
	setfont(0, FALSE, 1);
	setfont(0, FALSE, 2);
	switch(getfont(1)) {
			case 0:
			case 17:
			case 18:
			case 19:
			case 25:
			case 26:
			case 27:
			case 28:
			case 29:
			case 31:
				sep = 0xb3;
				break;
			default:
				sep = '|';
	}
deuce's avatar
deuce committed
	now=time(NULL);
	if(now==lastupd && speed==oldspeed) {
		setfont(oldfont_norm,0,1);
		setfont(oldfont_bright,0,2);
deuce's avatar
deuce committed
		return;
deuce's avatar
deuce committed
	lastupd=now;
deuce's avatar
deuce committed
	timeon=now - bbs->connected;
	gettextinfo(&txtinfo);
deuce's avatar
deuce committed
	oldscroll=_wscroll;
deuce's avatar
deuce committed
	hold_update=TRUE;
deuce's avatar
deuce committed
	textattr(YELLOW|(BLUE<<4));
	/* Move to status line thinger */
	window(term.x-1,term.y+term.height-1,term.x+term.width-2,term.y+term.height-1);
	gotoxy(1,1);
	_wscroll=0;
	strcpy(nbuf, bbs->name);
	if(safe_mode)
		strcat(nbuf, " (SAFE)");
		strcat(nbuf, " (Logging)");
		sprintf(strchr(nbuf,0)," (%d)", speed);
deuce's avatar
deuce committed
		strcat(nbuf, " (DrWy)");
deuce's avatar
deuce committed
	switch(ooii_mode) {
	case 1:
		strcat(nbuf, " (OOTerm)");
deuce's avatar
deuce committed
		break;
	case 2:
		strcat(nbuf, " (OOTerm1)");
		break;
	case 3:
		strcat(nbuf, " (OOTerm2)");
deuce's avatar
deuce committed
		break;
	}
	ciolib_setcolour(11, 4);
	switch(cio_api.mode) {
		case CIOLIB_MODE_CURSES:
		case CIOLIB_MODE_CURSES_IBM:
		case CIOLIB_MODE_ANSI:
			if(timeon>359999)
				cprintf(" %-29.29s %c %-6.6s %c Connected: Too Long %c CTRL-S for menu ",nbuf,sep,conn_types[bbs->conn_type],sep,sep);
				cprintf(" %-29.29s %c %-6.6s %c Connected: %02d:%02d:%02d %c CTRL-S for menu ",nbuf,sep,conn_types[bbs->conn_type],sep,timeon/3600,(timeon/60)%60,timeon%60,sep);
				cprintf(" %-30.30s %c %-6.6s %c Connected: Too Long %c "ALT_KEY_NAME3CH"-Z for menu ",nbuf,sep,conn_types[bbs->conn_type],sep,sep);
				cprintf(" %-30.30s %c %-6.6s %c Connected: %02d:%02d:%02d %c "ALT_KEY_NAME3CH"-Z for menu ",nbuf,sep,conn_types[bbs->conn_type],sep,timeon/3600,(timeon/60)%60,timeon%60,sep);
			break; /*    1+29     +3    +6    +3    +11        +3+3+2        +3    +6    +4  +5 */
deuce's avatar
deuce committed
	_wscroll=oldscroll;
	setfont(oldfont_norm,0,1);
	setfont(oldfont_bright,0,2);
deuce's avatar
deuce committed
	textattr(txtinfo.attribute);
	window(txtinfo.winleft,txtinfo.wintop,txtinfo.winright,txtinfo.winbottom);
	gotoxy(txtinfo.curx,txtinfo.cury);
deuce's avatar
deuce committed
	hold_update=olddmc;
#if defined(_WIN32) && defined(_DEBUG) && defined(DUMP)
void dump(BYTE* buf, int len)
{
	char str[128];
	int i,j;
	size_t slen=0;

	slen=sprintf(str,"RX: ");
	for(i=0;i<len;i+=j) {
		for(j=0;i+j<len && j<32;j++)
			slen+=sprintf(str+slen,"%02X ",buf[i+j]);
		OutputDebugString(str);
		slen=sprintf(str,"RX: ");
	}
}
#endif

int log_level = LOG_INFO;
struct zmodem_cbdata {
	zmodem_t		*zm;
	struct bbslist	*bbs;
};

enum { ZMODEM_MODE_SEND, ZMODEM_MODE_RECV } zmodem_mode;

static BOOL zmodem_check_abort(void* vp)
	struct zmodem_cbdata	*zcb=(struct zmodem_cbdata *)vp;
	zmodem_t*				zm=zcb->zm;
	static time_t			last_check=0;
	time_t					now=time(NULL);
deuce's avatar
deuce committed
	if (zm == NULL)
		return TRUE;
	if (quitting) {
		zm->cancelled=TRUE;
		zm->local_abort=TRUE;
		return TRUE;
	}
	if(last_check != now) {
		last_check=now;
deuce's avatar
deuce committed
		while(kbhit()) {
			switch((key=getch())) {
				case ESC:
				case CTRL_C:
				case CTRL_X:
					zm->cancelled=TRUE;
					zm->local_abort=TRUE;
					break;
				case 0:
				case 0xe0:
					key |= (getch() << 8);
					if(key==CIO_KEY_MOUSE)
						getmouse(NULL);
					if (key==CIO_KEY_QUIT) {
						if (check_exit(FALSE)) {
							zm->cancelled=TRUE;
							zm->local_abort=TRUE;
deuce's avatar
deuce committed
					}
					break;
extern FILE* log_fp;
extern char *log_levels[];

#if defined(__BORLANDC__)
	#pragma argsused
#endif
static int lputs(void* cbdata, int level, const char* str)
{
	char msg[512];
deuce's avatar
deuce committed
	int chars;
	int oldhold=hold_update;
#if defined(_WIN32) && defined(_DEBUG) && FALSE
	sprintf(msg,"SyncTerm: %s\n",str);
	OutputDebugString(msg);
	if(log_fp!=NULL && level <= log_level) {
		time_t t = time(NULL);
		fprintf(log_fp,"%.15s %s\n", ctime(&t) + 4, str);
	}
	/* Assumes the receive window has been drawn! */
	window(log_ti.winleft, log_ti.wintop, log_ti.winright, log_ti.winbottom);
	gotoxy(log_ti.curx, log_ti.cury);
	textbackground(BLUE);
deuce's avatar
deuce committed
#if 0	// Not possible because of above level > LOG_INFO check
			textcolor(LIGHTCYAN);
			SAFEPRINTF(msg,"%s\r\n",str);
			break;
deuce's avatar
deuce committed
#endif
deuce's avatar
deuce committed
		case LOG_INFO:
			SAFEPRINTF(msg,"%s\r\n",str);
			break;
		case LOG_NOTICE:
			textcolor(YELLOW);
			SAFEPRINTF(msg,"%s\r\n",str);
			break;
			SAFEPRINTF(msg,"Warning: %s\r\n",str);
			break;
		default:
			SAFEPRINTF(msg,"!ERROR: %s\r\n",str);
			break;
	}
	hold_update=FALSE;
deuce's avatar
deuce committed
	chars=cputs(msg);
	hold_update=oldhold;
	gettextinfo(&log_ti);
deuce's avatar
deuce committed
	return chars;
}

static int lprintf(int level, const char *fmt, ...)
{
	char sbuf[1024];
	va_list argptr;

    va_start(argptr,fmt);
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
    va_end(argptr);
    return(lputs(NULL,level,sbuf));
}

#if defined(__BORLANDC__)
	#pragma argsused
#endif
void zmodem_progress(void* cbdata, int64_t current_pos)
deuce's avatar
deuce committed
	int			old_hold=hold_update;
	struct zmodem_cbdata *zcb=(struct zmodem_cbdata *)cbdata;
	zmodem_t*	zm=zcb->zm;
	if(current_pos > zm->current_file_size)
		growing=TRUE;
	if(now != last_progress || (current_pos >= zm->current_file_size && growing==FALSE)) {
		hold_update = TRUE;
		window(((trans_ti.screenwidth-TRANSFER_WIN_WIDTH)/2)+2
				, ((trans_ti.screenheight-TRANSFER_WIN_HEIGHT)/2)+1
				, ((trans_ti.screenwidth-TRANSFER_WIN_WIDTH)/2) + TRANSFER_WIN_WIDTH - 2
				, ((trans_ti.screenheight-TRANSFER_WIN_HEIGHT)/2)+5);
		gotoxy(1,1);
		textattr(LIGHTCYAN | (BLUE<<4));
		if(zm->transfer_start_pos>current_pos)
			zm->transfer_start_pos=0;
		if((cps=(unsigned)((current_pos-zm->transfer_start_pos)/t))==0)
Deucе's avatar
Deucе committed
		l = (zm->current_file_size - current_pos) / cps;	/* remaining transfer est time */
			,zm->current_file_num, zm->total_files, TRANSFER_WIN_WIDTH - 20, zm->current_file_name);
		clreol();
		cputs("\r\n");
			sprintf(orig,"From: %"PRId64"  ", zm->transfer_start_pos);
		cprintf("%sByte: %"PRId64" of %"PRId64" (%"PRId64" KB)"
			,orig, current_pos, zm->current_file_size, zm->current_file_size/1024);
		clreol();
		cputs("\r\n");
		cprintf("Time: %lu:%02lu  ETA: %lu:%02lu  Block: %u/CRC-%u  %u cps"
			,(unsigned long)(t/60L)
			,(unsigned long)(t%60L)
			,(unsigned long)(l/60L)
			,(unsigned long)(l%60L)
			,zmodem_mode==ZMODEM_MODE_RECV ? (zm->receive_32bit_data ? 32:16) :
				(zm->can_fcs_32 && !zm->want_fcs_16) ? 32:16
		cputs("\r\n");
		if(zm->current_file_size==0) {
			cprintf("%*s%3d%%\r\n", TRANSFER_WIN_WIDTH/2-5, "", 100);
			l = 60;
		}
		else{
			cprintf("%*s%3d%%\r\n", TRANSFER_WIN_WIDTH/2-5, ""
				,(long)(((float)current_pos/(float)zm->current_file_size)*100.0));
			l = (long)(60*((float)current_pos/(float)zm->current_file_size));
		}
				"\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
				"\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
				"\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
				"\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
				"\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
				"\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
				, (int)(60-l), "");
		hold_update = FALSE;
		gotoxy(wherex(), wherey());
		hold_update = old_hold;
#if defined(__BORLANDC__)
	#pragma argsused
#endif

unsigned char transfer_buffer[BUFFER_SIZE/2];
unsigned transfer_buf_len=0;

static void flush_send(void *unused)
{
	int	sent;

	sent=conn_send(transfer_buffer, transfer_buf_len, 120*1000);
	if(sent < transfer_buf_len) {
		memmove(transfer_buffer, transfer_buffer+sent, transfer_buf_len-sent);
		transfer_buf_len -= sent;
	}
	else
		transfer_buf_len=0;
}

static int send_byte(void* unused, uchar ch, unsigned timeout /* seconds */)
	transfer_buffer[transfer_buf_len++]=ch;
	if(transfer_buf_len==sizeof(transfer_buffer))
		flush_send(unused);
	return(!(transfer_buf_len < sizeof(transfer_buffer)));
#if defined(__BORLANDC__)
	#pragma argsused
#endif
BYTE	recv_byte_buffer[BUFFER_SIZE];
unsigned recv_byte_buffer_len=0;
unsigned recv_byte_buffer_pos=0;

static void recv_bytes(unsigned timeout /* Milliseconds */)
{
	if(recv_byte_buffer_len == 0) {
		recv_byte_buffer_len = parse_rip(recv_byte_buffer, 0, sizeof(recv_byte_buffer));
		if (recv_byte_buffer_len == 0) {
			recv_byte_buffer_len = conn_recv_upto(recv_byte_buffer, sizeof(recv_byte_buffer)-3, timeout);
			if (recv_byte_buffer_len)
				recv_byte_buffer_len = parse_rip(recv_byte_buffer, recv_byte_buffer_len, sizeof(recv_byte_buffer));
		}
	}
static int recv_byte(void* unused, unsigned timeout /* seconds */)

	if(recv_byte_buffer_len > 0) {
		ch=recv_byte_buffer[recv_byte_buffer_pos++];
		if(recv_byte_buffer_pos == recv_byte_buffer_len)
			recv_byte_buffer_len=recv_byte_buffer_pos=0;
		return ch;
	}
#if defined(__BORLANDC__)
	#pragma argsused
#endif
BOOL data_waiting(void* unused, unsigned timeout /* seconds */)
deuce's avatar
deuce committed
	BOOL	ret;

deuce's avatar
deuce committed
	pthread_mutex_lock(&(conn_inbuf.mutex));
	ret = conn_buf_wait_bytes(&conn_inbuf, 1, timeout*1000)!=0;
	pthread_mutex_unlock(&(conn_inbuf.mutex));
	return ret;
size_t count_data_waiting(void)
{
	recv_bytes(0);
	return recv_byte_buffer_len;
}

void draw_transfer_window(char* title)
	char	outline[TRANSFER_WIN_WIDTH*2];
	char	shadow[TRANSFER_WIN_WIDTH*2];	/* Assumes that width*2 > height * 2 */
	int		i, top, left, old_hold;
	old_hold = hold_update;
	hold_update=TRUE;
	gettextinfo(&trans_ti);
	top=(trans_ti.screenheight-TRANSFER_WIN_HEIGHT)/2;
	left=(trans_ti.screenwidth-TRANSFER_WIN_WIDTH)/2;
	window(1, 1, trans_ti.screenwidth, trans_ti.screenheight);

	vmem_gettext(left, top, left + TRANSFER_WIN_WIDTH + 1, top + TRANSFER_WIN_HEIGHT, winbuf);
	memset(outline, YELLOW | (BLUE<<4), sizeof(outline));
	for(i=2;i < sizeof(outline) - 2; i+=2) {
		outline[i] = (char)0xcd;	/* Double horizontal line */
	outline[0]=(char)0xc9;
	outline[sizeof(outline)-2]=(char)0xbb;
	puttext(left, top, left + TRANSFER_WIN_WIDTH - 1, top, outline);

	/* Title */
	gotoxy(left+4,top);
	textattr(YELLOW|(BLUE<<4));
	cprintf("\xb5 %*s \xc6",strlen(title),"");
	gotoxy(left+6,top);
	textattr(WHITE|(BLUE<<4));
	cprintf("%s",title);

	for(i=2;i < sizeof(outline) - 2; i+=2) {
		outline[i] = (char)0xc4;	/* Single horizontal line */
	outline[0] = (char)0xc7;	/* 0xcc */
	outline[sizeof(outline)-2]=(char)0xb6;	/* 0xb6 */
	puttext(left, top+6, left + TRANSFER_WIN_WIDTH - 1, top+6, outline);

	for(i=2;i < sizeof(outline) - 2; i+=2) {
		outline[i] = (char)0xcd;	/* Double horizontal line */
	outline[0]=(char)0xc8;
	outline[sizeof(outline)-2]=(char)0xbc;
	puttext(left, top + TRANSFER_WIN_HEIGHT - 1, left+TRANSFER_WIN_WIDTH - 1, top + TRANSFER_WIN_HEIGHT - 1, outline);
	outline[0]=(char)0xba;
	outline[sizeof(outline)-2]=(char)0xba;
	for(i=2;i < sizeof(outline) - 2; i+=2) {
		outline[i] = ' ';
	}
	for(i=1; i<6; i++) {
		puttext(left, top + i, left + TRANSFER_WIN_WIDTH - 1, top+i, outline);
	}
/*	for(i=3;i < sizeof(outline) - 2; i+=2) { */
/*		outline[i] = LIGHTGRAY | (BLACK << 8); */
/*	} */
	for(i=7; i<TRANSFER_WIN_HEIGHT-1; i++) {
		puttext(left, top + i, left + TRANSFER_WIN_WIDTH - 1, top+i, outline);
	}
	/* Title */
	gotoxy(left + TRANSFER_WIN_WIDTH - 20, top + i);
	textattr(YELLOW|(BLUE<<4));
	cprintf("\xb5              \xc6");
	textattr(WHITE|(BLUE<<4));
	gotoxy(left + TRANSFER_WIN_WIDTH - 18, top + i);
	cprintf("ESC to Abort");

	/* Shadow */
	if(uifc.bclr==BLUE) {
		gettext(left + TRANSFER_WIN_WIDTH
				, top+1
				, left + TRANSFER_WIN_WIDTH + 1
				, top + (TRANSFER_WIN_HEIGHT - 1)
				, shadow);
		for(i=1;i<sizeof(shadow);i+=2)
			shadow[i]=DARKGRAY;
		puttext(left + TRANSFER_WIN_WIDTH
				, top+1
				, left + TRANSFER_WIN_WIDTH + 1
				, top + (TRANSFER_WIN_HEIGHT - 1)
				, shadow);
		gettext(left + 2
				, top + TRANSFER_WIN_HEIGHT
				, left + TRANSFER_WIN_WIDTH + 1
				, top + TRANSFER_WIN_HEIGHT
				, shadow);
		for(i=1;i<sizeof(shadow);i+=2)
			shadow[i]=DARKGRAY;
		puttext(left + 2
				, top + TRANSFER_WIN_HEIGHT
				, left + TRANSFER_WIN_WIDTH + 1
				, top + TRANSFER_WIN_HEIGHT
				, shadow);
	}

	window(left+2, top + 7, left + TRANSFER_WIN_WIDTH - 3, top + TRANSFER_WIN_HEIGHT - 2);
	hold_update = FALSE;
	gotoxy(1,1);
	hold_update = old_hold;
	gettextinfo(&log_ti);
	_setcursortype(_NOCURSOR);
void erase_transfer_window(void) {
		  ((trans_ti.screenwidth-TRANSFER_WIN_WIDTH)/2)
		, ((trans_ti.screenheight-TRANSFER_WIN_HEIGHT)/2)
		, ((trans_ti.screenwidth-TRANSFER_WIN_WIDTH)/2) + TRANSFER_WIN_WIDTH + 1
		, ((trans_ti.screenheight-TRANSFER_WIN_HEIGHT)/2) + TRANSFER_WIN_HEIGHT
	window(trans_ti.winleft, trans_ti.wintop, trans_ti.winright, trans_ti.winbottom);
	gotoxy(trans_ti.curx, trans_ti.cury);
	textattr(trans_ti.attribute);
	_setcursortype(_NORMALCURSOR);
void ascii_upload(FILE *fp);
deuce's avatar
deuce committed
void raw_upload(FILE *fp);
void begin_upload(struct bbslist *bbs, BOOL autozm, int lastch)
	char	str[MAX_PATH*2+1];
	char	path[MAX_PATH+1];
	int		result;
	FILE*	fp;
	struct file_pick fpick;
	char	*opts[7]={
deuce's avatar
deuce committed
			,"YMODEM"
			,"XMODEM-1K"
			,"XMODEM-128"
deuce's avatar
deuce committed
			,"Raw"
	struct	text_info txtinfo;
	struct ciolib_screen *savscrn;
	if(safe_mode)
		return;

deuce's avatar
deuce committed
    gettextinfo(&txtinfo);
    savscrn = savescreen();
	setfont(0, FALSE, 1);
	setfont(0, FALSE, 2);
	setfont(0, FALSE, 3);
	setfont(0, FALSE, 4);
	if(!isdir(bbs->uldir)) {
		SAFEPRINTF(str, "Invalid upload directory: %s", bbs->uldir);
		uifcmsg(str, "An invalid `UploadPath` was specified in the `syncterm.lst` file");
		uifcbail();
		restorescreen(savscrn);
		freescreen(savscrn);
		gotoxy(txtinfo.curx, txtinfo.cury);
		return;
	}
	result=filepick(&uifc, "Upload", &fpick, bbs->uldir, NULL, UIFC_FP_ALLOWENTRY);
	if(result==-1 || fpick.files<1) {
		filepick_free(&fpick);
		restorescreen(savscrn);
		freescreen(savscrn);
		gotoxy(txtinfo.curx, txtinfo.cury);
		return;
	}
	SAFECOPY(path,fpick.selected[0]);
	filepick_free(&fpick);
	restorescreen(savscrn);

	if((fp=fopen(path,"rb"))==NULL) {
		SAFEPRINTF2(str,"Error %d opening %s for read",errno,path);
		uifcmsg("Error opening file",str);
		restorescreen(savscrn);
		freescreen(savscrn);
		gotoxy(txtinfo.curx, txtinfo.cury);
		return;
	}
	setvbuf(fp,NULL,_IOFBF,0x10000);

	suspend_rip(true);
deuce's avatar
deuce committed
		uifc.helpbuf="Select Protocol";
		switch(uifc.list(WIN_MID|WIN_SAV,0,0,0,&i,NULL,"Protocol",opts)) {
				xmodem_upload(bbs, fp, path, YMODEM|SEND, lastch);
deuce's avatar
deuce committed
				break;
			case 2:
				xmodem_upload(bbs, fp, path, XMODEM|SEND, lastch);
deuce's avatar
deuce committed
				break;
			case 3:
				xmodem_upload(bbs, fp, path, XMODEM|SEND|XMODEM_128B, lastch);
deuce's avatar
deuce committed
			case 4:
				ascii_upload(fp);
				break;
			case 5:
deuce's avatar
deuce committed
				raw_upload(fp);
				break;
			default:
				fclose(fp);
				break;
	suspend_rip(false);
	restorescreen(savscrn);
	freescreen(savscrn);
	gotoxy(txtinfo.curx, txtinfo.cury);
deuce's avatar
deuce committed
void begin_download(struct bbslist *bbs)
{
	char	path[MAX_PATH+1];
	int i;
	char	*opts[6]={
deuce's avatar
deuce committed
			 "ZMODEM"
			,"XMODEM-CRC"
			,"XMODEM-CHKSUM"
deuce's avatar
deuce committed
			,""
		};
	struct	text_info txtinfo;
deuce's avatar
deuce committed
	int old_hold=hold_update;
	struct ciolib_screen *savscrn;
deuce's avatar
deuce committed

	if(safe_mode)
		return;

    gettextinfo(&txtinfo);
    savscrn = savescreen();
    setfont(0, FALSE, 1);
    setfont(0, FALSE, 2);
    setfont(0, FALSE, 3);
    setfont(0, FALSE, 4);
deuce's avatar
deuce committed

	init_uifc(FALSE, FALSE);

	i=0;
deuce's avatar
deuce committed
	uifc.helpbuf="Select Protocol";
deuce's avatar
deuce committed
	hold_update=FALSE;
	suspend_rip(true);
deuce's avatar
deuce committed
	switch(uifc.list(WIN_MID|WIN_SAV,0,0,0,&i,NULL,"Protocol",opts)) {
deuce's avatar
deuce committed
		case 0:
			zmodem_download(bbs);
			break;
		case 1:
			xmodem_download(bbs, YMODEM|CRC|GMODE|RECV, NULL);
deuce's avatar
deuce committed
			break;
		case 2:
deuce's avatar
deuce committed
			xmodem_download(bbs, YMODEM|CRC|RECV, NULL);
deuce's avatar
deuce committed
			break;
		case 3:
			if(uifc.input(WIN_MID|WIN_SAV,0,0,"Filename",path,sizeof(path),0)!=-1)
				xmodem_download(bbs, XMODEM|CRC|RECV,path);
			break;
		case 4:
			if(uifc.input(WIN_MID|WIN_SAV,0,0,"Filename",path,sizeof(path),0)!=-1)
				xmodem_download(bbs, XMODEM|RECV,path);
			break;
deuce's avatar
deuce committed
	}
	suspend_rip(false);
deuce's avatar
deuce committed
	hold_update=old_hold;
deuce's avatar
deuce committed
	uifcbail();
	restorescreen(savscrn);
	freescreen(savscrn);
deuce's avatar
deuce committed
	gotoxy(txtinfo.curx, txtinfo.cury);
}

#if defined(__BORLANDC__)
	#pragma argsused
#endif
static BOOL is_connected(void* unused)
{
	if(recv_byte_buffer_len)
		return TRUE;
deuce's avatar
deuce committed
void raw_upload(FILE *fp)
{
	char	buf[1024];
	int		r;
	int		inch;
	char	ch[2];

	ch[1]=0;
	for(;;) {
		r=fread(buf, 1, sizeof(buf), fp);
		if(r)
			conn_send(buf, r,0);
		/* Note, during RAW uploads, do NOT send ANSI responses and don't
		 * allow speed changes. */
		while((inch=recv_byte(NULL, 0))>=0) {
			ch[0]=inch;
			cterm_write(cterm, ch, 1, NULL, 0, NULL);
deuce's avatar
deuce committed
		}
		if(r==0)
			break;