1 /*
2  * util.c - utility functions
3  *
4  * Copyright (c) 2005 Piotr Fusik
5  * Copyright (c) 2005-2013 Atari800 development team (see DOC/CREDITS)
6  *
7  * This file is part of the Atari800 emulator project which emulates
8  * the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
9  *
10  * Atari800 is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * Atari800 is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Atari800; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
23 */
24 
25 #include "config.h"
26 /* suppress -ansi -pedantic warning for fdopen: */
27 #ifdef __STRICT_ANSI__
28 #undef __STRICT_ANSI__
29 #include <stdio.h>
30 #define __STRICT_ANSI__ 1
31 #else
32 #include <stdio.h>
33 #endif /* __STRICT_ANSI__ */
34 #include <stdlib.h>
35 #include <string.h>
36 #include <errno.h>
37 #ifdef HAVE_SYS_STAT_H
38 #include <sys/stat.h>
39 #endif
40 #ifdef HAVE_WINDOWS_H
41 #include <windows.h>
42 #endif
43 #ifdef TIME_WITH_SYS_TIME
44 # include <sys/time.h>
45 # include <time.h>
46 #else
47 # ifdef HAVE_SYS_TIME_H
48 #  include <sys/time.h>
49 # elif defined(HAVE_TIME_H)
50 #  include <time.h>
51 # endif
52 #endif
53 
54 #include "atari.h"
55 #include "platform.h"
56 #include "util.h"
57 
Util_chrieq(char c1,char c2)58 int Util_chrieq(char c1, char c2)
59 {
60 	switch (c1 ^ c2) {
61 	case 0x00:
62 		return TRUE;
63 	case 0x20:
64 		return (c1 >= 'A' && c1 <= 'Z') || (c1 >= 'a' && c1 <= 'z');
65 	default:
66 		return FALSE;
67 	}
68 }
69 
70 #ifdef __STRICT_ANSI__
Util_stricmp(const char * str1,const char * str2)71 int Util_stricmp(const char *str1, const char *str2)
72 {
73 	int retval;
74 
75 	while((retval = tolower(*str1) - tolower(*str2++)) == 0)
76 	{
77 		if (*str1++ == '\0')
78 			break;
79 	}
80 	return retval;
81 }
82 #endif
83 
Util_stpcpy(char * dest,const char * src)84 char *Util_stpcpy(char *dest, const char *src)
85 {
86 	size_t len = strlen(src);
87 	memcpy(dest, src, len + 1);
88 	return dest + len;
89 }
90 
91 #ifndef HAVE_STRNCPY
Util_strncpy(char * dest,const char * src,size_t size)92 char *Util_strncpy(char *dest, const char *src, size_t size) {
93 	while (size-- > 0) {
94 		if ((*dest++ = *src++) == '\0')
95 			break;
96 	}
97 	while (size-- > 0)
98 		*dest++ = '\0';
99 	return dest;
100 }
101 #endif
102 
safe_strncpy(char * buffer,const char * source,int bufsize)103 char *safe_strncpy(char *buffer, const char *source, int bufsize)
104 {
105 	if (buffer == NULL) return NULL;
106 	if (bufsize > 0) {
107 		strncpy(buffer, source != NULL ? source : "", bufsize);
108 		buffer[bufsize-1] = '\0';
109 	}
110 	return buffer;
111 }
112 
Util_strlcpy(char * dest,const char * src,size_t size)113 char *Util_strlcpy(char *dest, const char *src, size_t size)
114 {
115 	Util_strncpy(dest, src, size);
116 	dest[size - 1] = '\0';
117 	return dest;
118 }
119 
Util_strupper(char * s)120 char *Util_strupper(char *s)
121 {
122 	char *p;
123 	for (p = s; *p != '\0'; p++)
124 		if (*p >= 'a' && *p <= 'z')
125 			*p += 'A' - 'a';
126 	return s;
127 }
128 
Util_strlower(char * s)129 char *Util_strlower(char *s)
130 {
131 	char *p;
132 	for (p = s; *p != '\0'; p++)
133 		if (*p >= 'A' && *p <= 'Z')
134 			*p += 'a' - 'A';
135 	return s;
136 }
137 
Util_chomp(char * s)138 void Util_chomp(char *s)
139 {
140 	int len;
141 
142 	len = strlen(s);
143 	if (len >= 2 && s[len - 1] == '\n' && s[len - 2] == '\r')
144 		s[len - 2] = '\0';
145 	else if (len >= 1 && (s[len - 1] == '\n' || s[len - 1] == '\r'))
146 		s[len - 1] = '\0';
147 }
148 
Util_trim(char * s)149 void Util_trim(char *s)
150 {
151 	char *p = s;
152 	char *q;
153 	/* skip leading whitespace */
154 	while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
155 		p++;
156 	/* now p points at the first non-whitespace character */
157 
158 	if (*p == '\0') {
159 		/* only whitespace */
160 		*s = '\0';
161 		return;
162 	}
163 
164 	q = s + strlen(s);
165 	/* skip trailing whitespace */
166 	/* we have found p < q such that *p is non-whitespace,
167 	   so this loop terminates with q >= p */
168 	do
169 		q--;
170 	while (*q == ' ' || *q == '\t' || *q == '\r' || *q == '\n');
171 
172 	/* now q points at the last non-whitespace character */
173 	/* cut off trailing whitespace */
174 	*++q = '\0';
175 
176 	/* move to string */
177 	memmove(s, p, q + 1 - p);
178 }
179 
Util_sscandec(const char * s)180 int Util_sscandec(const char *s)
181 {
182 	int result;
183 	if (*s == '\0')
184 		return -1;
185 	result = 0;
186 	for (;;) {
187 		if (*s >= '0' && *s <= '9')
188 			result = 10 * result + *s - '0';
189 		else if (*s == '\0')
190 			return result;
191 		else
192 			return -1;
193 		s++;
194 	}
195 }
196 
Util_sscansdec(char const * s,int * dest)197 int Util_sscansdec(char const *s, int *dest)
198 {
199 	int minus = FALSE;
200 	switch(*s) {
201 	case '-':
202 		minus = TRUE;
203 		/* Fallthrough! */
204 	case '+':
205 		++s;
206 	}
207 	*dest = Util_sscandec(s);
208 	if (*dest == -1)
209 		return FALSE;
210 	if (minus)
211 		*dest = -*dest;
212 	return TRUE;
213 }
214 
Util_sscandouble(char const * s,double * dest)215 int Util_sscandouble(char const *s, double *dest)
216 {
217 	char *endptr;
218 	double result;
219 
220 	result = strtod(s, &endptr);
221 	if (endptr[0] != '\0' || errno == ERANGE)
222 		return FALSE;
223 	*dest = result;
224 	return TRUE;
225 
226 }
227 
Util_sscanhex(const char * s)228 int Util_sscanhex(const char *s)
229 {
230 	int result;
231 	if (*s == '\0')
232 		return -1;
233 	result = 0;
234 	for (;;) {
235 		if (*s >= '0' && *s <= '9')
236 			result = 16 * result + *s - '0';
237 		else if (*s >= 'A' && *s <= 'F')
238 			result = 16 * result + *s - 'A' + 10;
239 		else if (*s >= 'a' && *s <= 'f')
240 			result = 16 * result + *s - 'a' + 10;
241 		else if (*s == '\0')
242 			return result;
243 		else
244 			return -1;
245 		s++;
246 	}
247 }
248 
Util_sscanbool(const char * s)249 int Util_sscanbool(const char *s)
250 {
251 	if (*s == '0' && s[1] == '\0')
252 		return 0;
253 	if (*s == '1' && s[1] == '\0')
254 		return 1;
255 	return -1;
256 }
257 
258 #if !HAVE_ROUND
Util_round(double x)259 double Util_round(double x)
260 {
261 	return floor(x + 0.5);
262 }
263 #endif
264 
Util_malloc(size_t size)265 void *Util_malloc(size_t size)
266 {
267 	void *ptr = malloc(size);
268 	if (ptr == NULL) {
269 		Atari800_ErrExit();
270 		printf("Fatal error: out of memory\n");
271 		exit(1);
272 	}
273 	return ptr;
274 }
275 
Util_realloc(void * ptr,size_t size)276 void *Util_realloc(void *ptr, size_t size)
277 {
278 	ptr = realloc(ptr, size);
279 	if (ptr == NULL) {
280 		Atari800_ErrExit();
281 		printf("Fatal error: out of memory\n");
282 		exit(1);
283 	}
284 	return ptr;
285 }
286 
Util_strdup(const char * s)287 char *Util_strdup(const char *s)
288 {
289 	/* don't use strdup(): it is unavailable on WinCE */
290 	size_t size = strlen(s) + 1;
291 	char *ptr = (char *) Util_malloc(size);
292 	memcpy(ptr, s, size); /* faster than strcpy(ptr, s) */
293 	return ptr;
294 }
295 
Util_splitpath(const char * path,char * dir_part,char * file_part)296 void Util_splitpath(const char *path, char *dir_part, char *file_part)
297 {
298 	const char *p;
299 	/* find the last Util_DIR_SEP_CHAR except the last character */
300 	for (p = path + strlen(path) - 2; p >= path; p--) {
301 		if (*p == Util_DIR_SEP_CHAR
302 #ifdef DIR_SEP_BACKSLASH
303 /* on DOSish systems slash can be also used as a directory separator */
304 		 || *p == '/'
305 #endif
306 		   ) {
307 			if (dir_part != NULL) {
308 				int len = p - path;
309 				if (p == path || (p == path + 2 && path[1] == ':'))
310 					/* root dir: include Util_DIR_SEP_CHAR in dir_part */
311 					len++;
312 				memcpy(dir_part, path, len);
313 				dir_part[len] = '\0';
314 			}
315 			if (file_part != NULL)
316 				strcpy(file_part, p + 1);
317 			return;
318 		}
319 	}
320 	/* no Util_DIR_SEP_CHAR: current dir */
321 	if (dir_part != NULL)
322 		dir_part[0] = '\0';
323 	if (file_part != NULL)
324 		strcpy(file_part, path);
325 }
326 
Util_catpath(char * result,const char * path1,const char * path2)327 void Util_catpath(char *result, const char *path1, const char *path2)
328 {
329 	snprintf(result, FILENAME_MAX,
330 		path1[0] == '\0' || path2[0] == Util_DIR_SEP_CHAR || path1[strlen(path1) - 1] == Util_DIR_SEP_CHAR
331 #ifdef DIR_SEP_BACKSLASH
332 		 || path2[0] == '/' || path1[strlen(path1) - 1] == '/'
333 #endif
334 			? "%s%s" : "%s" Util_DIR_SEP_STR "%s", path1, path2);
335 }
336 
Util_fileexists(const char * filename)337 int Util_fileexists(const char *filename)
338 {
339 	FILE *fp;
340 	fp = fopen(filename, "rb");
341 	if (fp == NULL)
342 		return FALSE;
343 	fclose(fp);
344 	return TRUE;
345 }
346 
347 #ifdef HAVE_WINDOWS_H
348 
Util_direxists(const char * filename)349 int Util_direxists(const char *filename)
350 {
351 	DWORD attr;
352 #ifdef UNICODE
353 	WCHAR wfilename[FILENAME_MAX];
354 	if (MultiByteToWideChar(CP_ACP, 0, filename, -1, wfilename, FILENAME_MAX) <= 0)
355 		return FALSE;
356 	attr = GetFileAttributes(wfilename);
357 #else
358 	attr = GetFileAttributes(filename);
359 #endif /* UNICODE */
360 	if (attr == 0xffffffff)
361 		return FALSE;
362 #ifdef _WIN32_WCE
363 	/* WinCE: Make sure user does not up-dir from the root */
364 	if (*filename == 0)
365 		return FALSE;
366 #endif
367 	return (attr & FILE_ATTRIBUTE_DIRECTORY) ? TRUE : FALSE;
368 }
369 
370 #elif defined(HAVE_STAT)
371 
Util_direxists(const char * filename)372 int Util_direxists(const char *filename)
373 {
374 	struct stat filestatus;
375 	return stat(filename, &filestatus) == 0 && (filestatus.st_mode & S_IFDIR);
376 }
377 
378 #else
379 
Util_direxists(const char * filename)380 int Util_direxists(const char *filename)
381 {
382 	return TRUE;
383 }
384 
385 #endif /* defined(HAVE_STAT) */
386 
387 
Util_flen(FILE * fp)388 int Util_flen(FILE *fp)
389 {
390 	fseek(fp, 0, SEEK_END);
391 	return (int) ftell(fp);
392 }
393 
394 /* Creates a file that does not exist and fills in filename with its name.
395    filename must point to FILENAME_MAX characters buffer which doesn't need
396    to be initialized. */
Util_uniqopen(char * filename,const char * mode)397 FILE *Util_uniqopen(char *filename, const char *mode)
398 {
399 	/* We cannot simply call tmpfile(), because we don't want the file
400 	   to be deleted when we close it, and we need the filename. */
401 
402 #if defined(HAVE_MKSTEMP) && defined(HAVE_FDOPEN)
403 	/* this is the only implementation without a race condition */
404 	strcpy(filename, "a8XXXXXX");
405 	/* mkstemp() modifies the 'X'es and returns an open descriptor */
406 	return fdopen(mkstemp(filename), mode);
407 #elif defined(HAVE_TMPNAM)
408 	/* tmpnam() is better than mktemp(), because it creates filenames
409 	   in system's temporary directory. It is also more portable. */
410 	return fopen(tmpnam(filename), mode);
411 #elif defined(HAVE_MKTEMP)
412 	strcpy(filename, "a8XXXXXX");
413 	/* mktemp() modifies the 'X'es and returns filename */
414 	return fopen(mktemp(filename), mode);
415 #else
416 	/* Roll-your-own */
417 	int no;
418 	for (no = 0; no < 1000000; no++) {
419 		snprintf(filename, FILENAME_MAX, "a8%06d", no);
420 		if (!Util_fileexists(filename))
421 			return fopen(filename, mode);
422 	}
423 	return NULL;
424 #endif
425 }
426 
427 #if defined(HAVE_WINDOWS_H) && defined(UNICODE)
Util_unlink(const char * filename)428 int Util_unlink(const char *filename)
429 {
430 	WCHAR wfilename[FILENAME_MAX];
431 #ifdef _WIN32_WCE
432 	char cwd[FILENAME_MAX];
433 	char fullfilename[FILENAME_MAX];
434 	if (filename[0] != '\\' && filename[0] != '/') {
435 		getcwd(cwd, FILENAME_MAX);
436 		Util_catpath(fullfilename, cwd, filename);
437 		if (MultiByteToWideChar(CP_ACP, 0, fullfilename, -1, wfilename, FILENAME_MAX) <= 0)
438 			return -1;
439 	}
440 	else
441 #endif
442 	if (MultiByteToWideChar(CP_ACP, 0, filename, -1, wfilename, FILENAME_MAX) <= 0)
443 		return -1;
444 	return (DeleteFile(wfilename) != 0) ? 0 : -1;
445 }
446 #elif defined(HAVE_WINDOWS_H) && !defined(UNICODE)
Util_unlink(const char * filename)447 int Util_unlink(const char *filename)
448 {
449 	return (DeleteFile(filename) != 0) ? 0 : -1;
450 }
451 #endif /* defined(HAVE_WINDOWS_H) && defined(UNICODE) */
452 
Util_time(void)453 double Util_time(void)
454 {
455 #ifdef SUPPORTS_PLATFORM_TIME
456 	return PLATFORM_Time();
457 #elif defined(HAVE_WINDOWS_H)
458 	return GetTickCount() * 1e-3;
459 #elif defined(DJGPP)
460 	/* DJGPP has gettimeofday, but it's not more accurate than uclock */
461 	return uclock() * (1.0 / UCLOCKS_PER_SEC);
462 #elif defined(HAVE_GETTIMEOFDAY)
463 	struct timeval tp;
464 	gettimeofday(&tp, NULL);
465 	return tp.tv_sec + 1e-6 * tp.tv_usec;
466 #elif defined(HAVE_UCLOCK)
467 	return uclock() * (1.0 / UCLOCKS_PER_SEC);
468 #elif defined(HAVE_CLOCK)
469 	return clock() * (1.0 / CLK_TCK);
470 #else
471 #error No function found for Util_time()
472 #endif
473 }
474 
475 /* FIXME: Ports should use SUPPORTS_PLATFORM_SLEEP and SUPPORTS_PLATFORM_TIME */
476 /* and not this mess */
477 
Util_sleep(double s)478 void Util_sleep(double s)
479 {
480 #ifdef SUPPORTS_PLATFORM_SLEEP
481 	PLATFORM_Sleep(s);
482 #else /* !SUPPORTS_PLATFORM_SLEEP */
483 	if (s > 0) {
484 #ifdef HAVE_WINDOWS_H
485 		Sleep((DWORD) (s * 1e3));
486 #elif defined(DJGPP)
487 		/* DJGPP has usleep and select, but they don't work that good */
488 		/* XXX: find out why */
489 		double curtime = Util_time();
490 		while ((curtime + s) > Util_time());
491 #elif defined(HAVE_NANOSLEEP)
492 		struct timespec ts;
493 		ts.tv_sec = 0;
494 		ts.tv_nsec = s * 1e9;
495 		nanosleep(&ts, NULL);
496 #elif defined(HAVE_USLEEP)
497 		usleep(s * 1e6);
498 #elif defined(__BEOS__)
499 		/* added by Walter Las for BeOS */
500 		snooze(s * 1e6);
501 #elif defined(__EMX__)
502 		/* added by Brian Smith for os/2 */
503 		DosSleep(s);
504 #elif defined(HAVE_SELECT)
505 		/* linux */
506 		struct timeval tp;
507 		tp.tv_sec = 0;
508 		tp.tv_usec = s * 1e6;
509 		select(1, NULL, NULL, NULL, &tp);
510 #else
511 		double curtime = Util_time();
512 		while ((curtime + s) > Util_time());
513 #endif
514 	}
515 #endif /* !SUPPORTS_PLATFORM_SLEEP */
516 }
517