listfile.cpp 30.4 KB
Newer Older
1
2
3
4
5
6
/* Synchronet file database listing functions */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
7
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 *																			*
 * 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										*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include "sbbs.h"
23
#include "filedat.h"
24
25
26
27
28
29

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

int extdesclines(char *str);

/*****************************************************************************/
30
31
/* List files in directory 'dir' that match 'filespec'.						 */
/* 'mode' determines other criteria											 */
32
33
34
35
/* 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		 */
/*****************************************************************************/
36
int sbbs_t::listfiles(uint dirnum, const char *filespec, FILE* tofile, long mode)
37
{
38
	char	hdr[256],letter='A',*p;
39
	uchar	flagprompt=0;
40
	int		c, d;
41
	uint	i,j;
42
43
44
45
46
	int		found=0,lastbat=0,disp;
	size_t	m=0;
	long	anchor=0,next;
	file_t* bf[BF_MAX];	/* bf is batch flagged files */
	smb_t	smb;
47
	ulong	file_row[26];
48
	size_t	longest = 0;
49

50
51
	if(!smb_init_dir(&cfg, &smb, dirnum))
		return 0;
52
	if(mode&FL_ULTIME) {
53
54
55
		last_ns_time = now;
		if(!newfiles(&smb, ns_time))	// this is fast
			return 0;
56
	}
57
58
59
60
61
62
63
64
65
66
67
68
69
70
	if(smb_open_dir(&cfg, &smb, dirnum) != SMB_SUCCESS)
		return 0;

	size_t file_count = 0;
	file_t* file_list = loadfiles(&smb
		, (mode&(FL_FINDDESC|FL_EXFIND)) ? NULL : filespec
		, (mode&FL_ULTIME) ? ns_time : 0
		, file_detail_extdesc
		, (enum file_sort)cfg.dir[dirnum]->sort
		, &file_count);
	if(file_list == NULL || file_count < 1) {
		smb_close(&smb);
		free(file_list);
		return 0;
71
	}
72

73
74
75
76
77
78
	for(i = 0; i < file_count; i++) {
		size_t len = strlen(file_list[i].name);
		if(len > longest)
			longest = len;
	}

79
80
81
82
	if(!tofile) {
		action=NODE_LFIL;
		getnodedat(cfg.node_num,&thisnode,0);
		if(thisnode.action!=NODE_LFIL) {	/* was a sync */
83
84
85
86
87
88
			if(getnodedat(cfg.node_num,&thisnode,true)==0) {
				thisnode.action=NODE_LFIL;
				putnodedat(cfg.node_num,&thisnode); 
			} 
		} 
	}
89

90
91
92
	m = 0; // current file index
	file_t* f;
	while(online) {
93
94
		if(found<0)
			found=0;
95
		if(m>=file_count || flagprompt) {		  /* End of list */
96
97
98
99
			if(useron.misc&BATCHFLAG && !tofile && found && found!=lastbat
				&& !(mode&(FL_EXFIND|FL_VIEW))) {
				flagprompt=0;
				lncntr=0;
100
				if((i=batchflagprompt(&smb, bf, file_row, letter-'A', file_count))==2) {
101
102
					m=anchor;
					found-=letter-'A';
103
104
					letter='A'; 
				}
105
				else if(i==3) {
106
					if((long)anchor-(letter-'A')<0) {
107
						m=0;
108
109
						found=0; 
					}
110
					else {
111
						m=anchor-(letter-'A');
112
113
114
115
						found-=letter-'A'; 
					}
					letter='A'; 
				}
116
				else if((int)i==-1) {
117
118
					found = -1;
					break;
119
				}
120
121
122
				else
					break;
				getnodedat(cfg.node_num,&thisnode,0);
123
124
				nodesync(); 
			}
125
			else
126
127
				break; 
		}
128
129
		if(m < file_count)
			f = &file_list[m];
130
131
132
133
134
135
136

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

		if(msgabort()) {		 /* used to be !tofile && msgabort() */
137
138
			found = -1;
			break;
139
		}
140
#if 0 /* unnecessary? */
141
142
143
		if(!(mode&(FL_FINDDESC|FL_EXFIND)) && filespec[0]
			&& !filematch(str,filespec)) {
			m+=11;
144
145
			continue; 
		}
146
#endif
147
		if(mode&(FL_FINDDESC|FL_EXFIND)) {
148
			p = (f->desc == NULL) ? NULL : strcasestr(f->desc, filespec);
149
150
			if(p == NULL)
				p = strcasestr(f->name, filespec);
151
			if(!(mode&FL_EXFIND) && p==NULL) {
152
				m++;
153
154
				continue; 
			}
155
156
157
			if(mode&FL_EXFIND && f->extdesc != NULL) { /* search extended description */
				if(!strcasestr((char*)f->extdesc, filespec) && p == NULL) {	/* not in description or */
					m++;											 /* extended description */
158
159
160
					continue; 
				}
			}
161
162
163
164
			else if(p == NULL) {			 /* no extended description and not in desc */
				m++;
				continue; 
			} 
165
		}
166
/** necessary?
167
168
169
170
		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;
171
172
				continue; 
			}
173
		}
174
**/
175
176
177
178
		if(useron.misc&BATCHFLAG && letter=='A' && found && !tofile
			&& !(mode&(FL_EXFIND|FL_VIEW))
			&& (!mode || !(useron.misc&EXPERT)))
			bputs(text[FileListBatchCommands]);
179
		m++;
180
181
182
183
184
185
186
187
188
		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,'?')))) {
189
				sprintf(hdr,"%s%s.hdr",cfg.dir[dirnum]->data_dir,cfg.dir[dirnum]->code);
190
				if(fexistcase(hdr))
191
192
193
194
195
196
197
198
199
200
201
					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]);
202
						bputs("\xc9\xcd");            /* use to start with \r\n */
203
						for(c=0;c<d;c++)
204
205
							outchar('\xcd');
						bputs("\xbb\r\n\xba ");
206
207
208
						sprintf(hdr,text[BoxHdrLib],i+1,cfg.lib[usrlib[i]]->lname);
						bputs(hdr);
						for(c=bstrlen(hdr);c<d;c++)
209
							outchar(' ');
210
						attr(cfg.color[clr_filelsthdrbox]);
211
						bputs("\xba\r\n\xba ");
212
213
214
						sprintf(hdr,text[BoxHdrDir],j+1,cfg.dir[dirnum]->lname);
						bputs(hdr);
						for(c=bstrlen(hdr);c<d;c++)
215
							outchar(' ');
216
						attr(cfg.color[clr_filelsthdrbox]);
217
218
						bputs("\xba\r\n\xba ");
						sprintf(hdr,text[BoxHdrFiles], file_count);
219
220
						bputs(hdr);
						for(c=bstrlen(hdr);c<d;c++)
221
							outchar(' ');
222
						attr(cfg.color[clr_filelsthdrbox]);
223
						bputs("\xba\r\n\xc8\xcd");
224
						for(c=0;c<d;c++)
225
226
							outchar('\xcd');
						bputs("\xbc\r\n"); 
227
228
					}
				}
229
			}
230
231
			else {					/* short header */
				if(tofile) {
232
					c = fprintf(tofile,"\r\n(%u) %s ",i+1,cfg.lib[usrlib[i]]->sname) - 2;
233
				}
234
235
236
				else {
					sprintf(hdr,text[ShortHdrLib],i+1,cfg.lib[usrlib[i]]->sname);
					bputs("\r\1>\r\n");
237
					bputs(hdr); 
238
					c=bstrlen(hdr);
239
				}
240
				if(tofile) {
241
					c += fprintf(tofile,"(%u) %s",j+1,cfg.dir[dirnum]->lname);
242
				}
243
244
				else {
					sprintf(hdr,text[ShortHdrDir],j+1,cfg.dir[dirnum]->lname);
245
					bputs(hdr); 
246
					c+=bstrlen(hdr);
247
				}
248
				if(tofile) {
249
					fprintf(tofile,"\r\n%.*s\r\n", c, "----------------------------------------------------------------");
250
				}
251
252
253
254
				else {
					CRLF;
					attr(cfg.color[clr_filelstline]);
					while(c--)
rswindell's avatar
rswindell committed
255
						outchar('\xC4');
256
257
258
259
					CRLF; 
				} 
			} 
		}
260
		long currow = row;
261
262
263
264
265
		next=m;
		disp=1;
		if(mode&(FL_EXFIND|FL_VIEW)) {
			if(!found)
				bputs("\r\1>");
266
267
268
			if(!viewfile(f, INT_TO_BOOL(mode&FL_EXFIND))) {
				found = -1;
				break;
269
			}
270
			CRLF;
271
		}
272
		else if(tofile)
273
			listfiletofile(f, tofile);
274
		else if(mode&FL_FINDDESC)
275
			disp=listfile(f, dirnum, filespec, letter, longest);
276
		else
277
			disp=listfile(f, dirnum, nulstr, letter, longest);
278
		if(!disp && letter>'A') {
279
			next=m-1;
280
281
			letter--; 
		}
282
283
		else {
			disp=1;
284
285
			found++; 
		}
286
		if(sys_status&SS_ABORT) {
287
288
			found = -1;
			break;
289
		}
290
291
292
293
		if(mode&(FL_EXFIND|FL_VIEW))
			continue;
		if(useron.misc&BATCHFLAG && !tofile) {
			if(disp) {
294
				bf[letter-'A'] = f;
295
				file_row[letter-'A'] = currow;
296
			}
297
			m++;
298
299
300
301
302
303
304
305
			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;
306
				if((int)(i=batchflagprompt(&smb, bf, file_row, letter-'A'+1, file_count))<1) {
307
					if((int)i==-1)
308
309
						found = -1;
					break;
310
				}
311
312
				if(i==2) {
					next=anchor;
313
314
					found-=(letter-'A')+1; 
				}
315
				else if(i==3) {
316
					if((long)anchor-((letter-'A'+1))<0) {
317
						next=0;
318
319
						found=0; 
					}
320
					else {
321
322
323
						next=anchor-((letter-'A'+1));
						found-=letter-'A'+1; 
					} 
324
				}
325
326
				getnodedat(cfg.node_num,&thisnode,0);
				nodesync();
327
328
329
330
				letter='A';	
			}
			else
				letter++; 
331
		}
332
333
334
		if(useron.misc&BATCHFLAG && !tofile
			&& lncntr>=rows-2) {
			lncntr=0;		/* defeat pause() */
335
336
			flagprompt=1; 
		}
337
338
339
		m=next;
		if(mode&FL_FINDDESC) continue;
		if(filespec[0] && !strchr(filespec,'*') && !strchr(filespec,'?') && m)
340
341
			break; 
	}
342

343
344
345
	freefiles(file_list, file_count);
	smb_close(&smb);
	return found;
346
347
348
349
350
351
}

/****************************************************************************/
/* Prints one file's information on a single line                           */
/* Return 1 if displayed, 0 otherwise										*/
/****************************************************************************/
352
bool sbbs_t::listfile(file_t* f, uint dirnum, const char *search, const char letter, size_t namelen)
353
{
354
	char	*ptr;
355
356
	bool	exist = true;
	char*	ext=NULL;
357
	char	path[MAX_PATH+1];
358
    int		i,j;
359
    off_t	cdt;
360
	int		size_attr=clr_filecdt;
361

362
363
364
365
	if(f->extdesc != NULL && *f->extdesc && (useron.misc&EXTDESC)) {
		ext = f->extdesc;
		if((useron.misc&BATCHFLAG) && lncntr+extdesclines(ext)>=rows-2 && letter!='A')
			return false;
366
	}
367

368
	cond_newline();
369
	attr(cfg.color[(f->hdr.attr & MSG_DELETE) ? clr_err : clr_filename]);
370
371
372
373
374
	char fname[SMB_FILEIDX_NAMELEN + 1];
	if(namelen < 12 || cols < 132)
		namelen = 12;
	else if(namelen > sizeof(fname) - 1)
		namelen = sizeof(fname) - 1;
Rob Swindell's avatar
Rob Swindell committed
375
	bprintf("%-*s", (int)namelen, format_filename(f->name, fname, namelen, /* pad: */TRUE));
376
	getfilepath(&cfg, f, path);
377

378
379
	if(f->extdesc != NULL && *f->extdesc && !(useron.misc&EXTDESC))
		outchar('+');
380
	else
381
		outchar(' '); 
382
	if(useron.misc&BATCHFLAG) {
383
		attr(curatr ^ HIGH);
384
385
		bprintf("%c",letter); 
	}
386
387
388
389
	cdt = f->cost;
	if(f->size == -1) {
		exist = false;
		size_attr = clr_err; 
390
	}
391
392
393
394
395
396
397
398
	else if((cfg.dir[dirnum]->misc & (DIR_FREE | DIR_FCHK)) == (DIR_FREE | DIR_FCHK))
		cdt = getfilesize(&cfg, f);
	char bytes[32];
	unsigned units = 1;
	do {
		byte_estimate_to_str(cdt, bytes, sizeof(bytes), units, /* precision: */1);
		units *= 1024;
	} while(strlen(bytes) > 6 && units < 1024 * 1024 * 1024);
399
	attr(cfg.color[size_attr]);
400
	if(useron.misc&BATCHFLAG) {
401
		if(!cdt && !(cfg.dir[dirnum]->misc&DIR_FREE)) {
402
			attr(curatr^(HIGH|BLINK));
403
404
			bputs("  FREE"); 
		}
405
406
		else 
			bprintf("%6s", bytes);
407
	}
408
	else {
409
		if(!cdt && !(cfg.dir[dirnum]->misc&DIR_FREE)) {  /* FREE file */
410
			attr(curatr^(HIGH|BLINK));
411
412
			bputs("   FREE"); 
		}
413
414
		else 
			bprintf("%7s", bytes);
415
	}
416
	if(exist)
417
		outchar(' ');
418
419
420
	else
		outchar('-');
	attr(cfg.color[clr_filedesc]);
421

422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
	if(ext == NULL) {
		char* fdesc = f->desc;
		SKIP_WHITESPACE(fdesc);
		if(fdesc == NULL || *fdesc == '\0')
			bputs(P_TRUNCATE, f->name);
		else if(search[0]) { /* high-light string in string */
			ptr = strcasestr(fdesc, search);
			if(ptr != NULL) {
				i=strlen(search);
				j=ptr - fdesc;
				bprintf("%.*s",j,fdesc);
				attr(cfg.color[clr_filedesc]^HIGH);
				bprintf("%.*s",i,fdesc+j);
				attr(cfg.color[clr_filedesc]);
				bprintf("%.*s",(int)strlen(fdesc)-(j+i),fdesc+j+i);
			}
		}
		else {
			bputs(P_TRUNCATE, fdesc);
441
442
		}
		CRLF; 
443
	} else {
444
445
446
447
448
449
450
		truncsp(ext);
		while(strncmp(ext, "\r\n", 2) == 0
			|| strnicmp(ext, "\001N", 2) == 0
			|| strnicmp(ext, "\0010", 2) == 0
			|| strnicmp(ext, "\001W", 2) == 0)
			ext += 2;
		putmsg(ext, P_INDENT | P_NOATCODES | P_CPM_EOF | P_TRUNCATE);
451
	}
452
	return true;
453
454
455
456
457
458
459
}

/****************************************************************************/
/* 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. 														*/
/****************************************************************************/
460
int sbbs_t::batchflagprompt(smb_t* smb, file_t** bf, ulong* row, uint total
461
462
							,long totalfiles)
{
463
	char	ch,str[256],*p,remcdt=0,remfile=0;
464
	int		c, d;
465
	char 	path[MAX_PATH + 1];
466
	uint	i,j,ml=0,md=0,udir,ulib;
467
468

	for(ulib=0;ulib<usrlibs;ulib++)
469
		if(usrlib[ulib]==cfg.dir[smb->dirnum]->lib)
470
471
			break;
	for(udir=0;udir<usrdirs[ulib];udir++)
472
		if(usrdir[ulib][udir]==smb->dirnum)
473
474
475
476
477
478
			break;

	CRLF;
	while(online) {
		bprintf(text[BatchFlagPrompt]
			,ulib+1
479
			,cfg.lib[cfg.dir[smb->dirnum]->lib]->sname
480
			,udir+1
481
			,cfg.dir[smb->dirnum]->sname
482
			,total, totalfiles);
483
484
485
		ch=getkey(K_UPPER);
		clearline();
		if(ch=='?') {
486
			menu("batflag");
487
488
			if(lncntr)
				pause();
489
490
			return(2); 
		}
491
		if(ch==text[YNQP][2] || sys_status&SS_ABORT)
492
493
494
495
496
			return(-1);
		if(ch=='S')
			return(0);
		if(ch=='P' || ch=='-')
			return(3);
497
498
		if(ch=='T') {
			useron.misc ^= EXTDESC;
499
			putuserrec(&cfg, useron.number, U_MISC, 0, ultoa(useron.misc,str,16));
500
501
			return 2;
		}
502
		if(ch=='B' || ch=='D') {    /* Flag for batch download */
503
504
			if(useron.rest&FLAG('D')) {
				bputs(text[R_Download]);
505
506
				return(2); 
			}
507
			if(total==1) {
508
				addtobatdl(bf[0]);
509
				if(ch=='D')
510
					start_batch_download();
511
				CRLF;
512
513
				return(2); 
			}
514
515
516
517
			link_list_t saved_hotspots = mouse_hotspots;
			ZERO_VAR(mouse_hotspots);
			for(i=0; i < total; i++)
				add_hotspot((char)('A' + i), /* hungry: */true, -1, -1, row[i]);
518
			bputs(text[BatchDlFlags]);
519
			d=getstr(str, BF_MAX, K_NOCRLF);
520
521
			clear_hotspots();
			mouse_hotspots = saved_hotspots;
522
523
524
			lncntr=0;
			if(sys_status&SS_ABORT)
				return(-1);
525
526
			if(d > 0) { 	/* d is string length */
				strupr(str);
527
528
529
				CRLF;
				lncntr=0;
				for(c=0;c<d;c++) {
530
					if(batdn_total() >= cfg.max_batdn) {
531
						bprintf(text[BatchDlQueueIsFull],str+c);
532
533
						break; 
					}
534
					if(str[c]=='*' || strchr(str+c,'.')) {     /* filename or spec given */
535
//						f.dir=dirnum;
536
						p=strchr(str+c,' ');
537
538
539
						if(!p) p=strchr(str+c,',');
						if(p) *p=0;
						for(i=0;i<total;i++) {
540
							if(batdn_total() >= cfg.max_batdn) {
541
								bprintf(text[BatchDlQueueIsFull],str+c);
542
543
								break; 
							}
544
545
							if(filematch(bf[i]->name, str+c)) {
								addtobatdl(bf[i]); 
546
547
548
							} 
						} 
					}
549
550
551
					if(strchr(str+c,'.'))
						c+=strlen(str+c);
					else if(str[c]<'A'+(char)total && str[c]>='A') {
552
						addtobatdl(bf[str[c]-'A']);
553
					} 
554
				}
555
556
				if(ch=='D')
					start_batch_download();
557
				CRLF;
558
559
				return(2); 
			}
560
			clearline();
561
562
			continue; 
		}
563
564
565

		if(ch=='E' || ch=='V') {    /* Extended Info */
			if(total==1) {
566
				if(!viewfile(bf[0], ch=='E'))
567
					return(-1);
568
569
				return(2); 
			}
570
571
572
573
			link_list_t saved_hotspots = mouse_hotspots;
			ZERO_VAR(mouse_hotspots);
			for(i=0; i < total; i++)
				add_hotspot((char)('A' + i), /* hungry: */true, -1, -1, row[i]);
574
			bputs(text[BatchDlFlags]);
575
			d=getstr(str, BF_MAX, K_NOCRLF);
576
577
			clear_hotspots();
			mouse_hotspots = saved_hotspots;
578
579
580
			lncntr=0;
			if(sys_status&SS_ABORT)
				return(-1);
581
582
			if(d > 0) { 	/* d is string length */
				strupr(str);
583
584
585
				CRLF;
				lncntr=0;
				for(c=0;c<d;c++) {
586
					if(str[c]=='*' || strchr(str+c,'.')) {     /* filename or spec given */
587
//						f.dir=dirnum;
588
						p=strchr(str+c,' ');
589
590
591
						if(!p) p=strchr(str+c,',');
						if(p) *p=0;
						for(i=0;i<total;i++) {
592
593
							if(filematch(bf[i]->name, str+c)) {
								if(!viewfile(bf[i], ch=='E'))
594
595
596
597
									return(-1); 
							} 
						} 
					}
598
599
600
					if(strchr(str+c,'.'))
						c+=strlen(str+c);
					else if(str[c]<'A'+(char)total && str[c]>='A') {
601
602
603
						if(!viewfile(bf[str[c]-'A'], ch=='E'))
							return(-1); 
					} 
604
				}
605
				cond_newline();
606
607
				return(2); 
			}
608
			clearline();
609
610
			continue; 
		}
611

612
		if((ch=='R' || ch=='M')     /* Delete or Move */
613
			&& !(useron.rest&FLAG('R'))
614
			&& (dir_op(smb->dirnum) || useron.exempt&FLAG('R'))) {
615
616
			if(total==1) {
				strcpy(str,"A");
617
618
				d=1; 
			}
619
			else {
620
621
622
623
				link_list_t saved_hotspots = mouse_hotspots;
				ZERO_VAR(mouse_hotspots);
				for(i=0; i < total; i++)
					add_hotspot((char)('A' + i), /* hungry: */true, -1, -1, row[i]);
624
				bputs(text[BatchDlFlags]);
625
				d=getstr(str, BF_MAX, K_NOCRLF);
626
627
				clear_hotspots();
				mouse_hotspots = saved_hotspots;
628
			}
629
630
631
			lncntr=0;
			if(sys_status&SS_ABORT)
				return(-1);
632
633
634
635
			if(d > 0) { 	/* d is string length */
				strupr(str);
				if(total > 1)
					newline();
636
637
				if(ch=='R') {
					if(noyes(text[RemoveFileQ]))
638
						return(2);
639
640
641
642
					remcdt = TRUE;
					remfile = TRUE;
					if(dir_op(smb->dirnum)) {
						remfile=!noyes(text[DeleteFileQ]);
643
						remcdt=!noyes(text[RemoveCreditsQ]);
644
					}
645
				}
646
647
648
649
650
				else if(ch=='M') {
					CRLF;
					for(i=0;i<usrlibs;i++)
						bprintf(text[MoveToLibLstFmt],i+1,cfg.lib[usrlib[i]]->lname);
					SYNC;
651
					bprintf(text[MoveToLibPrompt],cfg.dir[smb->dirnum]->lib+1);
652
					if((int)(ml=getnum(usrlibs))==-1)
653
654
						return(2);
					if(!ml)
655
						ml=cfg.dir[smb->dirnum]->lib;
656
657
658
659
660
661
662
663
					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]);
664
					if((int)(md=getnum(usrdirs[ml]))==-1)
665
666
667
668
						return(2);
					if(!md)
						md=usrdirs[ml]-1;
					else md--;
669
670
					CRLF; 
				}
671
672
				lncntr=0;
				for(c=0;c<d;c++) {
673
					if(str[c]=='*' || strchr(str+c,'.')) {     /* filename or spec given */
674
//						f.dir=dirnum;
675
						p=strchr(str+c,' ');
676
677
678
						if(!p) p=strchr(str+c,',');
						if(p) *p=0;
						for(i=0;i<total;i++) {
679
							if(filematch(bf[i]->name, str+c)) {
680
								if(ch=='R') {
681
682
683
684
685
686
687
									if(removefile(smb, bf[i])) {
										if(remfile) {
											if(remove(getfilepath(&cfg, bf[i], path)) != 0)
												errormsg(WHERE, ERR_REMOVE, path);
										}
										if(remcdt)
											removefcdt(bf[i]);
688
689
									}
								}
690
								else if(ch=='M')
691
									movefile(smb, bf[i], usrdir[ml][md]); 
692
693
694
							} 
						} 
					}
695
696
697
					if(strchr(str+c,'.'))
						c+=strlen(str+c);
					else if(str[c]<'A'+(char)total && str[c]>='A') {
698
						file_t* f = bf[str[c]-'A'];
699
						if(ch=='R') {
700
701
702
703
704
705
706
							if(removefile(smb, f)) {
								if(remfile) {
									if(remove(getfilepath(&cfg, f, path)) != 0 && fexist(path))
										errormsg(WHERE, ERR_REMOVE, path);
								}
								if(remcdt)
									removefcdt(f);
707
708
							}
						}
709
						else if(ch=='M')
710
711
							movefile(smb, f, usrdir[ml][md]); 
					} 
712
713
714
				}
				return(2); 
			}
715
			clearline();
716
717
			continue; 
		}
718

719
720
		return(1); 
	}
721
722
723
724
725
726
727
728
729

	return(-1);
}

/****************************************************************************/
/* List detailed information about the files in 'filespec'. Prompts for     */
/* action depending on 'mode.'                                              */
/* Returns number of files matching filespec that were found                */
/****************************************************************************/
730
int sbbs_t::listfileinfo(uint dirnum, const char *filespec, long mode)
731
{
732
	char	str[MAX_PATH + 1],path[MAX_PATH + 1],dirpath[MAX_PATH + 1],done=0,ch;
733
	char 	tmp[512];
rswindell's avatar
rswindell committed
734
	int		error;
735
736
	int		found=0;
    uint	i,j;
737
    size_t	m;
738
    time_t	start,end,t;
739
    file_t*	f;
740
	struct	tm tm;
741
	smb_t	smb;
742

743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
	if(!smb_init_dir(&cfg, &smb, dirnum))
		return 0;
	if(smb_open_dir(&cfg, &smb, dirnum) != SMB_SUCCESS)
		return 0;

	size_t file_count = 0;
	file_t* file_list = loadfiles(&smb
		, filespec
		, /* time_t */0
		, file_detail_extdesc
		, (enum file_sort)cfg.dir[dirnum]->sort
		, &file_count);
	if(file_list == NULL || file_count < 1) {
		smb_close(&smb);
		free(file_list);
		return 0;
759
	}
760

761
	m=0;
762
763
	while(online && !done && m < file_count) {
		f = &file_list[m];
764
765
766
767
768
		if(mode==FI_REMOVE && dir_op(dirnum))
			action=NODE_SYSP;
		else action=NODE_LFIL;
		if(msgabort()) {
			found=-1;
769
770
			break; 
		}
771
772
		m++;
		if(mode==FI_OLD && f->hdr.last_downloaded > ns_time)
773
			continue;
774
		if((mode==FI_OLDUL || mode==FI_OLD) && f->hdr.when_written.time > ns_time)
775
			continue;
776
777
		curdirnum = dirnum;
		if(mode==FI_OFFLINE && getfilesize(&cfg, f) >= 0)
778
			continue;
779
780
781
782
783
784
785
786
787
		if(mode==FI_USERXFER) {
			str_list_t dest_user_list = strListSplitCopy(NULL, f->to_list, ",");
			char usernum[16];
			SAFEPRINTF(usernum, "%u", useron.number);
			int dest_user = strListFind(dest_user_list, usernum, /* case-sensitive: */true);
			strListFree(&dest_user_list);
			if(dest_user < 0)
				continue;
		}
788
789
		SAFECOPY(dirpath, cfg.dir[f->dir]->path);
		if((mode==FI_REMOVE) && (!dir_op(dirnum) && stricmp(f->from
790
791
792
793
			,useron.alias) && !(useron.exempt&FLAG('R'))))
			continue;
		found++;
		if(mode==FI_INFO) {
794
795
796
797
798
799
800
801
802
803
804
			switch(viewfile(f, true)) {
				case 0:
					done=1;
					found=-1;
					break;
				case -2:
					m--;
					if(m)
						m--;
					break;
			} 
805
		}
806
807
808
		else {
			showfileinfo(f, /* show_extdesc: */mode != FI_DOWNLOAD);
//			newline();
809
		}
810
		if(mode==FI_REMOVE || mode==FI_OLD || mode==FI_OLDUL
811
812
			|| mode==FI_OFFLINE) {
			SYNC;
813
814
815
//			CRLF;
			SAFECOPY(str, "VEQRNP\b-\r");
			if(dir_op(dirnum)) {
816
				mnemonics(text[SysopRemoveFilePrompt]);
817
				SAFECAT(str,"FMC");
818
			}
819
820
			else if(useron.exempt&FLAG('R')) {
				mnemonics(text[RExemptRemoveFilePrompt]);
821
				SAFECAT(str,"M");
822
			}
823
			else
824
825
826
				mnemonics(text[UserRemoveFilePrompt]);
			switch(getkeys(str,0)) {
				case 'V':
827
					viewfilecontents(f);
828
829
830
					CRLF;
					ASYNC;
					pause();
831
					m--;
832
833
834
835
					continue;
				case 'E':   /* edit file information */
					if(dir_op(dirnum)) {
						bputs(text[EditFilename]);
836
837
						SAFECOPY(str, f->name);
						if(!getstr(str, MAX_FILENAME_LEN, K_EDIT|K_AUTODEL))
838
							break;
839
840
841
						if(strcmp(str,f->name) != 0) { /* rename */
							if(stricmp(str,f->name)
								&& findfile(&cfg, f->dir, path, NULL))
842
843
								bprintf(text[FileAlreadyThere],path);
							else {
844
								SAFEPRINTF2(path,"%s%s",dirpath,f->name);
845
								SAFEPRINTF2(tmp,"%s%s",dirpath,str);
846
								if(fexistcase(path) && rename(path,tmp))
847
848
849
									bprintf(text[CouldntRenameFile],path,tmp);
								else {
									bprintf(text[FileRenamed],path,tmp);
850
851
									smb_new_hfield_str(f, SMB_FILENAME, str);
									updatefile(&cfg, f);
852
853
854
855
								} 
							} 
						} 
					}
856
					// Description
857
					bputs(text[EditDescription]);
858
859
860
					char fdesc[LEN_FDESC + 1];
					SAFECOPY(fdesc, f->desc);
					getstr(fdesc, sizeof(fdesc)-1, K_LINE|K_EDIT|K_AUTODEL|K_TRIM);
861
862
					if(sys_status&SS_ABORT)
						break;
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
					if(strcmp(fdesc, f->desc))
						smb_new_hfield_str(f, SMB_FILEDESC, fdesc);

					// Tags
					if((cfg.dir[dirnum]->misc & DIR_FILETAGS) || dir_op(dirnum)) {
						char tags[64] = "";
						bputs(text[TagFilePrompt]);
						if(f->tags != NULL)
							SAFECOPY(tags, f->tags);
						getstr(tags, sizeof(tags)-1, K_LINE|K_EDIT|K_AUTODEL|K_TRIM);
						if(sys_status&SS_ABORT)
							break;
						if((f->tags == NULL && *tags != '\0') || (f->tags != NULL && strcmp(tags, f->tags)))
							smb_new_hfield_str(f, SMB_TAGS, tags);
					}
					// Extended Description
					if(f->extdesc != NULL && *f->extdesc) {
880
						if(!noyes(text[DeleteExtDescriptionQ])) {
881
882
							// TODO
						} 
883
					}
884
					if(!dir_op(dirnum)) {
885
						updatefile(&cfg, f);
886
887
						break; 
					}
888
889
					char uploader[LEN_ALIAS + 1];
					SAFECOPY(uploader, f->from);
890
					bputs(text[EditUploader]);
891
					if(!getstr(uploader, sizeof(uploader), K_EDIT|K_AUTODEL))
892
						break;
893
894
					smb_new_hfield_str(f, SMB_FILEUPLOADER, uploader);
					ultoa(f->cost,str,10);
895
					bputs(text[EditCreditValue]);
896
					getstr(str,10,K_NUMBER|K_EDIT|K_AUTODEL);
897
898
					if(sys_status&SS_ABORT)
						break;
899
900
901
					f->cost = atol(str);
					smb_new_hfield(f, SMB_COST, sizeof(f->cost), &f->cost);
					ultoa(f->hdr.times_downloaded,str,10);
902
903
					bputs(text[EditTimesDownloaded]);
					getstr(str,5,K_NUMBER|K_EDIT|K_AUTODEL);
904
905
					if(sys_status&SS_ABORT)
						break;
906
					f->hdr.times_downloaded=atoi(str);
907
908
					if(sys_status&SS_ABORT)
						break;
909
910
					inputnstime32((time32_t*)&f->hdr.when_imported.time);
					updatefile(&cfg, f);
911
912
					break;
				case 'F':   /* delete file only */
913
					SAFEPRINTF2(str,"%s%s",dirpath,f->name);
914
					if(!fexistcase(str))
915
						bprintf(text[FileDoesNotExist],str);
916
					else {
917
						if(!noyes(text[DeleteFileQ])) {
918
919
920
							if(remove(str))
								bprintf(text[CouldntRemoveFile],str);
							else {
921
922
								SAFEPRINTF(tmp, "deleted %s", str);
								logline(nulstr, tmp); 
923
924
925
							} 
						} 
					}
926
927
					break;
				case 'R':   /* remove file from database */
928
					if(noyes(text[RemoveFileQ]))
929
						break;
930
931
932
933
934
935
936
937
938
939
940
					if(removefile(&smb, f)) {
						getfilepath(&cfg, f, path);
						if(fexistcase(path)) {
							if(dir_op(dirnum)) {
								if(!noyes(text[DeleteFileQ])) {
									if(remove(path) != 0)
										errormsg(WHERE, ERR_REMOVE, path);
									else {
										SAFEPRINTF(tmp, "deleted %s", path);
										logline(nulstr,path); 
									} 
9