Skip to content
Snippets Groups Projects
atcodes.cpp 65.9 KiB
Newer Older
/* Synchronet "@code" functions */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
 *																			*
 * This program is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU 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 General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include "sbbs.h"
#include "cmdshell.h"
#include "utf8.h"
#include "unicode.h"
extern WSADATA WSAData;
	#define SOCKLIB_DESC WSAData.szDescription
#else
	#define SOCKLIB_DESC NULL
	bool zero_padded = false;
	bool truncated = true;
	bool doubled = false;
	bool thousep = false;       // thousands-separated
	bool uppercase = false;
	bool width_specified = false;
	char* parse(char* sp) {
		char* p;
		disp_len = strlen(sp) + 2;
		if ((p = strchr(sp, '|')) != NULL) {
			if (strchr(p, 'T') != NULL)
			if (strchr(p, 'U') != NULL)
			if (strchr(p, 'L') != NULL)
			else if (strchr(p, 'R') != NULL)
			else if (strchr(p, 'C') != NULL)
			else if (strchr(p, 'W') != NULL)
			else if (strchr(p, 'Z') != NULL)
			else if (strchr(p, '>') != NULL)
		else if (strchr(sp, ':') != NULL)
		else if ((p = strstr(sp, "-L")) != NULL)
		else if ((p = strstr(sp, "-R")) != NULL)
		else if ((p = strstr(sp, "-C")) != NULL)
		else if ((p = strstr(sp, "-W")) != NULL)  /* wide */
			doubled = true;
		else if ((p = strstr(sp, "-Z")) != NULL)
			zero_padded = true;
		else if ((p = strstr(sp, "-T")) != NULL)
			thousep = true;
		else if ((p = strstr(sp, "-U")) != NULL)
			uppercase = true;
		else if ((p = strstr(sp, "->")) != NULL)  /* wrap */
			lp++;   // skip the '|' or '-'
			while (*lp == '>' || IS_ALPHA(*lp))
				width_specified = true;
			while (*lp && !IS_DIGIT(*lp))
			if (*lp && IS_DIGIT(*lp)) {
				disp_len = atoi(lp);

/****************************************************************************/
/* Returns 0 if invalid @ code. Returns length of @ code if valid.          */
/****************************************************************************/
int sbbs_t::show_atcode(const char *instr, JSObject* obj)
{
	char          str[128], str2[128], *tp, *sp, *p;
	int           len;
	int           pmode = 0;
	const char *  cp;
	SAFECOPY(str, instr);
	tp = strchr(str + 1, '@');
	if (!tp)                 /* no terminating @ */
	sp = strchr(str + 1, ' ');
	if (sp && sp < tp)         /* space before terminating @ */
	len = (tp - str) + 1;
	(*tp) = 0;
	sp = (str + 1);
	if (*sp == '~' && *(sp + 1)) {   // Mouse hot-spot (hungry)
		sp++;
		tp = strchr(sp + 1, '~');
			tp = sp;
		else {
			*tp = 0;
			tp++;
		}
		c_unescape_str(tp);
		add_hotspot(tp, /* hungry: */ true, column, column + strlen(sp) - 1, row);
	if (*sp == '`' && *(sp + 1)) {   // Mouse hot-spot (strict)
		add_hotspot(tp, /* hungry: */ false, column, column + strlen(sp) - 1, row);
		bputs(sp);
		return len;
	}

	// @!x@ for Ctrl-A x equivalent(s) */
	if (*sp == '!') {
		for (p = sp + 1; *p != '\0' && *p != '@'; p++)
	cp = atcode(sp, str2, sizeof(str2), &pmode, fmt.align == fmt.center, obj);
		cp = separate_thousands(cp, separated, sizeof(separated), ',');

	char upper[128];
	if (fmt.uppercase) {
	if (p == NULL || fmt.truncated == false || (fmt.width_specified == false && fmt.align == fmt.none))
rswindell's avatar
rswindell committed

	if (fmt.uppercase && fmt.align == fmt.none)
	if (fmt.truncated && strchr(cp, '\n') == NULL) {
		if (column + fmt.disp_len > cols - 1) {
			if (column >= cols - 1)
rswindell's avatar
rswindell committed
			else
rswindell's avatar
rswindell committed
		}
	}
	if (pmode & P_UTF8) {
		if (term_supports(UTF8))
Rob Swindell's avatar
Rob Swindell committed
			fmt.disp_len += strlen(cp) - utf8_str_total_width(cp, unicode_zerowidth);
			fmt.disp_len += strlen(cp) - utf8_str_count_width(cp, /* min: */ 1, /* max: */ 2, unicode_zerowidth);
	if (fmt.align == fmt.left)
		bprintf(pmode, "%-*.*s", fmt.disp_len, fmt.disp_len, cp);
	else if (fmt.align == fmt.right)
		bprintf(pmode, "%*.*s", fmt.disp_len, fmt.disp_len, cp);
	else if (fmt.align == fmt.center) {
rswindell's avatar
rswindell committed
		int vlen = strlen(cp);
		if (vlen < fmt.disp_len) {
			int left = (fmt.disp_len - vlen) / 2;
			bprintf(pmode, "%*s%-*s", left, "", fmt.disp_len - left, cp);
			bprintf(pmode, "%.*s", fmt.disp_len, cp);
	} else if (fmt.doubled) {
		wide(cp);
	} else if (fmt.zero_padded) {
		if (vlen < fmt.disp_len)
			bprintf(pmode, "%-.*s%s", (int)(fmt.disp_len - strlen(cp)), "0000000000", cp);
			bprintf(pmode, "%.*s", fmt.disp_len, cp);
		bprintf(pmode, "%.*s", fmt.disp_len, cp);
static const char* getpath(scfg_t* cfg, const char* path)
{
	for (int i = 0; i < cfg->total_dirs; i++) {
		if (stricmp(cfg->dir[i]->code, path) == 0)
			return cfg->dir[i]->path;
	}
	return path;
}

const char* sbbs_t::formatted_atcode(const char* sp, char* str, size_t maxlen)
{
	char          tmp[128];
	char          buf[256];
	char*         p = fmt.parse(tmp);
	const char*   cp = atcode(tmp, buf, sizeof buf);
	if (cp == nullptr)
	char          separated[128];
	if (fmt.thousep)
		cp = separate_thousands(cp, separated, sizeof(separated), ',');

	char          upper[128];
	if (fmt.uppercase) {
	if (p == NULL || fmt.truncated == false || (fmt.width_specified == false && fmt.align == fmt.none))
	if (fmt.align == fmt.left)
		snprintf(str, maxlen, "%-*.*s", fmt.disp_len, fmt.disp_len, cp);
	else if (fmt.align == fmt.right)
		snprintf(str, maxlen, "%*.*s", fmt.disp_len, fmt.disp_len, cp);
	else if (fmt.align == fmt.center) {
		if (vlen < fmt.disp_len) {
			int left = (fmt.disp_len - vlen) / 2;
			snprintf(str, maxlen, "%*s%-*s", left, "", fmt.disp_len - left, cp);
		} else
			snprintf(str, maxlen, "%.*s", fmt.disp_len, cp);
//	} else if(fmt.doubled) { // Unsupported in this context
//		wide(cp);
	} else if (fmt.zero_padded) {
		if (vlen < fmt.disp_len)
			snprintf(str, maxlen, "%-.*s%s", (int)(fmt.disp_len - strlen(cp)), "0000000000", cp);
		else
			snprintf(str, maxlen, "%.*s", fmt.disp_len, cp);
	} else
		snprintf(str, maxlen, "%.*s", fmt.disp_len, cp);

	return str;
}

const char* sbbs_t::atcode(const char* sp, char* str, size_t maxlen, int* pmode, bool centered, JSObject* obj)
	char       tmp[128];
	char*      tp = NULL;
	int        i;
	uint       ugrp;
	uint       usub;
	long       l;
	stats_t    stats;
	node_t     node;
	struct  tm tm;

	str[0] = 0;

	if (strcmp(sp, "SHOW") == 0) {
		console &= ~CON_ECHO_OFF;
		return nulstr;
	}
	if (strncmp(sp, "SHOW:", 5) == 0) {
		uchar* ar = arstr(NULL, sp + 5, &cfg, NULL);
		if (ar != NULL) {
			if (!chk_ar(ar, &useron, &client))
				console |= CON_ECHO_OFF;
			else
				console &= ~CON_ECHO_OFF;
			free(ar);
		}
		return nulstr;
	}

	if (strcmp(sp, "HOT") == 0) { // Auto-mouse hot-spot attribute
		hot_attr = curatr;
		return nulstr;
	if (strncmp(sp, "HOT:", 4) == 0) {   // Auto-mouse hot-spot attribute
		sp += 4;
		if (stricmp(sp, "hungry") == 0) {
		else if (stricmp(sp, "strict") == 0) {
		else if (stricmp(sp, "off") == 0)
			hot_attr = 0;
		else
			hot_attr = strtoattr(sp, /* endptr: */ NULL);
	if (strcmp(sp, "CLEAR_HOT") == 0) {
		clear_hotspots();
		return nulstr;
	}
	if (strncmp(sp, "MNE:", 4) == 0) {   // Mnemonic attribute control
		sp += 4;
		mneattr_low = strtoattr(sp, &tp);
		mneattr_high = mneattr_low ^ HIGH;
		if (tp != NULL && *tp != '\0')
			mneattr_high = strtoattr(tp + 1, &tp);
		if (tp != NULL && *tp != '\0')
			mneattr_cmd = strtoattr(tp + 1, NULL);
		return nulstr;
	}

	if (strncmp(sp, "RAINBOW:", 8) == 0) {
		memset(rainbow, 0, sizeof rainbow);
		parse_attr_str_list(rainbow, LEN_RAINBOW, sp + 8);
		return nulstr;
	}

	if (strncmp(sp, "U+", 2) == 0) { // UNICODE
		enum unicode_codepoint codepoint = (enum unicode_codepoint)strtoul(sp + 2, &tp, 16);
		if (tp == NULL || *tp == 0)
			outchar(codepoint, unicode_to_cp437(codepoint));
		else if (*tp == ':')
rswindell's avatar
rswindell committed
			outchar(codepoint, tp + 1);
		else {
			char fallback = (char)strtoul(tp + 1, NULL, 16);
				outchar(codepoint, fallback);
			else if (*tp == '!') {
				char ch = unicode_to_cp437(codepoint);
					fallback = ch;
				outchar(codepoint, fallback);
			}
			else
				return NULL;  // Invalid @-code
	if (strcmp(sp, "CHECKMARK") == 0) {
		outchar(UNICODE_CHECK_MARK, CP437_CHECK_MARK);
		return nulstr;
	}

	if (strcmp(sp, "ELLIPSIS") == 0) {
rswindell's avatar
rswindell committed
		outchar(UNICODE_HORIZONTAL_ELLIPSIS, "...");
		return nulstr;
	}
	if (strcmp(sp, "COPY") == 0) {
rswindell's avatar
rswindell committed
		outchar(UNICODE_COPYRIGHT_SIGN, "(C)");
		return nulstr;
	}
	if (strcmp(sp, "SOUNDCOPY") == 0) {
rswindell's avatar
rswindell committed
		outchar(UNICODE_SOUND_RECORDING_COPYRIGHT, "(P)");
		return nulstr;
	}
	if (strcmp(sp, "REGISTERED") == 0) {
rswindell's avatar
rswindell committed
		outchar(UNICODE_REGISTERED_SIGN, "(R)");
		return nulstr;
	}
	if (strcmp(sp, "TRADEMARK") == 0) {
rswindell's avatar
rswindell committed
		outchar(UNICODE_TRADE_MARK_SIGN, "(TM)");
		return nulstr;
	}
	if (strcmp(sp, "DEGREE_C") == 0) {
rswindell's avatar
rswindell committed
		outchar(UNICODE_DEGREE_CELSIUS, "\xF8""C");
rswindell's avatar
rswindell committed
		return nulstr;
	}
	if (strcmp(sp, "DEGREE_F") == 0) {
rswindell's avatar
rswindell committed
		outchar(UNICODE_DEGREE_FAHRENHEIT, "\xF8""F");
rswindell's avatar
rswindell committed
		return nulstr;
	}

	if (strncmp(sp, "WIDE:", 5) == 0) {
rswindell's avatar
rswindell committed
		wide(sp + 5);
rswindell's avatar
rswindell committed
	}

	if (!strcmp(sp, "VER"))
	if (!strcmp(sp, "REV")) {
		safe_snprintf(str, maxlen, "%c", REVISION);
	if (!strcmp(sp, "FULL_VER")) {
		safe_snprintf(str, maxlen, "%s%c%s", VERSION, REVISION, beta_version);
		truncsp(str);
		strcat(str, " Debug");
	if (!strcmp(sp, "VER_NOTICE"))
		return VERSION_NOTICE;
	if (!strcmp(sp, "OS_VER"))
		return os_version(str, maxlen);
	if (strcmp(sp, "OS_CPU") == 0)
		return os_cpuarch(str, maxlen);
	if (!strcmp(sp, "JS_VER"))
		return (char *)JS_GetImplementationVersion();
	if (!strcmp(sp, "PLATFORM"))
		return PLATFORM_DESC;
	if (!strcmp(sp, "COPYRIGHT"))
		return COPYRIGHT_NOTICE;
	if (!strcmp(sp, "COMPILER")) {
		char compiler[32];
		DESCRIBE_COMPILER(compiler);
		strncpy(str, compiler, maxlen);
	if (strcmp(sp, "GIT_HASH") == 0)
	if (strcmp(sp, "GIT_BRANCH") == 0)
	if (strcmp(sp, "GIT_DATE") == 0)
Rob Swindell's avatar
Rob Swindell committed
		return git_date;

	if (strcmp(sp, "BUILD_DATE") == 0)
		return __DATE__;

	if (strcmp(sp, "BUILD_TIME") == 0)
		return __TIME__;

	if (!strcmp(sp, "UPTIME")) {
		now = time(NULL);
		if (uptime != 0 && now >= uptime)
			up = now - uptime;
		char                   days[64] = "";
		if ((up / (24 * 60 * 60)) >= 2) {
			snprintf(days, sizeof days, "%u days ", (uint)(up / (24L * 60L * 60L)));
			up %= (24 * 60 * 60);
		safe_snprintf(str, maxlen, "%s%u:%02u"
		              , days
		              , (uint)(up / (60L * 60L))
		              , (uint)((up / 60L) % 60L)
		              );
	if (!strcmp(sp, "SERVED")) {
		extern volatile uint served;
		safe_snprintf(str, maxlen, "%u", served);
	if (!strcmp(sp, "SOCKET_LIB"))
		return socklib_version(str, maxlen, SOCKLIB_DESC);
	if (!strcmp(sp, "MSG_LIB")) {
		safe_snprintf(str, maxlen, "SMBLIB %s", smb_lib_ver());
	if (!strcmp(sp, "BBS") || !strcmp(sp, "BOARDNAME"))
		return cfg.sys_name;
	if (!strcmp(sp, "BAUD") || !strcmp(sp, "BPS")) {
		safe_snprintf(str, maxlen, "%u", cur_output_rate ? cur_output_rate : cur_rate);
	if (!strcmp(sp, "CPS")) {
		safe_snprintf(str, maxlen, "%u", cur_cps);
	if (!strcmp(sp, "COLS")) {
		safe_snprintf(str, maxlen, "%u", cols);
rswindell's avatar
rswindell committed
	}
	if (!strcmp(sp, "ROWS")) {
		safe_snprintf(str, maxlen, "%u", rows);
rswindell's avatar
rswindell committed
	}
	if (strcmp(sp, "TERM") == 0)
	if (strcmp(sp, "CHARSET") == 0)
	if (!strcmp(sp, "CONN"))
	if (!strcmp(sp, "SYSOP"))
	if (strcmp(sp, "SYSAVAIL") == 0)
		return text[sysop_available(&cfg) ? LiSysopAvailable : LiSysopNotAvailable];

	if (strcmp(sp, "SYSAVAILYN") == 0)
		return text[sysop_available(&cfg) ? Yes : No];

	if (!strcmp(sp, "LOCATION"))
		return cfg.sys_location;
	if (strcmp(sp, "NODE") == 0 || strcmp(sp, "NN") == 0) {
		safe_snprintf(str, maxlen, "%u", cfg.node_num);
	if (strcmp(sp, "TNODES") == 0 || strcmp(sp, "TNODE") == 0 || strcmp(sp, "TN") == 0) {
		safe_snprintf(str, maxlen, "%u", cfg.sys_nodes);
	if (strcmp(sp, "ANODES") == 0 || strcmp(sp, "ANODE") == 0 || strcmp(sp, "AN") == 0) {
		safe_snprintf(str, maxlen, "%u", count_nodes(/* self: */ true));
	if (strcmp(sp, "ONODES") == 0 || strcmp(sp, "ONODE") == 0 || strcmp(sp, "ON") == 0) {
		safe_snprintf(str, maxlen, "%u", count_nodes(/* self: */ false));
	if (strcmp(sp, "PWDAYS") == 0) {
		if (cfg.sys_pwdays) {
rswindell's avatar
rswindell committed
			safe_snprintf(str, maxlen, "%u", cfg.sys_pwdays);
			return str;
		}
		return text[Unlimited];
	}

	if (strcmp(sp, "AUTODEL") == 0) {
		if (cfg.sys_autodel) {
rswindell's avatar
rswindell committed
			safe_snprintf(str, maxlen, "%u", cfg.sys_autodel);
			return str;
		}
		return text[Unlimited];
	}

	if (strcmp(sp, "PAGER") == 0)
		return (thisnode.misc & NODE_POFF) ? text[Off] : text[On];
	if (strcmp(sp, "ALERTS") == 0)
		return (thisnode.misc & NODE_AOFF) ? text[Off] : text[On];
	if (strcmp(sp, "SPLITP") == 0)
		return (useron.chat & CHAT_SPLITP) ? text[On] : text[Off];
	if (!strcmp(sp, "INETADDR"))
		return cfg.sys_inetaddr;
	if (!strcmp(sp, "HOSTNAME"))
	if (!strcmp(sp, "FIDOADDR")) {
		if (cfg.total_faddrs)
			return smb_faddrtoa(&cfg.faddr[0], str);
		return nulstr;
	if (!strcmp(sp, "EMAILADDR"))
		return usermailaddr(&cfg, str
		                    , (cfg.inetmail_misc & NMAIL_ALIAS) || (useron.rest & FLAG('O')) ? useron.alias : useron.name);
	if (strcmp(sp, "NETMAIL") == 0)
	if (strcmp(sp, "TERMTYPE") == 0)
		return term_type(&useron, term_supports(), str, maxlen);

	if (strcmp(sp, "TERMROWS") == 0)
	if (strcmp(sp, "TERMCOLS") == 0)
	if (strcmp(sp, "AUTOTERM") == 0)
		return (useron.misc & AUTOTERM) ? text[On] : text[Off];

	if (strcmp(sp, "ANSI") == 0)
		return (useron.misc & ANSI) ? text[On] : text[Off];

	if (strcmp(sp, "ASCII") == 0)
		return (useron.misc & NO_EXASCII) ? text[On] : text[Off];

	if (strcmp(sp, "COLOR") == 0)
		return (useron.misc & COLOR) ? text[On] : text[Off];

	if (strcmp(sp, "ICE") == 0)
		return (useron.misc & ICE_COLOR) ? text[On] : text[Off];

	if (strcmp(sp, "RIP") == 0)
		return (useron.misc & RIP) ? text[On] : text[Off];

	if (strcmp(sp, "PETSCII") == 0)
		return (useron.misc & PETSCII) ? text[On] : text[Off];

	if (strcmp(sp, "PETGRFX") == 0) {
		if (term_supports(PETSCII))
	if (strcmp(sp, "SWAPDEL") == 0)
		return (useron.misc & SWAP_DELETE) ? text[On] : text[Off];

	if (strcmp(sp, "UTF8") == 0)
		return (useron.misc & UTF8) ? text[On] : text[Off];

	if (strcmp(sp, "MOUSE") == 0)
		return (useron.misc & MOUSE) ? text[On] : text[Off];

	if (strcmp(sp, "UPAUSE") == 0)
		return (useron.misc & UPAUSE) ? text[On] : text[Off];

	if (strcmp(sp, "SPIN") == 0)
		return (useron.misc & SPIN) ? text[On] : text[Off];

	if (strcmp(sp, "PAUSESPIN") == 0)
		return (useron.misc & NOPAUSESPIN) ? text[Off] : text[On];

	if (strcmp(sp, "EXPERT") == 0)
		return (useron.misc & EXPERT) ? text[On] : text[Off];

	if (strcmp(sp, "HOTKEYS") == 0)
		return (useron.misc & COLDKEYS) ? text[Off] : text[On];
	if (strcmp(sp, "MSGCLS") == 0)
		return (useron.misc & CLRSCRN) ? text[On] : text[Off];

	if (strcmp(sp, "FWD") == 0)
		return (useron.misc & NETMAIL) ? text[On] : text[Off];

	if (strcmp(sp, "REMSUBS") == 0)
		return (useron.misc & CURSUB) ? text[On] : text[Off];

	if (strcmp(sp, "FILEDESC") == 0)
		return (useron.misc & EXTDESC) ? text[On] : text[Off];

	if (strcmp(sp, "FILEFLAG") == 0)
		return (useron.misc & BATCHFLAG) ? text[On] : text[Off];

	if (strcmp(sp, "AUTOHANG") == 0)
		return (useron.misc & AUTOHANG) ? text[On] : text[Off];

	if (strcmp(sp, "AUTOLOGON") == 0)
		return (useron.misc & AUTOLOGON) ? text[On] : text[Off];

	if (strcmp(sp, "QUIET") == 0)
		return (useron.misc & QUIET) ? text[On] : text[Off];

	if (strcmp(sp, "ASKNSCAN") == 0)
		return (useron.misc & ASK_NSCAN) ? text[On] : text[Off];

	if (strcmp(sp, "ASKSSCAN") == 0)
		return (useron.misc & ASK_SSCAN) ? text[On] : text[Off];

	if (strcmp(sp, "ANFSCAN") == 0)
		return (useron.misc & ANFSCAN) ? text[On] : text[Off];

	if (strcmp(sp, "EDITOR") == 0)
		return (useron.xedit < 1 || useron.xedit >= cfg.total_xedits) ? text[None] : cfg.xedit[useron.xedit - 1]->name;

	if (strcmp(sp, "SHELL") == 0)
	if (strcmp(sp, "TMP") == 0)
	if (strcmp(sp, "PROT") == 0) {
		safe_snprintf(str, maxlen, "%c", useron.prot);
		return str;
	}
	if (strcmp(sp, "PROTNAME") == 0)
	if (strcmp(sp, "SEX") == 0) {
		safe_snprintf(str, maxlen, "%c", useron.sex);
		return str;
	}
	if (strcmp(sp, "GENDERS") == 0) {
		return cfg.new_genders;
	}

	if (!strcmp(sp, "QWKID"))
	if (!strcmp(sp, "TIME") || !strcmp(sp, "SYSTIME") || !strcmp(sp, "TIME_UTC")) {
		now = time(NULL);
		memset(&tm, 0, sizeof(tm));
		if (strcmp(sp, "TIME_UTC") == 0)
			gmtime_r(&now, &tm);
		else
			localtime_r(&now, &tm);
		if (cfg.sys_misc & SM_MILITARY)
			safe_snprintf(str, maxlen, "%02d:%02d:%02d"
			              , tm.tm_hour, tm.tm_min, tm.tm_sec);
			safe_snprintf(str, maxlen, "%02d:%02d %s"
			              , tm.tm_hour == 0 ? 12
			    : tm.tm_hour > 12 ? tm.tm_hour - 12
			    : tm.tm_hour, tm.tm_min, tm.tm_hour > 11 ? "pm":"am");
	if (!strcmp(sp, "TIMEZONE"))
		return smb_zonestr(sys_timezone(&cfg), str);
rswindell's avatar
rswindell committed

	if (!strcmp(sp, "DATE") || !strcmp(sp, "SYSDATE") || !strcmp(sp, "DATE_UTC")) {
		now = time(NULL);
		if (strcmp(sp, "DATE_UTC") == 0)
			now -= xpTimeZone_local() * 60;
		return datestr(now);
	if (strncmp(sp, "DATE:", 5) == 0 || strncmp(sp, "TIME:", 5) == 0) {
		now = time(NULL);
		memset(&tm, 0, sizeof(tm));
		localtime_r(&now, &tm);
	if (strncmp(sp, "UTC:", 4) == 0) {
		SAFECOPY(tmp, sp + 4);
		c_unescape_str(tmp);
		now = time(NULL);
		memset(&tm, 0, sizeof(tm));
		gmtime_r(&now, &tm);
		strftime(str, maxlen, tmp, &tm);
		return str;
	}

	if (!strcmp(sp, "DATETIME"))
		return timestr(time(NULL));
rswindell's avatar
rswindell committed

	if (!strcmp(sp, "DATETIME_UTC"))
		return timestr(time(NULL) - (xpTimeZone_local() * 60));

	if (!strcmp(sp, "DATETIMEZONE")) {
rswindell's avatar
rswindell committed
		char zone[32];
		safe_snprintf(str, maxlen, "%s %s", timestr(time(NULL)), smb_zonestr(sys_timezone(&cfg), zone));
rswindell's avatar
rswindell committed
		return str;
	}

	if (strcmp(sp, "DATEFMT") == 0) {
		return date_format(&cfg, str, maxlen);
rswindell's avatar
rswindell committed

	if (strcmp(sp, "BDATEFMT") == 0 || strcmp(sp, "BIRTHFMT") == 0) {
		return birthdate_format(&cfg, str, maxlen);
	if (strcmp(sp, "GENDERS") == 0)
	if (!strcmp(sp, "TMSG")) {
		l = 0;
		for (i = 0; i < cfg.total_subs; i++)
			l += getposts(&cfg, i);     /* l=total posts */
		safe_snprintf(str, maxlen, "%lu", l);
	if (!strcmp(sp, "TUSER")) {
		safe_snprintf(str, maxlen, "%u", total_users(&cfg));
	if (!strcmp(sp, "TFILE")) {
		l = 0;
		for (i = 0; i < cfg.total_dirs; i++)
			l += getfiles(&cfg, i);
		safe_snprintf(str, maxlen, "%lu", l);
	if (strncmp(sp, "FILES:", 6) == 0) { // Number of files in specified directory
		const char* path = getpath(&cfg, sp + 6);
		safe_snprintf(str, maxlen, "%u", getfilecount(path));
		return str;
	if (strcmp(sp, "FILES") == 0) {  // Number of files in current directory
		safe_snprintf(str, maxlen, "%u", getfiles(&cfg, usrdir[curlib][curdir[curlib]]));
	if (strncmp(sp, "FILESIZE:", 9) == 0) {
		const char* path = getpath(&cfg, sp + 9);
		byte_estimate_to_str(getfilesizetotal(path), str, maxlen, /* unit: */ 1, /* precision: */ 1);
	if (strcmp(sp, "FILESIZE") == 0) {
		byte_estimate_to_str(getfilesizetotal(cfg.dir[usrdir[curlib][curdir[curlib]]]->path)
		                     , str, maxlen, /* unit: */ 1, /* precision: */ 1);
	if (strncmp(sp, "FILEBYTES:", 10) == 0) {    // Number of bytes in current file directory
		const char* path = getpath(&cfg, sp + 10);
		safe_snprintf(str, maxlen, "%" PRIu64, getfilesizetotal(path));
		return str;
	}

	if (strcmp(sp, "FILEBYTES") == 0) {  // Number of bytes in current file directory
		safe_snprintf(str, maxlen, "%" PRIu64
		              , getfilesizetotal(cfg.dir[usrdir[curlib][curdir[curlib]]]->path));
	if (strncmp(sp, "FILEKB:", 7) == 0) {    // Number of kibibytes in current file directory
		const char* path = getpath(&cfg, sp + 7);
		safe_snprintf(str, maxlen, "%1.1f", getfilesizetotal(path) / 1024.0);
		return str;
	}

	if (strcmp(sp, "FILEKB") == 0) { // Number of kibibytes in current file directory
		safe_snprintf(str, maxlen, "%1.1f"
		              , getfilesizetotal(cfg.dir[usrdir[curlib][curdir[curlib]]]->path) / 1024.0);
	if (strncmp(sp, "FILEMB:", 7) == 0) {    // Number of mebibytes in current file directory
		const char* path = getpath(&cfg, sp + 7);
		safe_snprintf(str, maxlen, "%1.1f", getfilesizetotal(path) / (1024.0 * 1024.0));
		return str;
	}

	if (strcmp(sp, "FILEMB") == 0) { // Number of mebibytes in current file directory
		safe_snprintf(str, maxlen, "%1.1f"
		              , getfilesizetotal(cfg.dir[usrdir[curlib][curdir[curlib]]]->path) / (1024.0 * 1024.0));
	if (strncmp(sp, "FILEGB:", 7) == 0) {    // Number of gibibytes in current file directory
		const char* path = getpath(&cfg, sp + 7);
		safe_snprintf(str, maxlen, "%1.1f", getfilesizetotal(path) / (1024.0 * 1024.0 * 1024.0));
		return str;
	}

	if (strcmp(sp, "FILEGB") == 0) { // Number of gibibytes in current file directory
		safe_snprintf(str, maxlen, "%1.1f"
		              , getfilesizetotal(cfg.dir[usrdir[curlib][curdir[curlib]]]->path) / (1024.0 * 1024.0 * 1024.0));
	if (!strcmp(sp, "TCALLS") || !strcmp(sp, "NUMCALLS")) {
		getstats(&cfg, 0, &stats);
		safe_snprintf(str, maxlen, "%u", stats.logons);
	if (!strcmp(sp, "PREVON") || !strcmp(sp, "LASTCALLERNODE")
	    || !strcmp(sp, "LASTCALLERSYSTEM"))
	if (!strcmp(sp, "CLS") || !strcmp(sp, "CLEAR")) {
	if (strcmp(sp, "GETDIM") == 0) {
	if (strcmp(sp, "GETKEY") == 0) {
	if (strcmp(sp, "CONTINUE") == 0) {
Rob Swindell's avatar
Rob Swindell committed
		char ch = getkey(K_UPPER);
		if (ch == no_key() || ch == quit_key())
			sys_status |= SS_ABORT;
	if (strncmp(sp, "WAIT:", 5) == 0) {
Rob Swindell's avatar
Rob Swindell committed
		inkey(K_NONE, atoi(sp + 5) * 100);
	if (!strcmp(sp, "PAUSE") || !strcmp(sp, "MORE")) {
	if (!strcmp(sp, "RESETPAUSE")) {
		lncntr = 0;
	if (!strcmp(sp, "NOPAUSE") || !strcmp(sp, "POFF")) {
		sys_status ^= SS_PAUSEOFF;
	if (!strcmp(sp, "PON") || !strcmp(sp, "AUTOMORE")) {
		sys_status ^= SS_PAUSEON;
	if (strncmp(sp, "FILL:", 5) == 0) {
		int margin = centered ? column : 1;
		if (margin < 1)
			margin = 1;
		while (*tmp && online && column < cols - margin)
rswindell's avatar
rswindell committed
		return nulstr;
	}

	if (strncmp(sp, "POS:", 4) == 0) {   // PCBoard	(nn is 1 based)
		if (i >= 1)  // Convert to 0-based
		for (l = i - column; l > 0; l--)
			outchar(' ');
		return nulstr;
	}

	if (strncmp(sp, "DELAY:", 6) == 0) { // PCBoard
		mswait(atoi(sp + 6) * 100);
		return nulstr;
	}

	if (strcmp(sp, "YESCHAR") == 0) {    // PCBoard
		safe_snprintf(str, maxlen, "%c", yes_key());
	if (strcmp(sp, "NOCHAR") == 0) {     // PCBoard
		safe_snprintf(str, maxlen, "%c", no_key());
	if (strcmp(sp, "QUITCHAR") == 0) {
		safe_snprintf(str, maxlen, "%c", quit_key());