dirwrap.c 29 KB
Newer Older
1
2
3
4
5
6
/* Directory-related system-call wrappers */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
rswindell's avatar
rswindell committed
7
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
8
 *																			*
9
10
 * This library is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU Lesser General Public License		*
11
12
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
13
14
 * See the GNU Lesser General Public License for more details: lgpl.txt or	*
 * http://www.fsf.org/copyleft/lesser.html									*
15
16
17
18
19
20
21
 *																			*
 * 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.	*
 ****************************************************************************/

22
23
#include <string.h>	/* strrchr */

24
#if defined(_WIN32)
25

26
27
	#include <windows.h>	/* WINAPI, etc */
	#include <io.h>			/* _findfirst */
28
29
30

#elif defined __unix__

31
32
33
	#include <unistd.h>		/* usleep */
	#include <fcntl.h>		/* O_NOCCTY */
	#include <ctype.h>		/* toupper */
rswindell's avatar
rswindell committed
34
	#include <sys/param.h>
35

rswindell's avatar
rswindell committed
36
	#if defined(BSD)
37
		#include <sys/mount.h>
38
	#endif
rswindell's avatar
rswindell committed
39
	#if defined(__FreeBSD__)
40
41
		#include <sys/kbio.h>
	#endif
42
43
	#if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 300000000 /* NetBSD 3.0 */)
		#include <sys/statvfs.h>
rswindell's avatar
rswindell committed
44
	#endif
45

46
	#include <sys/ioctl.h>	/* ioctl */
47

48
49
50
	#if defined(__GLIBC__)		/* actually, BSD, but will work for now */
		#include <sys/vfs.h>    /* statfs() */
	#endif
51

deuce's avatar
deuce committed
52
53
54
55
	#if defined(__solaris__)
		#include <sys/statvfs.h>
	#endif

56
#endif /* __unix__ */
57

rswindell's avatar
rswindell committed
58
59
60
#if defined(__WATCOMC__)
	#include <dos.h>
#endif
61

62
63
64
65
66
67
#include <sys/types.h>	/* _dev_t */

#include <stdio.h>		/* sprintf */
#include <stdlib.h>		/* rand */
#include <errno.h>		/* ENOENT definitions */

68
#include "genwrap.h"	/* strupr/strlwr */
Rob Swindell's avatar
Rob Swindell committed
69
#include "dirwrap.h"
70
#include "filewrap.h"	/* stat */
71

72
73
74
75
#if !defined(S_ISDIR)
	#define S_ISDIR(x)	((x)&S_IFDIR)
#endif

76
77
78
/****************************************************************************/
/* Return the filename portion of a full pathname							*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
79
char* getfname(const char* path)
80
{
81
82
	const char* fname;
	const char* bslash;
83
84

	fname=strrchr(path,'/');
85
86
87
	bslash=strrchr(path,'\\');
	if(bslash>fname)
		fname=bslash;
88
	if(fname!=NULL)
89
		fname++;
90
	else
91
		fname=(char*)path;
92
	return((char*)fname);
93
94
}

95
/****************************************************************************/
96
97
98
/* Return the filename or last directory portion of a full pathname			*/
/* A directory pathname is expected to end in a '/'							*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
99
char* getdirname(const char* path)
100
101
102
103
104
{
	char* last = lastchar(path);
	if(*last == '/') {
		if(last == path)
			return last;
105
106
		for(last--; last >= path; last--) {
			if(IS_PATH_DELIM(*last))
107
108
				return last + 1;
		}
109
		return (char*)path;
110
111
112
113
114
115
	}
	return getfname(path);
}

/****************************************************************************/
/* Return a pointer to a file's extension/suffix (beginning with '.')		*/
116
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
117
char* getfext(const char* path)
118
119
120
121
122
123
{
	char *fname;
	char *fext;

	fname=getfname(path);
	fext=strrchr(fname,'.');
124
	if(fext==NULL || fext==fname)
125
126
127
		return(NULL);
	return(fext);
}
128
129
130
131
132

/****************************************************************************/
/* Break a path name into components.										*/
/****************************************************************************/
#if defined(__unix__)
Rob Swindell's avatar
Rob Swindell committed
133
void _splitpath(const char *path, char *drive, char *dir, char *fname, char *ext)
134
135
136
137
{
	char*	p;

	ext[0]=0;
138
139
	drive[0]=0;			/* no drive letters on Unix */

140
	snprintf(dir, MAX_PATH+1, "%s", path);	/* Optional directory path, including trailing slash. */
141
	p=getfname(dir);
142
	snprintf(fname, MAX_PATH+1, "%s", p);	/* Base filename (no extension) */
143
144
145
146
147
	if(p==dir)
		dir[0]=0;		/* no directory specified in path */
	else
		*p=0;			/* truncate dir at filename */
	p=getfext(fname);
148
	if(p!=NULL) {
149
		snprintf(ext, MAX_PATH+1, "%s", p);	/* Optional filename extension, including leading period (.) */
150
151
152
153
154
		*p=0;
	}
}
#endif

155
156
157
158
/****************************************************************************/
/* Win32 (minimal) implementation of POSIX.2 glob() function				*/
/* This code _may_ work on other DOS-based platforms (e.g. OS/2)			*/
/****************************************************************************/
159
#if !defined(__unix__)
deuce's avatar
deuce committed
160
static int __cdecl glob_compare( const void *arg1, const void *arg2 )
161
162
{
   /* Compare all of both strings: */
163
   return strcmp( * ( char** ) arg1, * ( char** ) arg2 );
164
165
}

166
#if defined(__BORLANDC__)
167
168
169
	#pragma argsused
#endif

rswindell's avatar
rswindell committed
170
171
#if defined(__WATCOMC__)

Rob Swindell's avatar
Rob Swindell committed
172
int	glob(const char *pattern, int flags, void* unused, glob_t* glob)
rswindell's avatar
rswindell committed
173
174
175
176
177
178
179
180
181
182
183
184
{
    struct	find_t ff;
	size_t	found=0;
	char	path[MAX_PATH+1];
	char*	p;
	char**	new_pathv;

	if(!(flags&GLOB_APPEND)) {
		glob->gl_pathc=0;
		glob->gl_pathv=NULL;
	}

185
	if(_dos_findfirst((char*)pattern,(flags&GLOB_PERIOD) ? _A_HIDDEN : _A_NORMAL,&ff)!=0)
rswindell's avatar
rswindell committed
186
187
188
		return(GLOB_NOMATCH);

	do {
189
190
		if((flags&GLOB_PERIOD || ff.name[0]!='.') &&
			(!(flags&GLOB_ONLYDIR) || ff.attrib&_A_SUBDIR)) {
rswindell's avatar
rswindell committed
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
			if((new_pathv=realloc(glob->gl_pathv
				,(glob->gl_pathc+1)*sizeof(char*)))==NULL) {
				globfree(glob);
				return(GLOB_NOSPACE);
			}
			glob->gl_pathv=new_pathv;

			/* build the full pathname */
			SAFECOPY(path,pattern);
			p=getfname(path);
			*p=0;
			strcat(path,ff.name);

			if((glob->gl_pathv[glob->gl_pathc]=malloc(strlen(path)+2))==NULL) {
				globfree(glob);
				return(GLOB_NOSPACE);
			}
			strcpy(glob->gl_pathv[glob->gl_pathc],path);
			if(flags&GLOB_MARK && ff.attrib&_A_SUBDIR)
				strcat(glob->gl_pathv[glob->gl_pathc],"/");

			glob->gl_pathc++;
			found++;
		}
	} while(_dos_findnext(&ff)==0);
	_dos_findclose(&ff);

	if(found==0)
		return(GLOB_NOMATCH);

	if(!(flags&GLOB_NOSORT)) {
		qsort(glob->gl_pathv,found,sizeof(char*),glob_compare);
	}

	return(0);	/* success */
}

#else

Rob Swindell's avatar
Rob Swindell committed
230
int	glob(const char *pattern, int flags, void* unused, glob_t* glob)
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
{
    struct	_finddata_t ff;
	long	ff_handle;
	size_t	found=0;
	char	path[MAX_PATH+1];
	char*	p;
	char**	new_pathv;

	if(!(flags&GLOB_APPEND)) {
		glob->gl_pathc=0;
		glob->gl_pathv=NULL;
	}

	ff_handle=_findfirst((char*)pattern,&ff);
	while(ff_handle!=-1) {
246
		if((flags&GLOB_PERIOD || (ff.name[0]!='.' && !(ff.attrib&_A_HIDDEN))) &&
247
			(!(flags&GLOB_ONLYDIR) || ff.attrib&_A_SUBDIR)) {
248
			if((new_pathv=(char**)realloc(glob->gl_pathv
249
250
251
252
253
254
255
				,(glob->gl_pathc+1)*sizeof(char*)))==NULL) {
				globfree(glob);
				return(GLOB_NOSPACE);
			}
			glob->gl_pathv=new_pathv;

			/* build the full pathname */
256
			SAFECOPY(path,pattern);
257
258
			p=getfname(path);
			*p=0;
259
			SAFECAT(path,ff.name);
260

261
			if((glob->gl_pathv[glob->gl_pathc]=(char*)malloc(strlen(path)+2))==NULL) {
262
263
264
265
266
267
268
269
270
271
272
273
				globfree(glob);
				return(GLOB_NOSPACE);
			}
			strcpy(glob->gl_pathv[glob->gl_pathc],path);
			if(flags&GLOB_MARK && ff.attrib&_A_SUBDIR)
				strcat(glob->gl_pathv[glob->gl_pathc],"/");

			glob->gl_pathc++;
			found++;
		}
		if(_findnext(ff_handle, &ff)!=0) {
			_findclose(ff_handle);
274
275
			ff_handle=-1;
		}
276
277
278
279
280
281
282
283
284
285
286
287
	}

	if(found==0)
		return(GLOB_NOMATCH);

	if(!(flags&GLOB_NOSORT)) {
		qsort(glob->gl_pathv,found,sizeof(char*),glob_compare);
	}

	return(0);	/* success */
}

rswindell's avatar
rswindell committed
288
289
#endif

Rob Swindell's avatar
Rob Swindell committed
290
void globfree(glob_t* glob)
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
{
	size_t i;

	if(glob==NULL)
		return;

	if(glob->gl_pathv!=NULL) {
		for(i=0;i<glob->gl_pathc;i++)
			if(glob->gl_pathv[i]!=NULL)
				free(glob->gl_pathv[i]);

		free(glob->gl_pathv);
		glob->gl_pathv=NULL;
	}
	glob->gl_pathc=0;
}

#endif /* !defined(__unix__) */

310
311
312
313
/****************************************************************************/
/* Returns number of files and/or sub-directories in directory (path)		*/
/* Similar, but not identical, to getfilecount()							*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
314
long getdirsize(const char* path, BOOL include_subdirs, BOOL subdir_only)
315
316
317
318
319
320
321
322
323
324
325
{
	char		match[MAX_PATH+1];
	glob_t		g;
	unsigned	gi;
	long		count=0;

	if(!isdir(path))
		return -1;

	SAFECOPY(match,path);
	backslash(match);
326
	SAFECAT(match,ALLFILES);
deuce's avatar
deuce committed
327
328
	if (glob(match,GLOB_MARK,NULL,&g) != 0)
		return 0;
329
	if(include_subdirs && !subdir_only)
330
331
		count=g.gl_pathc;
	else
332
333
334
335
336
337
338
339
340
		for(gi=0;gi<g.gl_pathc;gi++) {
			if(*lastchar(g.gl_pathv[gi])=='/') {
				if(!include_subdirs)
					continue;
			} else
				if(subdir_only)
					continue;
			count++;
		}
341
342
343
344
	globfree(&g);
	return(count);
}

345
346
347
/****************************************************************************/
/* POSIX directory operations using Microsoft _findfirst/next API.			*/
/****************************************************************************/
348
#if defined(_MSC_VER) || defined(__DMC__)
Rob Swindell's avatar
Rob Swindell committed
349
DIR* opendir(const char* dirname)
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
{
	DIR*	dir;

	if((dir=(DIR*)calloc(1,sizeof(DIR)))==NULL) {
		errno=ENOMEM;
		return(NULL);
	}
	sprintf(dir->filespec,"%.*s",sizeof(dir->filespec)-5,dirname);
	if(*dir->filespec && dir->filespec[strlen(dir->filespec)-1]!='\\')
		strcat(dir->filespec,"\\");
	strcat(dir->filespec,"*.*");
	dir->handle=_findfirst(dir->filespec,&dir->finddata);
	if(dir->handle==-1) {
		errno=ENOENT;
		free(dir);
		return(NULL);
	}
	return(dir);
}
Rob Swindell's avatar
Rob Swindell committed
369
struct dirent* readdir(DIR* dir)
370
371
372
373
374
375
376
377
378
379
380
381
{
	if(dir==NULL)
		return(NULL);
	if(dir->end==TRUE)
		return(NULL);
	if(dir->handle==-1)
		return(NULL);
	sprintf(dir->dirent.d_name,"%.*s",sizeof(struct dirent)-1,dir->finddata.name);
	if(_findnext(dir->handle,&dir->finddata)!=0)
		dir->end=TRUE;
	return(&dir->dirent);
}
Rob Swindell's avatar
Rob Swindell committed
382
int closedir (DIR* dir)
383
384
385
386
387
388
389
{
	if(dir==NULL)
		return(-1);
	_findclose(dir->handle);
	free(dir);
	return(0);
}
Rob Swindell's avatar
Rob Swindell committed
390
void rewinddir(DIR* dir)
391
392
393
394
395
396
397
398
399
{
	if(dir==NULL)
		return;
	_findclose(dir->handle);
	dir->end=FALSE;
	dir->handle=_findfirst(dir->filespec,&dir->finddata);
}
#endif /* defined(_MSC_VER) */

400
401
402
/****************************************************************************/
/* Returns the creation time of the file 'filename' in time_t format		*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
403
time_t fcdate(const char* filename)
404
405
406
407
408
409
410
411
412
{
	struct stat st;

	if(stat(filename, &st) != 0)
		return -1;

	return st.st_ctime;
}

413
414
415
/****************************************************************************/
/* Returns the time/date of the file in 'filename' in time_t (unix) format  */
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
416
time_t fdate(const char* filename)
417
418
419
420
421
422
423
424
425
{
	struct stat st;

	if(stat(filename, &st)!=0)
		return(-1);

	return(st.st_mtime);
}

426
427
428
/****************************************************************************/
/* Change the access and modification times for specified filename			*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
429
int setfdate(const char* filename, time_t t)
430
431
432
433
434
435
436
437
438
439
440
{
	struct utimbuf ut;

	memset(&ut,0,sizeof(ut));

	ut.actime=t;
	ut.modtime=t;

	return(utime(filename,&ut));
}

441
442
/****************************************************************************/
/* Returns the length of the file in 'filename'                             */
443
/* or -1 if the file doesn't exist											*/
444
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
445
off_t flength(const char *filename)
446
{
447
#if defined(__BORLANDC__) && !defined(__unix__)	/* stat() doesn't work right */
448
449
450
451

	long	handle;
	struct _finddata_t f;

452
	if((handle=_findfirst((char*)filename,&f))==-1)
453
454
455
456
457
458
		return(-1);

 	_findclose(handle);

	return(f.size);

459
#else
460
461
462
463

	struct stat st;

	if(stat(filename, &st)!=0)
464
		return(-1);
465
466
467
468
469
470

	return(st.st_size);

#endif
}

471
472

/****************************************************************************/
473
/* Checks the file system for the existence of a file.						*/
474
/* Returns TRUE if it exists, FALSE if it doesn't.                          */
475
/* 'filename' may *NOT* contain wildcards!									*/
476
477
478
/****************************************************************************/
static BOOL fnameexist(const char *filename)
{
479
480
481
482
483
484
485
486
487
	struct stat st;

	if(stat(filename, &st) != 0)
		return FALSE;

	if(S_ISDIR(st.st_mode))
		return FALSE;

	return TRUE;
488
489
}

490
491
492
493
494
/****************************************************************************/
/* Checks the file system for the existence of one or more files.			*/
/* Returns TRUE if it exists, FALSE if it doesn't.                          */
/* 'filespec' may contain wildcards!										*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
495
BOOL fexist(const char *filespec)
496
{
497
#if defined(_WIN32)
498
499
500

	long	handle;
	struct _finddata_t f;
501
	BOOL	found;
502

503
504
	if(!strchr(filespec,'*') && !strchr(filespec,'?'))
		return(fnameexist(filespec));
505

506
	if((handle=_findfirst((char*)filespec,&f))==-1)
507
		return(FALSE);
508
509
510
511
512
513
	found=TRUE;
	while(f.attrib&_A_SUBDIR)
		if(_findnext(handle,&f)!=0) {
			found=FALSE;
			break;
		}
514
515
516

 	_findclose(handle);

517
	return(found);
518

rswindell's avatar
rswindell committed
519
#else /* Unix or OS/2 */
520

rswindell's avatar
rswindell committed
521
	/* portion by cmartin */
522
523
524
525

	glob_t g;
    int c;

526
527
	if(!strchr(filespec,'*') && !strchr(filespec,'?'))
		return(fnameexist(filespec));
528

529
    /* start the search */
530
531
532
    glob(filespec, GLOB_MARK | GLOB_NOSORT, NULL, &g);

    if (!g.gl_pathc) {
533
	    /* no results */
534
535
536
537
    	globfree(&g);
    	return FALSE;
    }

538
    /* make sure it's not a directory */
539
540
	c = g.gl_pathc;
    while (c--) {
541
    	if (*lastchar(g.gl_pathv[c]) != '/') {
542
543
544
545
        	globfree(&g);
            return TRUE;
        }
    }
546

547
548
549
550
551
552
    globfree(&g);
    return FALSE;

#endif
}

553
554
555
/****************************************************************************/
/* Fixes upper/lowercase filename for Unix file systems						*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
556
BOOL fexistcase(char *path)
557
{
558
559
560
561
562
563
#if defined(_WIN32)

	char*	fname;
	long	handle;
	struct _finddata_t f;

564
	if(access(path, F_OK)==-1 && !strchr(path,'*') && !strchr(path,'?'))
565
		return(FALSE);
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581

	if((handle=_findfirst((char*)path,&f))==-1)
		return(FALSE);

 	_findclose(handle);

 	if(f.attrib&_A_SUBDIR)
		return(FALSE);

	fname=getfname(path);	/* Find filename in path */
	strcpy(fname,f.name);	/* Correct filename */

	return(TRUE);

#else /* Unix or OS/2 */

582
583
584
585
	char globme[MAX_PATH*4+1];
	char fname[MAX_PATH+1];
	char tmp[5];
	char *p;
586
	int  i;
587
	glob_t	glb;
588

589
	if(path[0]==0)		/* work around glibc bug 574274 */
590
591
		return FALSE;

592
593
594
	if(!strchr(path,'*') && !strchr(path,'?') && fnameexist(path))
		return(TRUE);

595
	SAFECOPY(globme,path);
596
	p=getfname(globme);
597
	SAFECOPY(fname,p);
598
599
	*p=0;
	for(i=0;fname[i];i++)  {
600
		if(IS_ALPHA(fname[i]))
601
			sprintf(tmp,"[%c%c]",toupper(fname[i]),tolower(fname[i]));
602
		else
603
604
605
			sprintf(tmp,"%c",fname[i]);
		strncat(globme,tmp,MAX_PATH*4);
	}
606
#if 0
607
	if(strcspn(path,"?*")!=strlen(path))  {
608
		sprintf(path,"%.*s",MAX_PATH,globme);
609
		return(fexist(path));
610
	}
611
#endif
612

613
	if(glob(globme,GLOB_MARK,NULL,&glb) != 0)
614
		return(FALSE);
615

616
617
	if(glb.gl_pathc>0)  {
		for(i=0;i<glb.gl_pathc;i++)  {
618
619
			if(*lastchar(glb.gl_pathv[i]) != '/')
				break;
620
		}
621
		if(i<glb.gl_pathc)  {
622
			sprintf(path,"%.*s",MAX_PATH,glb.gl_pathv[i]);
623
624
625
			globfree(&glb);
			return TRUE;
		}
626
	}
627
628
629

	globfree(&glb);
	return FALSE;
630

631
632
633
#endif
}

634
635
636
/****************************************************************************/
/* Returns TRUE if the filename specified is a directory					*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
637
BOOL isdir(const char *filename)
638
{
639
640
	char	path[MAX_PATH+1];
	char*	p;
641
642
	struct stat st;

643
644
645
	SAFECOPY(path,filename);

	p=lastchar(path);
646
	if(p!=path && IS_PATH_DELIM(*p)) {	/* chop off trailing slash */
647
648
649
650
651
652
#if !defined(__unix__)
		if(*(p-1)!=':')		/* Don't change C:\ to C: */
#endif
			*p=0;
	}

653
654
655
#if defined(__BORLANDC__) && !defined(__unix__)	/* stat() doesn't work right */
	if(stat(path, &st)!=0 || strchr(path,'*')!=NULL || strchr(path,'?')!=NULL)
#else
656
	if(stat(path, &st)!=0)
657
#endif
658
659
		return(FALSE);

660
	return(S_ISDIR(st.st_mode) ? TRUE : FALSE);
661
662
663
}

/****************************************************************************/
664
665
/* Returns the attributes (mode) for specified 'filename' or -1 on failure.	*/
/* The return value on Windows is *not* compatible with chmod().			*/
666
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
667
int getfattr(const char* filename)
668
{
669
#if defined(_WIN32)
670
671
672
	long handle;
	struct _finddata_t	finddata;

673
	if((handle=_findfirst((char*)filename,&finddata))==-1) {
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
		errno=ENOENT;
		return(-1);
	}
	_findclose(handle);
	return(finddata.attrib);
#else
	struct stat st;

	if(stat(filename, &st)!=0) {
		errno=ENOENT;
		return(-1L);
	}

	return(st.st_mode);
#endif
}

691
692
693
694
/****************************************************************************/
/* Returns the mode / type flags for specified 'filename'					*/
/* The return value *is* compatible with chmod(), or -1 upon failure.		*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
695
int getfmode(const char* filename)
696
697
698
699
700
701
702
703
704
705
{
	struct stat st;

	if(stat(filename, &st) != 0)
		return -1;

	return st.st_mode;
}


deuce's avatar
   
deuce committed
706
#ifdef __unix__
rswindell's avatar
rswindell committed
707
int removecase(const char *path)
deuce's avatar
   
deuce committed
708
709
710
711
712
713
714
715
716
717
718
719
720
{
	char inpath[MAX_PATH+1];
	char fname[MAX_PATH*4+1];
	char tmp[5];
	char *p;
	int  i;

	if(strchr(path,'?') || strchr(path,'*'))
		return(-1);
	SAFECOPY(inpath,path);
	p=getfname(inpath);
	fname[0]=0;
	for(i=0;p[i];i++)  {
721
		if(IS_ALPHA(p[i]))
deuce's avatar
   
deuce committed
722
723
724
725
726
727
728
			sprintf(tmp,"[%c%c]",toupper(p[i]),tolower(p[i]));
		else
			sprintf(tmp,"%c",p[i]);
		strncat(fname,tmp,MAX_PATH*4);
	}
	*p=0;

729
	return(delfiles(inpath,fname,0) >=1 ? 0 : -1);
deuce's avatar
   
deuce committed
730
731
732
733
734
}
#endif

/****************************************************************************/
/* Deletes all files in dir 'path' that match file spec 'spec'              */
735
/* Optionally, keep the last so many files (sorted by name)                 */
736
/* Returns number of files deleted or negative on error						*/
deuce's avatar
   
deuce committed
737
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
738
long delfiles(const char *inpath, const char *spec, size_t keep)
deuce's avatar
   
deuce committed
739
{
740
	char	*path;
741
	char	lastch;
742
	size_t	i;
743
    ulong	files = 0;
744
	long	errors = 0;
745
	glob_t	g;
746
	size_t	inpath_len=strlen(inpath);
747

748
749
750
751
752
753
	if(inpath_len==0)
		lastch=0;
	else
		lastch=inpath[inpath_len-1];
	path=(char *)malloc(inpath_len+1/*Delim*/+strlen(spec)+1/*Terminator*/);
	if(path==NULL)
754
		return -1;
755
756
757
758
759
760
	if(!IS_PATH_DELIM(lastch) && lastch)
		sprintf(path,"%s%c",inpath,PATH_DELIM);
	else
		strcpy(path,inpath);
	strcat(path,spec);
	glob(path,0,NULL,&g);
761
	free(path);
762
763
764
	if(keep >= g.gl_pathc)
		return 0;
	for(i = 0; i < g.gl_pathc && files < g.gl_pathc - keep; i++) {
765
766
		if(isdir(g.gl_pathv[i]))
			continue;
767
		(void)CHMOD(g.gl_pathv[i],S_IWRITE);	/* In case it's been marked RDONLY */
768
769
		if(remove(g.gl_pathv[i])==0)
			files++;
770
771
		else
			errors++;
772
773
	}
	globfree(&g);
774
775
776
	if(errors)
		return -errors;
	return files;
deuce's avatar
   
deuce committed
777
778
}

779
/****************************************************************************/
780
/* Returns number of files matching 'inpath'								*/
781
782
/* Similar, but not identical, to getdirsize(), e.g. subdirs never counted	*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
783
ulong getfilecount(const char *inpath)
784
785
786
787
788
789
790
{
	char path[MAX_PATH+1];
	glob_t	g;
	uint	gi;
	ulong	count = 0;

	SAFECOPY(path, inpath);
791
792
793
794
	if(isdir(path))
		backslash(path);
	if(IS_PATH_DELIM(*lastchar(path)))
		SAFECAT(path, ALLFILES);
795
796
797
798
799
800
801
802
803
804
805
	if(glob(path, GLOB_MARK, NULL, &g))
		return 0;
	for(gi = 0; gi < g.gl_pathc; ++gi) {
		if(*lastchar(g.gl_pathv[gi]) == '/')
			continue;
		count++;
	}
	globfree(&g);
	return count;
}

806
807
808
/****************************************************************************/
/* Returns number of bytes used by file(s) matching 'inpath'				*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
809
uint64_t getfilesizetotal(const char *inpath)
810
811
812
813
{
	char path[MAX_PATH+1];
	glob_t	g;
	uint	gi;
rswindell's avatar
rswindell committed
814
	off_t	size;
815
816
817
818
819
820
821
822
823
824
825
826
	uint64_t total = 0;

	SAFECOPY(path, inpath);
	if(isdir(path))
		backslash(path);
	if(IS_PATH_DELIM(*lastchar(path)))
		SAFECAT(path, ALLFILES);
	if(glob(path, GLOB_MARK, NULL, &g))
		return 0;
	for(gi = 0; gi < g.gl_pathc; ++gi) {
		if(*lastchar(g.gl_pathv[gi]) == '/')
			continue;
rswindell's avatar
rswindell committed
827
		size = flength(g.gl_pathv[gi]);
828
829
830
831
832
833
834
		if(size >= 1)
			total += size;
	}
	globfree(&g);
	return total;
}

835
/****************************************************************************/
836
837
/* Return free disk space in bytes (up to a maximum of 4GB)					*/
/****************************************************************************/
838
#if defined(_WIN32)
839
typedef BOOL(WINAPI * GetDiskFreeSpaceEx_t)
840
	(LPCTSTR,PULARGE_INTEGER,PULARGE_INTEGER,PULARGE_INTEGER);
841
842
843
844
845
846
847
848
849
850
851

static int bit_num(ulong val)
{
	int i;

	for(i=31;i>=0;i--)
		if(val&(1<<i))
			return(i);

	return(-1);
}
852
853
#endif

854
855
/* Unit should be a power-of-2 (e.g. 1024 to report kilobytes) or 1 (to report bytes) */
static ulong getdiskspace(const char* path, ulong unit, BOOL freespace)
856
{
857
#if defined(_WIN32)
858
859
860
861
862
863
864
865
866
867
868
869
	char			root[16];
	DWORD			TotalNumberOfClusters;
	DWORD			NumberOfFreeClusters;
	DWORD			BytesPerSector;
	DWORD			SectorsPerCluster;
	ULARGE_INTEGER	avail;
	ULARGE_INTEGER	size;
	static HINSTANCE hK32;
	GetDiskFreeSpaceEx_t GetDiskFreeSpaceEx;

	if(hK32 == NULL)
		hK32 = LoadLibrary("KERNEL32");
870
	GetDiskFreeSpaceEx
871
		= (GetDiskFreeSpaceEx_t)GetProcAddress(hK32,"GetDiskFreeSpaceExA");
872

873
874
	if (GetDiskFreeSpaceEx!=NULL) {	/* Windows 95-OSR2 or later */
		if(!GetDiskFreeSpaceEx(
875
876
877
878
			path,		/* pointer to the directory name */
			&avail,		/* receives the number of bytes on disk avail to the caller */
			&size,		/* receives the number of bytes on disk */
			NULL))		/* receives the free bytes on disk */
879
			return(0);
880

881
882
883
		if(freespace)
			size=avail;

884
		if(unit>1)
885
			size.QuadPart=Int64ShrlMod32(size.QuadPart,bit_num(unit));
886

887
#if defined(_ANONYMOUS_STRUCT)
888
		if(size.HighPart)
889
#else
890
		if(size.u.HighPart)
891
892
893
#endif
			return(0xffffffff);	/* 4GB max */

894
#if defined(_ANONYMOUS_STRUCT)
895
		return(size.LowPart);
896
#else
897
		return(size.u.LowPart);
898
899
900
901
902
903
#endif
	}

	/* Windows 95 (old way), limited to 2GB */
	sprintf(root,"%.3s",path);
	if(!GetDiskFreeSpace(
904
		root,					/* pointer to root path */
905
906
907
908
		(PDWORD)&SectorsPerCluster,		/* pointer to sectors per cluster */
		(PDWORD)&BytesPerSector,		/* pointer to bytes per sector */
		(PDWORD)&NumberOfFreeClusters,	/* pointer to number of free clusters */
		(PDWORD)&TotalNumberOfClusters  /* pointer to total number of clusters */
909
910
911
		))
		return(0);

912
913
	if(freespace)
		TotalNumberOfClusters = NumberOfFreeClusters;
914
	if(unit>1)
915
916
		TotalNumberOfClusters/=unit;
	return(TotalNumberOfClusters*SectorsPerCluster*BytesPerSector);
917
918


919
#elif defined(__solaris__) || (defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 300000000 /* NetBSD 3.0 */))
920

921
	struct statvfs fs;
922
	unsigned long blocks;
923

924
    if (statvfs(path, &fs) < 0)
925
926
    	return 0;

927
928
929
930
931
	if(freespace)
		blocks=fs.f_bavail;
	else
		blocks=fs.f_blocks;

932
	if(unit>1)
933
934
		blocks/=unit;
    return fs.f_bsize * blocks;
935

936
937
/* statfs is also used under FreeBSD (Though it *supports* statvfs() now too) */
#elif defined(__GLIBC__) || defined(BSD)
deuce's avatar
deuce committed
938

939
	struct statfs fs;
940
	unsigned long blocks;
deuce's avatar
deuce committed
941

942
	if(statfs(path, &fs) < 0)
deuce's avatar
deuce committed
943
944
    	return 0;

945
946
947
948
949
	if(freespace)
		blocks=fs.f_bavail;
	else
		blocks=fs.f_blocks;

deuce's avatar
deuce committed
950
	if(unit>1)
951
952
		blocks/=unit;
    return fs.f_bsize * blocks;
953

954
955
#else

rswindell's avatar
rswindell committed
956
	fprintf(stderr,"\n*** !Missing getfreediskspace implementation ***\n");
957
958
959
960
	return(0);

#endif
}
961

Rob Swindell's avatar
Rob Swindell committed
962
ulong getfreediskspace(const char* path, ulong unit)
963
964
965
966
{
	return getdiskspace(path, unit, /* freespace? */TRUE);
}

Rob Swindell's avatar
Rob Swindell committed
967
ulong getdisksize(const char* path, ulong unit)
968
969
970
971
{
	return getdiskspace(path, unit, /* freespace? */FALSE);
}

972
/****************************************************************************/
rswindell's avatar
rswindell committed
973
/* Resolves //, /./, and /../ in a path. Should work identically to Windows */
974
975
/****************************************************************************/
#if defined(__unix__)
Rob Swindell's avatar
Rob Swindell committed
976
char * _fullpath(char *target, const char *path, size_t size)  {
977
978
	char	*out;
	char	*p;
979
	BOOL	target_alloced=FALSE;
980

981
982
983
984
	if(target==NULL)  {
		if((target=malloc(MAX_PATH+1))==NULL) {
			return(NULL);
		}
985
		target_alloced=TRUE;
986
987
988
989
990
	}
	out=target;
	*out=0;

	if(*path != '/')  {
991
992
		if(*path == '~') {
			p=getenv("HOME");
993
			if(p==NULL || strlen(p)+strlen(path)>=size) {
994
995
				if(target_alloced)
					free(target);
996
				return(NULL);
997
			}
998
999
1000
1001
1002
1003
			strcpy(target,p);
			out=strrchr(target,'\0');
			path++;
		}
		else {
			p=getcwd(NULL,size);
1004
			if(p==NULL || strlen(p)+strlen(path)>=size) {
1005
1006
				if(target_alloced)
					free(target);
1007
				return(NULL);
1008
			}
1009
1010
1011
1012
1013
1014
1015
			strcpy(target,p);
			free(p);
			out=strrchr(target,'\0');
			*(out++)='/';
			*out=0;
			out--;
		}
1016
1017
	}
	strncat(target,path,size-1);
1018

1019
1020
1021
1022
1023
/*	if(stat(target,&sb))
		return(NULL);
	if(sb.st_mode&S_IFDIR)
		strcat(target,"/"); */

1024
1025
	for(;*out;out++) {
		while(*out=='/') {
1026
1027
			if(*(out+1)=='/')
				memmove(out,out+1,strlen(out));
1028
			else if(*(out+1)=='.' && (*(out+2)=='/' || *(out+2)==0))
1029
				memmove(out,out+2,strlen(out)-1);
1030
			else if(*(out+1)=='.' && *(out+2)=='.' && (*(out+3)=='/' || *(out+3)==0))  {
1031
1032
				*out=0;
				p=strrchr(target,'/');
1033
1034
				if(p==NULL)
					p=target;
1035
1036
1037
1038
1039
1040
1041
				memmove(p,out+3,strlen(out+3)+1);
				out=p;
			}
			else  {
				out++;
			}
		}
1042
1043
		if (!*out)
			break;
1044
1045
1046
1047
	}
	return(target);
}
#endif
1048
1049

/****************************************************************************/
1050
/* Adds a trailing slash/backslash (path delimiter) on path strings 		*/
1051
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
1052
char* backslash(char* path)
1053
1054
1055
{
	char* p;

rswindell's avatar
rswindell committed
1056
	p=lastchar(path);
1057

1058
	if(*p && !IS_PATH_DELIM(*p)) {
1059
1060
#if defined(__unix__)
		/* Convert trailing backslash to forwardslash on *nix */
1061
		if(*p!='\\')
1062
#endif
1063
			p++;
1064
		*p=PATH_DELIM;
rswindell's avatar
rswindell committed
1065
		*(++p)=0;
1066
1067
1068
	}
	return(path);
}
1069
1070

/****************************************************************************/
rswindell's avatar
rswindell committed
1071
/* Returns true if the specified filename an absolute pathname				*/
1072
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
1073
BOOL isabspath(const char *filename)
1074
1075
1076
1077
1078
1079
1080
1081
1082
{
	char path[MAX_PATH+1];

	return(stricmp(filename,FULLPATH(path,filename,sizeof(path)))==0);
}

/****************************************************************************/
/* Returns true if the specified filename is a full ("rooted") path			*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
1083
BOOL isfullpath(const char* filename)
1084
{
1085
	return(filename[0]=='/'
1086
#ifdef WIN32
1087
		|| filename[0]=='\\' || (IS_ALPHA(filename[0]) && filename[1]==':')