dirwrap.c 27.3 KB
Newer Older
1
/* Directory-related system-call wrappers */
2
// vi: tabstop=4
3
4
5
6
7
8
9

/* $Id$ */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
rswindell's avatar
rswindell committed
10
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
11
 *																			*
12
13
 * This library is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU Lesser General Public License		*
14
15
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
16
17
 * See the GNU Lesser General Public License for more details: lgpl.txt or	*
 * http://www.fsf.org/copyleft/lesser.html									*
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

37
38
#include <string.h>	/* strrchr */

39
#if defined(_WIN32)
40

41
42
	#include <windows.h>	/* WINAPI, etc */
	#include <io.h>			/* _findfirst */
43
44
45

#elif defined __unix__

46
47
48
	#include <unistd.h>		/* usleep */
	#include <fcntl.h>		/* O_NOCCTY */
	#include <ctype.h>		/* toupper */
rswindell's avatar
rswindell committed
49
	#include <sys/param.h>
50

rswindell's avatar
rswindell committed
51
	#if defined(BSD)
52
		#include <sys/mount.h>
53
	#endif
rswindell's avatar
rswindell committed
54
	#if defined(__FreeBSD__)
55
56
		#include <sys/kbio.h>
	#endif
57
58
	#if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 300000000 /* NetBSD 3.0 */)
		#include <sys/statvfs.h>
rswindell's avatar
rswindell committed
59
	#endif
60

61
	#include <sys/ioctl.h>	/* ioctl */
62

63
64
65
	#if defined(__GLIBC__)		/* actually, BSD, but will work for now */
		#include <sys/vfs.h>    /* statfs() */
	#endif
66

deuce's avatar
deuce committed
67
68
69
70
	#if defined(__solaris__)
		#include <sys/statvfs.h>
	#endif

71
#endif /* __unix__ */
72

rswindell's avatar
rswindell committed
73
74
75
#if defined(__WATCOMC__)
	#include <dos.h>
#endif
76

77
78
79
80
81
82
83
#include <sys/types.h>	/* _dev_t */
#include <sys/stat.h>	/* struct stat */

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

84
#include "genwrap.h"	/* strupr/strlwr */
85
86
87
88
89
#include "dirwrap.h"	/* DLLCALL */

/****************************************************************************/
/* Return the filename portion of a full pathname							*/
/****************************************************************************/
90
char* DLLCALL getfname(const char* path)
91
{
92
93
	const char* fname;
	const char* bslash;
94
95

	fname=strrchr(path,'/');
96
97
98
	bslash=strrchr(path,'\\');
	if(bslash>fname)
		fname=bslash;
99
	if(fname!=NULL)
100
		fname++;
101
	else
102
		fname=(char*)path;
103
	return((char*)fname);
104
105
}

106
107
108
109
110
111
112
113
114
115
/****************************************************************************/
/* Return a pointer to a file's extesion (beginning with '.')				*/
/****************************************************************************/
char* DLLCALL getfext(const char* path)
{
	char *fname;
	char *fext;

	fname=getfname(path);
	fext=strrchr(fname,'.');
116
	if(fext==NULL || fext==fname)
117
118
119
		return(NULL);
	return(fext);
}
120
121
122
123
124
125
126
127
128
129

/****************************************************************************/
/* Break a path name into components.										*/
/****************************************************************************/
#if defined(__unix__)
void DLLCALL _splitpath(const char *path, char *drive, char *dir, char *fname, char *ext)
{
	char*	p;

	ext[0]=0;
130
131
132
133
134
135
136
137
138
139
	drive[0]=0;			/* no drive letters on Unix */

	strcpy(dir,path);	/* Optional directory path, including trailing slash. */
	p=getfname(dir);
	strcpy(fname,p);	/* Base filename (no extension) */
	if(p==dir)
		dir[0]=0;		/* no directory specified in path */
	else
		*p=0;			/* truncate dir at filename */
	p=getfext(fname);
140
	if(p!=NULL) {
141
		strcpy(ext,p);	/* Optional filename extension, including leading period (.) */
142
143
144
145
146
		*p=0;
	}
}
#endif

147
148
149
150
/****************************************************************************/
/* Win32 (minimal) implementation of POSIX.2 glob() function				*/
/* This code _may_ work on other DOS-based platforms (e.g. OS/2)			*/
/****************************************************************************/
151
#if !defined(__unix__)
deuce's avatar
deuce committed
152
static int __cdecl glob_compare( const void *arg1, const void *arg2 )
153
154
155
156
157
{
   /* Compare all of both strings: */
   return stricmp( * ( char** ) arg1, * ( char** ) arg2 );
}

158
#if defined(__BORLANDC__)
159
160
161
	#pragma argsused
#endif

rswindell's avatar
rswindell committed
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#if defined(__WATCOMC__)

int	DLLCALL	glob(const char *pattern, int flags, void* unused, glob_t* glob)
{
    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;
	}

177
	if(_dos_findfirst((char*)pattern,(flags&GLOB_PERIOD) ? _A_HIDDEN : _A_NORMAL,&ff)!=0)
rswindell's avatar
rswindell committed
178
179
180
		return(GLOB_NOMATCH);

	do {
181
182
		if((flags&GLOB_PERIOD || ff.name[0]!='.') &&
			(!(flags&GLOB_ONLYDIR) || ff.attrib&_A_SUBDIR)) {
rswindell's avatar
rswindell committed
183
184
185
186
187
188
189
190
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
			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

222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
int	DLLCALL	glob(const char *pattern, int flags, void* unused, glob_t* glob)
{
    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) {
238
		if((flags&GLOB_PERIOD || (ff.name[0]!='.' && !(ff.attrib&_A_HIDDEN))) &&
239
			(!(flags&GLOB_ONLYDIR) || ff.attrib&_A_SUBDIR)) {
240
			if((new_pathv=(char**)realloc(glob->gl_pathv
241
242
243
244
245
246
247
				,(glob->gl_pathc+1)*sizeof(char*)))==NULL) {
				globfree(glob);
				return(GLOB_NOSPACE);
			}
			glob->gl_pathv=new_pathv;

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

253
			if((glob->gl_pathv[glob->gl_pathc]=(char*)malloc(strlen(path)+2))==NULL) {
254
255
256
257
258
259
260
261
262
263
264
265
				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);
266
267
			ff_handle=-1;
		}
268
269
270
271
272
273
274
275
276
277
278
279
	}

	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
280
281
#endif

282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
void DLLCALL globfree(glob_t* glob)
{
	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__) */

302
303
304
305
/****************************************************************************/
/* Returns number of files and/or sub-directories in directory (path)		*/
/* Similar, but not identical, to getfilecount()							*/
/****************************************************************************/
306
307
308
309
310
311
312
313
314
315
316
317
318
long DLLCALL getdirsize(const char* path, BOOL include_subdirs, BOOL subdir_only)
{
	char		match[MAX_PATH+1];
	glob_t		g;
	unsigned	gi;
	long		count=0;

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

	SAFECOPY(match,path);
	backslash(match);
	strcat(match,ALLFILES);
319
320
	glob(match,GLOB_MARK,NULL,&g);
	if(include_subdirs && !subdir_only)
321
322
		count=g.gl_pathc;
	else
323
324
325
326
327
328
329
330
331
		for(gi=0;gi<g.gl_pathc;gi++) {
			if(*lastchar(g.gl_pathv[gi])=='/') {
				if(!include_subdirs)
					continue;
			} else
				if(subdir_only)
					continue;
			count++;
		}
332
333
334
335
	globfree(&g);
	return(count);
}

336
337
338
/****************************************************************************/
/* POSIX directory operations using Microsoft _findfirst/next API.			*/
/****************************************************************************/
339
#if defined(_MSC_VER) || defined(__DMC__)
deuce's avatar
deuce committed
340
DIR* DLLCALL opendir(const char* dirname)
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
{
	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);
}
deuce's avatar
deuce committed
360
struct dirent* DLLCALL readdir(DIR* dir)
361
362
363
364
365
366
367
368
369
370
371
372
{
	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);
}
deuce's avatar
deuce committed
373
int DLLCALL closedir (DIR* dir)
374
375
376
377
378
379
380
{
	if(dir==NULL)
		return(-1);
	_findclose(dir->handle);
	free(dir);
	return(0);
}
deuce's avatar
deuce committed
381
void DLLCALL rewinddir(DIR* dir)
382
383
384
385
386
387
388
389
390
{
	if(dir==NULL)
		return;
	_findclose(dir->handle);
	dir->end=FALSE;
	dir->handle=_findfirst(dir->filespec,&dir->finddata);
}
#endif /* defined(_MSC_VER) */

391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
/****************************************************************************/
/* Returns the creation time of the file 'filename' in time_t format		*/
/****************************************************************************/
time_t DLLCALL fcdate(const char* filename)
{
	struct stat st;

	if(access(filename, 0) < 0)
		return -1;

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

	return st.st_ctime;
}

407
408
409
/****************************************************************************/
/* Returns the time/date of the file in 'filename' in time_t (unix) format  */
/****************************************************************************/
410
time_t DLLCALL fdate(const char* filename)
411
412
413
414
415
416
417
418
419
420
421
422
{
	struct stat st;

	if(access(filename,0)==-1)
		return(-1);

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

	return(st.st_mtime);
}

423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
/****************************************************************************/
/* Change the access and modification times for specified filename			*/
/****************************************************************************/
int DLLCALL setfdate(const char* filename, time_t t)
{
	struct utimbuf ut;

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

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

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

438
439
/****************************************************************************/
/* Returns the length of the file in 'filename'                             */
440
/* or -1 if the file doesn't exist											*/
441
/****************************************************************************/
442
off_t DLLCALL flength(const char *filename)
443
{
444
#if defined(__BORLANDC__) && !defined(__unix__)	/* stat() doesn't work right */
445
446
447
448

	long	handle;
	struct _finddata_t f;

449
	if(access((char*)filename,0)==-1)
450
		return(-1);
451

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(access(filename,0)==-1)
464
		return(-1);
465
466

	if(stat(filename, &st)!=0)
467
		return(-1);
468
469
470
471
472
473

	return(st.st_size);

#endif
}

474
475
476
477
478
479
480
481
482
483
484
485
486
487
488

/****************************************************************************/
/* Checks the file system for the existence of one or more files.			*/
/* Returns TRUE if it exists, FALSE if it doesn't.                          */
/* 'filespec' may *NOT* contain wildcards!									*/
/****************************************************************************/
static BOOL fnameexist(const char *filename)
{
	if(access(filename,0)==-1)
		return(FALSE);
	if(!isdir(filename))
		return(TRUE);
	return(FALSE);
}

489
490
491
492
493
/****************************************************************************/
/* 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!										*/
/****************************************************************************/
494
BOOL DLLCALL fexist(const char *filespec)
495
{
496
#if defined(_WIN32)
497
498
499

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

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

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

 	_findclose(handle);

516
	return(found);
517

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

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

	glob_t g;
    int c;

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

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

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

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

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

#endif
}

552
553
554
555
556
/****************************************************************************/
/* Fixes upper/lowercase filename for Unix file systems						*/
/****************************************************************************/
BOOL DLLCALL fexistcase(char *path)
{
557
558
559
560
561
562
#if defined(_WIN32)

	char*	fname;
	long	handle;
	struct _finddata_t f;

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

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

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

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

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

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

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

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

	globfree(&glb);
	return FALSE;
629

630
631
632
#endif
}

633
634
635
636
#if !defined(S_ISDIR)
	#define S_ISDIR(x)	((x)&S_IFDIR)
#endif

637
638
639
/****************************************************************************/
/* Returns TRUE if the filename specified is a directory					*/
/****************************************************************************/
640
BOOL DLLCALL isdir(const char *filename)
641
{
642
643
	char	path[MAX_PATH+1];
	char*	p;
644
645
	struct stat st;

646
647
648
	SAFECOPY(path,filename);

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

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

663
	return(S_ISDIR(st.st_mode) ? TRUE : FALSE);
664
665
666
667
668
}

/****************************************************************************/
/* Returns the attributes (mode) for specified 'filename'					*/
/****************************************************************************/
669
int DLLCALL getfattr(const char* filename)
670
{
671
#if defined(_WIN32)
672
673
674
	long handle;
	struct _finddata_t	finddata;

675
	if((handle=_findfirst((char*)filename,&finddata))==-1) {
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
		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
}

deuce's avatar
   
deuce committed
693
#ifdef __unix__
rswindell's avatar
rswindell committed
694
int removecase(const char *path)
deuce's avatar
   
deuce committed
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
{
	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++)  {
		if(isalpha(p[i]))
			sprintf(tmp,"[%c%c]",toupper(p[i]),tolower(p[i]));
		else
			sprintf(tmp,"%c",p[i]);
		strncat(fname,tmp,MAX_PATH*4);
	}
	*p=0;

716
	return(delfiles(inpath,fname) >=1 ? 0 : -1);
deuce's avatar
   
deuce committed
717
718
719
720
721
}
#endif

/****************************************************************************/
/* Deletes all files in dir 'path' that match file spec 'spec'              */
722
/* Returns number of files deleted or negative on error						*/
deuce's avatar
   
deuce committed
723
/****************************************************************************/
724
ulong DLLCALL delfiles(const char *inpath, const char *spec)
deuce's avatar
   
deuce committed
725
{
726
	char	*path;
727
728
729
	char	lastch;
    uint	i,files=0;
	glob_t	g;
730
	size_t	inpath_len=strlen(inpath);
731

732
733
734
735
736
737
	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)
738
		return -1;
739
740
741
742
743
744
	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);
745
	free(path);
746
747
748
749
750
751
752
753
754
	for(i=0;i<g.gl_pathc;i++) {
		if(isdir(g.gl_pathv[i]))
			continue;
		CHMOD(g.gl_pathv[i],S_IWRITE);	/* Incase it's been marked RDONLY */
		if(remove(g.gl_pathv[i])==0)
			files++;
	}
	globfree(&g);
	return(files);
deuce's avatar
   
deuce committed
755
756
}

757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
/****************************************************************************/
/* Returns number of files in a directory (inpath) matching 'pattern'		*/
/* Similar, but not identical, to getdirsize(), e.g. subdirs never counted	*/
/****************************************************************************/
ulong DLLCALL getfilecount(const char *inpath, const char* pattern)
{
	char path[MAX_PATH+1];
	glob_t	g;
	uint	gi;
	ulong	count = 0;

	SAFECOPY(path, inpath);
	backslash(path);
	strcat(path, pattern);
	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;
}

782
/****************************************************************************/
783
784
/* Return free disk space in bytes (up to a maximum of 4GB)					*/
/****************************************************************************/
785
#if defined(_WIN32)
786
typedef BOOL(WINAPI * GetDiskFreeSpaceEx_t)
787
	(LPCTSTR,PULARGE_INTEGER,PULARGE_INTEGER,PULARGE_INTEGER);
788
789
790
791
792
793
794
795
796
797
798

static int bit_num(ulong val)
{
	int i;

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

	return(-1);
}
799
800
#endif

801
802
/* 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)
803
{
804
#if defined(_WIN32)
805
806
807
808
809
810
811
812
813
814
815
816
	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");
817
	GetDiskFreeSpaceEx
818
		= (GetDiskFreeSpaceEx_t)GetProcAddress(hK32,"GetDiskFreeSpaceExA");
819

820
821
	if (GetDiskFreeSpaceEx!=NULL) {	/* Windows 95-OSR2 or later */
		if(!GetDiskFreeSpaceEx(
822
823
824
825
			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 */
826
			return(0);
827

828
829
830
		if(freespace)
			size=avail;

831
		if(unit>1)
832
			size.QuadPart=Int64ShrlMod32(size.QuadPart,bit_num(unit));
833

834
#if defined(_ANONYMOUS_STRUCT)
835
		if(size.HighPart)
836
#else
837
		if(size.u.HighPart)
838
839
840
#endif
			return(0xffffffff);	/* 4GB max */

841
#if defined(_ANONYMOUS_STRUCT)
842
		return(size.LowPart);
843
#else
844
		return(size.u.LowPart);
845
846
847
848
849
850
#endif
	}

	/* Windows 95 (old way), limited to 2GB */
	sprintf(root,"%.3s",path);
	if(!GetDiskFreeSpace(
851
		root,					/* pointer to root path */
852
853
854
855
		(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 */
856
857
858
		))
		return(0);

859
860
	if(freespace)
		TotalNumberOfClusters = NumberOfFreeClusters;
861
	if(unit>1)
862
863
		TotalNumberOfClusters/=unit;
	return(TotalNumberOfClusters*SectorsPerCluster*BytesPerSector);
864
865


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

868
	struct statvfs fs;
869
	unsigned long blocks;
870

871
    if (statvfs(path, &fs) < 0)
872
873
    	return 0;

874
875
876
877
878
	if(freespace)
		blocks=fs.f_bavail;
	else
		blocks=fs.f_blocks;

879
	if(unit>1)
880
881
		blocks/=unit;
    return fs.f_bsize * blocks;
882

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

886
	struct statfs fs;
887
	unsigned long blocks;
deuce's avatar
deuce committed
888

889
    if (statfs(path, &fs) < 0)
deuce's avatar
deuce committed
890
891
    	return 0;

892
893
894
895
896
	if(freespace)
		blocks=fs.f_bavail;
	else
		blocks=fs.f_blocks;

deuce's avatar
deuce committed
897
	if(unit>1)
898
899
		blocks/=unit;
    return fs.f_bsize * blocks;
900

901
902
#else

rswindell's avatar
rswindell committed
903
	fprintf(stderr,"\n*** !Missing getfreediskspace implementation ***\n");
904
905
906
907
	return(0);

#endif
}
908

909
910
911
912
913
914
915
916
917
918
ulong DLLCALL getfreediskspace(const char* path, ulong unit)
{
	return getdiskspace(path, unit, /* freespace? */TRUE);
}

ulong DLLCALL getdisksize(const char* path, ulong unit)
{
	return getdiskspace(path, unit, /* freespace? */FALSE);
}

919
/****************************************************************************/
rswindell's avatar
rswindell committed
920
/* Resolves //, /./, and /../ in a path. Should work identically to Windows */
921
922
923
924
925
/****************************************************************************/
#if defined(__unix__)
char * DLLCALL _fullpath(char *target, const char *path, size_t size)  {
	char	*out;
	char	*p;
926
	BOOL	target_alloced=FALSE;
927

928
929
930
931
	if(target==NULL)  {
		if((target=malloc(MAX_PATH+1))==NULL) {
			return(NULL);
		}
932
		target_alloced=TRUE;
933
934
935
936
937
	}
	out=target;
	*out=0;

	if(*path != '/')  {
938
939
		if(*path == '~') {
			p=getenv("HOME");
940
			if(p==NULL || strlen(p)+strlen(path)>=size) {
941
942
				if(target_alloced)
					free(target);
943
				return(NULL);
944
			}
945
946
947
948
949
950
			strcpy(target,p);
			out=strrchr(target,'\0');
			path++;
		}
		else {
			p=getcwd(NULL,size);
951
			if(p==NULL || strlen(p)+strlen(path)>=size) {
952
953
				if(target_alloced)
					free(target);
954
				return(NULL);
955
			}
956
957
958
959
960
961
962
			strcpy(target,p);
			free(p);
			out=strrchr(target,'\0');
			*(out++)='/';
			*out=0;
			out--;
		}
963
964
	}
	strncat(target,path,size-1);
965

966
967
968
969
970
971
972
973
974
/*	if(stat(target,&sb))
		return(NULL);
	if(sb.st_mode&S_IFDIR)
		strcat(target,"/"); */

	for(;*out;out++)  {
		while(*out=='/')  {
			if(*(out+1)=='/')
				memmove(out,out+1,strlen(out));
975
			else if(*(out+1)=='.' && (*(out+2)=='/' || *(out+2)==0))
976
				memmove(out,out+2,strlen(out)-1);
977
			else if(*(out+1)=='.' && *(out+2)=='.' && (*(out+3)=='/' || *(out+3)==0))  {
978
979
				*out=0;
				p=strrchr(target,'/');
980
981
				if(p==NULL)
					p=target;
982
983
984
985
986
987
988
989
990
991
992
				memmove(p,out+3,strlen(out+3)+1);
				out=p;
			}
			else  {
				out++;
			}
		}
	}
	return(target);
}
#endif
993
994

/****************************************************************************/
995
/* Adds a trailing slash/backslash (path delimiter) on path strings 		*/
996
997
998
999
1000
/****************************************************************************/
char* DLLCALL backslash(char* path)
{
	char* p;

rswindell's avatar
rswindell committed
1001
	p=lastchar(path);
1002

1003
	if(*p && !IS_PATH_DELIM(*p)) {
1004
1005
#if defined(__unix__)
		/* Convert trailing backslash to forwardslash on *nix */
1006
		if(*p!='\\')
1007
#endif
1008
			p++;
1009
		*p=PATH_DELIM;
rswindell's avatar
rswindell committed
1010
		*(++p)=0;
1011
1012
1013
	}
	return(path);
}
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029

/****************************************************************************/
/* Returns true if the specified filename an aboslute pathname				*/
/****************************************************************************/
BOOL DLLCALL isabspath(const char *filename)
{
	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			*/
/****************************************************************************/
BOOL DLLCALL isfullpath(const char* filename)
{
1030
	return(filename[0]=='/'
1031
1032
1033
1034
1035
#ifdef WIN32
		|| filename[0]=='\\' || filename[1]==':'
#endif
		);
}
1036
1037
1038
1039
1040

/****************************************************************************/
/* Matches file name against filespec										*/
/* Optionally not allowing * to match PATH_DELIM (for paths)				*/
/****************************************************************************/
1041

1042
1043
1044
1045
BOOL DLLCALL wildmatch(const char *fname, const char *spec, BOOL path)
{
	char *specp;
	char *fnamep;
1046
	char *wildend;
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060

	specp=(char *)spec;
	fnamep=(char *)fname;
	for(;;specp++, fnamep++) {
		switch(*specp) {
			case '?':
				if(!(*fnamep))
					return(FALSE);
				break;
			case 0:
				if(!*fnamep)
					return(TRUE);
				break;
			case '*':
1061
1062
				while(*specp=='*')
					specp++;
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
				if(path) {
					for(wildend=fnamep; *wildend; wildend++) {
						if(IS_PATH_DELIM(*wildend)) {
							wildend--;
							break;
						}
					}
				}
				else
					wildend=strchr(fnamep, 0);
				for(;wildend >= fnamep;wildend--) {
					if(wildmatch(wildend, specp, path))
						return(TRUE);
1076
				}
1077
				return(FALSE);
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
			default:
				if(*specp != *fnamep)
					return(FALSE);
		}
		if(!(*specp && *fnamep))
			break;
	}
	while(*specp=='*')
		specp++;
	if(*specp==*fnamep)
		return(TRUE);
	return(FALSE);
}
1091

1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
/****************************************************************************/
/* Matches file name against filespec, ignoring case						*/
/****************************************************************************/
BOOL DLLCALL wildmatchi(const char *fname, const char *spec, BOOL path)
{
	char* s1;
	char* s2;
	BOOL result;

	if((s1=strdup(fname))==NULL)
		return(FALSE);
rswindell's avatar
rswindell committed
1103
	if((s2=strdup(spec))==NULL) {
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
		free(s1);
		return(FALSE);
	}
	strupr(s1);
	strupr(s2);
	result = wildmatch(s1, s2, path);
	free(s1);
	free(s2);
	return(result);
}

1115
1116
1117
/****************************************************************************/
/* Creates all the necessary directories in the specified path				*/
/****************************************************************************/
rswindell's avatar
rswindell committed
1118
int DLLCALL mkpath(const char* path)
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
{
	const char*	p=path;
	const char*	tp;
	const char*	sep=
#ifdef _WIN32
		"\\"
#endif
		"/";
	char	dir[MAX_PATH+1];
	int		result=0;

#ifdef _WIN32
	if(p[1]==':')	/* Skip drive letter, if specified */
		p+=2;
#endif

	while(*p) {
		SKIP_CHARSET(p,sep);
1137
1138
		if(*p==0)
			break;
1139
1140
		tp=p;
		FIND_CHARSET(tp,sep);
1141
1142
1143
1144
		safe_snprintf(dir,sizeof(dir),"%.*s",tp-path, path);
		if(!isdir(dir)) {
			if((result=MKDIR(dir))!=0)
				break;
1145
1146
1147
1148
1149
1150
		}
		p=tp;
	}

	return(result);
}