dirwrap.c 29.8 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
{
	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;
}

308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
#else /* __unix__ */

// Case-insensitive version of glob()
int globi(const char *p, int flags,
	int (*errfunc) (const char *epath, int eerrno),
	glob_t *g)
{
	char pattern[MAX_PATH * 2] = "";
	int len = 0;

	if(p != NULL) {
		while(*p != '\0' && len < MAX_PATH) {
			if(IS_ALPHA(*p))
				len += sprintf(pattern + len, "[%c%c]", toupper(*p), tolower(*p));
			else
				pattern[len++] = *p;
		}
	}
	pattern[len] = '\0';
	return glob(pattern, flags, errfunc, g);
}

#endif
331

332 333 334 335
/****************************************************************************/
/* Returns number of files and/or sub-directories in directory (path)		*/
/* Similar, but not identical, to getfilecount()							*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
336
long getdirsize(const char* path, BOOL include_subdirs, BOOL subdir_only)
337 338 339 340 341 342 343 344 345 346 347
{
	char		match[MAX_PATH+1];
	glob_t		g;
	unsigned	gi;
	long		count=0;

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

	SAFECOPY(match,path);
	backslash(match);
348
	SAFECAT(match,ALLFILES);
deuce's avatar
deuce committed
349 350
	if (glob(match,GLOB_MARK,NULL,&g) != 0)
		return 0;
351
	if(include_subdirs && !subdir_only)
352 353
		count=g.gl_pathc;
	else
354 355 356 357 358 359 360 361 362
		for(gi=0;gi<g.gl_pathc;gi++) {
			if(*lastchar(g.gl_pathv[gi])=='/') {
				if(!include_subdirs)
					continue;
			} else
				if(subdir_only)
					continue;
			count++;
		}
363 364 365 366
	globfree(&g);
	return(count);
}

367 368 369
/****************************************************************************/
/* POSIX directory operations using Microsoft _findfirst/next API.			*/
/****************************************************************************/
370
#if defined(_MSC_VER) || defined(__DMC__)
Rob Swindell's avatar
Rob Swindell committed
371
DIR* opendir(const char* dirname)
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
{
	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
391
struct dirent* readdir(DIR* dir)
392 393 394 395 396 397 398 399 400 401 402 403
{
	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
404
int closedir (DIR* dir)
405 406 407 408 409 410 411
{
	if(dir==NULL)
		return(-1);
	_findclose(dir->handle);
	free(dir);
	return(0);
}
Rob Swindell's avatar
Rob Swindell committed
412
void rewinddir(DIR* dir)
413 414 415 416 417 418 419 420 421
{
	if(dir==NULL)
		return;
	_findclose(dir->handle);
	dir->end=FALSE;
	dir->handle=_findfirst(dir->filespec,&dir->finddata);
}
#endif /* defined(_MSC_VER) */

422 423 424
/****************************************************************************/
/* Returns the creation time of the file 'filename' in time_t format		*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
425
time_t fcdate(const char* filename)
426 427 428 429 430 431 432 433 434
{
	struct stat st;

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

	return st.st_ctime;
}

435 436 437
/****************************************************************************/
/* Returns the time/date of the file in 'filename' in time_t (unix) format  */
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
438
time_t fdate(const char* filename)
439 440 441 442 443 444 445 446 447
{
	struct stat st;

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

	return(st.st_mtime);
}

448 449 450
/****************************************************************************/
/* Change the access and modification times for specified filename			*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
451
int setfdate(const char* filename, time_t t)
452 453 454 455 456 457 458 459 460 461 462
{
	struct utimbuf ut;

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

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

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

463 464
/****************************************************************************/
/* Returns the length of the file in 'filename'                             */
465
/* or -1 if the file doesn't exist											*/
466
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
467
off_t flength(const char *filename)
468
{
469
#if defined(__BORLANDC__) && !defined(__unix__)	/* stat() doesn't work right */
470 471 472 473

	long	handle;
	struct _finddata_t f;

474
	if((handle=_findfirst((char*)filename,&f))==-1)
475 476 477 478 479 480
		return(-1);

 	_findclose(handle);

	return(f.size);

481
#else
482 483 484 485

	struct stat st;

	if(stat(filename, &st)!=0)
486
		return(-1);
487 488 489 490 491 492

	return(st.st_size);

#endif
}

493 494

/****************************************************************************/
495
/* Checks the file system for the existence of a file.						*/
496
/* Returns TRUE if it exists, FALSE if it doesn't.                          */
497
/* 'filename' may *NOT* contain wildcards!									*/
498 499 500
/****************************************************************************/
static BOOL fnameexist(const char *filename)
{
501 502 503 504 505 506 507 508 509
	struct stat st;

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

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

	return TRUE;
510 511
}

512 513 514 515 516
/****************************************************************************/
/* 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
517
BOOL fexist(const char *filespec)
518
{
519
#if defined(_WIN32)
520 521 522

	long	handle;
	struct _finddata_t f;
523
	BOOL	found;
524

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

528
	if((handle=_findfirst((char*)filespec,&f))==-1)
529
		return(FALSE);
530 531 532 533 534 535
	found=TRUE;
	while(f.attrib&_A_SUBDIR)
		if(_findnext(handle,&f)!=0) {
			found=FALSE;
			break;
		}
536 537 538

 	_findclose(handle);

539
	return(found);
540

rswindell's avatar
rswindell committed
541
#else /* Unix or OS/2 */
542

rswindell's avatar
rswindell committed
543
	/* portion by cmartin */
544 545 546 547

	glob_t g;
    int c;

548 549
	if(!strchr(filespec,'*') && !strchr(filespec,'?'))
		return(fnameexist(filespec));
550

551
    /* start the search */
552 553 554
    glob(filespec, GLOB_MARK | GLOB_NOSORT, NULL, &g);

    if (!g.gl_pathc) {
555
	    /* no results */
556 557 558 559
    	globfree(&g);
    	return FALSE;
    }

560
    /* make sure it's not a directory */
561 562
	c = g.gl_pathc;
    while (c--) {
563
    	if (*lastchar(g.gl_pathv[c]) != '/') {
564 565 566 567
        	globfree(&g);
            return TRUE;
        }
    }
568

569 570 571 572 573 574
    globfree(&g);
    return FALSE;

#endif
}

575 576 577
/****************************************************************************/
/* Fixes upper/lowercase filename for Unix file systems						*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
578
BOOL fexistcase(char *path)
579
{
580 581 582 583 584 585
#if defined(_WIN32)

	char*	fname;
	long	handle;
	struct _finddata_t f;

586
	if(access(path, F_OK)==-1 && !strchr(path,'*') && !strchr(path,'?'))
587
		return(FALSE);
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603

	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 */

604 605 606 607
	char globme[MAX_PATH*4+1];
	char fname[MAX_PATH+1];
	char tmp[5];
	char *p;
608
	int  i;
609
	glob_t	glb;
610

611
	if(path[0]==0)		/* work around glibc bug 574274 */
612 613
		return FALSE;

614 615 616
	if(!strchr(path,'*') && !strchr(path,'?') && fnameexist(path))
		return(TRUE);

617
	SAFECOPY(globme,path);
618
	p=getfname(globme);
619
	SAFECOPY(fname,p);
620 621
	*p=0;
	for(i=0;fname[i];i++)  {
622
		if(IS_ALPHA(fname[i]))
623
			sprintf(tmp,"[%c%c]",toupper(fname[i]),tolower(fname[i]));
624
		else
625 626 627
			sprintf(tmp,"%c",fname[i]);
		strncat(globme,tmp,MAX_PATH*4);
	}
628
#if 0
629
	if(strcspn(path,"?*")!=strlen(path))  {
630
		sprintf(path,"%.*s",MAX_PATH,globme);
631
		return(fexist(path));
632
	}
633
#endif
634

635
	if(glob(globme,GLOB_MARK,NULL,&glb) != 0)
636
		return(FALSE);
637

638 639
	if(glb.gl_pathc>0)  {
		for(i=0;i<glb.gl_pathc;i++)  {
640 641
			if(*lastchar(glb.gl_pathv[i]) != '/')
				break;
642
		}
643
		if(i<glb.gl_pathc)  {
644
			sprintf(path,"%.*s",MAX_PATH,glb.gl_pathv[i]);
645 646 647
			globfree(&glb);
			return TRUE;
		}
648
	}
649 650 651

	globfree(&glb);
	return FALSE;
652

653 654 655
#endif
}

656 657 658
/****************************************************************************/
/* Returns TRUE if the filename specified is a directory					*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
659
BOOL isdir(const char *filename)
660
{
661 662
	char	path[MAX_PATH+1];
	char*	p;
663 664
	struct stat st;

665 666 667
	SAFECOPY(path,filename);

	p=lastchar(path);
668
	if(p!=path && IS_PATH_DELIM(*p)) {	/* chop off trailing slash */
669 670 671 672 673 674
#if !defined(__unix__)
		if(*(p-1)!=':')		/* Don't change C:\ to C: */
#endif
			*p=0;
	}

675 676 677
#if defined(__BORLANDC__) && !defined(__unix__)	/* stat() doesn't work right */
	if(stat(path, &st)!=0 || strchr(path,'*')!=NULL || strchr(path,'?')!=NULL)
#else
678
	if(stat(path, &st)!=0)
679
#endif
680 681
		return(FALSE);

682
	return(S_ISDIR(st.st_mode) ? TRUE : FALSE);
683 684 685
}

/****************************************************************************/
686 687
/* Returns the attributes (mode) for specified 'filename' or -1 on failure.	*/
/* The return value on Windows is *not* compatible with chmod().			*/
688
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
689
int getfattr(const char* filename)
690
{
691
#if defined(_WIN32)
692 693 694
	long handle;
	struct _finddata_t	finddata;

695
	if((handle=_findfirst((char*)filename,&finddata))==-1) {
696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712
		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
}

713 714 715 716
/****************************************************************************/
/* 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
717
int getfmode(const char* filename)
718 719 720 721 722 723 724 725 726 727
{
	struct stat st;

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

	return st.st_mode;
}


deuce's avatar
 
deuce committed
728
#ifdef __unix__
rswindell's avatar
rswindell committed
729
int removecase(const char *path)
deuce's avatar
 
deuce committed
730 731 732 733 734 735 736 737 738 739 740 741 742
{
	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++)  {
743
		if(IS_ALPHA(p[i]))
deuce's avatar
 
deuce committed
744 745 746 747 748 749 750
			sprintf(tmp,"[%c%c]",toupper(p[i]),tolower(p[i]));
		else
			sprintf(tmp,"%c",p[i]);
		strncat(fname,tmp,MAX_PATH*4);
	}
	*p=0;

751
	return(delfiles(inpath,fname,0) >=1 ? 0 : -1);
deuce's avatar
 
deuce committed
752 753 754 755 756
}
#endif

/****************************************************************************/
/* Deletes all files in dir 'path' that match file spec 'spec'              */
757
/* If spec matches a sub-directory, it is traversed and removed recursively */			
758
/* Optionally, keep the last so many files (sorted by name)                 */
759
/* Returns number of files deleted or negative on error						*/
deuce's avatar
 
deuce committed
760
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
761
long delfiles(const char *inpath, const char *spec, size_t keep)
deuce's avatar
 
deuce committed
762
{
763 764 765
	char*	path;
	char*	fpath;
	char*	fname;
766
	char	lastch;
767
	size_t	i;
768
    ulong	files = 0;
769
	long	errors = 0;
770
	long	recursed;
771
	glob_t	g;
772
	size_t	inpath_len=strlen(inpath);
773 774 775 776 777 778 779
	int		flags =
#ifdef GLOB_PERIOD
		GLOB_PERIOD
#else
		0
#endif
	;
780

781 782 783 784 785 786
	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)
787
		return -1;
788 789 790 791 792
	if(!IS_PATH_DELIM(lastch) && lastch)
		sprintf(path,"%s%c",inpath,PATH_DELIM);
	else
		strcpy(path,inpath);
	strcat(path,spec);
793
	glob(path, flags, NULL, &g);
794
	free(path);
795 796 797
	if(keep >= g.gl_pathc)
		return 0;
	for(i = 0; i < g.gl_pathc && files < g.gl_pathc - keep; i++) {
798 799 800 801 802 803 804 805 806 807 808 809
		fpath = g.gl_pathv[i];
		if(isdir(fpath)) {
			fname = getfname(fpath);
			if(strcmp(fname, ".") == 0 || strcmp(fname, "..") == 0)
				continue;
			recursed = delfiles(fpath, spec, keep);
			if(recursed >= 0)
				files += recursed;
			else
				errors += recursed;
			if(rmdir(fpath) != 0)
				errors++;
810
			continue;
811 812 813
		}
		(void)CHMOD(fpath, S_IWRITE);	/* In case it's been marked RDONLY */
		if(remove(fpath)==0)
814
			files++;
815 816
		else
			errors++;
817 818
	}
	globfree(&g);
819 820 821
	if(errors)
		return -errors;
	return files;
deuce's avatar
 
deuce committed
822 823
}

824
/****************************************************************************/
825
/* Returns number of files matching 'inpath'								*/
826 827
/* Similar, but not identical, to getdirsize(), e.g. subdirs never counted	*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
828
ulong getfilecount(const char *inpath)
829 830 831 832 833 834 835
{
	char path[MAX_PATH+1];
	glob_t	g;
	uint	gi;
	ulong	count = 0;

	SAFECOPY(path, inpath);
836 837 838 839
	if(isdir(path))
		backslash(path);
	if(IS_PATH_DELIM(*lastchar(path)))
		SAFECAT(path, ALLFILES);
840 841 842 843 844 845 846 847 848 849 850
	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;
}

851 852 853
/****************************************************************************/
/* Returns number of bytes used by file(s) matching 'inpath'				*/
/****************************************************************************/
Rob Swindell's avatar
Rob Swindell committed
854
uint64_t getfilesizetotal(const char *inpath)
855 856 857 858
{
	char path[MAX_PATH+1];
	glob_t	g;
	uint	gi;
rswindell's avatar
rswindell committed
859
	off_t	size;
860 861 862 863 864 865 866 867 868 869 870 871
	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
872
		size = flength(g.gl_pathv[gi]);
873 874 875 876 877 878 879
		if(size >= 1)
			total += size;
	}
	globfree(&g);
	return total;
}

880
/****************************************************************************/
881 882
/* Return free disk space in bytes (up to a maximum of 4GB)					*/
/****************************************************************************/
883
#if defined(_WIN32)
884
typedef BOOL(WINAPI * GetDiskFreeSpaceEx_t)
885
	(LPCTSTR,PULARGE_INTEGER,PULARGE_INTEGER,PULARGE_INTEGER);
886 887 888 889 890 891 892 893 894 895 896

static int bit_num(ulong val)
{
	int i;

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

	return(-1);
}
897 898
#endif

899 900
/* 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)
901
{
902
#if defined(_WIN32)
903 904 905 906 907 908 909 910 911 912 913 914
	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");
915
	GetDiskFreeSpaceEx
916
		= (GetDiskFreeSpaceEx_t)GetProcAddress(hK32,"GetDiskFreeSpaceExA");
917

918 919
	if (GetDiskFreeSpaceEx!=NULL) {	/* Windows 95-OSR2 or later */
		if(!GetDiskFreeSpaceEx(
920 921 922 923
			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 */
924
			return(0);
925

926 927 928
		if(freespace)
			size=avail;

929
		if(unit>1)
930
			size.QuadPart=Int64ShrlMod32(size.QuadPart,bit_num(unit));
931

932
#if defined(_ANONYMOUS_STRUCT)
933
		if(size.HighPart)
934
#else
935
		if(size.u.HighPart)
936 937 938
#endif
			return(0xffffffff);	/* 4GB max */

939
#if defined(_ANONYMOUS_STRUCT)
940
		return(size.LowPart);
941
#else
942
		return(size.u.LowPart);
943 944 945 946 947 948
#endif
	}

	/* Windows 95 (old way), limited to 2GB */
	sprintf(root,"%.3s",path);
	if(!GetDiskFreeSpace(
949
		root,					/* pointer to root path */
950 951 952 953
		(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 */
954 955 956
		))
		return(0);

957 958
	if(freespace)
		TotalNumberOfClusters = NumberOfFreeClusters;
959
	if(unit>1)
960 961
		TotalNumberOfClusters/=unit;
	return(TotalNumberOfClusters*SectorsPerCluster*BytesPerSector);
962 963


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

966
	struct statvfs fs;
967
	unsigned long blocks;
968

969
    if (statvfs(path, &fs) < 0)
970 971
    	return 0;

972 973 974 975 976
	if(freespace)
		blocks=fs.f_bavail;
	else
		blocks=fs.f_blocks;

977
	if(unit>1)
978 979
		blocks/=unit;
    return fs.f_bsize * blocks;
980

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

984
	struct statfs fs;
985
	unsigned long blocks;
deuce's avatar
deuce committed
986

987
	if(statfs(path, &fs) < 0)
deuce's avatar
deuce committed
988 989
    	return 0;

990 991 992 993 994
	if(freespace)
		blocks=fs.f_bavail;
	else
		blocks=fs.f_blocks;