1 /*
2 Copyright (C) 2003 Tenebrae Team
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 */
20 
21 /*
22    Sys_Find* implementation for UNIX like systems
23    system conformance :
24 
25    system not having the glob system call :
26 
27        ISO/IEC 9945-2 (fnmatch)
28        BSD 4.3        (dirent syscalls)
29 
30    OTHER :
31 
32        POSIX.2        (glob)
33 
34 */
35 
36 #include "quakedef.h"
37 #include <errno.h>
38 
39 #if !defined(__GLIBC__) && !defined(__FreeBSD__)
40 
41 #include <dirent.h>
42 #include <fnmatch.h>
43 
44 #define UXDATA_GRANULARITY 10
45 
46 typedef struct {
47 	int pathlen;
48 	size_t count;
49 	size_t lsize;
50 	DIR *dir;
51 	struct dirent **list;
52 } uxdirdata_t;
53 
uxdata_free(uxdirdata_t * ud)54 int uxdata_free (uxdirdata_t *ud)
55 {
56 	int i;
57 	closedir (ud->dir);
58 	Z_Free (ud->list);
59 }
60 
61 
direntp_compare(struct dirent ** p1,struct dirent ** p2)62 int direntp_compare (struct dirent **p1, struct dirent **p2)
63 {
64 	return strncmp ((*p1)->d_name, (*p2)->d_name, sizeof ((*p1)->d_name));
65 }
66 
Sys_Findfirst(char * dirname,char * filter,dirdata_t * dirdata)67 dirdata_t *Sys_Findfirst (char *dirname, char *filter, dirdata_t *dirdata)
68 {
69 	uxdirdata_t *uxdata;
70 	DIR *dir;
71 	struct dirent *entry;
72 	struct dirent **list;
73 	int pathlen;
74 
75 	if (dirdata && filter){
76 		pathlen = strlen (dirname);
77 		if (pathlen >= MAX_OSPATH-1)
78 			return NULL;
79 		strncpy (dirdata->entry, dirname, sizeof(dirdata->entry));
80 		dir = opendir (dirdata->entry);
81 		if (dir == NULL) {
82 			return NULL;
83 		}
84 		uxdata = Z_Malloc (sizeof(uxdirdata_t));
85 		uxdata->count = 0;
86 		uxdata->pathlen = pathlen;
87 		uxdata->lsize = 10;
88 		uxdata->list = Z_Malloc (sizeof(struct dirent *) * (uxdata->lsize));
89 
90 		while (entry = readdir (dir)){
91 			int code;
92 
93 			// realloc entry list
94 			if (uxdata->lsize == uxdata->count) {
95 				list = Z_Malloc (sizeof(struct dirent *) * (uxdata->lsize += UXDATA_GRANULARITY));
96 				memcpy (list, uxdata->list, uxdata->count);
97 				Z_Free (uxdata->list);
98 				uxdata->list = list;
99 			}
100 			// check name matching filter
101 			code = fnmatch (filter, entry->d_name, 0);
102 			switch (code){
103 			case 0: /* match */
104 				uxdata->list[uxdata->count] = entry;
105 				uxdata->count++;
106 				break;
107 			case FNM_NOMATCH:
108 				break;
109 			default:
110 				Sys_Error ("Sys_Glob_select : fnmatch call (%d)\n",errno);
111 			}
112 		}
113 		if (uxdata->count) {
114 			uxdata->lsize = uxdata->count;
115 			uxdata->count = 0;
116 			// sort the entry list
117 			qsort(uxdata->list, uxdata->lsize, sizeof(struct dirent *),direntp_compare);
118 			if (dirname[pathlen-1] != '/') {
119 				dirdata->entry[pathlen]='/';
120 				uxdata->pathlen++;
121 				dirdata->entry[uxdata->pathlen]=0;
122 			}
123 			strncpy (dirdata->entry+uxdata->pathlen, uxdata->list[0]->d_name, sizeof(dirdata->entry)-uxdata->pathlen);
124 			uxdata->dir = dir;
125 			dirdata->internal = uxdata;
126 			return dirdata;
127 		}
128 		else uxdata_free (uxdata);
129 	}
130 	return NULL;
131 }
132 
Sys_Findnext(dirdata_t * dirdata)133 dirdata_t *Sys_Findnext (dirdata_t *dirdata)
134 {
135 	uxdirdata_t *uxdata;
136 	if (dirdata){
137 		uxdata=dirdata->internal;
138 		if (uxdata) {
139 			uxdata->count++;
140 			// next entry ?
141 			if (uxdata->count<uxdata->lsize){
142 				strncpy (dirdata->entry+uxdata->pathlen, uxdata->list[0]->d_name, sizeof(dirdata->entry)-uxdata->pathlen);
143 				return dirdata;
144 			}
145 			// no -> close (just in case Findclose isn't called)
146 			uxdata_free (dirdata->internal);
147 			dirdata->internal=NULL;
148 		}
149 	}
150 	return NULL;
151 }
152 
Sys_Findclose(dirdata_t * dirdata)153 void Sys_Findclose (dirdata_t *dirdata)
154 {
155 	uxdirdata_t *uxdata;
156 	if (dirdata){
157 		uxdata=dirdata->internal;
158 		if (uxdata){
159 			uxdata_free (uxdata);
160 			dirdata->internal=NULL;
161 		}
162 	}
163 }
164 
165 #else
166 
167 #include <glob.h>
168 
169 typedef struct {
170 	glob_t globbuf;
171 	size_t count;
172 } uxdirdata_t;
173 
174 
Sys_Findfirst(char * dir,char * filter,dirdata_t * dirdata)175 dirdata_t *Sys_Findfirst (char *dir, char *filter, dirdata_t *dirdata)
176 {
177 	uxdirdata_t *uxdata;
178 	if (dirdata && filter){
179 		char dirfilter[MAX_OSPATH];
180 		uxdata=Z_Malloc (sizeof(uxdirdata_t));
181 		sprintf (dirfilter,"%s/%s", dir, filter);
182 		glob (dirfilter,0,NULL,&uxdata->globbuf);
183 		if (uxdata->globbuf.gl_pathc){
184 			dirdata->internal=uxdata;
185 			strncpy (dirdata->entry,uxdata->globbuf.gl_pathv[0],sizeof(dirdata->entry));
186 			uxdata->count=0;
187 			return dirdata;
188 		}
189 	}
190 	return NULL;
191 }
192 
Sys_Findnext(dirdata_t * dirdata)193 dirdata_t *Sys_Findnext (dirdata_t *dirdata)
194 {
195 	uxdirdata_t *uxdata;
196 	if (dirdata){
197 		uxdata=dirdata->internal;
198 		if (uxdata) {
199 			uxdata->count++;
200 			// next entry ?
201 			if (uxdata->count<uxdata->globbuf.gl_pathc){
202 				strncpy (dirdata->entry,uxdata->globbuf.gl_pathv[uxdata->count],sizeof(dirdata->entry));
203 				return dirdata;
204 			}
205 			// no -> close (just in case Findclose isn't called)
206 			globfree (&uxdata->globbuf);
207 			Z_Free (dirdata->internal);
208 			dirdata->internal=NULL;
209 		}
210 	}
211 	return NULL;
212 }
213 
Sys_Findclose(dirdata_t * dirdata)214 void Sys_Findclose (dirdata_t *dirdata)
215 {
216 	uxdirdata_t *uxdata;
217 	if (dirdata){
218 		uxdata=dirdata->internal;
219 		if (uxdata){
220 			globfree (&uxdata->globbuf);
221 			Z_Free (uxdata);
222 			dirdata->internal=NULL;
223 		}
224 	}
225 }
226 
227 #endif
228 
229