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