Make sim build compilable under Cygwin. Fixes FS#11832.
[maemo-rb.git] / uisimulator / common / io.c
blobf77180dc21b483a1a624341575df2a0fa90ad294
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Daniel Stenberg
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdarg.h>
26 #include <sys/stat.h>
27 #include <time.h>
28 #include "config.h"
30 #define HAVE_STATVFS (!defined(WIN32))
31 #define HAVE_LSTAT (!defined(WIN32))
33 #if HAVE_STATVFS
34 #include <sys/statvfs.h>
35 #endif
37 #ifdef WIN32
38 #include <windows.h>
39 #endif
41 #ifndef _MSC_VER
42 #include <dirent.h>
43 #include <unistd.h>
44 #else
45 #include "dir-win32.h"
46 #endif
48 #include <fcntl.h>
49 #include <SDL.h>
50 #include <SDL_thread.h>
51 #include "thread-sdl.h"
52 #include "thread.h"
53 #include "kernel.h"
54 #include "debug.h"
55 #include "ata.h" /* for IF_MV2 et al. */
56 #include "rbpaths.h"
57 #include "load_code.h"
59 /* keep this in sync with file.h! */
60 #undef MAX_PATH /* this avoids problems when building simulator */
61 #define MAX_PATH 260
62 #define MAX_OPEN_FILES 11
64 /* Windows (and potentially other OSes) distinguish binary and text files.
65 * Define a dummy for the others. */
66 #ifndef O_BINARY
67 #define O_BINARY 0
68 #endif
70 /* Unicode compatibility for win32 */
71 #if defined __MINGW32__
72 /* Rockbox unicode functions */
73 extern const unsigned char* utf8decode(const unsigned char *utf8,
74 unsigned short *ucs);
75 extern unsigned char* utf8encode(unsigned long ucs, unsigned char *utf8);
77 /* Static buffers for the conversion results. This isn't thread safe,
78 * but it's sufficient for rockbox. */
79 static unsigned char convbuf1[3*MAX_PATH];
80 static unsigned char convbuf2[3*MAX_PATH];
82 static wchar_t* utf8_to_ucs2(const unsigned char *utf8, void *buffer)
84 wchar_t *ucs = buffer;
86 while (*utf8)
87 utf8 = utf8decode(utf8, ucs++);
89 *ucs = 0;
90 return buffer;
92 static unsigned char *ucs2_to_utf8(const wchar_t *ucs, unsigned char *buffer)
94 unsigned char *utf8 = buffer;
96 while (*ucs)
97 utf8 = utf8encode(*ucs++, utf8);
99 *utf8 = 0;
100 return buffer;
103 #define UTF8_TO_OS(a) utf8_to_ucs2(a,convbuf1)
104 #define OS_TO_UTF8(a) ucs2_to_utf8(a,convbuf1)
105 #define DIR_T _WDIR
106 #define DIRENT_T struct _wdirent
107 #define STAT_T struct _stat
108 extern int _wmkdir(const wchar_t*);
109 extern int _wrmdir(const wchar_t*);
110 #define MKDIR(a,b) (_wmkdir)(UTF8_TO_OS(a))
111 #define RMDIR(a) (_wrmdir)(UTF8_TO_OS(a))
112 #define OPENDIR(a) (_wopendir)(UTF8_TO_OS(a))
113 #define READDIR(a) (_wreaddir)(a)
114 #define CLOSEDIR(a) (_wclosedir)(a)
115 #define STAT(a,b) (_wstat)(UTF8_TO_OS(a),b)
116 /* empty variable parameter list doesn't work for variadic macros,
117 * so pretend the second parameter is variable too */
118 #define OPEN(a,...) (_wopen)(UTF8_TO_OS(a), __VA_ARGS__)
119 #define CLOSE(a) (close)(a)
120 #define REMOVE(a) (_wremove)(UTF8_TO_OS(a))
121 #define RENAME(a,b) (_wrename)(UTF8_TO_OS(a),utf8_to_ucs2(b,convbuf2))
123 #else /* !__MINGW32__ */
125 #define UTF8_TO_OS(a) (a)
126 #define OS_TO_UTF8(a) (a)
127 #define DIR_T DIR
128 #define DIRENT_T struct dirent
129 #define STAT_T struct stat
130 #define MKDIR(a,b) (mkdir)(a,b)
131 #define RMDIR(a) (rmdir)(a)
132 #define OPENDIR(a) (opendir)(a)
133 #define READDIR(a) (readdir)(a)
134 #define CLOSEDIR(a) (closedir)(a)
135 #define STAT(a,b) (stat)(a,b)
136 /* empty variable parameter list doesn't work for variadic macros,
137 * so pretend the second parameter is variable too */
138 #define OPEN(a, ...) (open)(a, __VA_ARGS__)
139 #define CLOSE(x) (close)(x)
140 #define REMOVE(a) (remove)(a)
141 #define RENAME(a,b) (rename)(a,b)
143 #endif /* !__MINGW32__ */
146 #ifdef HAVE_DIRCACHE
147 void dircache_remove(const char *name);
148 void dircache_rename(const char *oldname, const char *newname);
149 #endif
152 #define SIMULATOR_DEFAULT_ROOT "simdisk"
153 extern const char *sim_root_dir;
155 static int num_openfiles = 0;
157 /* from dir.h */
158 struct dirinfo {
159 int attribute;
160 long size;
161 unsigned short wrtdate;
162 unsigned short wrttime;
165 struct sim_dirent {
166 unsigned char d_name[MAX_PATH];
167 struct dirinfo info;
168 long startcluster;
171 struct dirstruct {
172 void *dir; /* actually a DIR* dir */
173 char *name;
174 } SIM_DIR;
176 struct mydir {
177 DIR_T *dir;
178 char *name;
181 typedef struct mydir MYDIR;
183 static unsigned int rockbox2sim(int opt)
185 #if 0
186 /* this shouldn't be needed since we use the host's versions */
187 int newopt = O_BINARY;
189 if(opt & 1)
190 newopt |= O_WRONLY;
191 if(opt & 2)
192 newopt |= O_RDWR;
193 if(opt & 4)
194 newopt |= O_CREAT;
195 if(opt & 8)
196 newopt |= O_APPEND;
197 if(opt & 0x10)
198 newopt |= O_TRUNC;
200 return newopt;
201 #else
202 return opt|O_BINARY;
203 #endif
206 /** Simulator I/O engine routines **/
207 #define IO_YIELD_THRESHOLD 512
209 enum io_dir
211 IO_READ,
212 IO_WRITE,
215 struct sim_io
217 struct mutex sim_mutex; /* Rockbox mutex */
218 int cmd; /* The command to perform */
219 int ready; /* I/O ready flag - 1= ready */
220 int fd; /* The file to read/write */
221 void *buf; /* The buffer to read/write */
222 size_t count; /* Number of bytes to read/write */
223 size_t accum; /* Acculated bytes transferred */
226 static struct sim_io io;
228 int ata_init(void)
230 /* Initialize the rockbox kernel objects on a rockbox thread */
231 mutex_init(&io.sim_mutex);
232 io.accum = 0;
233 return 1;
236 int ata_spinup_time(void)
238 return HZ;
241 static ssize_t io_trigger_and_wait(enum io_dir cmd)
243 void *mythread = NULL;
244 ssize_t result;
246 if (io.count > IO_YIELD_THRESHOLD ||
247 (io.accum += io.count) >= IO_YIELD_THRESHOLD)
249 /* Allow other rockbox threads to run */
250 io.accum = 0;
251 mythread = sim_thread_unlock();
254 switch (cmd)
256 case IO_READ:
257 result = read(io.fd, io.buf, io.count);
258 break;
259 case IO_WRITE:
260 result = write(io.fd, io.buf, io.count);
261 break;
262 /* shut up gcc */
263 default:
264 result = -1;
267 /* Regain our status as current */
268 if (mythread != NULL)
270 sim_thread_lock(mythread);
273 return result;
276 #if !defined(__PCTOOL__) && !defined(APPLICATION)
277 static const char *get_sim_pathname(const char *name)
279 static char buffer[MAX_PATH]; /* sufficiently big */
281 if(name[0] == '/')
283 snprintf(buffer, sizeof(buffer), "%s%s",
284 sim_root_dir != NULL ? sim_root_dir : SIMULATOR_DEFAULT_ROOT, name);
285 return buffer;
287 fprintf(stderr, "WARNING, bad file name lacks slash: %s\n", name);
288 return name;
290 #else
291 #define get_sim_pathname(name) name
292 #endif
294 MYDIR *sim_opendir(const char *name)
296 DIR_T *dir;
297 dir = (DIR_T *) OPENDIR(get_sim_pathname(name));
299 if (dir)
301 MYDIR *my = (MYDIR *)malloc(sizeof(MYDIR));
302 my->dir = dir;
303 my->name = (char *)malloc(strlen(name)+1);
304 strcpy(my->name, name);
306 return my;
308 /* failed open, return NULL */
309 return (MYDIR *)0;
312 #if defined(WIN32)
313 static inline struct tm* localtime_r (const time_t *clock, struct tm *result) {
314 if (!clock || !result) return NULL;
315 memcpy(result,localtime(clock),sizeof(*result));
316 return result;
318 #endif
320 struct sim_dirent *sim_readdir(MYDIR *dir)
322 char buffer[MAX_PATH]; /* sufficiently big */
323 static struct sim_dirent secret;
324 STAT_T s;
325 DIRENT_T *x11 = READDIR(dir->dir);
326 struct tm tm;
328 if(!x11)
329 return (struct sim_dirent *)0;
331 strcpy((char *)secret.d_name, OS_TO_UTF8(x11->d_name));
333 /* build file name */
334 snprintf(buffer, sizeof(buffer), "%s/%s",
335 get_sim_pathname(dir->name), secret.d_name);
336 if (STAT(buffer, &s)) /* get info */
337 return NULL;
339 #define ATTR_DIRECTORY 0x10
341 secret.info.attribute = 0;
343 if (S_ISDIR(s.st_mode))
344 secret.info.attribute = ATTR_DIRECTORY;
346 secret.info.size = s.st_size;
348 if (localtime_r(&(s.st_mtime), &tm) == NULL)
349 return NULL;
350 secret.info.wrtdate = ((tm.tm_year - 80) << 9) |
351 ((tm.tm_mon + 1) << 5) |
352 tm.tm_mday;
353 secret.info.wrttime = (tm.tm_hour << 11) |
354 (tm.tm_min << 5) |
355 (tm.tm_sec >> 1);
357 #if HAVE_LSTAT
358 #define ATTR_LINK 0x80
359 if (!lstat(buffer, &s) && S_ISLNK(s.st_mode))
361 secret.info.attribute |= ATTR_LINK;
363 #endif
365 return &secret;
368 void sim_closedir(MYDIR *dir)
370 free(dir->name);
371 CLOSEDIR(dir->dir);
373 free(dir);
376 int sim_open(const char *name, int o, ...)
378 int opts = rockbox2sim(o);
379 int ret;
380 if (num_openfiles >= MAX_OPEN_FILES)
381 return -2;
383 if (opts & O_CREAT)
385 va_list ap;
386 va_start(ap, o);
387 mode_t mode = va_arg(ap, unsigned int);
388 ret = OPEN(get_sim_pathname(name), opts, mode);
389 va_end(ap);
391 else
392 ret = OPEN(get_sim_pathname(name), opts);
394 if (ret >= 0)
395 num_openfiles++;
396 return ret;
399 int sim_close(int fd)
401 int ret;
402 ret = CLOSE(fd);
403 if (ret == 0)
404 num_openfiles--;
405 return ret;
408 int sim_creat(const char *name, mode_t mode)
410 return OPEN(get_sim_pathname(name), O_BINARY | O_WRONLY | O_CREAT | O_TRUNC, mode);
413 ssize_t sim_read(int fd, void *buf, size_t count)
415 ssize_t result;
417 mutex_lock(&io.sim_mutex);
419 /* Setup parameters */
420 io.fd = fd;
421 io.buf = buf;
422 io.count = count;
424 result = io_trigger_and_wait(IO_READ);
426 mutex_unlock(&io.sim_mutex);
428 return result;
431 ssize_t sim_write(int fd, const void *buf, size_t count)
433 ssize_t result;
435 mutex_lock(&io.sim_mutex);
437 io.fd = fd;
438 io.buf = (void*)buf;
439 io.count = count;
441 result = io_trigger_and_wait(IO_WRITE);
443 mutex_unlock(&io.sim_mutex);
445 return result;
448 int sim_mkdir(const char *name)
450 return MKDIR(get_sim_pathname(name), 0777);
453 int sim_rmdir(const char *name)
455 return RMDIR(get_sim_pathname(name));
458 int sim_remove(const char *name)
460 #ifdef HAVE_DIRCACHE
461 dircache_remove(name);
462 #endif
463 return REMOVE(get_sim_pathname(name));
466 int sim_rename(const char *oldname, const char *newname)
468 char sim_old[MAX_PATH];
469 char sim_new[MAX_PATH];
470 #ifdef HAVE_DIRCACHE
471 dircache_rename(oldname, newname);
472 #endif
473 // This is needed as get_sim_pathname() has a static buffer
474 strncpy(sim_old, get_sim_pathname(oldname), MAX_PATH);
475 strncpy(sim_new, get_sim_pathname(newname), MAX_PATH);
476 return RENAME(sim_old, sim_new);
479 /* rockbox off_t may be different from system off_t */
480 long sim_lseek(int fildes, long offset, int whence)
482 return lseek(fildes, offset, whence);
485 long sim_filesize(int fd)
487 #ifdef WIN32
488 return _filelength(fd);
489 #else
490 struct stat buf;
492 if (!fstat(fd, &buf))
493 return buf.st_size;
494 else
495 return -1;
496 #endif
499 void fat_size(IF_MV2(int volume,) unsigned long* size, unsigned long* free)
501 #ifdef HAVE_MULTIVOLUME
502 if (volume != 0) {
503 /* debugf("io.c: fat_size(volume=%d); simulator only supports volume 0\n",volume); */
505 if (size) *size = 0;
506 if (free) *free = 0;
507 return;
509 #endif
511 #ifdef WIN32
512 long secperclus, bytespersec, free_clusters, num_clusters;
514 if (GetDiskFreeSpace(NULL, &secperclus, &bytespersec, &free_clusters,
515 &num_clusters)) {
516 if (size)
517 *size = num_clusters * secperclus / 2 * (bytespersec / 512);
518 if (free)
519 *free = free_clusters * secperclus / 2 * (bytespersec / 512);
521 #elif HAVE_STATVFS
522 struct statvfs vfs;
524 if (!statvfs(".", &vfs)) {
525 DEBUGF("statvfs: frsize=%d blocks=%ld free=%ld\n",
526 (int)vfs.f_frsize, (long)vfs.f_blocks, (long)vfs.f_bfree);
527 if (size)
528 *size = vfs.f_blocks / 2 * (vfs.f_frsize / 512);
529 if (free)
530 *free = vfs.f_bfree / 2 * (vfs.f_frsize / 512);
531 } else
532 #endif
534 if (size)
535 *size = 0;
536 if (free)
537 *free = 0;
541 int sim_fsync(int fd)
543 #ifdef WIN32
544 return _commit(fd);
545 #else
546 return fsync(fd);
547 #endif
550 #ifndef __PCTOOL__
552 void *lc_open(const char *filename, unsigned char *buf, size_t buf_size)
554 const char *sim_path = get_sim_pathname(filename);
555 void *handle = _lc_open(UTF8_TO_OS(sim_path), buf, buf_size);
557 if (handle == NULL)
559 DEBUGF("failed to load %s\n", filename);
560 DEBUGF("lc_open(%s): %s\n", filename, lc_last_error());
562 return handle;
565 void *lc_get_header(void *handle)
567 return _lc_get_header(handle);
570 void lc_close(void *handle)
572 _lc_close(handle);
575 #endif /* __PCTOOL__ */
576 #ifdef WIN32
577 static unsigned old_cp;
579 void debug_exit(void)
581 /* Reset console output codepage */
582 SetConsoleOutputCP(old_cp);
585 void debug_init(void)
587 old_cp = GetConsoleOutputCP();
588 /* Set console output codepage to UTF8. Only works
589 * correctly when the console uses a truetype font. */
590 SetConsoleOutputCP(65001);
591 atexit(debug_exit);
593 #else
594 void debug_init(void)
596 /* nothing to be done */
598 #endif
600 void debugf(const char *fmt, ...)
602 va_list ap;
603 va_start( ap, fmt );
604 vfprintf( stderr, fmt, ap );
605 va_end( ap );
608 void ldebugf(const char* file, int line, const char *fmt, ...)
610 va_list ap;
611 va_start( ap, fmt );
612 fprintf( stderr, "%s:%d ", file, line );
613 vfprintf( stderr, fmt, ap );
614 va_end( ap );
617 /* rockbox off_t may be different from system off_t */
618 int sim_ftruncate(int fd, long length)
620 #ifdef WIN32
621 return _chsize(fd, length);
622 #else
623 return ftruncate(fd, length);
624 #endif