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