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

rswindell's avatar
rswindell committed
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
	if(*sp == '~' && *(sp + 1)) {	// Mouse hot-spot
		sp++;
		tp = strchr(sp + 1, '~');
		if(tp == NULL)
			tp = sp;
		else {
			*tp = 0;
			tp++;
		}
		c_unescape_str(tp);
		add_hotspot(tp, column, column + strlen(sp) - 1, row);
		bputs(sp);
		return len;
	}

122
	disp_len=len;
123
124
125
	if((p = strchr(sp, '|')) != NULL) {
		if(strchr(p, 'T') != NULL)
			thousep = true;
126
127
		if(strchr(p, 'U') != NULL)
			uppercase = true;
128
		if(strchr(p, 'L') != NULL)
129
			align = left;
130
		else if(strchr(p, 'R') != NULL)
131
			align = right;
132
		else if(strchr(p, 'C') != NULL)
133
			align = center;
134
135
136
137
138
139
140
141
		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)
142
143
		p = NULL;
	else if((p=strstr(sp,"-L"))!=NULL)
144
		align = left;
145
	else if((p=strstr(sp,"-R"))!=NULL)
146
		align = right;
147
	else if((p=strstr(sp,"-C"))!=NULL)
148
		align = center;
rswindell's avatar
rswindell committed
149
	else if((p=strstr(sp,"-W"))!=NULL)	/* wide */
rswindell's avatar
rswindell committed
150
		doubled=true;
151
152
	else if((p=strstr(sp,"-Z"))!=NULL)
		zero_padded=true;
153
154
	else if((p=strstr(sp,"-T"))!=NULL)
		thousep=true;
155
156
	else if((p=strstr(sp,"-U"))!=NULL)
		uppercase=true;
rswindell's avatar
rswindell committed
157
	else if((p=strstr(sp,"->"))!=NULL)	/* wrap */
rswindell's avatar
rswindell committed
158
		truncated = false;
159
	if(p!=NULL) {
160
		char* lp = p;
161
162
163
164
165
		lp++;	// skip the '|' or '-'
		while(*lp == '>'|| isalpha((uchar)*lp))
			lp++;
		if(*lp)
			width_specified = true;
166
		while(*lp && !isdigit((uchar)*lp))
167
			lp++;
168
		if(*lp && isdigit((uchar)*lp)) {
rswindell's avatar
rswindell committed
169
			disp_len=atoi(lp);
170
171
			width_specified = true;
		}
172
		*p=0;
173
	}
174

175
	cp = atcode(sp, str2, sizeof(str2), &pmode, align == center, obj);
176
	if(cp==NULL)
177
178
		return(0);

179
180
181
182
	char separated[128];
	if(thousep)
		cp = separate_thousands(cp, separated, sizeof(separated), ',');

183
184
185
186
187
188
189
	if(uppercase) {
		char upper[128];
		SAFECOPY(upper, cp);
		strupr(upper);
		cp = upper;
	}

190
	if(p==NULL || truncated == false || (width_specified == false && align == none))
rswindell's avatar
rswindell committed
191
192
		disp_len = strlen(cp);

193
194
195
	if(uppercase && align == none)
		align = left;

rswindell's avatar
rswindell committed
196
197
198
199
200
201
202
203
	if(truncated) {
		if(column + disp_len > cols - 1) {
			if(column >= cols - 1)
				disp_len = 0;
			else
				disp_len = (cols - 1) - column;
		}
	}
204
205
206
207
208
209
	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);
	}
210
	if(align == left)
211
		bprintf(pmode, "%-*.*s",disp_len,disp_len,cp);
212
	else if(align == right)
213
		bprintf(pmode, "%*.*s",disp_len,disp_len,cp);
214
	else if(align == center) {
rswindell's avatar
rswindell committed
215
		int vlen = strlen(cp);
216
217
		if(vlen < disp_len) {
			int left = (disp_len - vlen) / 2;
218
			bprintf(pmode, "%*s%-*s", left, "", disp_len - left, cp);
219
		} else
220
			bprintf(pmode, "%.*s", disp_len, cp);
rswindell's avatar
rswindell committed
221
222
	} else if(doubled) {
		wide(cp);
223
	} else if(zero_padded) {
224
225
		int vlen = strlen(cp);
		if(vlen < disp_len)
226
			bprintf(pmode, "%-.*s%s", (int)(disp_len - strlen(cp)), "0000000000", cp);
227
		else
228
			bprintf(pmode, "%.*s", disp_len, cp);
229
	} else
230
		bprintf(pmode, "%.*s", disp_len, cp);
231
232
233
234

	return(len);
}

rswindell's avatar
rswindell committed
235
236
237
238
239
240
241
242
243
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;
}

244
const char* sbbs_t::atcode(char* sp, char* str, size_t maxlen, long* pmode, bool centered, JSObject* obj)
245
{
246
	char*	tp = NULL;
247
	uint	i;
248
249
	uint	ugrp;
	uint	usub;
250
251
252
253
254
255
256
	long	l;
    stats_t stats;
    node_t  node;
	struct	tm tm;

	str[0]=0;

rswindell's avatar
rswindell committed
257
258
259
	if(strcmp(sp, "HOT") == 0) { // Auto-mouse hot-spot attribute
		hot_attr = curatr;
		return nulstr;
rswindell's avatar
rswindell committed
260
	}
261
	if(strncmp(sp, "HOT:", 4) == 0) {	// Auto-mouse hot-spot attribute
rswindell's avatar
rswindell committed
262
263
264
265
266
		sp += 4;
		if(stricmp(sp, "off") == 0)
			hot_attr = 0;
		else
			hot_attr = attrstr(sp);
267
268
269
		return nulstr;
	}

270
271
	if(strncmp(sp, "U+", 2) == 0) {	// UNICODE
		enum unicode_codepoint codepoint = (enum unicode_codepoint)strtoul(sp + 2, &tp, 16);
rswindell's avatar
rswindell committed
272
		if(tp == NULL || *tp == 0)
273
			outchar(codepoint, unicode_to_cp437(codepoint));
rswindell's avatar
rswindell committed
274
275
		else if(*tp == ':')
			outchar(codepoint, tp + 1);
276
277
		else {
			char fallback = (char)strtoul(tp + 1, NULL, 16);
rswindell's avatar
rswindell committed
278
			if(*tp == ',')
279
280
281
282
283
284
285
286
287
288
289
290
				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;
	}

291
292
293
294
295
	if(strcmp(sp, "CHECKMARK") == 0) {
		outchar(UNICODE_CHECK_MARK, CP437_CHECK_MARK);
		return nulstr;
	}

rswindell's avatar
rswindell committed
296
297
298
299
	if(strcmp(sp, "ELLIPSIS") == 0) {
		outchar(UNICODE_HORIZONTAL_ELLIPSIS, "...");
		return nulstr;
	}
300
	if(strcmp(sp, "COPY") == 0) {
rswindell's avatar
rswindell committed
301
302
303
304
305
306
307
		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
308
309
310
311
	if(strcmp(sp, "REGISTERED") == 0) {
		outchar(UNICODE_REGISTERED_SIGN, "(R)");
		return nulstr;
	}
rswindell's avatar
rswindell committed
312
313
314
315
316
	if(strcmp(sp, "TRADEMARK") == 0) {
		outchar(UNICODE_TRADE_MARK_SIGN, "(TM)");
		return nulstr;
	}
	if(strcmp(sp, "DEGREE_C") == 0) {
rswindell's avatar
rswindell committed
317
		outchar(UNICODE_DEGREE_CELSIUS, "\xF8""C");
rswindell's avatar
rswindell committed
318
319
320
		return nulstr;
	}
	if(strcmp(sp, "DEGREE_F") == 0) {
rswindell's avatar
rswindell committed
321
		outchar(UNICODE_DEGREE_FAHRENHEIT, "\xF8""F");
rswindell's avatar
rswindell committed
322
323
324
		return nulstr;
	}

rswindell's avatar
rswindell committed
325
326
327
328
329
	if(strncmp(sp, "WIDE:", 5) == 0) {
		wide(sp + 5);
		return(nulstr);
	}

330
	if(!strcmp(sp,"VER"))
331
		return(VERSION);
332

333
	if(!strcmp(sp,"REV")) {
334
		safe_snprintf(str,maxlen,"%c",REVISION);
335
336
		return(str);
	}
337

338
	if(!strcmp(sp,"FULL_VER")) {
339
		safe_snprintf(str,maxlen,"%s%c%s",VERSION,REVISION,beta_version);
340
		truncsp(str);
341
#if defined(_DEBUG)
342
		strcat(str," Debug");
343
#endif
344
		return(str);
345
346
	}

347
	if(!strcmp(sp,"VER_NOTICE"))
348
		return(VERSION_NOTICE);
349

350
351
	if(!strcmp(sp,"OS_VER"))
		return(os_version(str));
352
353

#ifdef JAVASCRIPT
354
355
	if(!strcmp(sp,"JS_VER"))
		return((char *)JS_GetImplementationVersion());
356
357
#endif

358
359
	if(!strcmp(sp,"PLATFORM"))
		return(PLATFORM_DESC);
360

361
362
	if(!strcmp(sp,"COPYRIGHT"))
		return(COPYRIGHT_NOTICE);
363

364
	if(!strcmp(sp,"COMPILER")) {
365
366
367
		char compiler[32];
		DESCRIBE_COMPILER(compiler);
		strncpy(str, compiler, maxlen);
368
		return(str);
369
370
	}

371
	if(!strcmp(sp,"UPTIME")) {
372
		extern volatile time_t uptime;
373
374
375
376
		time_t up=0;
		now = time(NULL);
		if (uptime != 0 && now >= uptime)
			up = now-uptime;
377
378
		char   days[64]="";
		if((up/(24*60*60))>=2) {
379
	        sprintf(days,"%lu days ",(ulong)(up/(24L*60L*60L)));
380
381
			up%=(24*60*60);
		}
382
		safe_snprintf(str,maxlen,"%s%lu:%02lu"
383
	        ,days
384
385
			,(ulong)(up/(60L*60L))
			,(ulong)((up/60L)%60L)
386
			);
387
		return(str);
388
389
	}

390
	if(!strcmp(sp,"SERVED")) {
391
		extern volatile ulong served;
392
		safe_snprintf(str,maxlen,"%lu",served);
393
394
395
		return(str);
	}

396
	if(!strcmp(sp,"SOCKET_LIB"))
397
		return(socklib_version(str,SOCKLIB_DESC));
398

399
	if(!strcmp(sp,"MSG_LIB")) {
400
		safe_snprintf(str,maxlen,"SMBLIB %s",smb_lib_ver());
401
402
		return(str);
	}
403

404
405
	if(!strcmp(sp,"BBS") || !strcmp(sp,"BOARDNAME"))
		return(cfg.sys_name);
406

407
	if(!strcmp(sp,"BAUD") || !strcmp(sp,"BPS")) {
408
		safe_snprintf(str,maxlen,"%lu",cur_output_rate ? cur_output_rate : cur_rate);
409
410
		return(str);
	}
411

rswindell's avatar
rswindell committed
412
413
414
415
416
417
418
419
	if(!strcmp(sp,"COLS")) {
		safe_snprintf(str,maxlen,"%lu",cols);
		return(str);
	}
	if(!strcmp(sp,"ROWS")) {
		safe_snprintf(str,maxlen,"%lu",rows);
		return(str);
	}
420
421
422
423
424
	if(strcmp(sp,"TERM") == 0)
		return term_type();

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

426
427
	if(!strcmp(sp,"CONN"))
		return(connection);
428

429
430
	if(!strcmp(sp,"SYSOP"))
		return(cfg.sys_op);
431

432
433
	if(!strcmp(sp,"LOCATION"))
		return(cfg.sys_location);
434

435
	if(strcmp(sp,"NODE") == 0 || strcmp(sp,"NN") == 0) {
436
		safe_snprintf(str,maxlen,"%u",cfg.node_num);
437
438
		return(str);
	}
439
	if(strcmp(sp, "TNODES") == 0 || strcmp(sp, "TNODE") == 0 || strcmp(sp, "TN") == 0) {
440
		safe_snprintf(str,maxlen,"%u",cfg.sys_nodes);
441
442
		return(str);
	}
443
444
445
446
447
448
449
450
	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;
	}
451

rswindell's avatar
rswindell committed
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
	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];
	}

468
469
470
471
472
473
	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];

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

477
478
	if(!strcmp(sp,"INETADDR"))
		return(cfg.sys_inetaddr);
479

480
481
482
	if(!strcmp(sp,"HOSTNAME"))
		return(startup->host_name);

483
	if(!strcmp(sp,"FIDOADDR")) {
484
		if(cfg.total_faddrs)
485
			return(smb_faddrtoa(&cfg.faddr[0],str));
486
		return(nulstr);
487
488
	}

489
	if(!strcmp(sp,"EMAILADDR"))
490
		return(usermailaddr(&cfg, str
491
			,cfg.inetmail_misc&NMAIL_ALIAS ? useron.alias : useron.name));
492

493
494
	if(!strcmp(sp,"QWKID"))
		return(cfg.sys_id);
495

496
	if(!strcmp(sp,"TIME") || !strcmp(sp,"SYSTIME")) {
497
		now=time(NULL);
498
		memset(&tm,0,sizeof(tm));
499
		localtime_r(&now,&tm);
500
		if(cfg.sys_misc&SM_MILITARY)
501
502
			safe_snprintf(str,maxlen,"%02d:%02d:%02d"
		        	,tm.tm_hour,tm.tm_min,tm.tm_sec);
503
		else
504
			safe_snprintf(str,maxlen,"%02d:%02d %s"
505
506
507
				,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");
508
		return(str);
509
510
	}

rswindell's avatar
rswindell committed
511
512
513
	if(!strcmp(sp,"TIMEZONE"))
		return(smb_zonestr(sys_timezone(&cfg),str));

514
	if(!strcmp(sp,"DATE") || !strcmp(sp,"SYSDATE")) {
515
		return(unixtodstr(&cfg,time32(NULL),str));
516
	}
517

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

rswindell's avatar
rswindell committed
521
522
523
524
525
	if(!strcmp(sp,"DATETIMEZONE")) {
		char zone[32];
		safe_snprintf(str, maxlen, "%s %s", timestr(time(NULL)), smb_zonestr(sys_timezone(&cfg),zone));
		return str;
	}
526
527
528
529
	
	if(strcmp(sp, "DATEFMT") == 0) {
		return cfg.sys_misc&SM_EURODATE ? "DD/MM/YY" : "MM/DD/YY";
	}
rswindell's avatar
rswindell committed
530

531
	if(!strcmp(sp,"TMSG")) {
532
533
		l=0;
		for(i=0;i<cfg.total_subs;i++)
534
			l+=getposts(&cfg,i); 		/* l=total posts */
535
		safe_snprintf(str,maxlen,"%lu",l);
536
537
		return(str);
	}
538

539
	if(!strcmp(sp,"TUSER")) {
540
		safe_snprintf(str,maxlen,"%u",total_users(&cfg));
541
542
		return(str);
	}
543

544
	if(!strcmp(sp,"TFILE")) {
545
546
		l=0;
		for(i=0;i<cfg.total_dirs;i++)
547
			l+=getfiles(&cfg,i);
548
		safe_snprintf(str,maxlen,"%lu",l);
549
550
		return(str);
	}
551

552
	if(strncmp(sp, "FILES:", 6) == 0) {	// Number of files in specified directory
rswindell's avatar
rswindell committed
553
		const char* path = getpath(&cfg, sp + 6);
554
		safe_snprintf(str, maxlen, "%lu", getfilecount(path));
rswindell's avatar
rswindell committed
555
		return str;
556
557
558
559
560
561
562
	}

	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
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
614
615
616
617
618
619
620
621
622
	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;
	}

623
	if(!strcmp(sp,"TCALLS") || !strcmp(sp,"NUMCALLS")) {
624
		getstats(&cfg,0,&stats);
625
		safe_snprintf(str,maxlen,"%lu", (ulong)stats.logons);
626
627
		return(str);
	}
628

629
	if(!strcmp(sp,"PREVON") || !strcmp(sp,"LASTCALLERNODE")
630
		|| !strcmp(sp,"LASTCALLERSYSTEM"))
631
		return(lastuseron);
632

rswindell's avatar
rswindell committed
633
	if(!strcmp(sp,"CLS") || !strcmp(sp,"CLEAR")) {
634
		CLS;
635
636
		return(nulstr);
	}
637

638
	if(!strcmp(sp,"PAUSE") || !strcmp(sp,"MORE")) {
639
		pause();
640
641
		return(nulstr);
	}
642

643
	if(!strcmp(sp,"RESETPAUSE")) {
644
		lncntr=0;
645
646
		return(nulstr);
	}
647

648
	if(!strcmp(sp,"NOPAUSE") || !strcmp(sp,"POFF")) {
649
		sys_status^=SS_PAUSEOFF;
650
651
		return(nulstr);
	}
652

653
	if(!strcmp(sp,"PON") || !strcmp(sp,"AUTOMORE")) {
654
		sys_status^=SS_PAUSEON;
655
656
		return(nulstr);
	}
657

rswindell's avatar
rswindell committed
658
659
	if(strncmp(sp, "FILL:", 5) == 0) {
		sp += 5;
660
661
662
		long margin = centered ? column : 1;
		if(margin < 1) margin = 1;
		while(*sp && online && column < cols - margin)
rswindell's avatar
rswindell committed
663
664
665
666
			bputs(sp, P_TRUNCATE);
		return nulstr;
	}

667
668
669
670
671
	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--)
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
			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;
	}

696
697
698
699
700
	if(strncmp(sp, "BPS:", 4) == 0) {
		set_output_rate((enum output_rate)atoi(sp + 4));
		return nulstr;
	}

701
702
703
704
	/* NOSTOP */

	/* STOP */

705
706
	if(!strcmp(sp,"BELL") || !strcmp(sp,"BEEP"))
		return("\a");
707

708
709
710
	if(!strcmp(sp,"EVENT")) {
		if(event_time==0)
			return("<none>");
711
		return(timestr(event_time));
712
	}
713
714
715

	/* LASTCALL */

716
	if(!strncmp(sp,"NODE",4)) {
717
718
719
		i=atoi(sp+4);
		if(i && i<=cfg.sys_nodes) {
			getnodedat(i,&node,0);
720
721
			printnodedat(i,&node);
		}
722
723
		return(nulstr);
	}
724

725
	if(!strcmp(sp,"WHO")) {
726
		whos_online(true);
727
728
		return(nulstr);
	}
729
730
731

	/* User Codes */

732
733
	if(!strcmp(sp,"USER") || !strcmp(sp,"ALIAS") || !strcmp(sp,"NAME"))
		return(useron.alias);
734

735
	if(!strcmp(sp,"FIRST")) {
736
		safe_snprintf(str,maxlen,"%s",useron.alias);
737
		tp=strchr(str,' ');
738
		if(tp) *tp=0;
739
		return(str);
740
	}
741

742
	if(!strcmp(sp,"USERNUM")) {
743
		safe_snprintf(str,maxlen,"%u",useron.number);
744
745
		return(str);
	}
746

747
	if(!strcmp(sp,"PHONE") || !strcmp(sp,"HOMEPHONE")
748
		|| !strcmp(sp,"DATAPHONE") || !strcmp(sp,"DATA"))
749
		return(useron.phone);
750

751
752
	if(!strcmp(sp,"ADDR1"))
		return(useron.address);
753

754
755
	if(!strcmp(sp,"FROM"))
		return(useron.location);
756

757
	if(!strcmp(sp,"CITY")) {
758
		safe_snprintf(str,maxlen,"%s",useron.location);
759
		char* p=strchr(str,',');
760
761
		if(p) {
			*p=0;
762
763
			return(str);
		}
764
765
		return(nulstr);
	}
766

767
768
	if(!strcmp(sp,"STATE")) {
		char* p=strchr(useron.location,',');
769
770
		if(p) {
			p++;
771
			if(*p==' ')
772
				p++;
773
774
			return(p);
		}
775
776
		return(nulstr);
	}
777

778
779
	if(!strcmp(sp,"CPU"))
		return(useron.comp);
780

781
782
	if(!strcmp(sp,"HOST"))
		return(client_name);
783

784
785
	if(!strcmp(sp,"BDATE"))
		return(useron.birth);
786

787
	if(!strcmp(sp,"AGE")) {
788
		safe_snprintf(str,maxlen,"%u",getage(&cfg,useron.birth));
789
790
		return(str);
	}
rswindell's avatar
rswindell committed
791

792
	if(!strcmp(sp,"CALLS") || !strcmp(sp,"NUMTIMESON")) {
793
		safe_snprintf(str,maxlen,"%u",useron.logons);
794
795
		return(str);
	}
796

rswindell's avatar
rswindell committed
797
798
799
800
801
802
803
	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)
804
		return(unixtodstr(&cfg,useron.pwmod,str));
805

806
	if(!strcmp(sp,"SEC") || !strcmp(sp,"SECURITY")) {
807
		safe_snprintf(str,maxlen,"%u",useron.level);
808
809
		return(str);
	}
810

811
812
	if(!strcmp(sp,"SINCE"))
		return(unixtodstr(&cfg,useron.firston,str));
813

814
	if(!strcmp(sp,"TIMEON") || !strcmp(sp,"TIMEUSED")) {
815
		now=time(NULL);
816
		safe_snprintf(str,maxlen,"%lu",(ulong)(now-logontime)/60L);
817
818
		return(str);
	}
819

820
	if(!strcmp(sp,"TUSED")) {              /* Synchronet only */
821
		now=time(NULL);
822
		return(sectostr((uint)(now-logontime),str)+1);
823
	}
824

825
	if(!strcmp(sp,"TLEFT")) {              /* Synchronet only */
826
		gettimeleft();
827
		return(sectostr(timeleft,str)+1);
828
	}
829

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

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

rswindell's avatar
Add:    
rswindell committed
836
	if(strcmp(sp, "MPERC") == 0 || strcmp(sp, "TIMELIMIT") == 0) {
837
		safe_snprintf(str,maxlen,"%u",cfg.level_timepercall[useron.level]);
838
839
		return(str);
	}
840

rswindell's avatar
Add:    
rswindell committed
841
842
843
844
845
	if(strcmp(sp, "MPERD") == 0) {
		safe_snprintf(str,maxlen,"%u",cfg.level_timeperday[useron.level]);
		return str;
	}

rswindell's avatar
Add:    
rswindell committed
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
	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;
	}

866
	if(!strcmp(sp,"MINLEFT") || !strcmp(sp,"LEFT") || !strcmp(sp,"TIMELEFT")) {
867
		gettimeleft();
868
		safe_snprintf(str,maxlen,"%lu",timeleft/60);
869
870
		return(str);
	}
871

872
	if(!strcmp(sp,"LASTON"))
873
		return(timestr(useron.laston));
874

875
876
	if(!strcmp(sp,"LASTDATEON"))
		return(unixtodstr(&cfg,useron.laston,str));
877

878
	if(!strcmp(sp,"LASTTIMEON")) {
879
		memset(&tm,0,sizeof(tm));
880
		localtime32(&useron.laston,&tm);
881
882
883
884
885
886
887
888
		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");
889
		return(str);
890
891
	}

rswindell's avatar
rswindell committed
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
	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);
	}

912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
	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
937
	if(strcmp(sp, "MTODAY") == 0) {
938
939
940
941
		safe_snprintf(str, maxlen, "%u", useron.ttoday);
		return str;
	}

rswindell's avatar
Add:    
rswindell committed
942
943
944
945
946
947
	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
948
		return sectostr(useron.ttoday, str) + 3;
rswindell's avatar
Add:    
rswindell committed
949
950

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

953
954
955
956
957
	if(strcmp(sp, "TLAST") == 0) {
		safe_snprintf(str, maxlen, "%u", useron.tlast);
		return str;
	}

rswindell's avatar
Add:    
rswindell committed
958
	if(strcmp(sp, "MEXTRA") == 0) {
959
960
961
		safe_snprintf(str, maxlen, "%u", useron.textra);
		return str;
	}
rswindell's avatar
rswindell committed
962

rswindell's avatar
Add:    
rswindell committed
963
964
965
966
	if(strcmp(sp, "TEXTRA") == 0)
		return sectostr(useron.textra, str) + 3;

	if(strcmp(sp, "MBANKED") == 0) {
967
		safe_snprintf(str, maxlen, "%lu", useron.min);
rswindell's avatar
Add:    
rswindell committed
968
969
970
971