atcodes.cpp 41.3 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
/****************************************************************************/
/* Returns 0 if invalid @ code. Returns length of @ code if valid.          */
/****************************************************************************/
53
int sbbs_t::show_atcode(const char *instr)
54
{
55
	char	str[128],str2[128],*tp,*sp,*p;
56
    int     len;
57
	int		disp_len;
58
59
	bool	padded_left=false;
	bool	padded_right=false;
60
	bool	centered=false;
61
	bool	zero_padded=false;
rswindell's avatar
rswindell committed
62
	bool	truncated = true;
rswindell's avatar
rswindell committed
63
	bool	doubled = false;
64
	long	pmode = 0;
65
	const char *cp;
66

67
	SAFECOPY(str,instr);
68
69
70
	tp=strchr(str+1,'@');
	if(!tp)                 /* no terminating @ */
		return(0);
71
	sp=strchr(str+1,' ');
72
73
74
75
76
77
	if(sp && sp<tp)         /* space before terminating @ */
		return(0);
	len=(tp-str)+1;
	(*tp)=0;
	sp=(str+1);

78
	disp_len=len;
79
80
81
	if(strchr(sp, ':') != NULL)
		p = NULL;
	else if((p=strstr(sp,"-L"))!=NULL)
82
83
84
		padded_left=true;
	else if((p=strstr(sp,"-R"))!=NULL)
		padded_right=true;
85
86
	else if((p=strstr(sp,"-C"))!=NULL)
		centered=true;
rswindell's avatar
rswindell committed
87
	else if((p=strstr(sp,"-W"))!=NULL)	/* wide */
rswindell's avatar
rswindell committed
88
		doubled=true;
89
90
	else if((p=strstr(sp,"-Z"))!=NULL)
		zero_padded=true;
rswindell's avatar
rswindell committed
91
	else if((p=strstr(sp,"->"))!=NULL)	/* wrap */
rswindell's avatar
rswindell committed
92
		truncated = false;
93
	if(p!=NULL) {
rswindell's avatar
rswindell committed
94
95
96
		char* lp = p + 2;
		if(*lp && isdigit(*lp))
			disp_len=atoi(lp);
97
		*p=0;
98
	}
99

100
	cp = atcode(sp, str2, sizeof(str2), &pmode);
101
	if(cp==NULL)
102
103
		return(0);

rswindell's avatar
rswindell committed
104
105
106
107
108
109
110
111
112
113
114
	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;
		}
	}
115
116
117
118
119
120
	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);
	}
121
	if(padded_left)
122
		bprintf(pmode, "%-*.*s",disp_len,disp_len,cp);
123
	else if(padded_right)
124
		bprintf(pmode, "%*.*s",disp_len,disp_len,cp);
125
	else if(centered) {
rswindell's avatar
rswindell committed
126
		int vlen = strlen(cp);
127
128
		if(vlen < disp_len) {
			int left = (disp_len - vlen) / 2;
129
			bprintf(pmode, "%*s%-*s", left, "", disp_len - left, cp);
130
		} else
131
			bprintf(pmode, "%.*s", disp_len, cp);
rswindell's avatar
rswindell committed
132
133
	} else if(doubled) {
		wide(cp);
134
	} else if(zero_padded) {
135
136
		int vlen = strlen(cp);
		if(vlen < disp_len)
137
			bprintf(pmode, "%-.*s%s", (int)(disp_len - strlen(cp)), "0000000000", cp);
138
		else
139
			bprintf(pmode, "%.*s", disp_len, cp);
140
	} else
141
		bprintf(pmode, "%.*s", disp_len, cp);
142
143
144
145

	return(len);
}

rswindell's avatar
rswindell committed
146
147
148
149
150
151
152
153
154
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;
}

155
const char* sbbs_t::atcode(char* sp, char* str, size_t maxlen, long* pmode)
156
{
157
	char*	tp = NULL;
158
	uint	i;
159
160
	uint	ugrp;
	uint	usub;
161
162
163
164
165
166
167
	long	l;
    stats_t stats;
    node_t  node;
	struct	tm tm;

	str[0]=0;

168
169
	if(strncmp(sp, "U+", 2) == 0) {	// UNICODE
		enum unicode_codepoint codepoint = (enum unicode_codepoint)strtoul(sp + 2, &tp, 16);
rswindell's avatar
rswindell committed
170
		if(tp == NULL || *tp == 0)
171
			outchar(codepoint, unicode_to_cp437(codepoint));
rswindell's avatar
rswindell committed
172
173
		else if(*tp == ':')
			outchar(codepoint, tp + 1);
174
175
		else {
			char fallback = (char)strtoul(tp + 1, NULL, 16);
rswindell's avatar
rswindell committed
176
			if(*tp == ',')
177
178
179
180
181
182
183
184
185
186
187
188
				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;
	}

189
190
191
192
193
	if(strcmp(sp, "CHECKMARK") == 0) {
		outchar(UNICODE_CHECK_MARK, CP437_CHECK_MARK);
		return nulstr;
	}

rswindell's avatar
rswindell committed
194
195
196
197
	if(strcmp(sp, "ELLIPSIS") == 0) {
		outchar(UNICODE_HORIZONTAL_ELLIPSIS, "...");
		return nulstr;
	}
198
	if(strcmp(sp, "COPY") == 0) {
rswindell's avatar
rswindell committed
199
200
201
202
203
204
205
		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
206
207
208
209
	if(strcmp(sp, "REGISTERED") == 0) {
		outchar(UNICODE_REGISTERED_SIGN, "(R)");
		return nulstr;
	}
rswindell's avatar
rswindell committed
210
211
212
213
214
	if(strcmp(sp, "TRADEMARK") == 0) {
		outchar(UNICODE_TRADE_MARK_SIGN, "(TM)");
		return nulstr;
	}
	if(strcmp(sp, "DEGREE_C") == 0) {
rswindell's avatar
rswindell committed
215
		outchar(UNICODE_DEGREE_CELSIUS, "\xF8""C");
rswindell's avatar
rswindell committed
216
217
218
		return nulstr;
	}
	if(strcmp(sp, "DEGREE_F") == 0) {
rswindell's avatar
rswindell committed
219
		outchar(UNICODE_DEGREE_FAHRENHEIT, "\xF8""F");
rswindell's avatar
rswindell committed
220
221
222
		return nulstr;
	}

rswindell's avatar
rswindell committed
223
224
225
226
227
	if(strncmp(sp, "WIDE:", 5) == 0) {
		wide(sp + 5);
		return(nulstr);
	}

228
	if(!strcmp(sp,"VER"))
229
		return(VERSION);
230

231
	if(!strcmp(sp,"REV")) {
232
		safe_snprintf(str,maxlen,"%c",REVISION);
233
234
		return(str);
	}
235

236
	if(!strcmp(sp,"FULL_VER")) {
237
		safe_snprintf(str,maxlen,"%s%c%s",VERSION,REVISION,beta_version);
238
		truncsp(str);
239
#if defined(_DEBUG)
240
		strcat(str," Debug");
241
#endif
242
		return(str);
243
244
	}

245
	if(!strcmp(sp,"VER_NOTICE"))
246
		return(VERSION_NOTICE);
247

248
249
	if(!strcmp(sp,"OS_VER"))
		return(os_version(str));
250
251

#ifdef JAVASCRIPT
252
253
	if(!strcmp(sp,"JS_VER"))
		return((char *)JS_GetImplementationVersion());
254
255
#endif

256
257
	if(!strcmp(sp,"PLATFORM"))
		return(PLATFORM_DESC);
258

259
260
	if(!strcmp(sp,"COPYRIGHT"))
		return(COPYRIGHT_NOTICE);
261

262
	if(!strcmp(sp,"COMPILER")) {
263
264
265
		char compiler[32];
		DESCRIBE_COMPILER(compiler);
		strncpy(str, compiler, maxlen);
266
		return(str);
267
268
	}

269
	if(!strcmp(sp,"UPTIME")) {
270
		extern volatile time_t uptime;
271
272
273
274
		time_t up=0;
		now = time(NULL);
		if (uptime != 0 && now >= uptime)
			up = now-uptime;
275
276
		char   days[64]="";
		if((up/(24*60*60))>=2) {
277
	        sprintf(days,"%lu days ",(ulong)(up/(24L*60L*60L)));
278
279
			up%=(24*60*60);
		}
280
		safe_snprintf(str,maxlen,"%s%lu:%02lu"
281
	        ,days
282
283
			,(ulong)(up/(60L*60L))
			,(ulong)((up/60L)%60L)
284
			);
285
		return(str);
286
287
	}

288
	if(!strcmp(sp,"SERVED")) {
289
		extern volatile ulong served;
290
		safe_snprintf(str,maxlen,"%lu",served);
291
292
293
		return(str);
	}

294
	if(!strcmp(sp,"SOCKET_LIB"))
295
		return(socklib_version(str,SOCKLIB_DESC));
296

297
	if(!strcmp(sp,"MSG_LIB")) {
298
		safe_snprintf(str,maxlen,"SMBLIB %s",smb_lib_ver());
299
300
		return(str);
	}
301

302
303
	if(!strcmp(sp,"BBS") || !strcmp(sp,"BOARDNAME"))
		return(cfg.sys_name);
304

305
	if(!strcmp(sp,"BAUD") || !strcmp(sp,"BPS")) {
306
		safe_snprintf(str,maxlen,"%lu",cur_output_rate ? cur_output_rate : cur_rate);
307
308
		return(str);
	}
309

rswindell's avatar
rswindell committed
310
311
312
313
314
315
316
317
	if(!strcmp(sp,"COLS")) {
		safe_snprintf(str,maxlen,"%lu",cols);
		return(str);
	}
	if(!strcmp(sp,"ROWS")) {
		safe_snprintf(str,maxlen,"%lu",rows);
		return(str);
	}
318
319
320
321
322
	if(strcmp(sp,"TERM") == 0)
		return term_type();

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

324
325
	if(!strcmp(sp,"CONN"))
		return(connection);
326

327
328
	if(!strcmp(sp,"SYSOP"))
		return(cfg.sys_op);
329

330
331
	if(!strcmp(sp,"LOCATION"))
		return(cfg.sys_location);
332

333
	if(strcmp(sp,"NODE") == 0 || strcmp(sp,"NN") == 0) {
334
		safe_snprintf(str,maxlen,"%u",cfg.node_num);
335
336
		return(str);
	}
337
	if(strcmp(sp, "TNODES") == 0 || strcmp(sp, "TNODE") == 0 || strcmp(sp, "TN") == 0) {
338
		safe_snprintf(str,maxlen,"%u",cfg.sys_nodes);
339
340
		return(str);
	}
341
342
343
344
345
346
347
348
	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;
	}
349

rswindell's avatar
rswindell committed
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
	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];
	}

366
367
368
369
370
371
	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];

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

375
376
	if(!strcmp(sp,"INETADDR"))
		return(cfg.sys_inetaddr);
377

378
379
380
	if(!strcmp(sp,"HOSTNAME"))
		return(startup->host_name);

381
	if(!strcmp(sp,"FIDOADDR")) {
382
		if(cfg.total_faddrs)
383
			return(smb_faddrtoa(&cfg.faddr[0],str));
384
		return(nulstr);
385
386
	}

387
	if(!strcmp(sp,"EMAILADDR"))
388
		return(usermailaddr(&cfg, str
389
			,cfg.inetmail_misc&NMAIL_ALIAS ? useron.alias : useron.name));
390

391
392
	if(!strcmp(sp,"QWKID"))
		return(cfg.sys_id);
393

394
	if(!strcmp(sp,"TIME") || !strcmp(sp,"SYSTIME")) {
395
		now=time(NULL);
396
		memset(&tm,0,sizeof(tm));
397
		localtime_r(&now,&tm);
398
		if(cfg.sys_misc&SM_MILITARY)
399
400
			safe_snprintf(str,maxlen,"%02d:%02d:%02d"
		        	,tm.tm_hour,tm.tm_min,tm.tm_sec);
401
		else
402
			safe_snprintf(str,maxlen,"%02d:%02d %s"
403
404
405
				,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");
406
		return(str);
407
408
	}

rswindell's avatar
rswindell committed
409
410
411
	if(!strcmp(sp,"TIMEZONE"))
		return(smb_zonestr(sys_timezone(&cfg),str));

412
	if(!strcmp(sp,"DATE") || !strcmp(sp,"SYSDATE")) {
413
		return(unixtodstr(&cfg,time32(NULL),str));
414
	}
415

rswindell's avatar
rswindell committed
416
417
418
	if(!strcmp(sp,"DATETIME"))
		return(timestr(time(NULL)));

rswindell's avatar
rswindell committed
419
420
421
422
423
	if(!strcmp(sp,"DATETIMEZONE")) {
		char zone[32];
		safe_snprintf(str, maxlen, "%s %s", timestr(time(NULL)), smb_zonestr(sys_timezone(&cfg),zone));
		return str;
	}
424
425
426
427
	
	if(strcmp(sp, "DATEFMT") == 0) {
		return cfg.sys_misc&SM_EURODATE ? "DD/MM/YY" : "MM/DD/YY";
	}
rswindell's avatar
rswindell committed
428

429
	if(!strcmp(sp,"TMSG")) {
430
431
		l=0;
		for(i=0;i<cfg.total_subs;i++)
432
			l+=getposts(&cfg,i); 		/* l=total posts */
433
		safe_snprintf(str,maxlen,"%lu",l);
434
435
		return(str);
	}
436

437
	if(!strcmp(sp,"TUSER")) {
438
		safe_snprintf(str,maxlen,"%u",total_users(&cfg));
439
440
		return(str);
	}
441

442
	if(!strcmp(sp,"TFILE")) {
443
444
		l=0;
		for(i=0;i<cfg.total_dirs;i++)
445
			l+=getfiles(&cfg,i);
446
		safe_snprintf(str,maxlen,"%lu",l);
447
448
		return(str);
	}
449

450
	if(strncmp(sp, "FILES:", 6) == 0) {	// Number of files in specified directory
rswindell's avatar
rswindell committed
451
		const char* path = getpath(&cfg, sp + 6);
452
		safe_snprintf(str, maxlen, "%lu", getfilecount(path));
rswindell's avatar
rswindell committed
453
		return str;
454
455
456
457
458
459
460
	}

	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
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
	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;
	}

521
	if(!strcmp(sp,"TCALLS") || !strcmp(sp,"NUMCALLS")) {
522
		getstats(&cfg,0,&stats);
523
		safe_snprintf(str,maxlen,"%lu", (ulong)stats.logons);
524
525
		return(str);
	}
526

527
	if(!strcmp(sp,"PREVON") || !strcmp(sp,"LASTCALLERNODE")
528
		|| !strcmp(sp,"LASTCALLERSYSTEM"))
529
		return(lastuseron);
530

rswindell's avatar
rswindell committed
531
	if(!strcmp(sp,"CLS") || !strcmp(sp,"CLEAR")) {
532
		CLS;
533
534
		return(nulstr);
	}
535

536
	if(!strcmp(sp,"PAUSE") || !strcmp(sp,"MORE")) {
537
		pause();
538
539
		return(nulstr);
	}
540

541
	if(!strcmp(sp,"RESETPAUSE")) {
542
		lncntr=0;
543
544
		return(nulstr);
	}
545

546
	if(!strcmp(sp,"NOPAUSE") || !strcmp(sp,"POFF")) {
547
		sys_status^=SS_PAUSEOFF;
548
549
		return(nulstr);
	}
550

551
	if(!strcmp(sp,"PON") || !strcmp(sp,"AUTOMORE")) {
552
		sys_status^=SS_PAUSEON;
553
554
		return(nulstr);
	}
555

rswindell's avatar
rswindell committed
556
557
558
559
560
561
562
	if(strncmp(sp, "FILL:", 5) == 0) {
		sp += 5;
		while(*sp && online && column < cols - 1)
			bputs(sp, P_TRUNCATE);
		return nulstr;
	}

563
564
565
566
567
	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--)
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
			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;
	}

592
593
594
595
596
	if(strncmp(sp, "BPS:", 4) == 0) {
		set_output_rate((enum output_rate)atoi(sp + 4));
		return nulstr;
	}

597
598
599
600
	/* NOSTOP */

	/* STOP */

601
602
	if(!strcmp(sp,"BELL") || !strcmp(sp,"BEEP"))
		return("\a");
603

604
605
606
	if(!strcmp(sp,"EVENT")) {
		if(event_time==0)
			return("<none>");
607
		return(timestr(event_time));
608
	}
609
610
611

	/* LASTCALL */

612
	if(!strncmp(sp,"NODE",4)) {
613
614
615
		i=atoi(sp+4);
		if(i && i<=cfg.sys_nodes) {
			getnodedat(i,&node,0);
616
617
			printnodedat(i,&node);
		}
618
619
		return(nulstr);
	}
620

621
	if(!strcmp(sp,"WHO")) {
622
		whos_online(true);
623
624
		return(nulstr);
	}
625
626
627

	/* User Codes */

628
629
	if(!strcmp(sp,"USER") || !strcmp(sp,"ALIAS") || !strcmp(sp,"NAME"))
		return(useron.alias);
630

631
	if(!strcmp(sp,"FIRST")) {
632
		safe_snprintf(str,maxlen,"%s",useron.alias);
633
		tp=strchr(str,' ');
634
		if(tp) *tp=0;
635
		return(str);
636
	}
637

638
	if(!strcmp(sp,"USERNUM")) {
639
		safe_snprintf(str,maxlen,"%u",useron.number);
640
641
		return(str);
	}
642

643
	if(!strcmp(sp,"PHONE") || !strcmp(sp,"HOMEPHONE")
644
		|| !strcmp(sp,"DATAPHONE") || !strcmp(sp,"DATA"))
645
		return(useron.phone);
646

647
648
	if(!strcmp(sp,"ADDR1"))
		return(useron.address);
649

650
651
	if(!strcmp(sp,"FROM"))
		return(useron.location);
652

653
	if(!strcmp(sp,"CITY")) {
654
		safe_snprintf(str,maxlen,"%s",useron.location);
655
		char* p=strchr(str,',');
656
657
		if(p) {
			*p=0;
658
659
			return(str);
		}
660
661
		return(nulstr);
	}
662

663
664
	if(!strcmp(sp,"STATE")) {
		char* p=strchr(useron.location,',');
665
666
		if(p) {
			p++;
667
			if(*p==' ')
668
				p++;
669
670
			return(p);
		}
671
672
		return(nulstr);
	}
673

674
675
	if(!strcmp(sp,"CPU"))
		return(useron.comp);
676

677
678
	if(!strcmp(sp,"HOST"))
		return(client_name);
679

680
681
	if(!strcmp(sp,"BDATE"))
		return(useron.birth);
682

683
	if(!strcmp(sp,"AGE")) {
684
		safe_snprintf(str,maxlen,"%u",getage(&cfg,useron.birth));
685
686
		return(str);
	}
rswindell's avatar
rswindell committed
687

688
	if(!strcmp(sp,"CALLS") || !strcmp(sp,"NUMTIMESON")) {
689
		safe_snprintf(str,maxlen,"%u",useron.logons);
690
691
		return(str);
	}
692

rswindell's avatar
rswindell committed
693
694
695
696
697
698
699
	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)
700
		return(unixtodstr(&cfg,useron.pwmod,str));
701

702
	if(!strcmp(sp,"SEC") || !strcmp(sp,"SECURITY")) {
703
		safe_snprintf(str,maxlen,"%u",useron.level);
704
705
		return(str);
	}
706

707
708
	if(!strcmp(sp,"SINCE"))
		return(unixtodstr(&cfg,useron.firston,str));
709

710
	if(!strcmp(sp,"TIMEON") || !strcmp(sp,"TIMEUSED")) {
711
		now=time(NULL);
712
		safe_snprintf(str,maxlen,"%lu",(ulong)(now-logontime)/60L);
713
714
		return(str);
	}
715

716
	if(!strcmp(sp,"TUSED")) {              /* Synchronet only */
717
		now=time(NULL);
718
		return(sectostr((uint)(now-logontime),str)+1);
719
	}
720

721
	if(!strcmp(sp,"TLEFT")) {              /* Synchronet only */
722
		gettimeleft();
723
		return(sectostr(timeleft,str)+1);
724
	}
725

726
727
	if(!strcmp(sp,"TPERD"))                /* Synchronet only */
		return(sectostr(cfg.level_timeperday[useron.level],str)+1);
728

729
730
	if(!strcmp(sp,"TPERC"))                /* Synchronet only */
		return(sectostr(cfg.level_timepercall[useron.level],str)+1);
731

732
	if(!strcmp(sp,"TIMELIMIT")) {
733
		safe_snprintf(str,maxlen,"%u",cfg.level_timepercall[useron.level]);
734
735
		return(str);
	}
736

737
	if(!strcmp(sp,"MINLEFT") || !strcmp(sp,"LEFT") || !strcmp(sp,"TIMELEFT")) {
738
		gettimeleft();
739
		safe_snprintf(str,maxlen,"%lu",timeleft/60);
740
741
		return(str);
	}
742

743
	if(!strcmp(sp,"LASTON"))
744
		return(timestr(useron.laston));
745

746
747
	if(!strcmp(sp,"LASTDATEON"))
		return(unixtodstr(&cfg,useron.laston,str));
748

749
	if(!strcmp(sp,"LASTTIMEON")) {
750
		memset(&tm,0,sizeof(tm));
751
		localtime32(&useron.laston,&tm);
752
753
754
755
756
757
758
759
		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");
760
		return(str);
761
762
	}

763
	if(!strcmp(sp,"MSGLEFT") || !strcmp(sp,"MSGSLEFT")) {
764
		safe_snprintf(str,maxlen,"%u",useron.posts);
765
766
		return(str);
	}
767

768
	if(!strcmp(sp,"MSGREAD")) {
769
		safe_snprintf(str,maxlen,"%lu",posts_read);
770
771
		return(str);
	}
772

773
	if(!strcmp(sp,"FREESPACE")) {
774
		safe_snprintf(str,maxlen,"%lu",getfreediskspace(cfg.temp_dir,0));
775
776
777
778
		return(str);
	}

	if(!strcmp(sp,"FREESPACEK")) {
779
		safe_snprintf(str,maxlen,"%lu",getfreediskspace(cfg.temp_dir,1024));
780
781
		return(str);
	}
782

783
	if(!strcmp(sp,"UPBYTES")) {
784
		safe_snprintf(str,maxlen,"%lu",useron.ulb);
785
786
		return(str);
	}
787

788
	if(!strcmp(sp,"UPK")) {
789
		safe_snprintf(str,maxlen,"%lu",useron.ulb/1024L);
790
791
		return(str);
	}
792

793
	if(!strcmp(sp,"UPS") || !strcmp(sp,"UPFILES")) {
794
		safe_snprintf(str,maxlen,"%u",useron.uls);
795
796
		return(str);
	}
797

798
	if(!strcmp(sp,"DLBYTES")) {
799
		safe_snprintf(str,maxlen,"%lu",useron.dlb);
800
801
		return(str);
	}
802

803
	if(!strcmp(sp,"DOWNK")) {
804
		safe_snprintf(str,maxlen,"%lu",useron.dlb/1024L);
805
806
		return(str);
	}
807

808
	if(!strcmp(sp,"DOWNS") || !strcmp(sp,"DLFILES")) {
809
		safe_snprintf(str,maxlen,"%u",useron.dls);
810
811
		return(str);
	}
812

813
	if(!strcmp(sp,"LASTNEW"))
814
		return(unixtodstr(&cfg,(time32_t)ns_time,str));
815

816
	if(!strcmp(sp,"NEWFILETIME"))
817
		return(timestr(ns_time));
818
819
820

	/* MAXDL */

821
	if(!strcmp(sp,"MAXDK") || !strcmp(sp,"DLKLIMIT") || !strcmp(sp,"KBLIMIT")) {
822
		safe_snprintf(str,maxlen,"%lu",cfg.level_freecdtperday[useron.level]/1024L);
823
824
		return(str);
	}
825

826
	if(!strcmp(sp,"DAYBYTES")) {    /* amt of free cdts used today */
827
		safe_snprintf(str,maxlen,"%lu",cfg.level_freecdtperday[useron.level]-useron.freecdt);
828
829
		return(str);
	}
830

831
	if(!strcmp(sp,"BYTELIMIT")) {
832
		safe_snprintf(str,maxlen,"%ld", (long)cfg.level_freecdtperday[useron.level]);
833
834
		return(str);
	}
835

836
	if(!strcmp(sp,"KBLEFT")) {
837
		safe_snprintf(str,maxlen,"%lu",(useron.cdt+useron.freecdt)/1024L);
838
839
		return(str);
	}
840

841
	if(!strcmp(sp,"BYTESLEFT")) {
842
		safe_snprintf(str,maxlen,"%lu",useron.cdt+useron.freecdt);
843
844
		return(str);
	}
845

846
	if(!strcmp(sp,"CONF")) {
847
		safe_snprintf(str,maxlen,"%s %s"
848
849
			,usrgrps ? cfg.grp[usrgrp[curgrp]]->sname :nulstr
			,usrgrps ? cfg.sub[usrsub[curgrp][cursub[curgrp]]]->sname : nulstr);
850
851
		return(str);
	}
852

853
	if(!strcmp(sp,"CONFNUM")) {
854
		safe_snprintf(str,maxlen,"%u %u",curgrp+1,cursub[curgrp]+1);
855
856
		return(str);
	}
857

858
	if(!strcmp(sp,"NUMDIR")) {
859
		safe_snprintf(str,maxlen,"%u %u",usrlibs ? curlib+1 : 0,usrlibs ? curdir[curlib]+1 : 0);
860
861
		return(str);
	}
862

863
864
	if(!strcmp(sp,"EXDATE") || !strcmp(sp,"EXPDATE"))
		return(unixtodstr(&cfg,useron.expire,str));
865

866
	if(!strcmp(sp,"EXPDAYS")) {
867
		now=time(NULL);
868
		l=(long)(useron.expire-now);
869
870
		if(l<0)
			l=0;