atcodes.cpp 45.6 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)
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);
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)
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
616
617
618
619
620
	if(strncmp(sp, "FILL:", 5) == 0) {
		sp += 5;
		while(*sp && online && column < cols - 1)
			bputs(sp, P_TRUNCATE);
		return nulstr;
	}

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

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

655
656
657
658
	/* NOSTOP */

	/* STOP */

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

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

	/* LASTCALL */

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

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

	/* User Codes */

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

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

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

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

705
706
	if(!strcmp(sp,"ADDR1"))
		return(useron.address);
707

708
709
	if(!strcmp(sp,"FROM"))
		return(useron.location);
710

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

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

732
733
	if(!strcmp(sp,"CPU"))
		return(useron.comp);
734

735
736
	if(!strcmp(sp,"HOST"))
		return(client_name);
737

738
739
	if(!strcmp(sp,"BDATE"))
		return(useron.birth);
740

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

rswindell's avatar
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,"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);
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

958
	if(!strcmp(sp,"UPS") || !strcmp(sp,"UPFILES")) {
959
		safe_snprintf(str,maxlen,"%u",useron.uls);
960
961
		return(str);
	}