Skip to content
Snippets Groups Projects
term.c 116 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 $ */
#include <assert.h>
deuce's avatar
deuce committed
#include <ciolib.h>
deuce's avatar
deuce committed
#include <cterm.h>
Deucе's avatar
Deucе committed
#include <genwrap.h>
Deucе's avatar
Deucе committed
#include <math.h>
Deucе's avatar
Deucе committed
#include <stdbool.h>
#include <string.h>
#include <vidmodes.h>
#include "conn.h"
Deucе's avatar
Deucе committed
#include "dirwrap.h"
#include "filepick.h"
Deucе's avatar
Deucе committed
#include "filewrap.h"
#include "gen_defs.h"
Deucе's avatar
Deucе committed
#include "saucedefs.h"
#include "sexyz.h"
#include "strwrap.h"
Deucе's avatar
Deucе committed
#include "syncterm.h"
Deucе's avatar
Deucе committed
#include "term.h"
#include "threadwrap.h"
#include "uifcinit.h"
#include "window.h"
#include "xmodem.h"
#include "xpbeep.h"
#include "xpendian.h"
#include "xpprintf.h"
Deucе's avatar
Deucе committed
#include "zmodem.h"
deuce's avatar
deuce committed
#ifndef WITHOUT_OOII
Deucе's avatar
Deucе committed
 #include "ooii.h"
deuce's avatar
deuce committed
#endif
#include "base64.h"
#include "md5.h"
deuce's avatar
deuce committed

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

Deucе's avatar
Deucе committed
 #define MIN(a, b) ((a) < (b) ? (a) : (b))
struct terminal   term;
struct cterminal *cterm;
Deucе's avatar
Deucе committed
#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;
static struct ciolib_pixels *pixmap_buffer[2];
static struct ciolib_mask *mask_buffer;
static uint8_t pnm_gamma[256] = {
	0,  13, 22, 28, 34, 38, 42, 46, 50, 53, 56, 59, 61, 64, 66, 69,
	71, 73, 75, 77, 79, 81, 83, 85, 86, 88, 90, 92, 93, 95, 96, 98, 
	99, 101, 102, 104, 105, 106, 108, 109, 110, 112, 113, 114, 115, 
	117, 118, 119, 120, 121, 122, 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, 148, 149, 150, 151, 152, 153, 154, 155, 
	155, 156, 157, 158, 159, 159, 160, 161, 162, 163, 163, 164, 165, 
	166, 167, 167, 168, 169, 170, 170, 171, 172, 173, 173, 174, 175, 
	175, 176, 177, 178, 178, 179, 180, 180, 181, 182, 182, 183, 184, 
	185, 185, 186, 187, 187, 188, 189, 189, 190, 190, 191, 192, 192, 
	193, 194, 194, 195, 196, 196, 197, 197, 198, 199, 199, 200, 200, 
	201, 202, 202, 203, 203, 204, 205, 205, 206, 206, 207, 208, 208, 
	209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, 215, 
	216, 216, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 
	223, 223, 224, 224, 225, 226, 226, 227, 227, 228, 228, 229, 229, 
	230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 
	236, 237, 237, 238, 238, 238, 239, 239, 240, 240, 241, 241, 242, 
	242, 243, 243, 244, 244, 245, 245, 246, 246, 246, 247, 247, 248, 
	248, 249, 249, 250, 250, 251, 251, 251, 252, 252, 253, 253, 254, 
	254, 255, 255
};
uint8_t pnm_gamma_max = 255;
Deucе's avatar
Deucе committed
void
get_cterm_size(int *cols, int *rows, int ns)
Deucе's avatar
Deucе committed
	if (cterm != NULL) {
		*cols = cterm->width;
		*rows = cterm->height;
	}
		get_term_win_size(cols, rows, NULL, NULL, &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 {
Deucе's avatar
Deucе committed
	uint32_t         flags;

#define MS_FLAGS_SGR (1 << 0)
#define MS_SGR_SET (1006)
Deucе's avatar
Deucе committed
void
setup_mouse_events(struct mouse_state *ms)
{
	ciomouse_setevents(0);
Deucе's avatar
Deucе committed
		switch (ms->mode) {
			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__)
Deucе's avatar
Deucе committed
 #pragma argsused
Deucе's avatar
Deucе committed
void
mousedrag(struct vmem_cell *scrollback)
	int                   key;
	struct mouse_event    mevent;
	struct vmem_cell     *screen;
	unsigned char        *tscreen;
	struct vmem_cell     *sbuffer;
	int                   sbufsize;
	int                   pos, startpos, endpos, lines;
	int                   outpos;
	char                 *copybuf = NULL;
	char                 *newcopybuf;
	int                   lastchar;
	struct ciolib_screen *savscrn;
Deucе's avatar
Deucе committed

	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]);
Deucе's avatar
Deucе committed
	while (1) {
		key = getch();
		if ((key == 0) || (key == 0xe0))
			key |= getch() << 8;
		switch (key) {
			case CIO_KEY_MOUSE:
				getmouse(&mevent);
Deucе's avatar
Deucе committed
				startpos = ((mevent.starty - 1) * term.width) + (mevent.startx - 1);
				endpos = ((mevent.endy - 1) * term.width) + (mevent.endx - 1);
				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;
Deucе's avatar
Deucе committed
				switch (mevent.event) {
Deucе's avatar
Deucе committed
						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;
Deucе's avatar
Deucе committed
								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);
Deucе's avatar
Deucе committed
						lines = abs(mevent.endy - mevent.starty) + 1;
						newcopybuf = realloc(copybuf, (endpos - startpos + 4 + lines * 2) * 4);
						if (newcopybuf)
							copybuf = newcopybuf;
						else
							goto cleanup;
Deucе's avatar
Deucе committed
						outpos = 0;
						lastchar = 0;
						for (pos = startpos; pos <= endpos; pos++) {
							size_t   outlen;
							uint8_t *utf8str;
							utf8str =
							    cp_to_utf8(conio_fontdata[screen[pos].font].cp,
							        (char *)&screen[pos].ch,
							        1,
							        &outlen);
deuce's avatar
deuce committed
							if (utf8str == NULL)
								continue;
							memcpy(copybuf + outpos, utf8str, outlen);
							outpos += outlen;
Deucе's avatar
Deucе committed
							if ((screen[pos].ch != ' ') && screen[pos].ch)
								lastchar = outpos;
							if ((pos + 1) % term.width == 0) {
								outpos = lastchar;
#ifdef _WIN32
								copybuf[outpos++] = '\r';
#endif
								copybuf[outpos++] = '\n';
								lastchar = outpos;
Deucе's avatar
Deucе committed
						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);
Deucе's avatar
Deucе committed
	if (copybuf)
	restorescreen(savscrn);
	freescreen(savscrn);
Deucе's avatar
Deucе committed
void
update_status(struct bbslist *bbs, int speed, int ooii_mode, bool ata_inv)
deuce's avatar
deuce committed
{
	char nbuf[LIST_NAME_MAX + 10 + 11 + 7 + 10 + 6 + 1]; /*
                                                 * Room for "Name (Logging) (115300) (DrWy) (OOTerm2) (INV)" and terminator
                                                 * SAFE and Logging should me be possible.
                                                 */
Deucе's avatar
Deucе committed
	int               oldscroll;
	int               olddmc = hold_update;
	struct  text_info txtinfo;
	time_t            now;
	static time_t     lastupd = 0;
	static int        oldspeed = 0;
	int               timeon;
	char              sep;
	int               oldfont_norm;
	int               oldfont_bright;
Deucе's avatar
Deucе committed
	oldfont_norm = getfont(1);
	oldfont_bright = getfont(2);
	setfont(0, false, 1);
	setfont(0, false, 2);
Deucе's avatar
Deucе committed
	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 = '|';
Deucе's avatar
Deucе 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;
Deucе's avatar
Deucе committed
	lastupd = now;
	oldspeed = speed;
	if (now > (bbs->connected + 359999))
		timeon = 350000;
	else if (now < bbs->connected)
		timeon = 0;
	else
Deucе's avatar
Deucе committed
		timeon = (int)(now - bbs->connected);
	gettextinfo(&txtinfo);
Deucе's avatar
Deucе committed
	oldscroll = _wscroll;
	hold_update = true;
	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);
Deucе's avatar
Deucе committed
	if (safe_mode)
		strcat(nbuf, " (SAFE)");
Deucе's avatar
Deucе committed
	if (cterm->log)
		strcat(nbuf, " (Logging)");
Deucе's avatar
Deucе committed
	if (speed)
		sprintf(strchr(nbuf, 0), " (%d)", speed);
	if (cterm->doorway_mode)
deuce's avatar
deuce committed
		strcat(nbuf, " (DrWy)");
Deucе's avatar
Deucе committed
	switch (ooii_mode) {
		case 1:
			strcat(nbuf, " (OOTerm)");
			break;
		case 2:
			strcat(nbuf, " (OOTerm1)");
			break;
		case 3:
			strcat(nbuf, " (OOTerm2)");
			break;
deuce's avatar
deuce committed
	}
	if (ata_inv)
		strcat(nbuf, " (INV)");
	ciolib_setcolour(11, 4);
Deucе's avatar
Deucе committed
	switch (cio_api.mode) {
		case CIOLIB_MODE_CURSES:
		case CIOLIB_MODE_CURSES_IBM:
		case CIOLIB_MODE_ANSI:
			if (term.width < 80) {
				cprintf(" %-30.30s %c %-6.6s ",
				    nbuf,
				    sep,
				    conn_types[bbs->conn_type]);
			}
			else 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);
			if (term.width < 80) {
				cprintf(" %-30.30s %c %-6.6s %*s",
				    nbuf,
				    sep,
				    conn_types[bbs->conn_type],
				    term.width - 40, "");
			}
			else if (timeon > 359999) {
				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 */
	if (wherex() - term.y + 1 >= term.width)
Deucе's avatar
Deucе committed
	_wscroll = oldscroll;
	setfont(oldfont_norm, 0, 1);
	setfont(oldfont_bright, 0, 2);
deuce's avatar
deuce committed
	textattr(txtinfo.attribute);
Deucе's avatar
Deucе committed
	window(txtinfo.winleft, txtinfo.wintop, txtinfo.winright, txtinfo.winbottom);
	gotoxy(txtinfo.curx, txtinfo.cury);
	hold_update = olddmc;
#if defined(_WIN32) && defined(_DEBUG) && defined(DUMP)
Deucе's avatar
Deucе committed
void
dump(BYTE *buf, int len)
Deucе's avatar
Deucе committed
	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]);
Deucе's avatar
Deucе committed
		slen = sprintf(str, "RX: ");
Deucе's avatar
Deucе committed

#endif /* if defined(_WIN32) && defined(_DEBUG) && defined(DUMP) */
int log_level = LOG_INFO;
	zmodem_t       *zm;
	struct bbslist *bbs;
Deucе's avatar
Deucе committed
enum {
	ZMODEM_MODE_SEND,
	ZMODEM_MODE_RECV
} zmodem_mode;
Deucе's avatar
Deucе committed
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);
	int                   key;
deuce's avatar
deuce committed
	if (zm == NULL)
		return true;
deuce's avatar
deuce committed
	if (quitting) {
Deucе's avatar
Deucе committed
		zm->cancelled = true;
		zm->local_abort = true;
		return true;
Deucе's avatar
Deucе committed
	if (last_check != now) {
		last_check = now;
		while (kbhit()) {
			switch ((key = getch())) {
deuce's avatar
deuce committed
				case ESC:
				case CTRL_C:
				case CTRL_X:
Deucе's avatar
Deucе committed
					zm->cancelled = true;
					zm->local_abort = true;
deuce's avatar
deuce committed
					break;
				case 0:
				case 0xe0:
					key |= (getch() << 8);
Deucе's avatar
Deucе committed
					if (key == CIO_KEY_MOUSE)
deuce's avatar
deuce committed
						getmouse(NULL);
Deucе's avatar
Deucе committed
					if (key == CIO_KEY_QUIT) {
						if (check_exit(false)) {
Deucе's avatar
Deucе committed
							zm->cancelled = true;
							zm->local_abort = true;
deuce's avatar
deuce committed
					}
					break;
Deucе's avatar
Deucе committed
	return zm->cancelled;
extern FILE *log_fp;
extern char *log_levels[];
#if defined(__BORLANDC__)
Deucе's avatar
Deucе committed
 #pragma argsused
Deucе's avatar
Deucе committed
static int
lputs(void *cbdata, int level, const char *str)
{
	char msg[512];
Deucе's avatar
Deucе committed
	int  chars;
	int  oldhold = hold_update;
#if defined(_WIN32) && defined(_DEBUG) && false
Deucе's avatar
Deucе committed
	sprintf(msg, "SyncTerm: %s\n", str);
	OutputDebugString(msg);
Deucе's avatar
Deucе committed
	if ((log_fp != NULL) && (level <= log_level)) {
		time_t t = time(NULL);
Deucе's avatar
Deucе committed
		fprintf(log_fp, "%.15s %s\n", ctime(&t) + 4, str);
Deucе's avatar
Deucе committed
	if (level > LOG_INFO)
Deucе's avatar
Deucе committed
        /* 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);
Deucе's avatar
Deucе committed
	switch (level) {
#if 0 // Not possible because of above level > LOG_INFO check
Deucе's avatar
Deucе committed
			SAFEPRINTF(msg, "%s\r\n", str);
deuce's avatar
deuce committed
#endif
deuce's avatar
deuce committed
		case LOG_INFO:
Deucе's avatar
Deucе committed
			SAFEPRINTF(msg, "%s\r\n", str);
Deucе's avatar
Deucе committed
			SAFEPRINTF(msg, "%s\r\n", str);
Deucе's avatar
Deucе committed
			SAFEPRINTF(msg, "Warning: %s\r\n", str);
Deucе's avatar
Deucе committed
			SAFEPRINTF(msg, "!ERROR: %s\r\n", str);
Deucе's avatar
Deucе committed
	hold_update = false;
	chars = cputs(msg);
	hold_update = oldhold;
	gettextinfo(&log_ti);
deuce's avatar
deuce committed
	return chars;
Deucе's avatar
Deucе committed
static int
lprintf(int level, const char *fmt, ...)
Deucе's avatar
Deucе committed
	char    sbuf[1024];
Deucе's avatar
Deucе committed
	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__)
Deucе's avatar
Deucе committed
 #pragma argsused
Deucе's avatar
Deucе committed
void
zmodem_progress(void *cbdata, int64_t current_pos)
	char                  orig[128];
	unsigned              cps;
	int                   l;
	time_t                t;
	time_t                now;
	static time_t         last_progress = 0;
	int                   old_hold = hold_update;
	struct zmodem_cbdata *zcb = (struct zmodem_cbdata *)cbdata;
	zmodem_t             *zm = zcb->zm;
	bool                  growing = false;
Deucе's avatar
Deucе committed

	now = time(NULL);
	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);
Deucе's avatar
Deucе committed
		gotoxy(1, 1);
		textattr(LIGHTCYAN | (BLUE << 4));
		t = now - zm->transfer_start_time;
		if (t <= 0)
			t = 1;
		if (zm->transfer_start_pos > current_pos)
			zm->transfer_start_pos = 0;
		if ((cps = (unsigned)((current_pos - zm->transfer_start_pos) / t)) == 0)
			cps = 1;                                 /* cps so far */
		l = (zm->current_file_size - current_pos) / cps; /* remaining transfer est time */
		if (l < 0)
			l = 0;
		cprintf("File (%u of %u): %-.*s",
		    zm->current_file_num, zm->total_files, TRANSFER_WIN_WIDTH - 20, zm->current_file_name);
		clreol();
		cputs("\r\n");
Deucе's avatar
Deucе committed
		if (zm->transfer_start_pos)
			sprintf(orig, "From: %" PRId64 "  ", zm->transfer_start_pos);
Deucе's avatar
Deucе committed
			orig[0] = 0;
		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"
Deucе's avatar
Deucе committed
		    ,
		    (unsigned long)(t / 60L)
		    ,
		    (unsigned long)(t % 60L)
		    ,
		    (unsigned long)(l / 60L)
		    ,
		    (unsigned long)(l % 60L)
		    ,
		    zm->block_size
		    ,
		    zmodem_mode == ZMODEM_MODE_RECV ? (zm->receive_32bit_data ? 32 : 16)
		                                                              : (zm->can_fcs_32 && !zm->want_fcs_16) ? 32 : 16
		    ,
		    cps);
		cputs("\r\n");
Deucе's avatar
Deucе committed
		if (zm->current_file_size == 0) {
			cprintf("%*s%3d%%\r\n", TRANSFER_WIN_WIDTH / 2 - 5, "", 100);
Deucе's avatar
Deucе committed
		else {
			cprintf("%*s%3d%%\r\n", TRANSFER_WIN_WIDTH / 2 - 5, "",
			    (long)(((float)current_pos / (float)zm->current_file_size) * 100.0));
Deucе's avatar
Deucе committed
			l = (long)(60 * ((float)current_pos / (float)zm->current_file_size));
		cprintf("[%*.*s%*s]", l, l,
Deucе's avatar
Deucе committed
		    "\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), "");
Deucе's avatar
Deucе committed
		last_progress = now;
		hold_update = false;
		gotoxy(wherex(), wherey());
		hold_update = old_hold;
#if defined(__BORLANDC__)
Deucе's avatar
Deucе committed
 #pragma argsused
Deucе's avatar
Deucе committed
unsigned char transfer_buffer[BUFFER_SIZE / 2];
unsigned      transfer_buf_len = 0;
Deucе's avatar
Deucе committed
static void
flush_send(void *unused)
Deucе's avatar
Deucе committed
	int sent;
Deucе's avatar
Deucе committed
	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;
	}
Deucе's avatar
Deucе committed
	else {
		transfer_buf_len = 0;
	}
Deucе's avatar
Deucе committed
static int
send_byte(void *unused, uchar ch, unsigned timeout /* seconds */)
Deucе's avatar
Deucе committed
	transfer_buffer[transfer_buf_len++] = ch;
	if (transfer_buf_len == sizeof(transfer_buffer))
		flush_send(unused);
Deucе's avatar
Deucе committed
	return !(transfer_buf_len < sizeof(transfer_buffer));
#if defined(__BORLANDC__)
Deucе's avatar
Deucе committed
 #pragma argsused
Deucе's avatar
Deucе committed
BYTE     recv_byte_buffer[BUFFER_SIZE];
unsigned recv_byte_buffer_len = 0;
unsigned recv_byte_buffer_pos = 0;
Deucе's avatar
Deucе committed
static void
recv_bytes(unsigned timeout /* Milliseconds */)
Deucе's avatar
Deucе committed
	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) {
Deucе's avatar
Deucе committed
			recv_byte_buffer_len = conn_recv_upto(recv_byte_buffer, sizeof(recv_byte_buffer) - 3, timeout);
			if (recv_byte_buffer_len)
Deucе's avatar
Deucе committed
				recv_byte_buffer_len =
				    parse_rip(recv_byte_buffer, recv_byte_buffer_len, sizeof(recv_byte_buffer));
Deucе's avatar
Deucе committed
static int
recv_byte(void *unused, unsigned timeout /* seconds */)
Deucе's avatar
Deucе committed
	recv_bytes(timeout * 1000);
Deucе's avatar
Deucе committed
	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;
Deucе's avatar
Deucе committed
 #pragma argsused
Deucе's avatar
Deucе committed
BOOL
data_waiting(void *unused, unsigned timeout /* seconds */)
Deucе's avatar
Deucе committed
	bool ret;
deuce's avatar
deuce committed

Deucе's avatar
Deucе committed
	if (recv_byte_buffer_len)
		return true;
deuce's avatar
deuce committed
	pthread_mutex_lock(&(conn_inbuf.mutex));
Deucе's avatar
Deucе committed
	ret = conn_buf_wait_bytes(&conn_inbuf, 1, timeout * 1000) != 0;
deuce's avatar
deuce committed
	pthread_mutex_unlock(&(conn_inbuf.mutex));
	return ret;
Deucе's avatar
Deucе committed
size_t
count_data_waiting(void)
Deucе's avatar
Deucе committed
void
draw_transfer_window(char *title)
Deucе's avatar
Deucе committed
	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;
Deucе's avatar
Deucе committed
	hold_update = true;
	gettextinfo(&trans_ti);
Deucе's avatar
Deucе committed
	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);
Deucе's avatar
Deucе committed
	memset(outline, YELLOW | (BLUE << 4), sizeof(outline));
	for (i = 2; i < sizeof(outline) - 2; i += 2) {
		outline[i] = (char)0xcd; /* Double horizontal line */
Deucе's avatar
Deucе committed
	outline[0] = (char)0xc9;
	outline[sizeof(outline) - 2] = (char)0xbb;
	puttext(left, top, left + TRANSFER_WIN_WIDTH - 1, top, outline);
Deucе's avatar
Deucе committed
        /* 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);
Deucе's avatar
Deucе committed
	for (i = 2; i < sizeof(outline) - 2; i += 2) {
		outline[i] = (char)0xc4;           /* Single horizontal line */
Deucе's avatar
Deucе committed
	outline[0] = (char)0xc7;                   /* 0xcc */
	outline[sizeof(outline) - 2] = (char)0xb6; /* 0xb6 */
	puttext(left, top + 6, left + TRANSFER_WIN_WIDTH - 1, top + 6, outline);
Deucе's avatar
Deucе committed
	for (i = 2; i < sizeof(outline) - 2; i += 2) {
		outline[i] = (char)0xcd; /* Double horizontal line */
Deucе's avatar
Deucе committed
	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);
Deucе's avatar
Deucе committed
	outline[0] = (char)0xba;
	outline[sizeof(outline) - 2] = (char)0xba;
	for (i = 2; i < sizeof(outline) - 2; i += 2)
		outline[i] = ' ';
Deucе's avatar
Deucе committed
	for (i = 1; i < 6; i++)
		puttext(left, top + i, left + TRANSFER_WIN_WIDTH - 1, top + i, outline);
Deucе's avatar
Deucе committed
/*
 *      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);
Deucе's avatar
Deucе committed
        /* Title */
	gotoxy(left + TRANSFER_WIN_WIDTH - 20, top + i);
Deucе's avatar
Deucе committed
	textattr(YELLOW | (BLUE << 4));
Deucе's avatar
Deucе committed
	textattr(WHITE | (BLUE << 4));
	gotoxy(left + TRANSFER_WIN_WIDTH - 18, top + i);
	cprintf("ESC to Abort");

Deucе's avatar
Deucе committed
        /* Shadow */
	if (uifc.bclr == BLUE) {
		gettext(left + TRANSFER_WIN_WIDTH,
		    top + 1,
		    left + TRANSFER_WIN_WIDTH + 1,
		    top + (TRANSFER_WIN_HEIGHT - 1),
		    shadow);
Deucе's avatar
Deucе committed
		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);
Deucе's avatar
Deucе committed
		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);
Deucе's avatar
Deucе committed
	window(left + 2, top + 7, left + TRANSFER_WIN_WIDTH - 3, top + TRANSFER_WIN_HEIGHT - 2);
	hold_update = false;
Deucе's avatar
Deucе committed
	gotoxy(1, 1);
	hold_update = old_hold;
	gettextinfo(&log_ti);
	_setcursortype(_NOCURSOR);
Deucе's avatar
Deucе committed
void
erase_transfer_window(void)
{
	vmem_puttext(((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,
	    winbuf);
	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);
void raw_upload(FILE *fp);
Deucе's avatar
Deucе committed
void
begin_upload(struct bbslist *bbs, bool autozm, int lastch)
	char                  str[MAX_PATH * 2 + 1];
	char                  path[MAX_PATH + 1];
	int                   result;
	int                   i;
	FILE                 *fp;
	struct file_pick      fpick;
	char                 *opts[7] = {
		"ZMODEM",
		"YMODEM",
		"XMODEM-1K",
		"XMODEM-128",
		"ASCII",
		"Raw",
		""
Deucе's avatar
Deucе committed
	};
	struct  text_info     txtinfo;
	struct ciolib_screen *savscrn;
Deucе's avatar
Deucе committed

	if (safe_mode)
Deucе's avatar
Deucе committed
	gettextinfo(&txtinfo);
	savscrn = savescreen();
	setfont(0, false, 1);
	setfont(0, false, 2);
	setfont(0, false, 3);
	setfont(0, false, 4);
	init_uifc(false, false);
Deucе's avatar
Deucе committed
	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;
	}
Deucе's avatar
Deucе committed
	result = filepick(&uifc, "Upload", &fpick, bbs->uldir, NULL, UIFC_FP_ALLOWENTRY);
Deucе's avatar
Deucе committed
	if ((result == -1) || (fpick.files < 1)) {
		check_exit(false);
		filepick_free(&fpick);
		restorescreen(savscrn);
		freescreen(savscrn);
		gotoxy(txtinfo.curx, txtinfo.cury);