atcodes.cpp 46.2 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
	bool	padded_left=false;
	bool	padded_right=false;
81
	bool	centered=false;
82
	bool	zero_padded=false;
rswindell's avatar
rswindell committed
83
	bool	truncated = true;
rswindell's avatar
rswindell committed
84
	bool	doubled = false;
85
	bool	thousep = false;	// thousands-separated
86
	bool	uppercase = false;
87
	long	pmode = 0;
88
	const char *cp;
89

90
	SAFECOPY(str,instr);
91
92
93
	tp=strchr(str+1,'@');
	if(!tp)                 /* no terminating @ */
		return(0);
94
	sp=strchr(str+1,' ');
95
96
97
98
99
100
	if(sp && sp<tp)         /* space before terminating @ */
		return(0);
	len=(tp-str)+1;
	(*tp)=0;
	sp=(str+1);

101
	disp_len=len;
102
103
104
	if((p = strchr(sp, '|')) != NULL) {
		if(strchr(p, 'T') != NULL)
			thousep = true;
105
106
		if(strchr(p, 'U') != NULL)
			uppercase = true;
107
108
109
110
111
112
113
114
115
116
117
118
119
120
		if(strchr(p, 'L') != NULL)
			padded_left = true;
		else if(strchr(p, 'R') != NULL)
			padded_right = true;
		else if(strchr(p, 'C') != NULL)
			centered = true;
		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)
121
122
		p = NULL;
	else if((p=strstr(sp,"-L"))!=NULL)
123
124
125
		padded_left=true;
	else if((p=strstr(sp,"-R"))!=NULL)
		padded_right=true;
126
127
	else if((p=strstr(sp,"-C"))!=NULL)
		centered=true;
rswindell's avatar
rswindell committed
128
	else if((p=strstr(sp,"-W"))!=NULL)	/* wide */
rswindell's avatar
rswindell committed
129
		doubled=true;
130
131
	else if((p=strstr(sp,"-Z"))!=NULL)
		zero_padded=true;
132
133
	else if((p=strstr(sp,"-T"))!=NULL)
		thousep=true;
134
135
	else if((p=strstr(sp,"-U"))!=NULL)
		uppercase=true;
rswindell's avatar
rswindell committed
136
	else if((p=strstr(sp,"->"))!=NULL)	/* wrap */
rswindell's avatar
rswindell committed
137
		truncated = false;
138
	if(p!=NULL) {
139
		char* lp = p;
140
		while(*lp && !isdigit((uchar)*lp))
141
			lp++;
142
		if(*lp && isdigit((uchar)*lp))
rswindell's avatar
rswindell committed
143
			disp_len=atoi(lp);
144
		*p=0;
145
	}
146

147
	cp = atcode(sp, str2, sizeof(str2), &pmode, centered, obj);
148
	if(cp==NULL)
149
150
		return(0);

151
152
153
154
	char separated[128];
	if(thousep)
		cp = separate_thousands(cp, separated, sizeof(separated), ',');

155
156
157
158
159
160
161
	if(uppercase) {
		char upper[128];
		SAFECOPY(upper, cp);
		strupr(upper);
		cp = upper;
	}

rswindell's avatar
rswindell committed
162
163
164
165
166
167
168
169
170
171
172
	if(p==NULL || truncated == false)
		disp_len = strlen(cp);

	if(truncated) {
		if(column + disp_len > cols - 1) {
			if(column >= cols - 1)
				disp_len = 0;
			else
				disp_len = (cols - 1) - column;
		}
	}
173
174
175
176
177
178
	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);
	}
179
	if(padded_left)
180
		bprintf(pmode, "%-*.*s",disp_len,disp_len,cp);
181
	else if(padded_right)
182
		bprintf(pmode, "%*.*s",disp_len,disp_len,cp);
183
	else if(centered) {
rswindell's avatar
rswindell committed
184
		int vlen = strlen(cp);
185
186
		if(vlen < disp_len) {
			int left = (disp_len - vlen) / 2;
187
			bprintf(pmode, "%*s%-*s", left, "", disp_len - left, cp);
188
		} else
189
			bprintf(pmode, "%.*s", disp_len, cp);
rswindell's avatar
rswindell committed
190
191
	} else if(doubled) {
		wide(cp);
192
	} else if(zero_padded) {
193
194
		int vlen = strlen(cp);
		if(vlen < disp_len)
195
			bprintf(pmode, "%-.*s%s", (int)(disp_len - strlen(cp)), "0000000000", cp);
196
		else
197
			bprintf(pmode, "%.*s", disp_len, cp);
198
	} else
199
		bprintf(pmode, "%.*s", disp_len, cp);
200
201
202
203

	return(len);
}

rswindell's avatar
rswindell committed
204
205
206
207
208
209
210
211
212
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;
}

213
const char* sbbs_t::atcode(char* sp, char* str, size_t maxlen, long* pmode, bool centered, JSObject* obj)
214
{
215
	char*	tp = NULL;
216
	uint	i;
217
218
	uint	ugrp;
	uint	usub;
219
220
221
222
223
224
225
	long	l;
    stats_t stats;
    node_t  node;
	struct	tm tm;

	str[0]=0;

226
227
	if(strncmp(sp, "U+", 2) == 0) {	// UNICODE
		enum unicode_codepoint codepoint = (enum unicode_codepoint)strtoul(sp + 2, &tp, 16);
rswindell's avatar
rswindell committed
228
		if(tp == NULL || *tp == 0)
229
			outchar(codepoint, unicode_to_cp437(codepoint));
rswindell's avatar
rswindell committed
230
231
		else if(*tp == ':')
			outchar(codepoint, tp + 1);
232
233
		else {
			char fallback = (char)strtoul(tp + 1, NULL, 16);
rswindell's avatar
rswindell committed
234
			if(*tp == ',')
235
236
237
238
239
240
241
242
243
244
245
246
				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;
	}

247
248
249
250
251
	if(strcmp(sp, "CHECKMARK") == 0) {
		outchar(UNICODE_CHECK_MARK, CP437_CHECK_MARK);
		return nulstr;
	}

rswindell's avatar
rswindell committed
252
253
254
255
	if(strcmp(sp, "ELLIPSIS") == 0) {
		outchar(UNICODE_HORIZONTAL_ELLIPSIS, "...");
		return nulstr;
	}
256
	if(strcmp(sp, "COPY") == 0) {
rswindell's avatar
rswindell committed
257
258
259
260
261
262
263
		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
264
265
266
267
	if(strcmp(sp, "REGISTERED") == 0) {
		outchar(UNICODE_REGISTERED_SIGN, "(R)");
		return nulstr;
	}
rswindell's avatar
rswindell committed
268
269
270
271
272
	if(strcmp(sp, "TRADEMARK") == 0) {
		outchar(UNICODE_TRADE_MARK_SIGN, "(TM)");
		return nulstr;
	}
	if(strcmp(sp, "DEGREE_C") == 0) {
rswindell's avatar
rswindell committed
273
		outchar(UNICODE_DEGREE_CELSIUS, "\xF8""C");
rswindell's avatar
rswindell committed
274
275
276
		return nulstr;
	}
	if(strcmp(sp, "DEGREE_F") == 0) {
rswindell's avatar
rswindell committed
277
		outchar(UNICODE_DEGREE_FAHRENHEIT, "\xF8""F");
rswindell's avatar
rswindell committed
278
279
280
		return nulstr;
	}

rswindell's avatar
rswindell committed
281
282
283
284
285
	if(strncmp(sp, "WIDE:", 5) == 0) {
		wide(sp + 5);
		return(nulstr);
	}

286
	if(!strcmp(sp,"VER"))
287
		return(VERSION);
288

289
	if(!strcmp(sp,"REV")) {
290
		safe_snprintf(str,maxlen,"%c",REVISION);
291
292
		return(str);
	}
293

294
	if(!strcmp(sp,"FULL_VER")) {
295
		safe_snprintf(str,maxlen,"%s%c%s",VERSION,REVISION,beta_version);
296
		truncsp(str);
297
#if defined(_DEBUG)
298
		strcat(str," Debug");
299
#endif
300
		return(str);
301
302
	}

303
	if(!strcmp(sp,"VER_NOTICE"))
304
		return(VERSION_NOTICE);
305

306
307
	if(!strcmp(sp,"OS_VER"))
		return(os_version(str));
308
309

#ifdef JAVASCRIPT
310
311
	if(!strcmp(sp,"JS_VER"))
		return((char *)JS_GetImplementationVersion());
312
313
#endif

314
315
	if(!strcmp(sp,"PLATFORM"))
		return(PLATFORM_DESC);
316

317
318
	if(!strcmp(sp,"COPYRIGHT"))
		return(COPYRIGHT_NOTICE);
319

320
	if(!strcmp(sp,"COMPILER")) {
321
322
323
		char compiler[32];
		DESCRIBE_COMPILER(compiler);
		strncpy(str, compiler, maxlen);
324
		return(str);
325
326
	}

327
	if(!strcmp(sp,"UPTIME")) {
328
		extern volatile time_t uptime;
329
330
331
332
		time_t up=0;
		now = time(NULL);
		if (uptime != 0 && now >= uptime)
			up = now-uptime;
333
334
		char   days[64]="";
		if((up/(24*60*60))>=2) {
335
	        sprintf(days,"%lu days ",(ulong)(up/(24L*60L*60L)));
336
337
			up%=(24*60*60);
		}
338
		safe_snprintf(str,maxlen,"%s%lu:%02lu"
339
	        ,days
340
341
			,(ulong)(up/(60L*60L))
			,(ulong)((up/60L)%60L)
342
			);
343
		return(str);
344
345
	}

346
	if(!strcmp(sp,"SERVED")) {
347
		extern volatile ulong served;
348
		safe_snprintf(str,maxlen,"%lu",served);
349
350
351
		return(str);
	}

352
	if(!strcmp(sp,"SOCKET_LIB"))
353
		return(socklib_version(str,SOCKLIB_DESC));
354

355
	if(!strcmp(sp,"MSG_LIB")) {
356
		safe_snprintf(str,maxlen,"SMBLIB %s",smb_lib_ver());
357
358
		return(str);
	}
359

360
361
	if(!strcmp(sp,"BBS") || !strcmp(sp,"BOARDNAME"))
		return(cfg.sys_name);
362

363
	if(!strcmp(sp,"BAUD") || !strcmp(sp,"BPS")) {
364
		safe_snprintf(str,maxlen,"%lu",cur_output_rate ? cur_output_rate : cur_rate);
365
366
		return(str);
	}
367

rswindell's avatar
rswindell committed
368
369
370
371
372
373
374
375
	if(!strcmp(sp,"COLS")) {
		safe_snprintf(str,maxlen,"%lu",cols);
		return(str);
	}
	if(!strcmp(sp,"ROWS")) {
		safe_snprintf(str,maxlen,"%lu",rows);
		return(str);
	}
376
377
378
379
380
	if(strcmp(sp,"TERM") == 0)
		return term_type();

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

382
383
	if(!strcmp(sp,"CONN"))
		return(connection);
384

385
386
	if(!strcmp(sp,"SYSOP"))
		return(cfg.sys_op);
387

388
389
	if(!strcmp(sp,"LOCATION"))
		return(cfg.sys_location);
390

391
	if(strcmp(sp,"NODE") == 0 || strcmp(sp,"NN") == 0) {
392
		safe_snprintf(str,maxlen,"%u",cfg.node_num);
393
394
		return(str);
	}
395
	if(strcmp(sp, "TNODES") == 0 || strcmp(sp, "TNODE") == 0 || strcmp(sp, "TN") == 0) {
396
		safe_snprintf(str,maxlen,"%u",cfg.sys_nodes);
397
398
		return(str);
	}
399
400
401
402
403
404
405
406
	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;
	}
407

rswindell's avatar
rswindell committed
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
	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];
	}

424
425
426
427
428
429
	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];

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

433
434
	if(!strcmp(sp,"INETADDR"))
		return(cfg.sys_inetaddr);
435

436
437
438
	if(!strcmp(sp,"HOSTNAME"))
		return(startup->host_name);

439
	if(!strcmp(sp,"FIDOADDR")) {
440
		if(cfg.total_faddrs)
441
			return(smb_faddrtoa(&cfg.faddr[0],str));
442
		return(nulstr);
443
444
	}

445
	if(!strcmp(sp,"EMAILADDR"))
446
		return(usermailaddr(&cfg, str
447
			,cfg.inetmail_misc&NMAIL_ALIAS ? useron.alias : useron.name));
448

449
450
	if(!strcmp(sp,"QWKID"))
		return(cfg.sys_id);
451

452
	if(!strcmp(sp,"TIME") || !strcmp(sp,"SYSTIME")) {
453
		now=time(NULL);
454
		memset(&tm,0,sizeof(tm));
455
		localtime_r(&now,&tm);
456
		if(cfg.sys_misc&SM_MILITARY)
457
458
			safe_snprintf(str,maxlen,"%02d:%02d:%02d"
		        	,tm.tm_hour,tm.tm_min,tm.tm_sec);
459
		else
460
			safe_snprintf(str,maxlen,"%02d:%02d %s"
461
462
463
				,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");
464
		return(str);
465
466
	}

rswindell's avatar
rswindell committed
467
468
469
	if(!strcmp(sp,"TIMEZONE"))
		return(smb_zonestr(sys_timezone(&cfg),str));

470
	if(!strcmp(sp,"DATE") || !strcmp(sp,"SYSDATE")) {
471
		return(unixtodstr(&cfg,time32(NULL),str));
472
	}
473

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

rswindell's avatar
rswindell committed
477
478
479
480
481
	if(!strcmp(sp,"DATETIMEZONE")) {
		char zone[32];
		safe_snprintf(str, maxlen, "%s %s", timestr(time(NULL)), smb_zonestr(sys_timezone(&cfg),zone));
		return str;
	}
482
483
484
485
	
	if(strcmp(sp, "DATEFMT") == 0) {
		return cfg.sys_misc&SM_EURODATE ? "DD/MM/YY" : "MM/DD/YY";
	}
rswindell's avatar
rswindell committed
486

487
	if(!strcmp(sp,"TMSG")) {
488
489
		l=0;
		for(i=0;i<cfg.total_subs;i++)
490
			l+=getposts(&cfg,i); 		/* l=total posts */
491
		safe_snprintf(str,maxlen,"%lu",l);
492
493
		return(str);
	}
494

495
	if(!strcmp(sp,"TUSER")) {
496
		safe_snprintf(str,maxlen,"%u",total_users(&cfg));
497
498
		return(str);
	}
499

500
	if(!strcmp(sp,"TFILE")) {
501
502
		l=0;
		for(i=0;i<cfg.total_dirs;i++)
503
			l+=getfiles(&cfg,i);
504
		safe_snprintf(str,maxlen,"%lu",l);
505
506
		return(str);
	}
507

508
	if(strncmp(sp, "FILES:", 6) == 0) {	// Number of files in specified directory
rswindell's avatar
rswindell committed
509
		const char* path = getpath(&cfg, sp + 6);
510
		safe_snprintf(str, maxlen, "%lu", getfilecount(path));
rswindell's avatar
rswindell committed
511
		return str;
512
513
514
515
516
517
518
	}

	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
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
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
	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;
	}

579
	if(!strcmp(sp,"TCALLS") || !strcmp(sp,"NUMCALLS")) {
580
		getstats(&cfg,0,&stats);
581
		safe_snprintf(str,maxlen,"%lu", (ulong)stats.logons);
582
583
		return(str);
	}
584

585
	if(!strcmp(sp,"PREVON") || !strcmp(sp,"LASTCALLERNODE")
586
		|| !strcmp(sp,"LASTCALLERSYSTEM"))
587
		return(lastuseron);
588

rswindell's avatar
rswindell committed
589
	if(!strcmp(sp,"CLS") || !strcmp(sp,"CLEAR")) {
590
		CLS;
591
592
		return(nulstr);
	}
593

594
	if(!strcmp(sp,"PAUSE") || !strcmp(sp,"MORE")) {
595
		pause();
596
597
		return(nulstr);
	}
598

599
	if(!strcmp(sp,"RESETPAUSE")) {
600
		lncntr=0;
601
602
		return(nulstr);
	}
603

604
	if(!strcmp(sp,"NOPAUSE") || !strcmp(sp,"POFF")) {
605
		sys_status^=SS_PAUSEOFF;
606
607
		return(nulstr);
	}
608

609
	if(!strcmp(sp,"PON") || !strcmp(sp,"AUTOMORE")) {
610
		sys_status^=SS_PAUSEON;
611
612
		return(nulstr);
	}
613

rswindell's avatar
rswindell committed
614
615
	if(strncmp(sp, "FILL:", 5) == 0) {
		sp += 5;
616
617
618
		long margin = centered ? column : 1;
		if(margin < 1) margin = 1;
		while(*sp && online && column < cols - margin)
rswindell's avatar
rswindell committed
619
620
621
622
			bputs(sp, P_TRUNCATE);
		return nulstr;
	}

623
624
625
626
627
	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--)
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
			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;
	}

652
653
654
655
656
	if(strncmp(sp, "BPS:", 4) == 0) {
		set_output_rate((enum output_rate)atoi(sp + 4));
		return nulstr;
	}

657
658
659
660
	/* NOSTOP */

	/* STOP */

661
662
	if(!strcmp(sp,"BELL") || !strcmp(sp,"BEEP"))
		return("\a");
663

664
665
666
	if(!strcmp(sp,"EVENT")) {
		if(event_time==0)
			return("<none>");
667
		return(timestr(event_time));
668
	}
669
670
671

	/* LASTCALL */

672
	if(!strncmp(sp,"NODE",4)) {
673
674
675
		i=atoi(sp+4);
		if(i && i<=cfg.sys_nodes) {
			getnodedat(i,&node,0);
676
677
			printnodedat(i,&node);
		}
678
679
		return(nulstr);
	}
680

681
	if(!strcmp(sp,"WHO")) {
682
		whos_online(true);
683
684
		return(nulstr);
	}
685
686
687

	/* User Codes */

688
689
	if(!strcmp(sp,"USER") || !strcmp(sp,"ALIAS") || !strcmp(sp,"NAME"))
		return(useron.alias);
690

691
	if(!strcmp(sp,"FIRST")) {
692
		safe_snprintf(str,maxlen,"%s",useron.alias);
693
		tp=strchr(str,' ');
694
		if(tp) *tp=0;
695
		return(str);
696
	}
697

698
	if(!strcmp(sp,"USERNUM")) {
699
		safe_snprintf(str,maxlen,"%u",useron.number);
700
701
		return(str);
	}
702

703
	if(!strcmp(sp,"PHONE") || !strcmp(sp,"HOMEPHONE")
704
		|| !strcmp(sp,"DATAPHONE") || !strcmp(sp,"DATA"))
705
		return(useron.phone);
706

707
708
	if(!strcmp(sp,"ADDR1"))
		return(useron.address);
709

710
711
	if(!strcmp(sp,"FROM"))
		return(useron.location);
712

713
	if(!strcmp(sp,"CITY")) {
714
		safe_snprintf(str,maxlen,"%s",useron.location);
715
		char* p=strchr(str,',');
716
717
		if(p) {
			*p=0;
718
719
			return(str);
		}
720
721
		return(nulstr);
	}
722

723
724
	if(!strcmp(sp,"STATE")) {
		char* p=strchr(useron.location,',');
725
726
		if(p) {
			p++;
727
			if(*p==' ')
728
				p++;
729
730
			return(p);
		}
731
732
		return(nulstr);
	}
733

734
735
	if(!strcmp(sp,"CPU"))
		return(useron.comp);
736

737
738
	if(!strcmp(sp,"HOST"))
		return(client_name);
739

740
741
	if(!strcmp(sp,"BDATE"))
		return(useron.birth);
742

743
	if(!strcmp(sp,"AGE")) {
744
		safe_snprintf(str,maxlen,"%u",getage(&cfg,useron.birth));
745
746
		return(str);
	}
rswindell's avatar
rswindell committed
747

748
	if(!strcmp(sp,"CALLS") || !strcmp(sp,"NUMTIMESON")) {
749
		safe_snprintf(str,maxlen,"%u",useron.logons);
750
751
		return(str);
	}
752

rswindell's avatar
rswindell committed
753
754
755
756
757
758
759
	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)
760
		return(unixtodstr(&cfg,useron.pwmod,str));
761

762
	if(!strcmp(sp,"SEC") || !strcmp(sp,"SECURITY")) {
763
		safe_snprintf(str,maxlen,"%u",useron.level);
764
765
		return(str);
	}
766

767
768
	if(!strcmp(sp,"SINCE"))
		return(unixtodstr(&cfg,useron.firston,str));
769

770
	if(!strcmp(sp,"TIMEON") || !strcmp(sp,"TIMEUSED")) {
771
		now=time(NULL);
772
		safe_snprintf(str,maxlen,"%lu",(ulong)(now-logontime)/60L);
773
774
		return(str);
	}
775

776
	if(!strcmp(sp,"TUSED")) {              /* Synchronet only */
777
		now=time(NULL);
778
		return(sectostr((uint)(now-logontime),str)+1);
779
	}
780

781
	if(!strcmp(sp,"TLEFT")) {              /* Synchronet only */
782
		gettimeleft();
783
		return(sectostr(timeleft,str)+1);
784
	}
785

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

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

rswindell's avatar
Add:    
rswindell committed
792
	if(strcmp(sp, "MPERC") == 0 || strcmp(sp, "TIMELIMIT") == 0) {
793
		safe_snprintf(str,maxlen,"%u",cfg.level_timepercall[useron.level]);
794
795
		return(str);
	}
796

rswindell's avatar
Add:    
rswindell committed
797
798
799
800
801
	if(strcmp(sp, "MPERD") == 0) {
		safe_snprintf(str,maxlen,"%u",cfg.level_timeperday[useron.level]);
		return str;
	}

rswindell's avatar
Add:    
rswindell committed
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
	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;
	}

822
	if(!strcmp(sp,"MINLEFT") || !strcmp(sp,"LEFT") || !strcmp(sp,"TIMELEFT")) {
823
		gettimeleft();
824
		safe_snprintf(str,maxlen,"%lu",timeleft/60);
825
826
		return(str);
	}
827

828
	if(!strcmp(sp,"LASTON"))
829
		return(timestr(useron.laston));
830

831
832
	if(!strcmp(sp,"LASTDATEON"))
		return(unixtodstr(&cfg,useron.laston,str));
833

834
	if(!strcmp(sp,"LASTTIMEON")) {
835
		memset(&tm,0,sizeof(tm));
836
		localtime32(&useron.laston,&tm);
837
838
839
840
841
842
843
844
		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");
845
		return(str);
846
847
	}

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

868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
	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
893
	if(strcmp(sp, "MTODAY") == 0) {
894
895
896
897
		safe_snprintf(str, maxlen, "%u", useron.ttoday);
		return str;
	}

rswindell's avatar
Add:    
rswindell committed
898
899
900
901
902
903
	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
904
		return sectostr(useron.ttoday, str) + 3;
rswindell's avatar
Add:    
rswindell committed
905
906

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

909
910
911
912
913
	if(strcmp(sp, "TLAST") == 0) {
		safe_snprintf(str, maxlen, "%u", useron.tlast);
		return str;
	}

rswindell's avatar
Add:    
rswindell committed
914
	if(strcmp(sp, "MEXTRA") == 0) {
915
916
917
		safe_snprintf(str, maxlen, "%u", useron.textra);
		return str;
	}
rswindell's avatar
rswindell committed
918

rswindell's avatar
Add:    
rswindell committed
919
920
921
922
	if(strcmp(sp, "TEXTRA") == 0)
		return sectostr(useron.textra, str) + 3;

	if(strcmp(sp, "MBANKED") == 0) {
923
		safe_snprintf(str, maxlen, "%lu", useron.min);
rswindell's avatar
Add:    
rswindell committed
924
925
926
927
928
929
		return str;
	}

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

930
	if(!strcmp(sp,"MSGLEFT") || !strcmp(sp,"MSGSLEFT")) {
931
		safe_snprintf(str,maxlen,"%u",useron.posts);
932
933
		return(str);
	}
934

935
	if(!strcmp(sp,"MSGREAD")) {
936
		safe_snprintf(str,maxlen,"%lu",posts_read);
937
938
		return(str);
	}
939

940
	if(!strcmp(sp,"FREESPACE")) {
941
		safe_snprintf(str,maxlen,"%lu",getfreediskspace(cfg.temp_dir,0));
942
943
944
945
		return(str);
	}

	if(!strcmp(sp,"FREESPACEK")) {
946
		safe_snprintf(str,maxlen,"%lu",getfreediskspace(cfg.temp_dir,1024));
947
948
		return(str);
	}
949

950
	if(!strcmp(sp,"UPBYTES")) {
951
		safe_snprintf(str,maxlen,"%lu",useron.ulb);
952
953
		return(str);
	}
954

955
	if(!strcmp(sp,"UPK")) {
956
		safe_snprintf(str,maxlen,"%lu",useron.ulb/1024L);
957
958
		return(str);
	}
959

960
	if(!strcmp(sp,"UPS") || !strcmp(sp,"UPFILES")) {