| /* |
| * Creation Date: <2001/05/06 22:47:23 samuel> |
| * Time-stamp: <2004/01/12 10:24:35 samuel> |
| * |
| * /packages/hfs-files |
| * |
| * HFS world interface |
| * |
| * Copyright (C) 2001-2004 Samuel Rydh (samuel@ibrium.se) |
| * Copyright (C) 2010 Mark Cave-Ayland (mark.cave-ayland@siriusit.co.uk) |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation |
| * |
| */ |
| |
| #include "config.h" |
| #include "libopenbios/bindings.h" |
| #include "fs/fs.h" |
| #include "libc/vsprintf.h" |
| #include "libc/diskio.h" |
| #include "libhfs.h" |
| |
| #define MAC_OS_ROM_CREATOR 0x63687270 /* 'chrp' */ |
| #define MAC_OS_ROM_TYPE 0x74627869 /* 'tbxi' */ |
| #define MAC_OS_ROM_NAME "Mac OS ROM" |
| |
| #define FINDER_TYPE 0x464E4452 /* 'FNDR' */ |
| #define FINDER_CREATOR 0x4D414353 /* 'MACS' */ |
| #define SYSTEM_TYPE 0x7A737973 /* 'zsys' */ |
| #define SYSTEM_CREATOR 0x4D414353 /* 'MACS' */ |
| |
| #define VOLNAME_SIZE 64 |
| |
| extern void hfs_init( void ); |
| |
| typedef struct { |
| enum { FILE, DIR } type; |
| union { |
| hfsdir *dir; |
| hfsfile *file; |
| }; |
| } hfscommon; |
| |
| typedef struct { |
| hfsvol *vol; |
| hfscommon *common; |
| } hfs_info_t; |
| |
| DECLARE_NODE( hfs, 0, sizeof(hfs_info_t), "+/packages/hfs-files" ); |
| |
| /************************************************************************/ |
| /* Search Functions */ |
| /************************************************************************/ |
| |
| static int |
| _find_file( hfsvol *vol, const char *path, unsigned long type, unsigned long creator ) |
| { |
| hfsdirent ent; |
| hfsdir *dir; |
| int ret=1; |
| |
| if( !(dir=hfs_opendir(vol, path)) ) |
| return 1; |
| |
| while( ret && !hfs_readdir(dir, &ent) ) { |
| if( ent.flags & HFS_ISDIR ) |
| continue; |
| ret = !(*(unsigned long*)ent.u.file.type == type && *(unsigned long*)ent.u.file.creator == creator ); |
| } |
| |
| hfs_closedir( dir ); |
| return ret; |
| } |
| |
| |
| /* ret: 0=success, 1=not_found, 2=not_a_dir */ |
| static int |
| _search( hfsvol *vol, const char *path, const char *sname, hfsfile **ret_fd ) |
| { |
| hfsdir *dir; |
| hfsdirent ent; |
| int topdir=0, status = 1; |
| char *p, buf[256]; |
| |
| strncpy( buf, path, sizeof(buf) ); |
| if( buf[strlen(buf)-1] != ':' ) |
| strncat( buf, ":", sizeof(buf) - 1 ); |
| buf[sizeof(buf)-1] = 0; |
| p = buf + strlen( buf ); |
| |
| if( !(dir=hfs_opendir(vol, path)) ) |
| return 2; |
| |
| /* printk("DIRECTORY: %s\n", path ); */ |
| |
| while( status && !hfs_readdir(dir, &ent) ) { |
| unsigned long type, creator; |
| |
| *p = 0; |
| topdir = 0; |
| |
| strncat( buf, ent.name, sizeof(buf) - 1); |
| if( (status=_search(vol, buf, sname, ret_fd)) != 2 ) |
| continue; |
| topdir = 1; |
| |
| /* name search? */ |
| if( sname ) { |
| status = strcasecmp( ent.name, sname ); |
| continue; |
| } |
| |
| type = *(unsigned long*)ent.u.file.type; |
| creator = *(unsigned long*)ent.u.file.creator; |
| |
| /* look for Mac OS ROM, System and Finder in the same directory */ |
| if( type == MAC_OS_ROM_TYPE && creator == MAC_OS_ROM_CREATOR ) { |
| if( strcasecmp(ent.name, MAC_OS_ROM_NAME) ) |
| continue; |
| |
| status = _find_file( vol, path, FINDER_TYPE, FINDER_CREATOR ) |
| || _find_file( vol, path, SYSTEM_TYPE, SYSTEM_CREATOR ); |
| } |
| } |
| if( !status && topdir && ret_fd && !(*ret_fd=hfs_open(vol, buf)) ) { |
| printk("Unexpected error: failed to open matched ROM\n"); |
| status = 1; |
| } |
| |
| hfs_closedir( dir ); |
| return status; |
| } |
| |
| static hfsfile * |
| _do_search( hfs_info_t *mi, const char *sname ) |
| { |
| hfsvol *vol = hfs_getvol( NULL ); |
| |
| mi->common->type = FILE; |
| (void)_search( vol, ":", sname, &mi->common->file ); |
| |
| return mi->common->file; |
| } |
| |
| |
| static const int days_month[12] = |
| { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; |
| static const int days_month_leap[12] = |
| { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; |
| |
| static inline int is_leap(int year) |
| { |
| return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0); |
| } |
| |
| static void |
| print_date(time_t sec) |
| { |
| unsigned int second, minute, hour, month, day, year; |
| int current; |
| const int *days; |
| |
| second = sec % 60; |
| sec /= 60; |
| |
| minute = sec % 60; |
| sec /= 60; |
| |
| hour = sec % 24; |
| sec /= 24; |
| |
| year = sec * 100 / 36525; |
| sec -= year * 36525 / 100; |
| year += 1970; |
| |
| days = is_leap(year) ? days_month_leap : days_month; |
| |
| current = 0; |
| month = 0; |
| while (month < 12) { |
| if (sec <= current + days[month]) { |
| break; |
| } |
| current += days[month]; |
| month++; |
| } |
| month++; |
| |
| day = sec - current + 1; |
| |
| forth_printf("%d-%02d-%02d %02d:%02d:%02d ", |
| year, month, day, hour, minute, second); |
| } |
| |
| /* |
| static void |
| dir_fs( file_desc_t *fd ) |
| { |
| hfscommon *common = (hfscommon*)fd; |
| hfsdirent ent; |
| |
| if (common->type != DIR) |
| return; |
| |
| forth_printf("\n"); |
| while( !hfs_readdir(common->dir, &ent) ) { |
| forth_printf("% 10d ", ent.u.file.dsize); |
| print_date(ent.mddate); |
| if( ent.flags & HFS_ISDIR ) |
| forth_printf("%s\\\n", ent.name); |
| else |
| forth_printf("%s\n", ent.name); |
| } |
| } |
| */ |
| |
| /************************************************************************/ |
| /* Standard package methods */ |
| /************************************************************************/ |
| |
| /* ( -- success? ) */ |
| static void |
| hfs_files_open( hfs_info_t *mi ) |
| { |
| int fd; |
| char *path = my_args_copy(); |
| |
| const char *s; |
| char buf[256]; |
| |
| fd = open_ih( my_parent() ); |
| if ( fd == -1 ) { |
| free( path ); |
| RET( 0 ); |
| } |
| |
| mi->vol = hfs_mount(fd, 0); |
| if (!mi->vol) { |
| RET( 0 ); |
| } |
| |
| if( !strncmp(path, "\\\\", 2) ) { |
| hfsvolent ent; |
| |
| /* \\ is an alias for the (blessed) system folder */ |
| if( hfs_vstat(mi->vol, &ent) < 0 || hfs_setcwd(mi->vol, ent.blessed) ) { |
| free(path); |
| RET( -1 ); |
| } |
| path += 2; |
| } else { |
| hfs_chdir( mi->vol, ":" ); |
| } |
| |
| mi->common = malloc(sizeof(hfscommon)); |
| if (!mi->common) { |
| free(path); |
| RET( 0 ); |
| } |
| |
| if (strcmp(path, "\\") == 0) { |
| /* root directory is in fact ":" */ |
| mi->common->dir = hfs_opendir(mi->vol, ":"); |
| mi->common->type = DIR; |
| free(path); |
| RET( -1 ); |
| } |
| |
| if (path[strlen(path) - 1] == '\\') { |
| path[strlen(path) - 1] = 0; |
| } |
| |
| for( path-- ;; ) { |
| int n; |
| |
| s = ++path; |
| path = strchr(s, '\\'); |
| if( !path || !path[1]) |
| break; |
| n = MIN( sizeof(buf)-1, (path-s) ); |
| if( !n ) |
| continue; |
| |
| strncpy( buf, s, n ); |
| buf[n] = 0; |
| if( hfs_chdir(mi->vol, buf) ) { |
| free(mi->common); |
| free(path); |
| RET( 0 ); |
| } |
| } |
| |
| /* support the ':filetype' syntax */ |
| if( *s == ':' ) { |
| unsigned long id, oldid = hfs_getcwd(mi->vol); |
| hfsdirent ent; |
| hfsdir *dir; |
| |
| s++; |
| id = oldid; |
| hfs_dirinfo( mi->vol, &id, buf ); |
| hfs_setcwd( mi->vol, id ); |
| |
| if( !(dir=hfs_opendir(mi->vol, buf)) ) { |
| free(mi->common); |
| free(path); |
| RET( 0 ); |
| } |
| hfs_setcwd( mi->vol, oldid ); |
| |
| while( !hfs_readdir(dir, &ent) ) { |
| if( ent.flags & HFS_ISDIR ) |
| continue; |
| if( !strncmp(s, ent.u.file.type, 4) ) { |
| mi->common->type = FILE; |
| mi->common->file = hfs_open( mi->vol, ent.name ); |
| break; |
| } |
| } |
| hfs_closedir( dir ); |
| free(path); |
| RET( -1 ); |
| } |
| |
| mi->common->dir = hfs_opendir(mi->vol, s); |
| if (!mi->common->dir) { |
| mi->common->file = hfs_open( mi->vol, s ); |
| if (mi->common->file == NULL) { |
| free(mi->common); |
| free(path); |
| RET( 0 ); |
| } |
| mi->common->type = FILE; |
| free(path); |
| RET( -1 ); |
| } |
| mi->common->type = DIR; |
| free(path); |
| |
| RET( -1 ); |
| } |
| |
| /* ( -- ) */ |
| static void |
| hfs_files_close( hfs_info_t *mi ) |
| { |
| hfscommon *common = mi->common; |
| if (common->type == FILE) |
| hfs_close( common->file ); |
| else if (common->type == DIR) |
| hfs_closedir( common->dir ); |
| free(common); |
| } |
| |
| /* ( buf len -- actlen ) */ |
| static void |
| hfs_files_read( hfs_info_t *mi ) |
| { |
| int count = POP(); |
| char *buf = (char *)cell2pointer(POP()); |
| |
| hfscommon *common = mi->common; |
| if (common->type != FILE) |
| RET( -1 ); |
| |
| RET ( hfs_read( common->file, buf, count ) ); |
| } |
| |
| /* ( pos.d -- status ) */ |
| static void |
| hfs_files_seek( hfs_info_t *mi ) |
| { |
| long long pos = DPOP(); |
| int offs = (int)pos; |
| int whence = SEEK_SET; |
| int ret; |
| hfscommon *common = mi->common; |
| |
| if (common->type != FILE) |
| RET( -1 ); |
| |
| switch( whence ) { |
| case SEEK_END: |
| whence = HFS_SEEK_END; |
| break; |
| default: |
| case SEEK_SET: |
| whence = HFS_SEEK_SET; |
| break; |
| } |
| |
| ret = hfs_seek( common->file, offs, whence ); |
| if (ret != -1) |
| RET( 0 ); |
| else |
| RET( -1 ); |
| } |
| |
| /* ( addr -- size ) */ |
| static void |
| hfs_files_load( hfs_info_t *mi ) |
| { |
| char *buf = (char *)cell2pointer(POP()); |
| int count; |
| |
| hfscommon *common = mi->common; |
| if (common->type != FILE) |
| RET( -1 ); |
| |
| /* Seek to the end in order to get the file size */ |
| hfs_seek(common->file, 0, HFS_SEEK_END); |
| count = common->file->pos; |
| hfs_seek(common->file, 0, HFS_SEEK_SET); |
| |
| RET ( hfs_read( common->file, buf, count ) ); |
| } |
| |
| /* ( -- success? ) */ |
| static void |
| hfs_files_open_nwrom( hfs_info_t *mi ) |
| { |
| /* Switch to an existing ROM image file on the fs! */ |
| if ( _do_search( mi, NULL ) ) |
| RET( -1 ); |
| |
| RET( 0 ); |
| } |
| |
| /* ( -- cstr ) */ |
| static void |
| hfs_files_get_path( hfs_info_t *mi ) |
| { |
| char buf[256], buf2[256]; |
| hfscommon *common = mi->common; |
| hfsvol *vol = hfs_getvol( NULL ); |
| hfsdirent ent; |
| int start, ns; |
| unsigned long id; |
| |
| if (common->type != FILE) |
| RET( 0 ); |
| |
| hfs_fstat( common->file, &ent ); |
| start = sizeof(buf) - strlen(ent.name) - 1; |
| if( start <= 0 ) |
| RET ( 0 ); |
| strcpy( buf+start, ent.name ); |
| buf[--start] = '\\'; |
| |
| ns = start; |
| for( id=ent.parid ; !hfs_dirinfo(vol, &id, buf2) ; ) { |
| start = ns; |
| ns -= strlen(buf2); |
| if( ns <= 0 ) |
| RET( 0 ); |
| strcpy( buf+ns, buf2 ); |
| buf[--ns] = buf[start] = '\\'; |
| } |
| if( strlen(buf) >= sizeof(buf) ) |
| RET( 0 ); |
| |
| RET( pointer2cell(strdup(buf+start)) ); |
| } |
| |
| /* ( -- cstr ) */ |
| static void |
| hfs_files_get_fstype( hfs_info_t *mi ) |
| { |
| PUSH( pointer2cell(strdup("HFS")) ); |
| } |
| |
| /* ( -- cstr|0 ) */ |
| static void |
| hfs_files_volume_name( hfs_info_t *mi ) |
| { |
| int fd; |
| char *volname = malloc(VOLNAME_SIZE); |
| |
| fd = open_ih(my_self()); |
| if (fd >= 0) { |
| get_hfs_vol_name(fd, volname, VOLNAME_SIZE); |
| close_io(fd); |
| } else { |
| volname[0] = '\0'; |
| } |
| |
| PUSH(pointer2cell(volname)); |
| } |
| |
| /* static method, ( pathstr len ihandle -- ) */ |
| static void |
| hfs_files_dir( hfs_info_t *dummy ) |
| { |
| hfsvol *volume; |
| hfscommon *common; |
| hfsdirent ent; |
| int i; |
| int fd; |
| |
| ihandle_t ih = POP(); |
| char *path = pop_fstr_copy(); |
| |
| fd = open_ih( ih ); |
| if ( fd == -1 ) { |
| free( path ); |
| return; |
| } |
| |
| volume = hfs_mount(fd, 0); |
| if (!volume) { |
| return; |
| } |
| |
| common = malloc(sizeof(hfscommon)); |
| |
| /* HFS paths are colon separated, not backslash separated */ |
| for (i = 0; i < strlen(path); i++) |
| if (path[i] == '\\') |
| path[i] = ':'; |
| |
| common->dir = hfs_opendir(volume, path); |
| |
| forth_printf("\n"); |
| while( !hfs_readdir(common->dir, &ent) ) { |
| forth_printf("% 10ld ", ent.u.file.dsize); |
| print_date(ent.mddate); |
| if( ent.flags & HFS_ISDIR ) |
| forth_printf("%s\\\n", ent.name); |
| else |
| forth_printf("%s\n", ent.name); |
| } |
| |
| hfs_closedir( common->dir ); |
| hfs_umount( volume ); |
| |
| close_io( fd ); |
| |
| free( common ); |
| free( path ); |
| } |
| |
| /* static method, ( pos.d ih -- flag? ) */ |
| static void |
| hfs_files_probe( hfs_info_t *dummy ) |
| { |
| ihandle_t ih = POP_ih(); |
| long long offs = DPOP(); |
| int fd, ret = 0; |
| |
| fd = open_ih(ih); |
| if (fd >= 0) { |
| if (hfs_probe(fd, offs)) { |
| ret = -1; |
| } |
| close_io(fd); |
| } else { |
| ret = -1; |
| } |
| |
| RET (ret); |
| } |
| |
| static void |
| hfs_initializer( hfs_info_t *dummy ) |
| { |
| fword("register-fs-package"); |
| } |
| |
| NODE_METHODS( hfs ) = { |
| { "probe", hfs_files_probe }, |
| { "open", hfs_files_open }, |
| { "close", hfs_files_close }, |
| { "read", hfs_files_read }, |
| { "seek", hfs_files_seek }, |
| { "load", hfs_files_load }, |
| { "dir", hfs_files_dir }, |
| |
| /* special */ |
| { "open-nwrom", hfs_files_open_nwrom }, |
| { "get-path", hfs_files_get_path }, |
| { "get-fstype", hfs_files_get_fstype }, |
| { "volume-name", hfs_files_volume_name }, |
| |
| { NULL, hfs_initializer }, |
| }; |
| |
| void |
| hfs_init( void ) |
| { |
| REGISTER_NODE( hfs ); |
| } |