Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

listfile.cpp 39.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/* listfile.cpp */

/* Synchronet file database listing functions */

/* $Id$ */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
11
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
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 39 40 41 42 43 44 45 46 47 48 49 50
 *																			*
 * 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"

#define BF_MAX	26	/* Batch Flag max: A-Z */	

int extdesclines(char *str);

/*****************************************************************************/
/* List files in directory 'dir' that match 'filespec'. Filespec must be 	 */
/* padded. ex: FILE*   .EXT, not FILE*.EXT. 'mode' determines other critiria */
/* the files must meet before they'll be listed. 'mode' bit FL_NOHDR doesn't */
/* list the directory header.                                                */
/* Returns -1 if the listing was aborted, otherwise total files listed		 */
/*****************************************************************************/
51
int sbbs_t::listfiles(uint dirnum, const char *filespec, int tofile, long mode)
52
{
53
	char	str[256],hdr[256],letter='A',*p,*datbuf,ext[513];
54
	char 	tmp[512];
55 56
	uchar*	ixbbuf;
	uchar	flagprompt=0;
57
	int		c, d;
58 59
	uint	i,j;
	int		file,found=0,lastbat=0,disp;
deuce's avatar
64-bit  
deuce committed
60 61
	long	m=0,n,anchor=0,next,datbuflen;
	int32_t	l;
62 63 64 65
	file_t	f,bf[26];	/* bf is batch flagged files */

	if(mode&FL_ULTIME) {
		last_ns_time=now;
66
		sprintf(str,"%s%s.dab",cfg.dir[dirnum]->data_dir,cfg.dir[dirnum]->code);
67 68 69 70
		if((file=nopen(str,O_RDONLY))!=-1) {
			read(file,&l,4);
			close(file);
			if(ns_time>(time_t)l)
71 72 73
				return(0); 
		}
	}
74
	sprintf(str,"%s%s.ixb",cfg.dir[dirnum]->data_dir,cfg.dir[dirnum]->code);
75 76
	if((file=nopen(str,O_RDONLY))==-1)
		return(0);
77
	l=(long)filelength(file);
78 79
	if(!l) {
		close(file);
80 81
		return(0); 
	}
deuce's avatar
deuce committed
82
	if((ixbbuf=(uchar *)malloc(l))==NULL) {
83 84
		close(file);
		errormsg(WHERE,ERR_ALLOC,str,l);
85 86
		return(0); 
	}
87 88 89
	if(lread(file,ixbbuf,l)!=l) {
		close(file);
		errormsg(WHERE,ERR_READ,str,l);
deuce's avatar
deuce committed
90
		free((char *)ixbbuf);
91 92
		return(0); 
	}
93
	close(file);
94
	sprintf(str,"%s%s.dat",cfg.dir[dirnum]->data_dir,cfg.dir[dirnum]->code);
95 96
	if((file=nopen(str,O_RDONLY))==-1) {
		errormsg(WHERE,ERR_OPEN,str,O_RDONLY);
deuce's avatar
deuce committed
97
		free((char *)ixbbuf);
98 99
		return(0); 
	}
100
	datbuflen=(long)filelength(file);
deuce's avatar
deuce committed
101
	if((datbuf=(char *)malloc(datbuflen))==NULL) {
102 103
		close(file);
		errormsg(WHERE,ERR_ALLOC,str,datbuflen);
deuce's avatar
deuce committed
104
		free((char *)ixbbuf);
105 106
		return(0); 
	}
107 108 109
	if(lread(file,datbuf,datbuflen)!=datbuflen) {
		close(file);
		errormsg(WHERE,ERR_READ,str,datbuflen);
deuce's avatar
deuce committed
110 111
		free((char *)datbuf);
		free((char *)ixbbuf);
112 113
		return(0); 
	}
114 115 116 117 118
	close(file);
	if(!tofile) {
		action=NODE_LFIL;
		getnodedat(cfg.node_num,&thisnode,0);
		if(thisnode.action!=NODE_LFIL) {	/* was a sync */
119 120 121 122 123 124
			if(getnodedat(cfg.node_num,&thisnode,true)==0) {
				thisnode.action=NODE_LFIL;
				putnodedat(cfg.node_num,&thisnode); 
			} 
		} 
	}
125 126 127 128 129 130 131 132 133 134 135 136

	while(online && found<MAX_FILES) {
		if(found<0)
			found=0;
		if(m>=l || flagprompt) {		  /* End of list */
			if(useron.misc&BATCHFLAG && !tofile && found && found!=lastbat
				&& !(mode&(FL_EXFIND|FL_VIEW))) {
				flagprompt=0;
				lncntr=0;
				if((i=batchflagprompt(dirnum,bf,letter-'A',l/F_IXBSIZE))==2) {
					m=anchor;
					found-=letter-'A';
137 138
					letter='A'; 
				}
139 140 141
				else if(i==3) {
					if((long)anchor-((letter-'A')*F_IXBSIZE)<0) {
						m=0;
142 143
						found=0; 
					}
144 145
					else {
						m=anchor-((letter-'A')*F_IXBSIZE);
146 147 148 149
						found-=letter-'A'; 
					}
					letter='A'; 
				}
150
				else if((int)i==-1) {
deuce's avatar
deuce committed
151 152
					free((char *)ixbbuf);
					free((char *)datbuf);
153 154
					return(-1); 
				}
155 156 157
				else
					break;
				getnodedat(cfg.node_num,&thisnode,0);
158 159
				nodesync(); 
			}
160
			else
161 162
				break; 
		}
163 164 165 166 167 168 169

		if(letter>'Z')
			letter='A';
		if(letter=='A')
			anchor=m;

		if(msgabort()) {		 /* used to be !tofile && msgabort() */
deuce's avatar
deuce committed
170 171
			free((char *)ixbbuf);
			free((char *)datbuf);
172 173
			return(-1); 
		}
174
		for(j=0;j<12 && m<l;j++)
175 176
			if(j==8)
				str[j]=ixbbuf[m]>' ' ? '.' : ' ';
177 178 179 180 181 182
			else
				str[j]=ixbbuf[m++];		/* Turns FILENAMEEXT into FILENAME.EXT */
		str[j]=0;
		if(!(mode&(FL_FINDDESC|FL_EXFIND)) && filespec[0]
			&& !filematch(str,filespec)) {
			m+=11;
183 184
			continue; 
		}
185 186 187
		n=ixbbuf[m]|((long)ixbbuf[m+1]<<8)|((long)ixbbuf[m+2]<<16);
		if(n>=datbuflen) {	/* out of bounds */
			m+=11;
188 189
			continue; 
		}
190 191 192 193 194 195
		if(mode&(FL_FINDDESC|FL_EXFIND)) {
			getrec((char *)&datbuf[n],F_DESC,LEN_FDESC,tmp);
			strupr(tmp);
			p=strstr(tmp,filespec);
			if(!(mode&FL_EXFIND) && p==NULL) {
				m+=11;
196 197
				continue; 
			}
198 199
			getrec((char *)&datbuf[n],F_MISC,1,tmp);
			j=tmp[0];  /* misc bits */
200
			if(j) j-=' ';
201
			if(mode&FL_EXFIND && j&FM_EXTDESC) { /* search extended description */
202
				getextdesc(&cfg,dirnum,n,ext);
203 204 205
				strupr(ext);
				if(!strstr(ext,filespec) && !p) {	/* not in description or */
					m+=11;						 /* extended description */
206 207 208
					continue; 
				}
			}
209 210
			else if(!p) {			 /* no extended description and not in desc */
				m+=11;
211 212
				continue; } 
		}
213 214 215 216
		if(mode&FL_ULTIME) {
			if(ns_time>(ixbbuf[m+3]|((long)ixbbuf[m+4]<<8)|((long)ixbbuf[m+5]<<16)
				|((long)ixbbuf[m+6]<<24))) {
				m+=11;
217 218
				continue; } 
		}
219 220 221 222 223 224 225 226 227 228 229 230 231 232
		if(useron.misc&BATCHFLAG && letter=='A' && found && !tofile
			&& !(mode&(FL_EXFIND|FL_VIEW))
			&& (!mode || !(useron.misc&EXPERT)))
			bputs(text[FileListBatchCommands]);
		m+=11;
		if(!found && !(mode&(FL_EXFIND|FL_VIEW))) {
			for(i=0;i<usrlibs;i++)
				if(usrlib[i]==cfg.dir[dirnum]->lib)
					break;
			for(j=0;j<usrdirs[i];j++)
				if(usrdir[i][j]==dirnum)
					break;						/* big header */
			if((!mode || !(useron.misc&EXPERT)) && !tofile && (!filespec[0]
				|| (strchr(filespec,'*') || strchr(filespec,'?')))) {
233
				sprintf(hdr,"%s%s.hdr",cfg.dir[dirnum]->data_dir,cfg.dir[dirnum]->code);
234
				if(fexistcase(hdr))
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
					printfile(hdr,0);	/* Use DATA\DIRS\<CODE>.HDR */
				else {
					if(useron.misc&BATCHFLAG)
						bputs(text[FileListBatchCommands]);
					else {
						CLS;
						d=strlen(cfg.lib[usrlib[i]]->lname)>strlen(cfg.dir[dirnum]->lname) ?
							strlen(cfg.lib[usrlib[i]]->lname)+17
							: strlen(cfg.dir[dirnum]->lname)+17;
						if(i>8 || j>8) d++;
						attr(cfg.color[clr_filelsthdrbox]);
						bputs("");            /* use to start with \r\n */
						for(c=0;c<d;c++)
							outchar('');
						bputs("\r\n ");
						sprintf(hdr,text[BoxHdrLib],i+1,cfg.lib[usrlib[i]]->lname);
						bputs(hdr);
						for(c=bstrlen(hdr);c<d;c++)
253
							outchar(' ');
254
						attr(cfg.color[clr_filelsthdrbox]);
255 256 257 258
						bputs("\r\n ");
						sprintf(hdr,text[BoxHdrDir],j+1,cfg.dir[dirnum]->lname);
						bputs(hdr);
						for(c=bstrlen(hdr);c<d;c++)
259
							outchar(' ');
260
						attr(cfg.color[clr_filelsthdrbox]);
261 262 263 264
						bputs("\r\n ");
						sprintf(hdr,text[BoxHdrFiles],l/F_IXBSIZE);
						bputs(hdr);
						for(c=bstrlen(hdr);c<d;c++)
265
							outchar(' ');
266
						attr(cfg.color[clr_filelsthdrbox]);
267 268 269
						bputs("\r\n");
						for(c=0;c<d;c++)
							outchar('');
270
						bputs("\r\n"); 
271 272
					}
				}
273
			}
274 275 276 277
			else {					/* short header */
				if(tofile) {
					sprintf(hdr,"(%u) %s ",i+1,cfg.lib[usrlib[i]]->sname);
					write(tofile,crlf,2);
278 279
					write(tofile,hdr,strlen(hdr)); 
				}
280 281 282
				else {
					sprintf(hdr,text[ShortHdrLib],i+1,cfg.lib[usrlib[i]]->sname);
					bputs("\r\1>\r\n");
283 284
					bputs(hdr); 
				}
285 286 287
				c=bstrlen(hdr);
				if(tofile) {
					sprintf(hdr,"(%u) %s",j+1,cfg.dir[dirnum]->lname);
288 289
					write(tofile,hdr,strlen(hdr)); 
				}
290 291
				else {
					sprintf(hdr,text[ShortHdrDir],j+1,cfg.dir[dirnum]->lname);
292 293
					bputs(hdr); 
				}
294 295 296 297
				c+=bstrlen(hdr);
				if(tofile) {
					write(tofile,crlf,2);
					sprintf(hdr,"%*s",c,nulstr);
rswindell's avatar
rswindell committed
298
					memset(hdr,0xC4,c);
299
					strcat(hdr,crlf);
300 301
					write(tofile,hdr,strlen(hdr)); 
				}
302 303 304 305
				else {
					CRLF;
					attr(cfg.color[clr_filelstline]);
					while(c--)
rswindell's avatar
rswindell committed
306
						outchar('\xC4');
307 308 309 310
					CRLF; 
				} 
			} 
		}
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
		next=m;
		disp=1;
		if(mode&(FL_EXFIND|FL_VIEW)) {
			f.dir=dirnum;
			strcpy(f.name,str);
			m-=11;
			f.datoffset=n;
			f.dateuled=ixbbuf[m+3]|((long)ixbbuf[m+4]<<8)
				|((long)ixbbuf[m+5]<<16)|((long)ixbbuf[m+6]<<24);
			f.datedled=ixbbuf[m+7]|((long)ixbbuf[m+8]<<8)
				|((long)ixbbuf[m+9]<<16)|((long)ixbbuf[m+10]<<24);
			m+=11;
			f.size=0;
			getfiledat(&cfg,&f);
			if(!found)
				bputs("\r\1>");
			if(mode&FL_EXFIND) {
				if(!viewfile(&f,1)) {
deuce's avatar
deuce committed
329 330
					free((char *)ixbbuf);
					free((char *)datbuf);
331 332
					return(-1); } 
			}
333 334
			else {
				if(!viewfile(&f,0)) {
deuce's avatar
deuce committed
335 336
					free((char *)ixbbuf);
					free((char *)datbuf);
337 338 339 340
					return(-1); 
				} 
			} 
		}
341 342 343 344 345 346 347 348 349

		else if(tofile)
			listfiletofile(str,&datbuf[n],dirnum,tofile);
		else if(mode&FL_FINDDESC)
			disp=listfile(str,&datbuf[n],dirnum,filespec,letter,n);
		else
			disp=listfile(str,&datbuf[n],dirnum,nulstr,letter,n);
		if(!disp && letter>'A') {
			next=m-F_IXBSIZE;
350 351
			letter--; 
		}
352 353
		else {
			disp=1;
354 355
			found++; 
		}
356
		if(sys_status&SS_ABORT) {
deuce's avatar
deuce committed
357 358
			free((char *)ixbbuf);
			free((char *)datbuf);
359 360
			return(-1); 
		}
361 362 363 364 365 366 367 368 369 370
		if(mode&(FL_EXFIND|FL_VIEW))
			continue;
		if(useron.misc&BATCHFLAG && !tofile) {
			if(disp) {
				strcpy(bf[letter-'A'].name,str);
				m-=11;
				bf[letter-'A'].datoffset=n;
				bf[letter-'A'].dateuled=ixbbuf[m+3]|((long)ixbbuf[m+4]<<8)
					|((long)ixbbuf[m+5]<<16)|((long)ixbbuf[m+6]<<24);
				bf[letter-'A'].datedled=ixbbuf[m+7]|((long)ixbbuf[m+8]<<8)
371 372
					|((long)ixbbuf[m+9]<<16)|((long)ixbbuf[m+10]<<24); 
			}
373 374 375 376 377 378 379 380 381 382
			m+=11;
			if(flagprompt || letter=='Z' || !disp ||
				(filespec[0] && !strchr(filespec,'*') && !strchr(filespec,'?')
				&& !(mode&FL_FINDDESC))
				|| (useron.misc&BATCHFLAG && !tofile && lncntr>=rows-2)
				) {
				flagprompt=0;
				lncntr=0;
				lastbat=found;
				if((int)(i=batchflagprompt(dirnum,bf,letter-'A'+1,l/F_IXBSIZE))<1) {
deuce's avatar
deuce committed
383 384
					free((char *)ixbbuf);
					free((char *)datbuf);
385
					if((int)i==-1)
386 387
						return(-1);
					else
388 389
						return(found); 
				}
390 391
				if(i==2) {
					next=anchor;
392 393
					found-=(letter-'A')+1; 
				}
394 395 396
				else if(i==3) {
					if((long)anchor-((letter-'A'+1)*F_IXBSIZE)<0) {
						next=0;
397 398
						found=0; 
					}
399 400
					else {
						next=anchor-((letter-'A'+1)*F_IXBSIZE);
401 402
						found-=letter-'A'+1; } 
				}
403 404 405
				getnodedat(cfg.node_num,&thisnode,0);
				nodesync();
				letter='A';	}
406 407
			else letter++; 
		}
408 409 410
		if(useron.misc&BATCHFLAG && !tofile
			&& lncntr>=rows-2) {
			lncntr=0;		/* defeat pause() */
411 412
			flagprompt=1; 
		}
413 414 415
		m=next;
		if(mode&FL_FINDDESC) continue;
		if(filespec[0] && !strchr(filespec,'*') && !strchr(filespec,'?') && m)
416 417
			break; 
	}
418

deuce's avatar
deuce committed
419 420
	free((char *)ixbbuf);
	free((char *)datbuf);
421 422 423 424 425 426 427
	return(found);
}

/****************************************************************************/
/* Prints one file's information on a single line                           */
/* Return 1 if displayed, 0 otherwise										*/
/****************************************************************************/
428 429
bool sbbs_t::listfile(const char *fname, const char *buf, uint dirnum
	, const char *search, const char letter, ulong datoffset)
430
{
431
	char	str[256],ext[513]="",*ptr,*cr,*lf,exist=1;
432
	char	path[MAX_PATH+1];
433 434 435 436
	char 	tmp[512];
    uchar	alt;
    int		i,j;
    ulong	cdt;
437
	off_t	size;
438
	int		size_attr=clr_filecdt;
439

440
	if(buf[F_MISC]!=ETX && (buf[F_MISC]-' ')&FM_EXTDESC && useron.misc&EXTDESC) {
441
		getextdesc(&cfg,dirnum,datoffset,ext);
442
		if(useron.misc&BATCHFLAG && lncntr+extdesclines(ext)>=rows-2 && letter!='A')
443 444
			return(false); 
	}
445

446 447
	attr(cfg.color[clr_filename]);
	bputs(fname);
448

449
	getrec(buf,F_ALTPATH,2,str);
450 451 452 453
	alt=(uchar)ahtoul(str);
	sprintf(path,"%s%s",alt>0 && alt<=cfg.altpaths ? cfg.altpath[alt-1]:cfg.dir[dirnum]->path
		,unpadfname(fname,tmp));

454
	if(buf[F_MISC]!=ETX && (buf[F_MISC]-' ')&FM_EXTDESC) {
455 456 457
		if(!(useron.misc&EXTDESC))
			outchar('+');
		else
458 459
			outchar(' '); 
	}
460
	else
461
		outchar(' ');
462 463
	if(useron.misc&BATCHFLAG) {
		attr(cfg.color[clr_filedesc]);
464 465
		bprintf("%c",letter); 
	}
466 467 468 469 470
	getrec(buf,F_CDT,LEN_FCDT,str);
	cdt=atol(str);
	if(cfg.dir[dirnum]->misc&DIR_FCHK) {
		if(!fexistcase(path)) {
			exist=0;
471
			size_attr = clr_err; 
472 473 474
		}
		else if((cfg.dir[dirnum]->misc&DIR_FREE) && (size=flength(path)) >= 0)
			cdt = size;
475
	}
476
	attr(cfg.color[size_attr]);
477
	if(useron.misc&BATCHFLAG) {
478
		if(!cdt && !(cfg.dir[dirnum]->misc&DIR_FREE)) {
479
			attr(curatr^(HIGH|BLINK));
480 481
			bputs("  FREE"); 
		}
482 483 484 485 486 487 488 489
		else if(cdt>=(1024*1024*1024))
			bprintf("%5.1fG",cdt/(1024.0*1024.0*1024.0));
		else if(cdt>=(1024*1024))
			bprintf("%5.1fM",cdt/(1024.0*1024.0));
		else if(cdt>=1024)
			bprintf("%5.1fK",cdt/1024.0); 
		else
			bprintf("%5luB", cdt);
490
	}
491
	else {
492
		if(!cdt && !(cfg.dir[dirnum]->misc&DIR_FREE)) {  /* FREE file */
493
			attr(curatr^(HIGH|BLINK));
494 495
			bputs("   FREE"); 
		}
496 497 498 499 500 501
		else if(cdt>=(1024*1024*1024))
			bprintf("%6.1fG",cdt/(1024.0*1024.0*1024.0));
		else if(cdt>=(1024*1024))
			bprintf("%6.1fM",cdt/(1024.0*1024.0));
		else if(cdt>=1024)
			bprintf("%6.1fK",cdt/1024.0); 
502
		else
503
			bprintf("%6luB", cdt);
504
	}
505
	if(exist)
506
		outchar(' ');
507 508
	else
		outchar('-');
509
	getrec(buf,F_DESC,LEN_FDESC,str);
510
	attr(cfg.color[clr_filedesc]);
511 512 513

#ifdef _WIN32
 
514
	if(exist && !(cfg.file_misc&FM_NO_LFN)) {
515 516
		fexistcase(path);	/* Get real (long?) filename */
		ptr=getfname(path);
517
		if(stricmp(ptr,tmp) && stricmp(ptr,str))
518
			bprintf("%.*s\r\n%21s",LEN_FDESC,ptr,"");
519
	}
520

521
#endif
522

523 524 525 526 527 528 529 530 531 532 533
	if(!ext[0]) {
		if(search[0]) { /* high-light string in string */
			strcpy(tmp,str);
			strupr(tmp);
			ptr=strstr(tmp,search);
			i=strlen(search);
			j=ptr-tmp;
			bprintf("%.*s",j,str);
			attr(cfg.color[clr_filedesc]^HIGH);
			bprintf("%.*s",i,str+j);
			attr(cfg.color[clr_filedesc]);
534
			bprintf("%.*s",(int)(strlen(str)-(j+i)),str+j+i); 
535
		}
536 537
		else
			bputs(str);
538 539
		CRLF; 
	}
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
	ptr=ext;
	while(*ptr && ptr<ext+512 && !msgabort()) {
		cr=strchr(ptr,CR);
		lf=strchr(ptr,LF);
		if(lf && (lf<cr || !cr)) cr=lf;
		if(cr>ptr+LEN_FDESC)
			cr=ptr+LEN_FDESC;
		else if(cr)
			*cr=0;
		sprintf(str,"%.*s\r\n",LEN_FDESC,ptr);
		putmsg(str,P_NOATCODES|P_SAVEATR);
		if(!cr) {
			if(strlen(ptr)>LEN_FDESC)
				cr=ptr+LEN_FDESC;
			else
555 556
				break; 
		}
557 558 559 560 561
		if(!(*(cr+1)) || !(*(cr+2)))
			break;
		bprintf("%21s",nulstr);
		ptr=cr;
		if(!(*ptr)) ptr++;
562 563
		while(*ptr==LF || *ptr==CR) ptr++; 
	}
564 565 566 567 568 569 570 571
	return(true);
}

/****************************************************************************/
/* Remove credits from uploader of file 'f'                                 */
/****************************************************************************/
bool sbbs_t::removefcdt(file_t* f)
{
572 573 574 575
	char	str[128];
	char 	tmp[512];
	int		u;
	long	cdt;
576

577
	if((u=matchuser(&cfg,f->uler,TRUE /*sysop_alias*/))==0) {
578
	   bputs(text[UnknownUser]);
579 580
	   return(false); 
	}
581 582 583 584 585 586 587 588 589 590 591 592
	cdt=0L;
	if(cfg.dir[f->dir]->misc&DIR_CDTMIN && cur_cps) {
		if(cfg.dir[f->dir]->misc&DIR_CDTUL)
			cdt=((ulong)(f->cdt*(cfg.dir[f->dir]->up_pct/100.0))/cur_cps)/60;
		if(cfg.dir[f->dir]->misc&DIR_CDTDL
			&& f->timesdled)  /* all downloads */
			cdt+=((ulong)((long)f->timesdled
				*f->cdt*(cfg.dir[f->dir]->dn_pct/100.0))/cur_cps)/60;
		adjustuserrec(&cfg,u,U_MIN,10,-cdt);
		sprintf(str,"%lu minute",cdt);
		sprintf(tmp,text[FileRemovedUserMsg]
			,f->name,cdt ? str : text[No]);
593 594
		putsmsg(&cfg,u,tmp); 
	}
595 596 597 598 599 600 601 602 603 604
	else {
		if(cfg.dir[f->dir]->misc&DIR_CDTUL)
			cdt=(ulong)(f->cdt*(cfg.dir[f->dir]->up_pct/100.0));
		if(cfg.dir[f->dir]->misc&DIR_CDTDL
			&& f->timesdled)  /* all downloads */
			cdt+=(ulong)((long)f->timesdled
				*f->cdt*(cfg.dir[f->dir]->dn_pct/100.0));
		adjustuserrec(&cfg,u,U_CDT,10,-cdt);
		sprintf(tmp,text[FileRemovedUserMsg]
			,f->name,cdt ? ultoac(cdt,str) : text[No]);
605 606
		putsmsg(&cfg,u,tmp); 
	}
607 608 609 610 611 612

	adjustuserrec(&cfg,u,U_ULB,10,-f->size);
	adjustuserrec(&cfg,u,U_ULS,5,-1);
	return(true);
}

613 614 615 616 617
bool sbbs_t::removefile(file_t* f)
{
	char str[256];

	if(removefiledat(&cfg,f)) {
618
		SAFEPRINTF3(str,"removed %s from %s %s"
619 620 621 622 623 624 625 626 627 628
			,f->name
			,cfg.lib[cfg.dir[f->dir]->lib]->sname,cfg.dir[f->dir]->sname);
		logline("U-",str);
		return(true);
	}
	SAFEPRINTF2(str,"%s %s",cfg.lib[cfg.dir[f->dir]->lib]->sname,cfg.dir[f->dir]->sname);
	errormsg(WHERE, ERR_REMOVE, f->name, 0, str);
	return(false);
}

629 630 631 632 633
/****************************************************************************/
/* Move file 'f' from f.dir to newdir                                       */
/****************************************************************************/
bool sbbs_t::movefile(file_t* f, int newdir)
{
634
	char str[MAX_PATH+1],path[MAX_PATH+1],fname[128],ext[1024];
635 636 637 638
	int olddir=f->dir;

	if(findfile(&cfg,newdir,f->name)) {
		bprintf(text[FileAlreadyThere],f->name);
639 640
		return(false); 
	}
641
	getextdesc(&cfg,olddir,f->datoffset,ext);
642
	if(cfg.dir[olddir]->misc&DIR_MOVENEW)
643
		f->dateuled=time32(NULL);
644 645 646 647 648 649
	unpadfname(f->name,fname);
	removefiledat(&cfg,f);
	f->dir=newdir;
	addfiledat(&cfg,f);
	bprintf(text[MovedFile],f->name
		,cfg.lib[cfg.dir[f->dir]->lib]->sname,cfg.dir[f->dir]->sname);
650
	sprintf(str,"moved %s to %s %s",f->name
651 652 653 654
		,cfg.lib[cfg.dir[f->dir]->lib]->sname,cfg.dir[f->dir]->sname);
	logline(nulstr,str);
	if(!f->altpath) {	/* move actual file */
		sprintf(str,"%s%s",cfg.dir[olddir]->path,fname);
655
		if(fexistcase(str)) {
656 657 658 659
			sprintf(path,"%s%s",cfg.dir[f->dir]->path,getfname(str));
			mv(str,path,0); 
		} 
	}
660
	if(f->misc&FM_EXTDESC)
661
		putextdesc(&cfg,f->dir,f->datoffset,ext);
662 663 664 665 666 667 668 669 670 671 672
	return(true);
}

/****************************************************************************/
/* Batch flagging prompt for download, extended info, and archive viewing	*/
/* Returns -1 if 'Q' or Ctrl-C, 0 if skip, 1 if [Enter], 2 otherwise        */
/* or 3, backwards. 														*/
/****************************************************************************/
int sbbs_t::batchflagprompt(uint dirnum, file_t* bf, uint total
							,long totalfiles)
{
673 674
	char	ch,str[256],fname[128],*p,remcdt=0,remfile=0;
	int		c, d;
675
	char 	tmp[512];
676
	uint	i,j,ml=0,md=0,udir,ulib;
677
	file_t	f;
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696

	for(ulib=0;ulib<usrlibs;ulib++)
		if(usrlib[ulib]==cfg.dir[dirnum]->lib)
			break;
	for(udir=0;udir<usrdirs[ulib];udir++)
		if(usrdir[ulib][udir]==dirnum)
			break;

	CRLF;
	while(online) {
		bprintf(text[BatchFlagPrompt]
			,ulib+1
			,cfg.lib[cfg.dir[dirnum]->lib]->sname
			,udir+1
			,cfg.dir[dirnum]->sname
			,totalfiles);
		ch=getkey(K_UPPER);
		clearline();
		if(ch=='?') {
697
			menu("batflag");
698 699
			if(lncntr)
				pause();
700 701
			return(2); 
		}
702
		if(ch==text[YNQP][2] || sys_status&SS_ABORT)
703 704 705 706 707
			return(-1);
		if(ch=='S')
			return(0);
		if(ch=='P' || ch=='-')
			return(3);
708
		if(ch=='B' || ch=='D') {    /* Flag for batch download */
709 710
			if(useron.rest&FLAG('D')) {
				bputs(text[R_Download]);
711 712
				return(2); 
			}
713 714 715 716 717 718
			if(total==1) {
				f.dir=dirnum;
				strcpy(f.name,bf[0].name);
				f.datoffset=bf[0].datoffset;
				f.size=0;
				getfiledat(&cfg,&f);
719
				addtobatdl(&f);
720
				if(ch=='D')
721
					start_batch_download();
722
				CRLF;
723 724
				return(2); 
			}
725 726 727 728 729 730 731 732 733 734 735
			bputs(text[BatchDlFlags]);
			d=getstr(str,BF_MAX,K_UPPER|K_LOWPRIO|K_NOCRLF);
			lncntr=0;
			if(sys_status&SS_ABORT)
				return(-1);
			if(d) { 	/* d is string length */
				CRLF;
				lncntr=0;
				for(c=0;c<d;c++) {
					if(batdn_total>=cfg.max_batdn) {
						bprintf(text[BatchDlQueueIsFull],str+c);
736 737
						break; 
					}
738
					if(str[c]=='*' || strchr(str+c,'.')) {     /* filename or spec given */
739
						f.dir=dirnum;
740
						p=strchr(str+c,' ');
741 742 743 744 745
						if(!p) p=strchr(str+c,',');
						if(p) *p=0;
						for(i=0;i<total;i++) {
							if(batdn_total>=cfg.max_batdn) {
								bprintf(text[BatchDlQueueIsFull],str+c);
746 747
								break; 
							}
748 749 750 751 752 753
							padfname(str+c,tmp);
							if(filematch(bf[i].name,tmp)) {
								strcpy(f.name,bf[i].name);
								f.datoffset=bf[i].datoffset;
								f.size=0;
								getfiledat(&cfg,&f);
754 755 756 757
								addtobatdl(&f); 
							} 
						} 
					}
758 759 760 761 762 763 764 765
					if(strchr(str+c,'.'))
						c+=strlen(str+c);
					else if(str[c]<'A'+(char)total && str[c]>='A') {
						f.dir=dirnum;
						strcpy(f.name,bf[str[c]-'A'].name);
						f.datoffset=bf[str[c]-'A'].datoffset;
						f.size=0;
						getfiledat(&cfg,&f);
766 767
						addtobatdl(&f);
					} 
768
				}
769 770
				if(ch=='D')
					start_batch_download();
771
				CRLF;
772 773
				return(2); 
			}
774
			clearline();
775 776
			continue; 
		}
777 778 779 780 781 782 783 784 785 786 787 788

		if(ch=='E' || ch=='V') {    /* Extended Info */
			if(total==1) {
				f.dir=dirnum;
				strcpy(f.name,bf[0].name);
				f.datoffset=bf[0].datoffset;
				f.dateuled=bf[0].dateuled;
				f.datedled=bf[0].datedled;
				f.size=0;
				getfiledat(&cfg,&f);
				if(!viewfile(&f,ch=='E'))
					return(-1);
789 790
				return(2); 
			}
791 792 793 794 795 796 797 798 799
			bputs(text[BatchDlFlags]);
			d=getstr(str,BF_MAX,K_UPPER|K_LOWPRIO|K_NOCRLF);
			lncntr=0;
			if(sys_status&SS_ABORT)
				return(-1);
			if(d) { 	/* d is string length */
				CRLF;
				lncntr=0;
				for(c=0;c<d;c++) {
800
					if(str[c]=='*' || strchr(str+c,'.')) {     /* filename or spec given */
801
						f.dir=dirnum;
802
						p=strchr(str+c,' ');
803 804 805 806 807 808 809 810 811 812 813 814
						if(!p) p=strchr(str+c,',');
						if(p) *p=0;
						for(i=0;i<total;i++) {
							padfname(str+c,tmp);
							if(filematch(bf[i].name,tmp)) {
								strcpy(f.name,bf[i].name);
								f.datoffset=bf[i].datoffset;
								f.dateuled=bf[i].dateuled;
								f.datedled=bf[i].datedled;
								f.size=0;
								getfiledat(&cfg,&f);
								if(!viewfile(&f,ch=='E'))
815 816 817 818
									return(-1); 
							} 
						} 
					}
819 820 821 822 823 824 825 826 827 828 829
					if(strchr(str+c,'.'))
						c+=strlen(str+c);
					else if(str[c]<'A'+(char)total && str[c]>='A') {
						f.dir=dirnum;
						strcpy(f.name,bf[str[c]-'A'].name);
						f.datoffset=bf[str[c]-'A'].datoffset;
						f.dateuled=bf[str[c]-'A'].dateuled;
						f.datedled=bf[str[c]-'A'].datedled;
						f.size=0;
						getfiledat(&cfg,&f);
						if(!viewfile(&f,ch=='E'))
830 831 832 833
							return(-1); } 
				}
				return(2); 
			}
834
			clearline();
835 836
			continue; 
		}
837

838
		if((ch=='R' || ch=='M')     /* Delete or Move */
839 840 841 842
			&& !(useron.rest&FLAG('R'))
			&& (dir_op(dirnum) || useron.exempt&FLAG('R'))) {
			if(total==1) {
				strcpy(str,"A");
843 844
				d=1; 
			}
845 846
			else {
				bputs(text[BatchDlFlags]);
847 848
				d=getstr(str,BF_MAX,K_UPPER|K_LOWPRIO|K_NOCRLF); 
			}
849 850 851 852 853
			lncntr=0;
			if(sys_status&SS_ABORT)
				return(-1);
			if(d) { 	/* d is string length */
				CRLF;
854 855
				if(ch=='R') {
					if(noyes(text[RemoveFileQ]))
856 857 858 859
						return(2);
					remcdt=remfile=1;
					if(dir_op(dirnum)) {
						remcdt=!noyes(text[RemoveCreditsQ]);
860 861
						remfile=!noyes(text[DeleteFileQ]); } 
				}
862 863 864 865 866 867
				else if(ch=='M') {
					CRLF;
					for(i=0;i<usrlibs;i++)
						bprintf(text[MoveToLibLstFmt],i+1,cfg.lib[usrlib[i]]->lname);
					SYNC;
					bprintf(text[MoveToLibPrompt],cfg.dir[dirnum]->lib+1);
868
					if((int)(ml=getnum(usrlibs))==-1)
869 870 871 872 873 874 875 876 877 878 879
						return(2);
					if(!ml)
						ml=cfg.dir[dirnum]->lib;
					else
						ml--;
					CRLF;
					for(j=0;j<usrdirs[ml];j++)
						bprintf(text[MoveToDirLstFmt]
							,j+1,cfg.dir[usrdir[ml][j]]->lname);
					SYNC;
					bprintf(text[MoveToDirPrompt],usrdirs[ml]);
880
					if((int)(md=getnum(usrdirs[ml]))==-1)
881 882 883 884
						return(2);
					if(!md)
						md=usrdirs[ml]-1;
					else md--;
885 886
					CRLF; 
				}
887 888
				lncntr=0;
				for(c=0;c<d;c++) {
889
					if(str[c]=='*' || strchr(str+c,'.')) {     /* filename or spec given */
890
						f.dir=dirnum;
891
						p=strchr(str+c,' ');
892 893 894 895 896 897 898 899 900 901 902 903 904 905 906
						if(!p) p=strchr(str+c,',');
						if(p) *p=0;
						for(i=0;i<total;i++) {
							padfname(str+c,tmp);
							if(filematch(bf[i].name,tmp)) {
								strcpy(f.name,bf[i].name);
								unpadfname(f.name,fname);
								f.datoffset=bf[i].datoffset;
								f.dateuled=bf[i].dateuled;
								f.datedled=bf[i].datedled;
								f.size=0;
								getfiledat(&cfg,&f);
								if(f.opencount) {
									bprintf(text[FileIsOpen]
										,f.opencount,f.opencount>1 ? "s":nulstr);
907 908
									continue; 
								}
909
								if(ch=='R') {
910
									removefile(&f);
911 912
									if(remfile) {
										sprintf(tmp,"%s%s",cfg.dir[f.dir]->path,fname);
913 914
										remove(tmp); 
									}
915
									if(remcdt)
916 917
										removefcdt(&f); 
								}
918
								else if(ch=='M')
919 920 921 922
									movefile(&f,usrdir[ml][md]); 
							} 
						} 
					}
923 924 925 926 927 928 929 930 931 932 933 934 935 936
					if(strchr(str+c,'.'))
						c+=strlen(str+c);
					else if(str[c]<'A'+(char)total && str[c]>='A') {
						f.dir=dirnum;
						strcpy(f.name,bf[str[c]-'A'].name);
						unpadfname(f.name,fname);
						f.datoffset=bf[str[c]-'A'].datoffset;
						f.dateuled=bf[str[c]-'A'].dateuled;
						f.datedled=bf[str[c]-'A'].datedled;
						f.size=0;
						getfiledat(&cfg,&f);
						if(f.opencount) {
							bprintf(text[FileIsOpen]
								,f.opencount,f.opencount>1 ? "s":nulstr);
937 938
							continue; 
						}
939
						if(ch=='R') {
940
							removefile(&f);
941 942
							if(remfile) {
								sprintf(tmp,"%s%s",cfg.dir[f.dir]->path,fname);
943 944
								remove(tmp); 
							}
945
							if(remcdt)
946 947
								removefcdt(&f); 
						}
948
						else if(ch=='M')
949 950 951 952
							movefile(&f,usrdir[ml][md]); } 
				}
				return(2); 
			}
953
			clearline();
954 955
			continue; 
		}
956

957 958
		return(1); 
	}
959 960 961 962 963 964