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

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

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

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

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

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

	return(len);
}

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

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

	str[0]=0;

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

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

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

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

288
	if(!strcmp(sp,"VER"))
289
		return(VERSION);
290

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

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

305
	if(!strcmp(sp,"VER_NOTICE"))
306
		return(VERSION_NOTICE);
307

308
309
	if(!strcmp(sp,"OS_VER"))
		return(os_version(str));
310
311

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

316
317
	if(!strcmp(sp,"PLATFORM"))
		return(PLATFORM_DESC);
318

319
320
	if(!strcmp(sp,"COPYRIGHT"))
		return(COPYRIGHT_NOTICE);
321

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

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

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

354
	if(!strcmp(sp,"SOCKET_LIB"))
355
		return(socklib_version(str,SOCKLIB_DESC));
356

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

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

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

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

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

384
385
	if(!strcmp(sp,"CONN"))
		return(connection);
386

387
388
	if(!strcmp(sp,"SYSOP"))
		return(cfg.sys_op);
389

390
391
	if(!strcmp(sp,"LOCATION"))
		return(cfg.sys_location);
392

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

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

426
427
428
429
430
431
	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];

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

435
436
	if(!strcmp(sp,"INETADDR"))
		return(cfg.sys_inetaddr);
437

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

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

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

451
452
	if(!strcmp(sp,"QWKID"))
		return(cfg.sys_id);
453

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

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

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

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

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

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

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

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

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

	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
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
579
580
	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;
	}

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

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

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

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

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

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

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

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

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

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

659
660
661
662
	/* NOSTOP */

	/* STOP */

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

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

	/* LASTCALL */

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

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

	/* User Codes */

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

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

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

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

709
710
	if(!strcmp(sp,"ADDR1"))
		return(useron.address);
711

712
713
	if(!strcmp(sp,"FROM"))
		return(useron.location);
714

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

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

736
737
	if(!strcmp(sp,"CPU"))
		return(useron.comp);
738

739
740
	if(!strcmp(sp,"HOST"))
		return(client_name);
741

742
743
	if(!strcmp(sp,"BDATE"))
		return(useron.birth);
744

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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