| /* |
| * libhfsp - library for reading and writing Macintosh HFS+ volumes. |
| * |
| * a record contains a key and a folder or file and is part |
| * of a btree. |
| * |
| * Copyright (C) 2000 Klaus Halfmann <khalfmann@libra.de> |
| * Original 1996-1998 Robert Leslie <rob@mars.org> |
| * Additional work by Brad Boyer (flar@pants.nu) |
| * |
| * 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; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
| * MA 02110-1301, USA. |
| * |
| * $Id: record.c,v 1.24 2000/10/17 05:58:46 hasi Exp $ |
| */ |
| |
| #include "config.h" |
| #include "libhfsp.h" |
| #include "hfstime.h" |
| #include "record.h" |
| #include "volume.h" |
| #include "btree.h" |
| #include "unicode.h" |
| #include "swab.h" |
| |
| /* read a hfsp_cat_key from memory */ |
| void* record_readkey(void* p, void* buf) |
| { |
| hfsp_cat_key* key = (hfsp_cat_key*) buf; |
| const void* check; |
| UInt16 key_length, len,i; |
| UInt16* cp; |
| |
| key->key_length = key_length = bswabU16_inc(p); |
| check = p; |
| key->parent_cnid = bswabU32_inc(p); |
| key->name.strlen = len = bswabU16_inc(p); |
| cp = key->name.name; |
| for (i=0; i < len; i++, cp++) |
| *cp = bswabU16_inc(p); |
| /* check if keylenght was correct */ |
| if (key_length != ((char*) p) - ((char*) check)) |
| HFSP_ERROR(EINVAL, "Invalid key length in record_readkey"); |
| return p; |
| fail: |
| return NULL; |
| } |
| |
| /* read a hfsp_extent_key from memory */ |
| void* record_extent_readkey(void* p, void* buf) |
| { |
| hfsp_extent_key* key = (hfsp_extent_key*) buf; |
| UInt16 key_length; |
| |
| key->key_length = key_length = bswabU16_inc(p); |
| key->fork_type = bswabU8_inc(p); |
| key->filler = bswabU8_inc(p); |
| if (key_length != 10) |
| HFSP_ERROR(-1, "Invalid key length in record_extent_readkey"); |
| key->file_id = bswabU32_inc(p); |
| key->start_block = bswabU32_inc(p); |
| return p; |
| fail: |
| return NULL; |
| } |
| |
| |
| /* read posix permission from memory */ |
| static inline void* record_readperm(void *p, hfsp_perm* perm) |
| { |
| perm->owner= bswabU32_inc(p); |
| perm->group= bswabU32_inc(p); |
| perm->mode = bswabU32_inc(p); |
| perm->dev = bswabU32_inc(p); |
| return p; |
| } |
| |
| /* read directory info */ |
| static inline void* record_readDInfo(void *p, DInfo* info) |
| { |
| info->frRect.top = bswabU16_inc(p); |
| info->frRect.left = bswabU16_inc(p); |
| info->frRect.bottom = bswabU16_inc(p); |
| info->frRect.right = bswabU16_inc(p); |
| info->frFlags = bswabU16_inc(p); |
| info->frLocation.v = bswabU16_inc(p); |
| info->frLocation.h = bswabU16_inc(p); |
| info->frView = bswabU16_inc(p); |
| return p; |
| } |
| |
| /* read extra Directory info */ |
| static inline void* record_readDXInfo(void *p, DXInfo* xinfo) |
| { |
| xinfo->frScroll.v = bswabU16_inc(p); |
| xinfo->frScroll.h = bswabU16_inc(p); |
| xinfo->frOpenChain = bswabU32_inc(p); |
| xinfo->frUnused = bswabU16_inc(p); |
| xinfo->frComment = bswabU16_inc(p); |
| xinfo->frPutAway = bswabU32_inc(p); |
| return p; |
| } |
| |
| /* read a hfsp_cat_folder from memory */ |
| static void* record_readfolder(void *p, hfsp_cat_folder* folder) |
| { |
| folder->flags = bswabU16_inc(p); |
| folder->valence = bswabU32_inc(p); |
| folder->id = bswabU32_inc(p); |
| folder->create_date = bswabU32_inc(p); |
| folder->content_mod_date = bswabU32_inc(p); |
| folder->attribute_mod_date = bswabU32_inc(p); |
| folder->access_date = bswabU32_inc(p); |
| folder->backup_date = bswabU32_inc(p); |
| p = record_readperm (p, &folder->permissions); |
| p = record_readDInfo (p, &folder->user_info); |
| p = record_readDXInfo (p, &folder->finder_info); |
| folder->text_encoding = bswabU32_inc(p); |
| folder->reserved = bswabU32_inc(p); |
| return p; |
| } |
| |
| /* read file info */ |
| static inline void* record_readFInfo(void *p, FInfo* info) |
| { |
| info->fdType = bswabU32_inc(p); |
| info->fdCreator = bswabU32_inc(p); |
| info->fdFlags = bswabU16_inc(p); |
| info->fdLocation.v = bswabU16_inc(p); |
| info->fdLocation.h = bswabU16_inc(p); |
| info->fdFldr = bswabU16_inc(p); |
| return p; |
| } |
| |
| /* read extra File info */ |
| static inline void* record_readFXInfo(void *p, FXInfo* xinfo) |
| { |
| SInt16 *q; |
| xinfo->fdIconID = bswabU16_inc(p); |
| q=(SInt16*) p; |
| q+=4; // skip unused |
| p=(void *)q; |
| xinfo->fdComment = bswabU16_inc(p); |
| xinfo->fdPutAway = bswabU32_inc(p); |
| return p; |
| } |
| |
| /* read a hfsp_cat_file from memory */ |
| static void* record_readfile(void *p, hfsp_cat_file* file) |
| { |
| file->flags = bswabU16_inc(p); |
| file->reserved1 = bswabU32_inc(p); |
| file->id = bswabU32_inc(p); |
| file->create_date = bswabU32_inc(p); |
| file->content_mod_date = bswabU32_inc(p); |
| file->attribute_mod_date = bswabU32_inc(p); |
| file->access_date = bswabU32_inc(p); |
| file->backup_date = bswabU32_inc(p); |
| p = record_readperm (p, &file->permissions); |
| p = record_readFInfo (p, &file->user_info); |
| p = record_readFXInfo (p, &file->finder_info); |
| file->text_encoding = bswabU32_inc(p); |
| file->reserved2 = bswabU32_inc(p); |
| p = volume_readfork (p, &file->data_fork); |
| return volume_readfork (p, &file->res_fork); |
| } |
| |
| /* read a hfsp_cat_thread from memory */ |
| static void* record_readthread(void *p, hfsp_cat_thread* entry) |
| { |
| int i; |
| UInt16 len; |
| UInt16* cp; |
| |
| entry-> reserved = bswabU16_inc(p); |
| entry-> parentID = bswabU32_inc(p); |
| entry->nodeName.strlen = len= bswabU16_inc(p); |
| cp = entry->nodeName.name; |
| if (len > 255) |
| HFSP_ERROR(-1, "Invalid key length in record thread"); |
| for (i=0; i < len; i++, cp++) |
| *cp = bswabU16_inc(p); |
| return p; |
| fail: |
| return NULL; |
| } |
| |
| /* read a hfsp_cat_entry from memory */ |
| static void* record_readentry(void *p, hfsp_cat_entry* entry) |
| { |
| UInt16 type = bswabU16_inc(p); |
| entry->type = type; |
| switch (type) |
| { |
| case HFSP_FOLDER: |
| return record_readfolder(p, &entry->u.folder); |
| case HFSP_FILE: |
| return record_readfile (p, &entry->u.file); |
| case HFSP_FOLDER_THREAD: |
| case HFSP_FILE_THREAD: |
| return record_readthread(p, &entry->u.thread); |
| default: |
| HFSP_ERROR(-1, "Unexpected record type in record_readentry"); |
| } ; |
| fail: |
| return NULL; |
| } |
| |
| |
| /* Most of the functions here will not change the node in the btree, |
| But this must be changed in the future ... */ |
| |
| |
| /* intialize the record with the given index entry in the btree. */ |
| static int record_init(record* r, btree* bt, node_buf* buf, UInt16 index) |
| { |
| void *p; |
| r-> tree = bt; |
| p = btree_key_by_index(bt,buf,index); |
| if (!p) |
| return -1; |
| p = record_readkey (p, &r->key); |
| if (!p) |
| return -1; |
| p = record_readentry(p, &r->record); |
| if (!p) |
| return -1; |
| r->node_index = buf->index; |
| r-> keyind = index; |
| |
| return 0; |
| } |
| |
| /* intialize the record with the given index entry in the btree. */ |
| static int record_init_extent(extent_record* r, btree* bt, node_buf* buf, UInt16 index) |
| { |
| void *p; |
| r-> tree = bt; |
| p = btree_key_by_index(bt, buf,index); |
| if (!p) |
| return -1; |
| p = record_extent_readkey(p, &r->key); |
| if (!p) |
| return -1; |
| p = volume_readextent(p, r->extent); |
| if (!p) |
| return -1; |
| r->node_index = buf->index; |
| r-> keyind = index; |
| |
| return 0; |
| } |
| |
| /* intialize the record to the first record of the tree |
| * which is (per design) the root node. |
| */ |
| int record_init_root(record* r, btree* tree) |
| { |
| // Position to first leaf node ... |
| UInt32 leaf_head = tree->head.leaf_head; |
| node_buf* buf = btree_node_by_index(tree, leaf_head); |
| if (!buf) |
| return -1; |
| return record_init(r, tree, buf, 0); |
| } |
| |
| /* Compare two cat_keys ... */ |
| int record_key_compare(void* k1, void* k2) |
| { |
| hfsp_cat_key* key1 = (hfsp_cat_key*) k1; |
| hfsp_cat_key* key2 = (hfsp_cat_key*) k2; |
| int diff = key2->parent_cnid - key1->parent_cnid; |
| if (!diff) // same parent |
| diff = fast_unicode_compare(&key1->name, &key2->name); |
| return diff; |
| } |
| |
| /* Compare two extent_keys ... */ |
| int record_extent_key_compare(void* k1, void* k2) |
| { |
| hfsp_extent_key* key1 = (hfsp_extent_key*) k1; |
| hfsp_extent_key* key2 = (hfsp_extent_key*) k2; |
| int diff = key2->fork_type - key1->fork_type; |
| if (!diff) // same type |
| { |
| diff = key2->file_id - key1->file_id; |
| if (!diff) // same file |
| diff = key2->start_block - key1->start_block; |
| } |
| return diff; |
| } |
| |
| /* Position node in btree so that key might be inside */ |
| static node_buf* record_find_node(btree* tree, void *key) |
| { |
| int start, end, mid, comp; // components of a binary search |
| void *p = NULL; |
| char curr_key[tree->head.max_key_len]; |
| // The current key under examination |
| hfsp_key_read readkey = tree->kread; |
| hfsp_key_compare key_compare = tree->kcomp; |
| UInt32 index; |
| node_buf* node = btree_node_by_index(tree, tree->head.root); |
| if (!node) |
| HFSP_ERROR(-1, "record_find_node: Cant position to root node"); |
| while (node->desc.kind == HFSP_NODE_NDX) |
| { |
| mid = start = 0; |
| end = node->desc.num_rec; |
| comp = -1; |
| while (start < end) |
| { |
| mid = (start + end) >> 1; |
| p = btree_key_by_index(tree, node, mid); |
| if (!p) |
| HFSP_ERROR(-1, "record_find_node: unexpected error"); |
| p = readkey (p, curr_key); |
| if (!p) |
| HFSP_ERROR(-1, "record_find_node: unexpected error"); |
| comp = key_compare(curr_key, key); |
| if (comp > 0) |
| start = mid + 1; |
| else if (comp < 0) |
| end = mid; |
| else |
| break; |
| } |
| if (!p) // Empty tree, fascinating ... |
| HFSP_ERROR(-1, "record_find_node: unexpected empty node"); |
| if (comp < 0) // mmh interesting key is before this key ... |
| { |
| if (mid == 0) |
| return NULL; // nothing before this key .. |
| p = btree_key_by_index(tree, node, mid-1); |
| if (!p) |
| HFSP_ERROR(-1, "record_find_node: unexpected error"); |
| p = readkey (p, curr_key); |
| if (!p) |
| HFSP_ERROR(-1, "record_find_node: unexpected error"); |
| } |
| |
| index = bswabU32_inc(p); |
| node = btree_node_by_index(tree, index); |
| } |
| return node; // go on and use the found node |
| fail: |
| return NULL; |
| } |
| |
| /* search for the given key in the btree. |
| * |
| * returns pointer to memory just after key or NULL |
| * In any case *keyind recives the index where the |
| * key was found (or could be inserted.) |
| */ |
| static void * |
| record_find_key(btree* tree, void* key, int* keyind, UInt16* node_index) |
| { |
| node_buf* buf = record_find_node(tree, key); |
| if (buf) |
| { |
| int comp = -1; |
| int start = 0; // components of a binary search |
| int end = buf->desc.num_rec; |
| int mid = -1; |
| void *p = NULL; |
| char curr_key[tree->head.max_key_len]; |
| hfsp_key_read readkey = tree->kread; |
| hfsp_key_compare key_compare = tree->kcomp; |
| while (start < end) |
| { |
| mid = (start + end) >> 1; |
| p = btree_key_by_index(tree, buf, mid); |
| if (!p) |
| HFSP_ERROR(-1, "record_init_key: unexpected error"); |
| p = readkey (p, curr_key); |
| if (!p) |
| HFSP_ERROR(-1, "record_init_cat_key: unexpected error"); |
| comp = key_compare(curr_key, key); |
| if (comp > 0) |
| start = mid + 1; |
| else if (comp < 0) |
| end = mid; |
| else |
| break; |
| } |
| if (!p) // Empty tree, fascinating ... |
| HFSP_ERROR(ENOENT, "record_init_key: unexpected empty node"); |
| *keyind = mid; |
| *node_index = buf->index; |
| if (!comp) // found something ... |
| return p; |
| } |
| HFSP_ERROR(ENOENT, NULL); |
| fail: |
| return NULL; |
| } |
| |
| /* intialize the record by searching for the given key in the btree. |
| * |
| * r is umodified on error. |
| */ |
| static int |
| record_init_key(record* r, btree* tree, hfsp_cat_key* key) |
| { |
| int keyind; |
| UInt16 node_index; |
| void *p = record_find_key(tree, key, &keyind, &node_index); |
| |
| if (p) |
| { |
| r -> tree = tree; |
| r -> node_index= node_index; |
| r -> keyind = keyind; |
| r -> key = *key; // Better use a record_key_copy ... |
| p = record_readentry(p, &r->record); |
| if (!p) |
| HFSP_ERROR(-1, "record_init_key: unexpected error"); |
| return 0; |
| } |
| fail: |
| return -1; |
| } |
| |
| /* intialize the extent_record to the extent identified by the |
| * (first) blockindex. |
| * |
| * forktype: either HFSP_EXTEND_DATA or HFSP_EXTEND_RSRC |
| */ |
| int record_init_file(extent_record* r, btree* tree, |
| UInt8 forktype, UInt32 fileId, UInt32 blockindex) |
| { |
| int keyind; |
| UInt16 node_index; |
| hfsp_extent_key key = { 10, forktype, 0, fileId, blockindex }; |
| void *p = record_find_key(tree, &key, &keyind, &node_index); |
| |
| if (p) |
| { |
| r -> tree = tree; |
| r -> node_index= node_index; |
| r -> keyind = keyind; |
| r -> key = key; // Better use a record_key_copy ... |
| p = volume_readextent(p, r->extent); |
| if (!p) |
| HFSP_ERROR(-1, "record_init_file: unexpected error"); |
| return 0; |
| } |
| fail: |
| return -1; |
| } |
| |
| /* intialize the record to the folder identified by cnid |
| */ |
| int record_init_cnid(record* r, btree* tree, UInt32 cnid) |
| { |
| hfsp_cat_key thread_key; // the thread is the first record |
| |
| thread_key.key_length = 6; // null name (like '.' in unix ) |
| thread_key.parent_cnid = cnid; |
| thread_key.name.strlen = 0; |
| |
| return record_init_key(r, tree, &thread_key); |
| } |
| |
| /* intialize the record to the first record of the parent. |
| */ |
| int record_init_parent(record* r, record* parent) |
| { |
| if (parent->record.type == HFSP_FOLDER) |
| return record_init_cnid(r, parent->tree, parent->record.u.folder.id); |
| else if(parent->record.type == HFSP_FOLDER_THREAD) |
| { |
| if (r != parent) |
| *r = *parent; // The folder thread is in fact the first entry, like '.' |
| return 0; |
| } |
| HFSP_ERROR(EINVAL, |
| "record_init_parent: parent is neither folder nor folder thread."); |
| |
| fail: |
| return EINVAL; |
| } |
| |
| |
| /* find correct node record for given node and *pindex. |
| * |
| * index of record in this (or next) node |
| * */ |
| static node_buf* prepare_next(btree* tree, UInt16 node_index, UInt16* pindex) |
| { |
| node_buf* buf = btree_node_by_index(tree, node_index); |
| btree_node_desc* desc = &buf->desc; |
| UInt32 numrec = desc->num_rec; |
| if (*pindex >= numrec) // move on to next node |
| { |
| UInt16 next = desc->next; |
| *pindex = 0; |
| if (!next /* is there a next node ? */ |
| || !( buf = btree_node_by_index(tree, next))) |
| return NULL; |
| } |
| return buf; |
| } |
| /* move record foreward to next entry. |
| * |
| * In case of an error the value of *r is undefined ! |
| */ |
| int record_next(record* r) |
| { |
| btree* tree = r->tree; |
| UInt16 index = r->keyind +1; |
| UInt32 parent; |
| node_buf* buf = prepare_next(tree, r->node_index, &index); |
| |
| if (!buf) |
| return ENOENT; // No (more) such file or directory |
| |
| parent = r->key.parent_cnid; |
| |
| if (record_init(r, tree, buf, index)) |
| return -1; |
| |
| if (r->key.parent_cnid != parent || // end of current directory |
| index != r->keyind) // internal error ? |
| return ENOENT; // No (more) such file or directory |
| |
| return 0; |
| } |
| |
| /* move record foreward to next extent record. |
| * |
| * In case of an error the value of *r is undefined ! |
| */ |
| int record_next_extent(extent_record* r) |
| { |
| btree* tree = r->tree; |
| UInt16 index = r->keyind +1; |
| UInt32 file_id; |
| UInt8 fork_type; |
| node_buf* buf = prepare_next(tree, r->node_index, &index); |
| |
| if (!buf) |
| return ENOENT; // No (more) such file or directory |
| |
| file_id = r->key.file_id; |
| fork_type = r->key.fork_type; |
| |
| if (record_init_extent(r, tree, buf, index)) |
| return -1; |
| |
| if (r->key.file_id != file_id || // end of current file |
| r->key.fork_type != fork_type || // end of current fork |
| index != r->keyind) // internal error ? |
| return ENOENT; // No (more) such file or directory |
| |
| return 0; |
| } |
| |
| /* intialize the record by searching for the given string in the given folder. |
| * |
| * parent and r may be the same. |
| */ |
| int record_init_string_parent(record* r, record* parent, char* name) |
| { |
| hfsp_cat_key key; |
| |
| if (parent->record.type == HFSP_FOLDER) |
| key.parent_cnid = parent->record.u.folder.id; |
| else if(parent->record.type == HFSP_FOLDER_THREAD) |
| key.parent_cnid = parent->key.parent_cnid; |
| else |
| HFSP_ERROR(-1, "record_init_string_parent: parent is not a folder."); |
| |
| key.key_length = 6 + unicode_asc2uni(&key.name,name); // 6 for minumum size |
| return record_init_key(r, parent->tree, &key); |
| |
| fail: |
| return -1; |
| } |
| |
| /* move record up in folder hierarchy (if possible) */ |
| int record_up(record* r) |
| { |
| if (r->record.type == HFSP_FOLDER) |
| { |
| // locate folder thread |
| if (record_init_cnid(r, r->tree, r->record.u.folder.id)) |
| return -1; |
| } |
| else if(r->record.type == HFSP_FOLDER_THREAD) |
| { |
| // do nothing were are already where we want to be |
| } |
| else |
| HFSP_ERROR(-1, "record_up: record is neither folder nor folder thread."); |
| |
| if(r->record.type != HFSP_FOLDER_THREAD) |
| HFSP_ERROR(-1, "record_up: unable to locate parent"); |
| return record_init_cnid(r, r->tree, r->record.u.thread.parentID); |
| |
| fail: |
| return -1; |
| } |
| |
| #ifdef DEBUG |
| |
| /* print Quickdraw Point */ |
| static void record_print_Point(Point* p) |
| { |
| printf("[ v=%d, h=%d ]", p->v, p->h); |
| } |
| |
| /* print Quickdraw Rect */ |
| static void record_print_Rect(Rect* r) |
| { |
| printf("[ top=%d, left=%d, bottom=%d, right=%d ]", |
| r->top, r->left, r->bottom, r->right); |
| } |
| |
| /* print the key of a record */ |
| static void record_print_key(hfsp_cat_key* key) |
| { |
| char buf[255]; // mh this _might_ overflow |
| unicode_uni2asc(buf, &key->name, 255); |
| printf("parent cnid : %ld\n", key->parent_cnid); |
| printf("name : %s\n", buf); |
| } |
| |
| /* print permissions */ |
| static void record_print_perm(hfsp_perm* perm) |
| { |
| printf("owner :\t%ld\n", perm->owner); |
| printf("group :\t%ld\n", perm->group); |
| printf("perm :\t0x%lX\n",perm->mode); |
| printf("dev :\t%ld\n", perm->dev); |
| } |
| |
| /* print Directory info */ |
| static void record_print_DInfo(DInfo* dinfo) |
| { |
| printf( "frRect :\t"); record_print_Rect(&dinfo->frRect); |
| printf("\nfrFlags :\t0X%X\n", dinfo->frFlags); |
| printf( "frLocation :\t"); record_print_Point(&dinfo->frLocation); |
| printf("\nfrView :\t0X%X\n", dinfo->frView); |
| } |
| |
| /* print extended Directory info */ |
| static void record_print_DXInfo(DXInfo* xinfo) |
| { |
| printf( "frScroll :\t"); record_print_Point(&xinfo->frScroll); |
| printf("\nfrOpenChain :\t%ld\n", xinfo->frOpenChain); |
| printf( "frUnused :\t%d\n", xinfo->frUnused); |
| printf( "frComment :\t%d\n", xinfo->frComment); |
| printf( "frPutAway :\t%ld\n", xinfo->frPutAway); |
| } |
| |
| static void record_print_folder(hfsp_cat_folder* folder) |
| { |
| printf("flags :\t0x%X\n", folder->flags); |
| printf("valence :\t0x%lX\n", folder->valence); |
| printf("id :\t%ld\n", folder->id); |
| record_print_perm (&folder->permissions); |
| record_print_DInfo (&folder->user_info); |
| record_print_DXInfo (&folder->finder_info); |
| printf("text_encoding :\t0x%lX\n", folder->text_encoding); |
| printf("reserved :\t0x%lX\n", folder->reserved); |
| } |
| |
| /* print File info */ |
| static void record_print_FInfo(FInfo* finfo) |
| { |
| printf( "fdType :\t%4.4s\n", (char*) &finfo->fdType); |
| printf( "fdCreator :\t%4.4s\n", (char*) &finfo->fdCreator); |
| printf( "fdFlags :\t0X%X\n", finfo->fdFlags); |
| printf( "fdLocation :\t"); record_print_Point(&finfo->fdLocation); |
| printf("\nfdFldr :\t%d\n", finfo->fdFldr); |
| } |
| |
| /* print extended File info */ |
| static void record_print_FXInfo(FXInfo* xinfo) |
| { |
| printf( "fdIconID :\t%d\n", xinfo->fdIconID); |
| // xinfo -> fdUnused; |
| printf( "fdComment :\t%d\n", xinfo->fdComment); |
| printf( "fdPutAway :\t%ld\n", xinfo->fdPutAway); |
| } |
| |
| /* print folder entry */ |
| |
| /* print file entry */ |
| static void record_print_file(hfsp_cat_file* file) |
| { |
| printf("flags :\t0x%X\n", file->flags); |
| printf("reserved1 :\t0x%lX\n", file->reserved1); |
| printf("id :\t%ld\n", file->id); |
| record_print_perm (&file->permissions); |
| record_print_FInfo (&file->user_info); |
| record_print_FXInfo (&file->finder_info); |
| printf("text_encoding :\t0x%lX\n", file->text_encoding); |
| printf("reserved :\t0x%lX\n", file->reserved2); |
| printf("Datafork:\n"); |
| volume_print_fork (&file->data_fork); |
| printf("Rsrcfork:\n"); |
| volume_print_fork (&file->res_fork); |
| } |
| |
| /* print info for a file or folder thread */ |
| static void record_print_thread(hfsp_cat_thread* entry) |
| { |
| char buf[255]; // mh this _might_ overflow |
| unicode_uni2asc(buf, &entry->nodeName, 255); |
| printf("parent cnid :\t%ld\n", entry->parentID); |
| printf("name :\t%s\n" , buf); |
| } |
| |
| /* print the information for a record */ |
| static void record_print_entry(hfsp_cat_entry* entry) |
| { |
| switch (entry->type) |
| { |
| case HFSP_FOLDER: |
| printf("=== Folder ===\n"); |
| return record_print_folder(&entry->u.folder); |
| case HFSP_FILE: |
| printf("=== File ===\n"); |
| return record_print_file (&entry->u.file); |
| case HFSP_FOLDER_THREAD: |
| printf("=== Folder Thread ===\n"); |
| return record_print_thread(&entry->u.thread); |
| case HFSP_FILE_THREAD: |
| printf("=== File Thread ==\n"); |
| return record_print_thread(&entry->u.thread); |
| default: |
| printf("=== Unknown Record Type ===\n"); |
| } ; |
| } |
| |
| /* Dump all the record information to stdout */ |
| void record_print(record* r) |
| { |
| printf ("keyind : %u\n", r->keyind); |
| record_print_key (&r->key); |
| record_print_entry(&r->record); |
| } |
| |
| #endif |