atcodes.cpp 46.8 KB
Newer Older
1
/* Synchronet "@code" functions */
2
// vi: tabstop=4
3
4
5
6
7
8
9

/* $Id$ */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
10
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
11
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
37
38
 *																			*
 * 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										*
 *																			*
 * 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 src		*
 *																			*
 * 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.	*
 ****************************************************************************/

#include "sbbs.h"
#include "cmdshell.h"
39
40
#include "utf8.h"
#include "unicode.h"
41
#include "cp437defs.h"
42

43
44
45
46
47
48
49
#if defined(_WINSOCKAPI_)
	extern WSADATA WSAData;
	#define SOCKLIB_DESC WSAData.szDescription
#else
	#define	SOCKLIB_DESC NULL
#endif

50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
static char* separate_thousands(const char* src, char *dest, size_t maxlen, char sep)
{
	if(strlen(src) * 1.3 > maxlen)
		return (char*)src;
	const char* tail = src;
	while(*tail && isdigit(*tail))
		tail++;
	if(tail == src)
		return (char*)src;
	size_t digits = tail - src;
	char* d = dest;
	for(size_t i = 0; i < digits; d++, i++) {
		*d = src[i];
		if(i && i + 3 < digits && (digits - (i + 1)) % 3 == 0)
			*(++d) = sep;
	}
	*d = 0;
	strcpy(d, tail);
	return dest;
}

71
72
73
/****************************************************************************/
/* Returns 0 if invalid @ code. Returns length of @ code if valid.          */
/****************************************************************************/
74
int sbbs_t::show_atcode(const char *instr, JSObject* obj)
75
{
76
	char	str[128],str2[128],*tp,*sp,*p;
77
    int     len;
78
	int		disp_len;
79
80
81
82
83
84
	enum {
		none,
		left,
		right,
		center
	} align = none;
85
	bool	zero_padded=false;
rswindell's avatar
rswindell committed
86
	bool	truncated = true;
rswindell's avatar
rswindell committed
87
	bool	doubled = false;
88
	bool	thousep = false;	// thousands-separated
89
	bool	uppercase = false;
90
	bool	width_specified = false;
91
	long	pmode = 0;
92
	const char *cp;
93

94
95
	if(*instr != '@')
		return 0;
96
	SAFECOPY(str,instr);
97
98
99
	tp=strchr(str+1,'@');
	if(!tp)                 /* no terminating @ */
		return(0);
100
	sp=strchr(str+1,' ');
101
102
103
104
105
106
	if(sp && sp<tp)         /* space before terminating @ */
		return(0);
	len=(tp-str)+1;
	(*tp)=0;
	sp=(str+1);

107
	disp_len=len;
108
109
110
	if((p = strchr(sp, '|')) != NULL) {
		if(strchr(p, 'T') != NULL)
			thousep = true;
111
112
		if(strchr(p, 'U') != NULL)
			uppercase = true;
113
		if(strchr(p, 'L') != NULL)
114
			align = left;
115
		else if(strchr(p, 'R') != NULL)
116
			align = right;
117
		else if(strchr(p, 'C') != NULL)
118
			align = center;
119
120
121
122
123
124
125
126
		else if(strchr(p, 'W') != NULL)
			doubled = true;
		else if(strchr(p, 'Z') != NULL)
			zero_padded = true;
		else if(strchr(p, '>') != NULL)
			truncated = false;
	}
	else if(strchr(sp, ':') != NULL)
127
128
		p = NULL;
	else if((p=strstr(sp,"-L"))!=NULL)
129
		align = left;
130
	else if((p=strstr(sp,"-R"))!=NULL)
131
		align = right;
132
	else if((p=strstr(sp,"-C"))!=NULL)
133
		align = center;
rswindell's avatar
rswindell committed
134
	else if((p=strstr(sp,"-W"))!=NULL)	/* wide */
rswindell's avatar
rswindell committed
135
		doubled=true;
136
137
	else if((p=strstr(sp,"-Z"))!=NULL)
		zero_padded=true;
138
139
	else if((p=strstr(sp,"-T"))!=NULL)
		thousep=true;
140
141
	else if((p=strstr(sp,"-U"))!=NULL)
		uppercase=true;
rswindell's avatar
rswindell committed
142
	else if((p=strstr(sp,"->"))!=NULL)	/* wrap */
rswindell's avatar
rswindell committed
143
		truncated = false;
144
	if(p!=NULL) {
145
		char* lp = p;
146
147
148
149
150
		lp++;	// skip the '|' or '-'
		while(*lp == '>'|| isalpha((uchar)*lp))
			lp++;
		if(*lp)
			width_specified = true;
151
		while(*lp && !isdigit((uchar)*lp))
152
			lp++;
153
		if(*lp && isdigit((uchar)*lp)) {
rswindell's avatar
rswindell committed
154
			disp_len=atoi(lp);
155
156
			width_specified = true;
		}
157
		*p=0;
158
	}
159

160
	cp = atcode(sp, str2, sizeof(str2), &pmode, align == center, obj);
161
	if(cp==NULL)
162
163
		return(0);

164
165
166
167
	char separated[128];
	if(thousep)
		cp = separate_thousands(cp, separated, sizeof(separated), ',');

168
169
170
171
172
173
174
	if(uppercase) {
		char upper[128];
		SAFECOPY(upper, cp);
		strupr(upper);
		cp = upper;
	}

175
	if(p==NULL || truncated == false || (width_specified == false && align == none))
rswindell's avatar
rswindell committed
176
177
		disp_len = strlen(cp);

178
179
180
	if(uppercase && align == none)
		align = left;

rswindell's avatar
rswindell committed
181
182
183
184
185
186
187
188
	if(truncated) {
		if(column + disp_len > cols - 1) {
			if(column >= cols - 1)
				disp_len = 0;
			else
				disp_len = (cols - 1) - column;
		}
	}
189
190
191
192
193
194
	if(pmode & P_UTF8) {
		if(term_supports(UTF8))
			disp_len += strlen(cp) - utf8_str_total_width(cp);
		else
			disp_len += strlen(cp) - utf8_str_count_width(cp, /* min: */1, /* max: */2);
	}
195
	if(align == left)
196
		bprintf(pmode, "%-*.*s",disp_len,disp_len,cp);
197
	else if(align == right)
198
		bprintf(pmode, "%*.*s",disp_len,disp_len,cp);
199
	else if(align == center) {
rswindell's avatar
rswindell committed
200
		int vlen = strlen(cp);
201
202
		if(vlen < disp_len) {
			int left = (disp_len - vlen) / 2;
203
			bprintf(pmode, "%*s%-*s", left, "", disp_len - left, cp);
204
		} else
205
			bprintf(pmode, "%.*s", disp_len, cp);
rswindell's avatar
rswindell committed
206
207
	} else if(doubled) {
		wide(cp);
208
	} else if(zero_padded) {
209
210
		int vlen = strlen(cp);
		if(vlen < disp_len)
211
			bprintf(pmode, "%-.*s%s", (int)(disp_len - strlen(cp)), "0000000000", cp);
212
		else
213
			bprintf(pmode, "%.*s", disp_len, cp);
214
	} else
215
		bprintf(pmode, "%.*s", disp_len, cp);
216
217
218
219

	return(len);
}

rswindell's avatar
rswindell committed
220
221
222
223
224
225
226
227
228
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;
}

229
const char* sbbs_t::atcode(char* sp, char* str, size_t maxlen, long* pmode, bool centered, JSObject* obj)
230
{
231
	char*	tp = NULL;
232
	uint	i;
233
234
	uint	ugrp;
	uint	usub;
235
236
237
238
239
240
241
	long	l;
    stats_t stats;
    node_t  node;
	struct	tm tm;

	str[0]=0;

rswindell's avatar
rswindell committed
242
243
244
245
246
247
248
249
250
	if(*sp == '~' && *(sp + 1)) {
		sp++;
		tp = strchr(sp + 1, '~');
		if(tp == NULL)
			tp = sp;
		else {
			*tp = 0;
			tp++;
		}
rswindell's avatar
rswindell committed
251
		c_unescape_str(tp);
rswindell's avatar
rswindell committed
252
253
254
255
		add_hotspot(tp, column, column + strlen(sp) - 1, row);
		return sp;
	}

256
257
258
259
260
	if(strncmp(sp, "HOT:", 4) == 0) {	// Auto-mouse hot-spot attribute
		hot_attr = attrstr(sp + 4);
		return nulstr;
	}

261
262
	if(strncmp(sp, "U+", 2) == 0) {	// UNICODE
		enum unicode_codepoint codepoint = (enum unicode_codepoint)strtoul(sp + 2, &tp, 16);
rswindell's avatar
rswindell committed
263
		if(tp == NULL || *tp == 0)
264
			outchar(codepoint, unicode_to_cp437(codepoint));
rswindell's avatar
rswindell committed
265
266
		else if(*tp == ':')
			outchar(codepoint, tp + 1);
267
268
		else {
			char fallback = (char)strtoul(tp + 1, NULL, 16);
rswindell's avatar
rswindell committed
269
			if(*tp == ',')
270
271
272
273
274
275
276
277
278
279
280
281
				outchar(codepoint, fallback);
			else if(*tp == '!') {
				char ch = unicode_to_cp437(codepoint);
				if(ch != 0)
					fallback = ch;
				outchar(codepoint, fallback);
			}
			else return NULL; // Invalid @-code
		}
		return nulstr;
	}

282
283
284
285
286
	if(strcmp(sp, "CHECKMARK") == 0) {
		outchar(UNICODE_CHECK_MARK, CP437_CHECK_MARK);
		return nulstr;
	}

rswindell's avatar
rswindell committed
287
288
289
290
	if(strcmp(sp, "ELLIPSIS") == 0) {
		outchar(UNICODE_HORIZONTAL_ELLIPSIS, "...");
		return nulstr;
	}
291
	if(strcmp(sp, "COPY") == 0) {
rswindell's avatar
rswindell committed
292
293
294
295
296
297
298
		outchar(UNICODE_COPYRIGHT_SIGN, "(C)");
		return nulstr;
	}
	if(strcmp(sp, "SOUNDCOPY") == 0) {
		outchar(UNICODE_SOUND_RECORDING_COPYRIGHT, "(P)");
		return nulstr;
	}
rswindell's avatar
rswindell committed
299
300
301
302
	if(strcmp(sp, "REGISTERED") == 0) {
		outchar(UNICODE_REGISTERED_SIGN, "(R)");
		return nulstr;
	}
rswindell's avatar
rswindell committed
303
304
305
306
307
	if(strcmp(sp, "TRADEMARK") == 0) {
		outchar(UNICODE_TRADE_MARK_SIGN, "(TM)");
		return nulstr;
	}
	if(strcmp(sp, "DEGREE_C") == 0) {
rswindell's avatar
rswindell committed
308
		outchar(UNICODE_DEGREE_CELSIUS, "\xF8""C");
rswindell's avatar
rswindell committed
309
310
311
		return nulstr;
	}
	if(strcmp(sp, "DEGREE_F") == 0) {
rswindell's avatar
rswindell committed
312
		outchar(UNICODE_DEGREE_FAHRENHEIT, "\xF8""F");
rswindell's avatar
rswindell committed
313
314
315
		return nulstr;
	}

rswindell's avatar
rswindell committed
316
317
318
319
320
	if(strncmp(sp, "WIDE:", 5) == 0) {
		wide(sp + 5);
		return(nulstr);
	}

321
	if(!strcmp(sp,"VER"))
322
		return(VERSION);
323

324
	if(!strcmp(sp,"REV")) {
325
		safe_snprintf(str,maxlen,"%c",REVISION);
326
327
		return(str);
	}
328

329
	if(!strcmp(sp,"FULL_VER")) {
330
		safe_snprintf(str,maxlen,"%s%c%s",VERSION,REVISION,beta_version);
331
		truncsp(str);
332
#if defined(_DEBUG)
333
		strcat(str," Debug");
334
#endif
335
		return(str);
336
337
	}

338
	if(!strcmp(sp,"VER_NOTICE"))
339
		return(VERSION_NOTICE);
340

341
342
	if(!strcmp(sp,"OS_VER"))
		return(os_version(str));
343
344

#ifdef JAVASCRIPT
345
346
	if(!strcmp(sp,"JS_VER"))
		return((char *)JS_GetImplementationVersion());
347
348
#endif

349
350
	if(!strcmp(sp,"PLATFORM"))
		return(PLATFORM_DESC);
351

352
353
	if(!strcmp(sp,"COPYRIGHT"))
		return(COPYRIGHT_NOTICE);
354

355
	if(!strcmp(sp,"COMPILER")) {
356
357
358
		char compiler[32];
		DESCRIBE_COMPILER(compiler);
		strncpy(str, compiler, maxlen);
359
		return(str);
360
361
	}

362
	if(!strcmp(sp,"UPTIME")) {
363
		extern volatile time_t uptime;
364
365
366
367
		time_t up=0;
		now = time(NULL);
		if (uptime != 0 && now >= uptime)
			up = now-uptime;
368
369
		char   days[64]="";
		if((up/(24*60*60))>=2) {
370
	        sprintf(days,"%lu days ",(ulong)(up/(24L*60L*60L)));
371
372
			up%=(24*60*60);
		}
373
		safe_snprintf(str,maxlen,"%s%lu:%02lu"
374
	        ,days
375
376
			,(ulong)(up/(60L*60L))
			,(ulong)((up/60L)%60L)
377
			);
378
		return(str);
379
380
	}

381
	if(!strcmp(sp,"SERVED")) {
382
		extern volatile ulong served;
383
		safe_snprintf(str,maxlen,"%lu",served);
384
385
386
		return(str);
	}

387
	if(!strcmp(sp,"SOCKET_LIB"))
388
		return(socklib_version(str,SOCKLIB_DESC));
389

390
	if(!strcmp(sp,"MSG_LIB")) {
391
		safe_snprintf(str,maxlen,"SMBLIB %s",smb_lib_ver());
392
393
		return(str);
	}
394

395
396
	if(!strcmp(sp,"BBS") || !strcmp(sp,"BOARDNAME"))
		return(cfg.sys_name);
397

398
	if(!strcmp(sp,"BAUD") || !strcmp(sp,"BPS")) {
399
		safe_snprintf(str,maxlen,"%lu",cur_output_rate ? cur_output_rate : cur_rate);
400
401
		return(str);
	}
402

rswindell's avatar
rswindell committed
403
404
405
406
407
408
409
410
	if(!strcmp(sp,"COLS")) {
		safe_snprintf(str,maxlen,"%lu",cols);
		return(str);
	}
	if(!strcmp(sp,"ROWS")) {
		safe_snprintf(str,maxlen,"%lu",rows);
		return(str);
	}
411
412
413
414
415
	if(strcmp(sp,"TERM") == 0)
		return term_type();

	if(strcmp(sp,"CHARSET") == 0)
		return term_charset();
rswindell's avatar
rswindell committed
416

417
418
	if(!strcmp(sp,"CONN"))
		return(connection);
419

420
421
	if(!strcmp(sp,"SYSOP"))
		return(cfg.sys_op);
422

423
424
	if(!strcmp(sp,"LOCATION"))
		return(cfg.sys_location);
425

426
	if(strcmp(sp,"NODE") == 0 || strcmp(sp,"NN") == 0) {
427
		safe_snprintf(str,maxlen,"%u",cfg.node_num);
428
429
		return(str);
	}
430
	if(strcmp(sp, "TNODES") == 0 || strcmp(sp, "TNODE") == 0 || strcmp(sp, "TN") == 0) {
431
		safe_snprintf(str,maxlen,"%u",cfg.sys_nodes);
432
433
		return(str);
	}
434
435
436
437
438
439
440
441
	if(strcmp(sp, "ANODES") == 0 || strcmp(sp, "ANODE") == 0 || strcmp(sp, "AN") == 0) {
		safe_snprintf(str, maxlen, "%u", count_nodes(/* self: */true));
		return str;
	}
	if(strcmp(sp, "ONODES") == 0 || strcmp(sp, "ONODE") == 0 || strcmp(sp, "ON") == 0) {
		safe_snprintf(str, maxlen, "%u", count_nodes(/* self: */false));
		return str;
	}
442

rswindell's avatar
rswindell committed
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
	if(strcmp(sp, "PWDAYS") == 0) {
		if(cfg.sys_pwdays) {
			safe_snprintf(str, maxlen, "%u", cfg.sys_pwdays);
			return str;
		}
		return text[Unlimited];
	}

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

459
460
461
462
463
464
	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];

465
466
467
	if(strcmp(sp, "SPLITP") == 0)
		return (useron.chat&CHAT_SPLITP) ? text[On] : text[Off];

468
469
	if(!strcmp(sp,"INETADDR"))
		return(cfg.sys_inetaddr);
470

471
472
473
	if(!strcmp(sp,"HOSTNAME"))
		return(startup->host_name);

474
	if(!strcmp(sp,"FIDOADDR")) {
475
		if(cfg.total_faddrs)
476
			return(smb_faddrtoa(&cfg.faddr[0],str));
477
		return(nulstr);
478
479
	}

480
	if(!strcmp(sp,"EMAILADDR"))
481
		return(usermailaddr(&cfg, str
482
			,cfg.inetmail_misc&NMAIL_ALIAS ? useron.alias : useron.name));
483

484
485
	if(!strcmp(sp,"QWKID"))
		return(cfg.sys_id);
486

487
	if(!strcmp(sp,"TIME") || !strcmp(sp,"SYSTIME")) {
488
		now=time(NULL);
489
		memset(&tm,0,sizeof(tm));
490
		localtime_r(&now,&tm);
491
		if(cfg.sys_misc&SM_MILITARY)
492
493
			safe_snprintf(str,maxlen,"%02d:%02d:%02d"
		        	,tm.tm_hour,tm.tm_min,tm.tm_sec);
494
		else
495
			safe_snprintf(str,maxlen,"%02d:%02d %s"
496
497
498
				,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");
499
		return(str);
500
501
	}

rswindell's avatar
rswindell committed
502
503
504
	if(!strcmp(sp,"TIMEZONE"))
		return(smb_zonestr(sys_timezone(&cfg),str));

505
	if(!strcmp(sp,"DATE") || !strcmp(sp,"SYSDATE")) {
506
		return(unixtodstr(&cfg,time32(NULL),str));
507
	}
508

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

rswindell's avatar
rswindell committed
512
513
514
515
516
	if(!strcmp(sp,"DATETIMEZONE")) {
		char zone[32];
		safe_snprintf(str, maxlen, "%s %s", timestr(time(NULL)), smb_zonestr(sys_timezone(&cfg),zone));
		return str;
	}
517
518
519
520
	
	if(strcmp(sp, "DATEFMT") == 0) {
		return cfg.sys_misc&SM_EURODATE ? "DD/MM/YY" : "MM/DD/YY";
	}
rswindell's avatar
rswindell committed
521

522
	if(!strcmp(sp,"TMSG")) {
523
524
		l=0;
		for(i=0;i<cfg.total_subs;i++)
525
			l+=getposts(&cfg,i); 		/* l=total posts */
526
		safe_snprintf(str,maxlen,"%lu",l);
527
528
		return(str);
	}
529

530
	if(!strcmp(sp,"TUSER")) {
531
		safe_snprintf(str,maxlen,"%u",total_users(&cfg));
532
533
		return(str);
	}
534

535
	if(!strcmp(sp,"TFILE")) {
536
537
		l=0;
		for(i=0;i<cfg.total_dirs;i++)
538
			l+=getfiles(&cfg,i);
539
		safe_snprintf(str,maxlen,"%lu",l);
540
541
		return(str);
	}
542

543
	if(strncmp(sp, "FILES:", 6) == 0) {	// Number of files in specified directory
rswindell's avatar
rswindell committed
544
		const char* path = getpath(&cfg, sp + 6);
545
		safe_snprintf(str, maxlen, "%lu", getfilecount(path));
rswindell's avatar
rswindell committed
546
		return str;
547
548
549
550
551
552
553
	}

	if(strcmp(sp, "FILES") == 0) {	// Number of files in current directory
		safe_snprintf(str, maxlen, "%lu", (ulong)getfiles(&cfg, usrdir[curlib][curdir[curlib]]));
		return str;
	}

rswindell's avatar
rswindell committed
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
	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);
		return str;
	}

	if(strcmp(sp, "FILESIZE") == 0) {
		byte_estimate_to_str(getfilesizetotal(cfg.dir[usrdir[curlib][curdir[curlib]]]->path)
			,str, maxlen, /* unit: */1, /* precision: */1);
		return str;
	}

	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));
		return str;
	}

	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);
		return str;
	}

	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));
		return str;
	}

	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));
		return str;
	}

614
	if(!strcmp(sp,"TCALLS") || !strcmp(sp,"NUMCALLS")) {
615
		getstats(&cfg,0,&stats);
616
		safe_snprintf(str,maxlen,"%lu", (ulong)stats.logons);
617
618
		return(str);
	}
619

620
	if(!strcmp(sp,"PREVON") || !strcmp(sp,"LASTCALLERNODE")
621
		|| !strcmp(sp,"LASTCALLERSYSTEM"))
622
		return(lastuseron);
623

rswindell's avatar
rswindell committed
624
	if(!strcmp(sp,"CLS") || !strcmp(sp,"CLEAR")) {
625
		CLS;
626
627
		return(nulstr);
	}
628

629
	if(!strcmp(sp,"PAUSE") || !strcmp(sp,"MORE")) {
630
		pause();
631
632
		return(nulstr);
	}
633

634
	if(!strcmp(sp,"RESETPAUSE")) {
635
		lncntr=0;
636
637
		return(nulstr);
	}
638

639
	if(!strcmp(sp,"NOPAUSE") || !strcmp(sp,"POFF")) {
640
		sys_status^=SS_PAUSEOFF;
641
642
		return(nulstr);
	}
643

644
	if(!strcmp(sp,"PON") || !strcmp(sp,"AUTOMORE")) {
645
		sys_status^=SS_PAUSEON;
646
647
		return(nulstr);
	}
648

rswindell's avatar
rswindell committed
649
650
	if(strncmp(sp, "FILL:", 5) == 0) {
		sp += 5;
651
652
653
		long margin = centered ? column : 1;
		if(margin < 1) margin = 1;
		while(*sp && online && column < cols - margin)
rswindell's avatar
rswindell committed
654
655
656
657
			bputs(sp, P_TRUNCATE);
		return nulstr;
	}

658
659
660
661
662
	if(strncmp(sp, "POS:", 4) == 0) {	// PCBoard	(nn is 1 based)
		i = atoi(sp + 4);
		if(i >= 1)	// Convert to 0-based
			i--;
		for(l = i - column; l > 0; l--)
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
			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", text[YNQP][0]);
		return str;
	}

	if(strcmp(sp, "NOCHAR") == 0) {		// PCBoard
		safe_snprintf(str, maxlen, "%c", text[YNQP][1]);
		return str;
	}

	if(strcmp(sp, "QUITCHAR") == 0) {
		safe_snprintf(str, maxlen, "%c", text[YNQP][2]);
		return str;
	}

687
688
689
690
691
	if(strncmp(sp, "BPS:", 4) == 0) {
		set_output_rate((enum output_rate)atoi(sp + 4));
		return nulstr;
	}

692
693
694
695
	/* NOSTOP */

	/* STOP */

696
697
	if(!strcmp(sp,"BELL") || !strcmp(sp,"BEEP"))
		return("\a");
698

699
700
701
	if(!strcmp(sp,"EVENT")) {
		if(event_time==0)
			return("<none>");
702
		return(timestr(event_time));
703
	}
704
705
706

	/* LASTCALL */

707
	if(!strncmp(sp,"NODE",4)) {
708
709
710
		i=atoi(sp+4);
		if(i && i<=cfg.sys_nodes) {
			getnodedat(i,&node,0);
711
712
			printnodedat(i,&node);
		}
713
714
		return(nulstr);
	}
715

716
	if(!strcmp(sp,"WHO")) {
717
		whos_online(true);
718
719
		return(nulstr);
	}
720
721
722

	/* User Codes */

723
724
	if(!strcmp(sp,"USER") || !strcmp(sp,"ALIAS") || !strcmp(sp,"NAME"))
		return(useron.alias);
725

726
	if(!strcmp(sp,"FIRST")) {
727
		safe_snprintf(str,maxlen,"%s",useron.alias);
728
		tp=strchr(str,' ');
729
		if(tp) *tp=0;
730
		return(str);
731
	}
732

733
	if(!strcmp(sp,"USERNUM")) {
734
		safe_snprintf(str,maxlen,"%u",useron.number);
735
736
		return(str);
	}
737

738
	if(!strcmp(sp,"PHONE") || !strcmp(sp,"HOMEPHONE")
739
		|| !strcmp(sp,"DATAPHONE") || !strcmp(sp,"DATA"))
740
		return(useron.phone);
741

742
743
	if(!strcmp(sp,"ADDR1"))
		return(useron.address);
744

745
746
	if(!strcmp(sp,"FROM"))
		return(useron.location);
747

748
	if(!strcmp(sp,"CITY")) {
749
		safe_snprintf(str,maxlen,"%s",useron.location);
750
		char* p=strchr(str,',');
751
752
		if(p) {
			*p=0;
753
754
			return(str);
		}
755
756
		return(nulstr);
	}
757

758
759
	if(!strcmp(sp,"STATE")) {
		char* p=strchr(useron.location,',');
760
761
		if(p) {
			p++;
762
			if(*p==' ')
763
				p++;
764
765
			return(p);
		}
766
767
		return(nulstr);
	}
768

769
770
	if(!strcmp(sp,"CPU"))
		return(useron.comp);
771

772
773
	if(!strcmp(sp,"HOST"))
		return(client_name);
774

775
776
	if(!strcmp(sp,"BDATE"))
		return(useron.birth);
777

778
	if(!strcmp(sp,"AGE")) {
779
		safe_snprintf(str,maxlen,"%u",getage(&cfg,useron.birth));
780
781
		return(str);
	}
rswindell's avatar
rswindell committed
782

783
	if(!strcmp(sp,"CALLS") || !strcmp(sp,"NUMTIMESON")) {
784
		safe_snprintf(str,maxlen,"%u",useron.logons);
785
786
		return(str);
	}
787

rswindell's avatar
rswindell committed
788
789
790
791
792
793
794
	if(strcmp(sp, "PWAGE") == 0) {
		time_t age = time(NULL) - useron.pwmod;
		safe_snprintf(str, maxlen, "%ld", (long)(age/(24*60*60)));
		return str;
	}

	if(strcmp(sp, "PWDATE") == 0 || strcmp(sp, "MEMO") == 0)
795
		return(unixtodstr(&cfg,useron.pwmod,str));
796

797
	if(!strcmp(sp,"SEC") || !strcmp(sp,"SECURITY")) {
798
		safe_snprintf(str,maxlen,"%u",useron.level);
799
800
		return(str);
	}
801

802
803
	if(!strcmp(sp,"SINCE"))
		return(unixtodstr(&cfg,useron.firston,str));
804

805
	if(!strcmp(sp,"TIMEON") || !strcmp(sp,"TIMEUSED")) {
806
		now=time(NULL);
807
		safe_snprintf(str,maxlen,"%lu",(ulong)(now-logontime)/60L);
808
809
		return(str);
	}
810

811
	if(!strcmp(sp,"TUSED")) {              /* Synchronet only */
812
		now=time(NULL);
813
		return(sectostr((uint)(now-logontime),str)+1);
814
	}
815

816
	if(!strcmp(sp,"TLEFT")) {              /* Synchronet only */
817
		gettimeleft();
818
		return(sectostr(timeleft,str)+1);
819
	}
820

821
	if(!strcmp(sp,"TPERD"))                /* Synchronet only */
rswindell's avatar
Add:    
rswindell committed
822
		return(sectostr(cfg.level_timeperday[useron.level],str)+4);
823

824
	if(!strcmp(sp,"TPERC"))                /* Synchronet only */
rswindell's avatar
Add:    
rswindell committed
825
		return(sectostr(cfg.level_timepercall[useron.level],str)+4);
826

rswindell's avatar
Add:    
rswindell committed
827
	if(strcmp(sp, "MPERC") == 0 || strcmp(sp, "TIMELIMIT") == 0) {
828
		safe_snprintf(str,maxlen,"%u",cfg.level_timepercall[useron.level]);
829
830
		return(str);
	}
831

rswindell's avatar
Add:    
rswindell committed
832
833
834
835
836
	if(strcmp(sp, "MPERD") == 0) {
		safe_snprintf(str,maxlen,"%u",cfg.level_timeperday[useron.level]);
		return str;
	}

rswindell's avatar
Add:    
rswindell committed
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
	if(strcmp(sp, "MAXCALLS") == 0) {
		safe_snprintf(str,maxlen,"%u",cfg.level_callsperday[useron.level]);
		return str;
	}

	if(strcmp(sp, "MAXPOSTS") == 0) {
		safe_snprintf(str,maxlen,"%u",cfg.level_postsperday[useron.level]);
		return str;
	}

	if(strcmp(sp, "MAXMAILS") == 0) {
		safe_snprintf(str,maxlen,"%u",cfg.level_emailperday[useron.level]);
		return str;
	}

	if(strcmp(sp, "MAXLINES") == 0) {
		safe_snprintf(str,maxlen,"%u",cfg.level_linespermsg[useron.level]);
		return str;
	}

857
	if(!strcmp(sp,"MINLEFT") || !strcmp(sp,"LEFT") || !strcmp(sp,"TIMELEFT")) {
858
		gettimeleft();
859
		safe_snprintf(str,maxlen,"%lu",timeleft/60);
860
861
		return(str);
	}
862

863
	if(!strcmp(sp,"LASTON"))
864
		return(timestr(useron.laston));
865

866
867
	if(!strcmp(sp,"LASTDATEON"))
		return(unixtodstr(&cfg,useron.laston,str));
868

869
	if(!strcmp(sp,"LASTTIMEON")) {
870
		memset(&tm,0,sizeof(tm));
871
		localtime32(&useron.laston,&tm);
872
873
874
875
876
877
878
879
		if(cfg.sys_misc&SM_MILITARY)
			safe_snprintf(str,maxlen,"%02d:%02d:%02d"
				,tm.tm_hour, tm.tm_min, tm.tm_sec);
		else
			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");
880
		return(str);
881
882
	}

rswindell's avatar
rswindell committed
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
	if(!strcmp(sp,"FIRSTON"))
		return(timestr(useron.firston));

	if(!strcmp(sp,"FIRSTDATEON"))
		return(unixtodstr(&cfg,useron.firston,str));

	if(!strcmp(sp,"FIRSTTIMEON")) {
		memset(&tm,0,sizeof(tm));
		localtime32(&useron.firston,&tm);
		if(cfg.sys_misc&SM_MILITARY)
			safe_snprintf(str,maxlen,"%02d:%02d:%02d"
				,tm.tm_hour, tm.tm_min, tm.tm_sec);
		else
			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");
		return(str);
	}

903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
	if(strcmp(sp, "EMAILS") == 0) {
		safe_snprintf(str, maxlen, "%u", useron.emails);
		return str;
	}

	if(strcmp(sp, "FBACKS") == 0) {
		safe_snprintf(str, maxlen, "%u", useron.fbacks);
		return str;
	}

	if(strcmp(sp, "ETODAY") == 0) {
		safe_snprintf(str, maxlen, "%u", useron.etoday);
		return str;
	}

	if(strcmp(sp, "PTODAY") == 0) {
		safe_snprintf(str, maxlen, "%u", useron.ptoday);
		return str;
	}

	if(strcmp(sp, "LTODAY") == 0) {
		safe_snprintf(str, maxlen, "%u", useron.ltoday);
		return str;
	}

rswindell's avatar
Add:    
rswindell committed
928
	if(strcmp(sp, "MTODAY") == 0) {
929
930
931
932
		safe_snprintf(str, maxlen, "%u", useron.ttoday);
		return str;
	}

rswindell's avatar
Add:    
rswindell committed
933
934
935
936
937
938
	if(strcmp(sp, "MTOTAL") == 0) {
		safe_snprintf(str, maxlen, "%u", useron.timeon);
		return str;
	}

	if(strcmp(sp, "TTODAY") == 0)
rswindell's avatar
Add:    
rswindell committed
939
		return sectostr(useron.ttoday, str) + 3;
rswindell's avatar
Add:    
rswindell committed
940
941

	if(strcmp(sp, "TTOTAL") == 0)
rswindell's avatar
Add:    
rswindell committed
942
		return sectostr(useron.timeon, str) + 3;
rswindell's avatar
Add:    
rswindell committed
943

944
945
946
947
948
	if(strcmp(sp, "TLAST") == 0) {
		safe_snprintf(str, maxlen, "%u", useron.tlast);
		return str;
	}

rswindell's avatar
Add:    
rswindell committed
949
	if(strcmp(sp, "MEXTRA") == 0) {
950
951
952
		safe_snprintf(str, maxlen, "%u", useron.textra);
		return str;
	}
rswindell's avatar
rswindell committed
953

rswindell's avatar
Add:    
rswindell committed
954
955
956
957
	if(strcmp(sp, "TEXTRA") == 0)
		return sectostr(useron.textra, str) + 3;

	if(strcmp(sp, "MBANKED") == 0) {
958
		safe_snprintf(str, maxlen, "%lu", useron.min);
rswindell's avatar
Add:    
rswindell committed
959
960
961
962
963
964
		return str;
	}

	if(strcmp(sp, "TBANKED") == 0)
		return sectostr(useron.min, str) + 3;

965
	if(!strcmp(sp,"MSGLEFT") || !strcmp(sp,"MSGSLEFT")) {
966
		safe_snprintf(str,maxlen,"%u",useron.posts);
rswindell's avatar