1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 Copyright (C) 2010 COR Entertainment, LLC.
4 
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 
14 See the GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #if defined HAVE_MREMAP
26 #define _GNU_SOURCE
27 #endif
28 
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <errno.h>
32 #include <stdio.h>
33 #include <dirent.h>
34 #include <unistd.h>
35 #include <sys/mman.h>
36 
37 #if defined HAVE_TIME_H
38 # include <time.h>
39 #elif defined HAVE_SYS_TIME_H
40 # include <sys/time.h>
41 #endif
42 
43 #include "unix/glob.h"
44 #include "qcommon/qcommon.h"
45 
46 
47 /*
48  * State of currently open Hunk.
49  * Valid between Hunk_Begin() and Hunk_End()
50  */
51 byte *hunk_base = NULL;
52 size_t *phunk_size_store = NULL;
53 static const size_t hunk_header_size = 32;
54 byte *user_hunk_base = NULL ;
55 size_t rsvd_hunk_size;
56 size_t user_hunk_size;
57 size_t total_hunk_used_size;
58 
Hunk_Begin(int maxsize)59 void *Hunk_Begin (int maxsize)
60 {
61 
62 	Com_DPrintf("Hunk_Begin:0x%X:\n", maxsize );
63 	if ( hunk_base != NULL )
64 		Com_DPrintf("Warning: Hunk_Begin: hunk_base != NULL\n");
65 
66 	// reserve virtual memory space
67 	rsvd_hunk_size = (size_t)maxsize + hunk_header_size;
68 	hunk_base = mmap(0, rsvd_hunk_size, PROT_READ|PROT_WRITE,
69 		MAP_PRIVATE|MAP_ANON, -1, 0);
70 	if ( hunk_base == NULL || hunk_base == (byte *)-1)
71 		Sys_Error("unable to virtual allocate %d bytes", maxsize);
72 
73 	total_hunk_used_size = hunk_header_size;
74 	user_hunk_size = 0;
75 	user_hunk_base = hunk_base + hunk_header_size;
76 
77 	// store the size reserved for Hunk_Free()
78 	phunk_size_store = (size_t *)hunk_base;
79 	*phunk_size_store = rsvd_hunk_size; // the initial reserved size
80 
81 	return user_hunk_base;
82 }
83 
Hunk_Alloc(int size)84 void *Hunk_Alloc (int size)
85 {
86 	byte *user_hunk_bfr;
87 	size_t user_hunk_block_size;
88 	size_t new_user_hunk_size;
89 
90 	if ( hunk_base == NULL ) {
91 		Sys_Error("Program Error: Hunk_Alloc: hunk_base==NULL");
92 	}
93 
94 	// size is sometimes odd, so increment size to even boundary to avoid
95 	//  odd-aligned accesses.
96 	user_hunk_block_size = ((size_t)size + 1) & ~0x01;
97 	new_user_hunk_size = user_hunk_size + user_hunk_block_size;
98 	total_hunk_used_size += user_hunk_block_size;
99 
100 	if ( total_hunk_used_size > rsvd_hunk_size )
101 		Sys_Error("Program Error: Hunk_Alloc: overflow");
102 
103 	user_hunk_bfr = user_hunk_base + user_hunk_size; // set base of new block
104 	user_hunk_size = new_user_hunk_size; // then update user hunk size
105 
106 	// This will dump each allocate call. Too much info for regular use.
107 	//Com_DPrintf("Hunk_Alloc:%u @ %p:\n", user_hunk_block_size, user_hunk_bfr );
108 
109 	return (void *)user_hunk_bfr;
110 }
111 
112 #if defined HAVE_MREMAP
113 // easy version, mremap() should be available on all Linux
Hunk_End(void)114 int Hunk_End (void)
115 {
116 	byte *remap_base;
117 	size_t new_rsvd_hunk_size;
118 
119 	new_rsvd_hunk_size = total_hunk_used_size;
120 	remap_base = mremap( hunk_base, rsvd_hunk_size, new_rsvd_hunk_size, 0 );
121 	if ( remap_base != hunk_base ) {
122 		Sys_Error("Hunk_End:  Could not remap virtual block (%d)", errno);
123 	}
124 	// "close" this hunk, setting reserved size for Hunk_Free
125 	rsvd_hunk_size = new_rsvd_hunk_size;
126 	*phunk_size_store = rsvd_hunk_size;
127 
128 	Com_DPrintf("Hunk_End.1:0x%X @ %p:\n", rsvd_hunk_size, remap_base );
129 
130 	hunk_base = user_hunk_base = NULL;
131 	phunk_size_store = NULL;
132 
133 	return user_hunk_size; // user buffer is user_hunk_size @ user_hunk_base
134 }
135 
136 #else
137 // portable version (we hope)
Hunk_End()138 int Hunk_End()
139 {
140 	size_t sys_pagesize;
141 	size_t rsvd_pages;
142 	size_t rsvd_size;
143 	size_t used_pages;
144 	size_t used_size;
145 	size_t unmap_size;
146 	void * unmap_base;
147 
148 	// the portable way to get pagesize, according to documentation
149 	sys_pagesize = (size_t)sysconf( _SC_PAGESIZE );
150 
151 	// calculate page-aligned size that was reserved
152 	rsvd_pages = (rsvd_hunk_size / sys_pagesize);
153 	if ( (rsvd_hunk_size % sys_pagesize) != 0 )
154 		rsvd_pages += 1;
155 	rsvd_size = rsvd_pages * sys_pagesize;
156 
157 	// calculate page-aligned size that was used
158 	used_pages = total_hunk_used_size / sys_pagesize;
159 	if ( (total_hunk_used_size % sys_pagesize) != 0 )
160 		used_pages += 1;
161 	used_size = used_pages * sys_pagesize;
162 
163 	// unmap the unused space
164 	if ( used_size < rsvd_size ) {
165 		unmap_size = rsvd_size - used_size;
166 		unmap_base = (void *)(hunk_base + used_size);
167 		if ( ( munmap( unmap_base, unmap_size )) != 0 ) {
168 			Com_DPrintf("Hunk_End: munmap failed [0x%X @ %p]\n",
169 					unmap_size, unmap_base );
170 			// throwing a Sys_Error is probably too drastic
171 			// Sys_Error("Program Error: Hunk_End: munmap failed");
172 		}
173 		else
174 		{
175 			// update size reserved for Hunk_Free
176 			rsvd_hunk_size = used_size;
177 			*phunk_size_store = rsvd_hunk_size;
178 		}
179 
180 	}
181 
182 	Com_DPrintf( "Hunk_End.2:0x%X @ %p:\n", rsvd_hunk_size, hunk_base );
183 
184 	hunk_base = user_hunk_base = NULL;
185 	phunk_size_store = NULL;
186 
187 	return user_hunk_size;
188 }
189 #endif
190 
191 
Hunk_Free(void * base)192 void Hunk_Free (void *base)
193 {
194 	byte *hunk_base;
195 	size_t hunk_rsvd_size;
196 
197 	if ( base != NULL )
198 	{
199 		// find hunk base and retreive the hunk reserved size
200 		hunk_base = base - hunk_header_size;
201 		hunk_rsvd_size = *((size_t *)hunk_base);
202 
203 		Com_DPrintf("Hunk_Free:0x%X @ %p:\n", hunk_rsvd_size, hunk_base );
204 
205 		if ( munmap( hunk_base, hunk_rsvd_size ) )
206 			Sys_Error("Hunk_Free: munmap failed (%d)", errno);
207 	}
208 }
209 
210 //===============================================================================
211 
212 
213 /*
214 ================
215 Sys_Milliseconds
216 ================
217 */
218 
219 int curtime; // curtime set at beginning of the main loop
220 
221 #if defined HAVE_CLOCK_GETTIME
222 // version with more modern system time function
Sys_Milliseconds(void)223 int Sys_Milliseconds( void )
224 {
225 	static qboolean first_time = true;
226 	static time_t start_secs = 0;
227 	int errorflag;
228 	struct timespec tp;
229 	long timeofday;
230 
231 	errorflag = clock_gettime( CLOCK_REALTIME, &tp );
232 	if ( errorflag )
233 	{
234 		Com_Printf("Sys_Milliseconds: clock_gettime() error\n");
235 		timeofday = 0L;
236 		// fail
237 	}
238 	else if ( first_time )
239 	{
240 		start_secs = tp.tv_sec;
241 		timeofday = tp.tv_nsec / 1000000L;
242 		first_time = false;
243 	}
244 	else
245 	{
246 		timeofday = ( tp.tv_sec - start_secs ) * 1000L;
247 		timeofday += tp.tv_nsec / 1000000L;
248 	}
249 
250 	return (int)timeofday;
251 }
252 
253 #else
254 // version with old time function
Sys_Milliseconds(void)255 int Sys_Milliseconds (void)
256 {
257 	struct timeval tp;
258 	static long secbase;
259 	long timeofday;
260 
261 	// per documentation TZ arg is obsolete, never used, should be NULL.
262 	// POSIX.1-2008 recommends clock_gettime()
263 	gettimeofday( &tp, NULL );
264 
265 	if (!secbase)
266 	{
267 		secbase = tp.tv_sec;
268 		return tp.tv_usec/1000;
269 	}
270 
271 	timeofday = (tp.tv_sec - secbase)*1000 + tp.tv_usec/1000;
272 
273 	return (int)timeofday;
274 }
275 #endif
276 
Sys_Mkdir(char * path)277 void Sys_Mkdir (char *path)
278 {
279 	int result;
280 
281 	result = mkdir( path, 0700 );
282 	// drwx------ appears to be preferred unix/linux practice for home dirs
283 	if ( !result )
284 	{ // success
285 		Com_Printf("Created directory %s\n", path );
286 	}
287 	else
288 	{
289 		if ( errno != EEXIST )
290 		{
291 			Com_DPrintf("Creating directory %s failed\n", path );
292 		}
293 	}
294 }
295 
296 //============================================
297 
298 static	char	findbase[MAX_OSPATH];
299 static	char	findpath[MAX_OSPATH];
300 static	char	findpattern[MAX_OSPATH];
301 static	DIR		*fdir;
302 
CompareAttributes(char * path,char * name,unsigned musthave,unsigned canthave)303 static qboolean CompareAttributes(char *path, char *name,
304 	unsigned musthave, unsigned canthave )
305 {
306 	struct stat st;
307 	char fn[MAX_OSPATH];
308 
309 // . and .. never match
310 	if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
311 		return false;
312 
313 	return true;
314 
315 	if (stat(fn, &st) == -1)
316 		return false; // shouldn't happen
317 
318 	if ( ( st.st_mode & S_IFDIR ) && ( canthave & SFF_SUBDIR ) )
319 		return false;
320 
321 	if ( ( musthave & SFF_SUBDIR ) && !( st.st_mode & S_IFDIR ) )
322 		return false;
323 
324 	return true;
325 }
326 
Sys_FindFirst(char * path,unsigned musthave,unsigned canhave)327 char *Sys_FindFirst (char *path, unsigned musthave, unsigned canhave)
328 {
329 	struct dirent *d;
330 	char *p;
331 
332 	if (fdir)
333 		Sys_Error ("Sys_BeginFind without close");
334 
335 //	COM_FilePath (path, findbase);
336 	strcpy(findbase, path);
337 
338 	if ((p = strrchr(findbase, '/')) != NULL) {
339 		*p = 0;
340 		strcpy(findpattern, p + 1);
341 	} else
342 		strcpy(findpattern, "*");
343 
344 	if (strcmp(findpattern, "*.*") == 0)
345 		strcpy(findpattern, "*");
346 
347 	if ((fdir = opendir(findbase)) == NULL)
348 		return NULL;
349 	while ((d = readdir(fdir)) != NULL) {
350 		if (!*findpattern || glob_match(findpattern, d->d_name)) {
351 //			if (*findpattern)
352 //				printf("%s matched %s\n", findpattern, d->d_name);
353 			if (CompareAttributes(findbase, d->d_name, musthave, canhave)) {
354 				sprintf (findpath, "%s/%s", findbase, d->d_name);
355 				return findpath;
356 			}
357 		}
358 	}
359 	return NULL;
360 }
361 
Sys_FindNext(unsigned musthave,unsigned canhave)362 char *Sys_FindNext (unsigned musthave, unsigned canhave)
363 {
364 	struct dirent *d;
365 
366 	if (fdir == NULL)
367 		return NULL;
368 	while ((d = readdir(fdir)) != NULL) {
369 		if (!*findpattern || glob_match(findpattern, d->d_name)) {
370 //			if (*findpattern)
371 //				printf("%s matched %s\n", findpattern, d->d_name);
372 			if (CompareAttributes(findbase, d->d_name, musthave, canhave)) {
373 				sprintf (findpath, "%s/%s", findbase, d->d_name);
374 				return findpath;
375 			}
376 		}
377 	}
378 	return NULL;
379 }
380 
Sys_FindClose(void)381 void Sys_FindClose (void)
382 {
383 	if (fdir != NULL)
384 		closedir(fdir);
385 	fdir = NULL;
386 }
387 
388 
389 //============================================
390 
391 
392