atcodes.cpp 46.4 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;

242
243
	if(strncmp(sp, "U+", 2) == 0) {	// UNICODE
		enum unicode_codepoint codepoint = (enum unicode_codepoint)strtoul(sp + 2, &tp, 16);
rswindell's avatar
rswindell committed
244
		if(tp == NULL || *tp == 0)
245
			outchar(codepoint, unicode_to_cp437(codepoint));
rswindell's avatar
rswindell committed
246
247
		else if(*tp == ':')
			outchar(codepoint, tp + 1);
248
249
		else {
			char fallback = (char)strtoul(tp + 1, NULL, 16);
rswindell's avatar
rswindell committed
250
			if(*tp == ',')
251
252
253
254
255
256
257
258
259
260
261
262
				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;
	}

263
264
265
266
267
	if(strcmp(sp, "CHECKMARK") == 0) {
		outchar(UNICODE_CHECK_MARK, CP437_CHECK_MARK);
		return nulstr;
	}

rswindell's avatar
rswindell committed
268
269
270
271
	if(strcmp(sp, "ELLIPSIS") == 0) {
		outchar(UNICODE_HORIZONTAL_ELLIPSIS, "...");
		return nulstr;
	}
272
	if(strcmp(sp, "COPY") == 0) {
rswindell's avatar
rswindell committed
273
274
275
276
277
278
279
		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
280
281
282
283
	if(strcmp(sp, "REGISTERED") == 0) {
		outchar(UNICODE_REGISTERED_SIGN, "(R)");
		return nulstr;
	}
rswindell's avatar
rswindell committed
284
285
286
287
288
	if(strcmp(sp, "TRADEMARK") == 0) {
		outchar(UNICODE_TRADE_MARK_SIGN, "(TM)");
		return nulstr;
	}
	if(strcmp(sp, "DEGREE_C") == 0) {
rswindell's avatar
rswindell committed
289
		outchar(UNICODE_DEGREE_CELSIUS, "\xF8""C");
rswindell's avatar
rswindell committed
290
291
292
		return nulstr;
	}
	if(strcmp(sp, "DEGREE_F") == 0) {
rswindell's avatar
rswindell committed
293
		outchar(UNICODE_DEGREE_FAHRENHEIT, "\xF8""F");
rswindell's avatar
rswindell committed
294
295
296
		return nulstr;
	}

rswindell's avatar
rswindell committed
297
298
299
300
301
	if(strncmp(sp, "WIDE:", 5) == 0) {
		wide(sp + 5);
		return(nulstr);
	}

302
	if(!strcmp(sp,"VER"))
303
		return(VERSION);
304

305
	if(!strcmp(sp,"REV")) {
306
		safe_snprintf(str,maxlen,"%c",REVISION);
307
308
		return(str);
	}
309

310
	if(!strcmp(sp,"FULL_VER")) {
311
		safe_snprintf(str,maxlen,"%s%c%s",VERSION,REVISION,beta_version);
312
		truncsp(str);
313
#if defined(_DEBUG)
314
		strcat(str," Debug");
315
#endif
316
		return(str);
317
318
	}

319
	if(!strcmp(sp,"VER_NOTICE"))
320
		return(VERSION_NOTICE);
321

322
323
	if(!strcmp(sp,"OS_VER"))
		return(os_version(str));
324
325

#ifdef JAVASCRIPT
326
327
	if(!strcmp(sp,"JS_VER"))
		return((char *)JS_GetImplementationVersion());
328
329
#endif

330
331
	if(!strcmp(sp,"PLATFORM"))
		return(PLATFORM_DESC);
332

333
334
	if(!strcmp(sp,"COPYRIGHT"))
		return(COPYRIGHT_NOTICE);
335

336
	if(!strcmp(sp,"COMPILER")) {
337
338
339
		char compiler[32];
		DESCRIBE_COMPILER(compiler);
		strncpy(str, compiler, maxlen);
340
		return(str);
341
342
	}

343
	if(!strcmp(sp,"UPTIME")) {
344
		extern volatile time_t uptime;
345
346
347
348
		time_t up=0;
		now = time(NULL);
		if (uptime != 0 && now >= uptime)
			up = now-uptime;
349
350
		char   days[64]="";
		if((up/(24*60*60))>=2) {
351
	        sprintf(days,"%lu days ",(ulong)(up/(24L*60L*60L)));
352
353
			up%=(24*60*60);
		}
354
		safe_snprintf(str,maxlen,"%s%lu:%02lu"
355
	        ,days
356
357
			,(ulong)(up/(60L*60L))
			,(ulong)((up/60L)%60L)
358
			);
359
		return(str);
360
361
	}

362
	if(!strcmp(sp,"SERVED")) {
363
		extern volatile ulong served;
364
		safe_snprintf(str,maxlen,"%lu",served);
365
366
367
		return(str);
	}

368
	if(!strcmp(sp,"SOCKET_LIB"))
369
		return(socklib_version(str,SOCKLIB_DESC));
370

371
	if(!strcmp(sp,"MSG_LIB")) {
372
		safe_snprintf(str,maxlen,"SMBLIB %s",smb_lib_ver());
373
374
		return(str);
	}
375

376
377
	if(!strcmp(sp,"BBS") || !strcmp(sp,"BOARDNAME"))
		return(cfg.sys_name);
378

379
	if(!strcmp(sp,"BAUD") || !strcmp(sp,"BPS")) {
380
		safe_snprintf(str,maxlen,"%lu",cur_output_rate ? cur_output_rate : cur_rate);
381
382
		return(str);
	}
383

rswindell's avatar
rswindell committed
384
385
386
387
388
389
390
391
	if(!strcmp(sp,"COLS")) {
		safe_snprintf(str,maxlen,"%lu",cols);
		return(str);
	}
	if(!strcmp(sp,"ROWS")) {
		safe_snprintf(str,maxlen,"%lu",rows);
		return(str);
	}
392
393
394
395
396
	if(strcmp(sp,"TERM") == 0)
		return term_type();

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

398
399
	if(!strcmp(sp,"CONN"))
		return(connection);
400

401
402
	if(!strcmp(sp,"SYSOP"))
		return(cfg.sys_op);
403

404
405
	if(!strcmp(sp,"LOCATION"))
		return(cfg.sys_location);
406

407
	if(strcmp(sp,"NODE") == 0 || strcmp(sp,"NN") == 0) {
408
		safe_snprintf(str,maxlen,"%u",cfg.node_num);
409
410
		return(str);
	}
411
	if(strcmp(sp, "TNODES") == 0 || strcmp(sp, "TNODE") == 0 || strcmp(sp, "TN") == 0) {
412
		safe_snprintf(str,maxlen,"%u",cfg.sys_nodes);
413
414
		return(str);
	}
415
416
417
418
419
420
421
422
	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;
	}
423

rswindell's avatar
rswindell committed
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
	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];
	}

440
441
442
443
444
445
	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];

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

449
450
	if(!strcmp(sp,"INETADDR"))
		return(cfg.sys_inetaddr);
451

452
453
454
	if(!strcmp(sp,"HOSTNAME"))
		return(startup->host_name);

455
	if(!strcmp(sp,"FIDOADDR")) {
456
		if(cfg.total_faddrs)
457
			return(smb_faddrtoa(&cfg.faddr[0],str));
458
		return(nulstr);
459
460
	}

461
	if(!strcmp(sp,"EMAILADDR"))
462
		return(usermailaddr(&cfg, str
463
			,cfg.inetmail_misc&NMAIL_ALIAS ? useron.alias : useron.name));
464

465
466
	if(!strcmp(sp,"QWKID"))
		return(cfg.sys_id);
467

468
	if(!strcmp(sp,"TIME") || !strcmp(sp,"SYSTIME")) {
469
		now=time(NULL);
470
		memset(&tm,0,sizeof(tm));
471
		localtime_r(&now,&tm);
472
		if(cfg.sys_misc&SM_MILITARY)
473
474
			safe_snprintf(str,maxlen,"%02d:%02d:%02d"
		        	,tm.tm_hour,tm.tm_min,tm.tm_sec);
475
		else
476
			safe_snprintf(str,maxlen,"%02d:%02d %s"
477
478
479
				,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");
480
		return(str);
481
482
	}

rswindell's avatar
rswindell committed
483
484
485
	if(!strcmp(sp,"TIMEZONE"))
		return(smb_zonestr(sys_timezone(&cfg),str));

486
	if(!strcmp(sp,"DATE") || !strcmp(sp,"SYSDATE")) {
487
		return(unixtodstr(&cfg,time32(NULL),str));
488
	}
489

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

rswindell's avatar
rswindell committed
493
494
495
496
497
	if(!strcmp(sp,"DATETIMEZONE")) {
		char zone[32];
		safe_snprintf(str, maxlen, "%s %s", timestr(time(NULL)), smb_zonestr(sys_timezone(&cfg),zone));
		return str;
	}
498
499
500
501
	
	if(strcmp(sp, "DATEFMT") == 0) {
		return cfg.sys_misc&SM_EURODATE ? "DD/MM/YY" : "MM/DD/YY";
	}
rswindell's avatar
rswindell committed
502

503
	if(!strcmp(sp,"TMSG")) {
504
505
		l=0;
		for(i=0;i<cfg.total_subs;i++)
506
			l+=getposts(&cfg,i); 		/* l=total posts */
507
		safe_snprintf(str,maxlen,"%lu",l);
508
509
		return(str);
	}
510

511
	if(!strcmp(sp,"TUSER")) {
512
		safe_snprintf(str,maxlen,"%u",total_users(&cfg));
513
514
		return(str);
	}
515

516
	if(!strcmp(sp,"TFILE")) {
517
518
		l=0;
		for(i=0;i<cfg.total_dirs;i++)
519
			l+=getfiles(&cfg,i);
520
		safe_snprintf(str,maxlen,"%lu",l);
521
522
		return(str);
	}
523

524
	if(strncmp(sp, "FILES:", 6) == 0) {	// Number of files in specified directory
rswindell's avatar
rswindell committed
525
		const char* path = getpath(&cfg, sp + 6);
526
		safe_snprintf(str, maxlen, "%lu", getfilecount(path));
rswindell's avatar
rswindell committed
527
		return str;
528
529
530
531
532
533
534
	}

	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
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
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
	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;
	}

595
	if(!strcmp(sp,"TCALLS") || !strcmp(sp,"NUMCALLS")) {
596
		getstats(&cfg,0,&stats);
597
		safe_snprintf(str,maxlen,"%lu", (ulong)stats.logons);
598
599
		return(str);
	}
600

601
	if(!strcmp(sp,"PREVON") || !strcmp(sp,"LASTCALLERNODE")
602
		|| !strcmp(sp,"LASTCALLERSYSTEM"))
603
		return(lastuseron);
604

rswindell's avatar
rswindell committed
605
	if(!strcmp(sp,"CLS") || !strcmp(sp,"CLEAR")) {
606
		CLS;
607
608
		return(nulstr);
	}
609

610
	if(!strcmp(sp,"PAUSE") || !strcmp(sp,"MORE")) {
611
		pause();
612
613
		return(nulstr);
	}
614

615
	if(!strcmp(sp,"RESETPAUSE")) {
616
		lncntr=0;
617
618
		return(nulstr);
	}
619

620
	if(!strcmp(sp,"NOPAUSE") || !strcmp(sp,"POFF")) {
621
		sys_status^=SS_PAUSEOFF;
622
623
		return(nulstr);
	}
624

625
	if(!strcmp(sp,"PON") || !strcmp(sp,"AUTOMORE")) {
626
		sys_status^=SS_PAUSEON;
627
628
		return(nulstr);
	}
629

rswindell's avatar
rswindell committed
630
631
	if(strncmp(sp, "FILL:", 5) == 0) {
		sp += 5;
632
633
634
		long margin = centered ? column : 1;
		if(margin < 1) margin = 1;
		while(*sp && online && column < cols - margin)
rswindell's avatar
rswindell committed
635
636
637
638
			bputs(sp, P_TRUNCATE);
		return nulstr;
	}

639
640
641
642
643
	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--)
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
			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;
	}

668
669
670
671
672
	if(strncmp(sp, "BPS:", 4) == 0) {
		set_output_rate((enum output_rate)atoi(sp + 4));
		return nulstr;
	}

673
674
675
676
	/* NOSTOP */

	/* STOP */

677
678
	if(!strcmp(sp,"BELL") || !strcmp(sp,"BEEP"))
		return("\a");
679

680
681
682
	if(!strcmp(sp,"EVENT")) {
		if(event_time==0)
			return("<none>");
683
		return(timestr(event_time));
684
	}
685
686
687

	/* LASTCALL */

688
	if(!strncmp(sp,"NODE",4)) {
689
690
691
		i=atoi(sp+4);
		if(i && i<=cfg.sys_nodes) {
			getnodedat(i,&node,0);
692
693
			printnodedat(i,&node);
		}
694
695
		return(nulstr);
	}
696

697
	if(!strcmp(sp,"WHO")) {
698
		whos_online(true);
699
700
		return(nulstr);
	}
701
702
703

	/* User Codes */

704
705
	if(!strcmp(sp,"USER") || !strcmp(sp,"ALIAS") || !strcmp(sp,"NAME"))
		return(useron.alias);
706

707
	if(!strcmp(sp,"FIRST")) {
708
		safe_snprintf(str,maxlen,"%s",useron.alias);
709
		tp=strchr(str,' ');
710
		if(tp) *tp=0;
711
		return(str);
712
	}
713

714
	if(!strcmp(sp,"USERNUM")) {
715
		safe_snprintf(str,maxlen,"%u",useron.number);
716
717
		return(str);
	}
718

719
	if(!strcmp(sp,"PHONE") || !strcmp(sp,"HOMEPHONE")
720
		|| !strcmp(sp,"DATAPHONE") || !strcmp(sp,"DATA"))
721
		return(useron.phone);
722

723
724
	if(!strcmp(sp,"ADDR1"))
		return(useron.address);
725

726
727
	if(!strcmp(sp,"FROM"))
		return(useron.location);
728

729
	if(!strcmp(sp,"CITY")) {
730
		safe_snprintf(str,maxlen,"%s",useron.location);
731
		char* p=strchr(str,',');
732
733
		if(p) {
			*p=0;
734
735
			return(str);
		}
736
737
		return(nulstr);
	}
738

739
740
	if(!strcmp(sp,"STATE")) {
		char* p=strchr(useron.location,',');
741
742
		if(p) {
			p++;
743
			if(*p==' ')
744
				p++;
745
746
			return(p);
		}
747
748
		return(nulstr);
	}
749

750
751
	if(!strcmp(sp,"CPU"))
		return(useron.comp);
752

753
754
	if(!strcmp(sp,"HOST"))
		return(client_name);
755

756
757
	if(!strcmp(sp,"BDATE"))
		return(useron.birth);
758

759
	if(!strcmp(sp,"AGE")) {
760
		safe_snprintf(str,maxlen,"%u",getage(&cfg,useron.birth));
761
762
		return(str);
	}
rswindell's avatar
rswindell committed
763

764
	if(!strcmp(sp,"CALLS") || !strcmp(sp,"NUMTIMESON")) {
765
		safe_snprintf(str,maxlen,"%u",useron.logons);
766
767
		return(str);
	}
768

rswindell's avatar
rswindell committed
769
770
771
772
773
774
775
	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)
776
		return(unixtodstr(&cfg,useron.pwmod,str));
777

778
	if(!strcmp(sp,"SEC") || !strcmp(sp,"SECURITY")) {
779
		safe_snprintf(str,maxlen,"%u",useron.level);
780
781
		return(str);
	}
782

783
784
	if(!strcmp(sp,"SINCE"))
		return(unixtodstr(&cfg,useron.firston,str));
785

786
	if(!strcmp(sp,"TIMEON") || !strcmp(sp,"TIMEUSED")) {
787
		now=time(NULL);
788
		safe_snprintf(str,maxlen,"%lu",(ulong)(now-logontime)/60L);
789
790
		return(str);
	}
791

792
	if(!strcmp(sp,"TUSED")) {              /* Synchronet only */
793
		now=time(NULL);
794
		return(sectostr((uint)(now-logontime),str)+1);
795
	}
796

797
	if(!strcmp(sp,"TLEFT")) {              /* Synchronet only */
798
		gettimeleft();
799
		return(sectostr(timeleft,str)+1);
800
	}
801

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

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

rswindell's avatar
Add:    
rswindell committed
808
	if(strcmp(sp, "MPERC") == 0 || strcmp(sp, "TIMELIMIT") == 0) {
809
		safe_snprintf(str,maxlen,"%u",cfg.level_timepercall[useron.level]);
810
811
		return(str);
	}
812

rswindell's avatar
Add:    
rswindell committed
813
814
815
816
817
	if(strcmp(sp, "MPERD") == 0) {
		safe_snprintf(str,maxlen,"%u",cfg.level_timeperday[useron.level]);
		return str;
	}

rswindell's avatar
Add:    
rswindell committed
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
	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;
	}

838
	if(!strcmp(sp,"MINLEFT") || !strcmp(sp,"LEFT") || !strcmp(sp,"TIMELEFT")) {
839
		gettimeleft();
840
		safe_snprintf(str,maxlen,"%lu",timeleft/60);
841
842
		return(str);
	}
843

844
	if(!strcmp(sp,"LASTON"))
845
		return(timestr(useron.laston));
846

847
848
	if(!strcmp(sp,"LASTDATEON"))
		return(unixtodstr(&cfg,useron.laston,str));
849

850
	if(!strcmp(sp,"LASTTIMEON")) {
851
		memset(&tm,0,sizeof(tm));
852
		localtime32(&useron.laston,&tm);
853
854
855
856
857
858
859
860
		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");
861
		return(str);
862
863
	}

rswindell's avatar
rswindell committed
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
	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);
	}

884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
	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
909
	if(strcmp(sp, "MTODAY") == 0) {
910
911
912
913
		safe_snprintf(str, maxlen, "%u", useron.ttoday);
		return str;
	}

rswindell's avatar
Add:    
rswindell committed
914
915
916
917
918
919
	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
920
		return sectostr(useron.ttoday, str) + 3;
rswindell's avatar
Add:    
rswindell committed
921
922

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

925
926
927
928
929
	if(strcmp(sp, "TLAST") == 0) {
		safe_snprintf(str, maxlen, "%u", useron.tlast);
		return str;
	}

rswindell's avatar
Add:    
rswindell committed
930
	if(strcmp(sp, "MEXTRA") == 0) {
931
932
933
		safe_snprintf(str, maxlen, "%u", useron.textra);
		return str;
	}
rswindell's avatar
rswindell committed
934

rswindell's avatar
Add:    
rswindell committed
935
936
937
938
	if(strcmp(sp, "TEXTRA") == 0)
		return sectostr(useron.textra, str) + 3;

	if(strcmp(sp, "MBANKED") == 0) {
939
		safe_snprintf(str, maxlen, "%lu", useron.min);
rswindell's avatar
Add:    
rswindell committed
940
941
942
943
944
945
		return str;
	}

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

946
	if(!strcmp(sp,"MSGLEFT") || !strcmp(sp,"MSGSLEFT")) {
947
		safe_snprintf(str,maxlen,"%u",useron.posts);
948
949
		return(str);
	}
950

951
	if(!strcmp(sp,"MSGREAD")) {
952
		safe_snprintf(str,maxlen,"%lu",posts_read);
953
954
		return(str);
	}
955

956
	if(!strcmp(sp,"FREESPACE")) {