switch-arch: Prepare ppc64 support
[openbios/afaerber.git] / fs / hfsplus / record.c
blobd4e7af1bf93c35c8d42408fa18dce1ccaa61e2bc
1 /*
2 * libhfsp - library for reading and writing Macintosh HFS+ volumes.
4 * a record contains a key and a folder or file and is part
5 * of a btree.
7 * Copyright (C) 2000 Klaus Halfmann <khalfmann@libra.de>
8 * Original 1996-1998 Robert Leslie <rob@mars.org>
9 * Additional work by Brad Boyer (flar@pants.nu)
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
24 * MA 02110-1301, USA.
26 * $Id: record.c,v 1.24 2000/10/17 05:58:46 hasi Exp $
29 #include "config.h"
30 #include "libhfsp.h"
31 #include "hfstime.h"
32 #include "record.h"
33 #include "volume.h"
34 #include "btree.h"
35 #include "unicode.h"
36 #include "swab.h"
38 /* read a hfsp_cat_key from memory */
39 void* record_readkey(void* p, void* buf)
41 hfsp_cat_key* key = (hfsp_cat_key*) buf;
42 const void* check;
43 UInt16 key_length, len,i;
44 UInt16* cp;
46 key->key_length = key_length = bswabU16_inc(p);
47 check = p;
48 key->parent_cnid = bswabU32_inc(p);
49 key->name.strlen = len = bswabU16_inc(p);
50 cp = key->name.name;
51 for (i=0; i < len; i++, cp++)
52 *cp = bswabU16_inc(p);
53 /* check if keylenght was correct */
54 if (key_length != ((char*) p) - ((char*) check))
55 HFSP_ERROR(EINVAL, "Invalid key length in record_readkey");
56 return p;
57 fail:
58 return NULL;
61 /* read a hfsp_extent_key from memory */
62 void* record_extent_readkey(void* p, void* buf)
64 hfsp_extent_key* key = (hfsp_extent_key*) buf;
65 UInt16 key_length;
67 key->key_length = key_length = bswabU16_inc(p);
68 key->fork_type = bswabU8_inc(p);
69 key->filler = bswabU8_inc(p);
70 if (key_length != 10)
71 HFSP_ERROR(-1, "Invalid key length in record_extent_readkey");
72 key->file_id = bswabU32_inc(p);
73 key->start_block = bswabU32_inc(p);
74 return p;
75 fail:
76 return NULL;
80 /* read posix permission from memory */
81 static inline void* record_readperm(void *p, hfsp_perm* perm)
83 perm->owner= bswabU32_inc(p);
84 perm->group= bswabU32_inc(p);
85 perm->mode = bswabU32_inc(p);
86 perm->dev = bswabU32_inc(p);
87 return p;
90 /* read directory info */
91 static inline void* record_readDInfo(void *p, DInfo* info)
93 info->frRect.top = bswabU16_inc(p);
94 info->frRect.left = bswabU16_inc(p);
95 info->frRect.bottom = bswabU16_inc(p);
96 info->frRect.right = bswabU16_inc(p);
97 info->frFlags = bswabU16_inc(p);
98 info->frLocation.v = bswabU16_inc(p);
99 info->frLocation.h = bswabU16_inc(p);
100 info->frView = bswabU16_inc(p);
101 return p;
104 /* read extra Directory info */
105 static inline void* record_readDXInfo(void *p, DXInfo* xinfo)
107 xinfo->frScroll.v = bswabU16_inc(p);
108 xinfo->frScroll.h = bswabU16_inc(p);
109 xinfo->frOpenChain = bswabU32_inc(p);
110 xinfo->frUnused = bswabU16_inc(p);
111 xinfo->frComment = bswabU16_inc(p);
112 xinfo->frPutAway = bswabU32_inc(p);
113 return p;
116 /* read a hfsp_cat_folder from memory */
117 static void* record_readfolder(void *p, hfsp_cat_folder* folder)
119 folder->flags = bswabU16_inc(p);
120 folder->valence = bswabU32_inc(p);
121 folder->id = bswabU32_inc(p);
122 folder->create_date = bswabU32_inc(p);
123 folder->content_mod_date = bswabU32_inc(p);
124 folder->attribute_mod_date = bswabU32_inc(p);
125 folder->access_date = bswabU32_inc(p);
126 folder->backup_date = bswabU32_inc(p);
127 p = record_readperm (p, &folder->permissions);
128 p = record_readDInfo (p, &folder->user_info);
129 p = record_readDXInfo (p, &folder->finder_info);
130 folder->text_encoding = bswabU32_inc(p);
131 folder->reserved = bswabU32_inc(p);
132 return p;
135 /* read file info */
136 static inline void* record_readFInfo(void *p, FInfo* info)
138 info->fdType = bswabU32_inc(p);
139 info->fdCreator = bswabU32_inc(p);
140 info->fdFlags = bswabU16_inc(p);
141 info->fdLocation.v = bswabU16_inc(p);
142 info->fdLocation.h = bswabU16_inc(p);
143 info->fdFldr = bswabU16_inc(p);
144 return p;
147 /* read extra File info */
148 static inline void* record_readFXInfo(void *p, FXInfo* xinfo)
150 SInt16 *q;
151 xinfo->fdIconID = bswabU16_inc(p);
152 q=(SInt16*) p;
153 q+=4; // skip unused
154 p=(void *)q;
155 xinfo->fdComment = bswabU16_inc(p);
156 xinfo->fdPutAway = bswabU32_inc(p);
157 return p;
160 /* read a hfsp_cat_file from memory */
161 static void* record_readfile(void *p, hfsp_cat_file* file)
163 file->flags = bswabU16_inc(p);
164 file->reserved1 = bswabU32_inc(p);
165 file->id = bswabU32_inc(p);
166 file->create_date = bswabU32_inc(p);
167 file->content_mod_date = bswabU32_inc(p);
168 file->attribute_mod_date = bswabU32_inc(p);
169 file->access_date = bswabU32_inc(p);
170 file->backup_date = bswabU32_inc(p);
171 p = record_readperm (p, &file->permissions);
172 p = record_readFInfo (p, &file->user_info);
173 p = record_readFXInfo (p, &file->finder_info);
174 file->text_encoding = bswabU32_inc(p);
175 file->reserved2 = bswabU32_inc(p);
176 p = volume_readfork (p, &file->data_fork);
177 return volume_readfork (p, &file->res_fork);
180 /* read a hfsp_cat_thread from memory */
181 static void* record_readthread(void *p, hfsp_cat_thread* entry)
183 int i;
184 UInt16 len;
185 UInt16* cp;
187 entry-> reserved = bswabU16_inc(p);
188 entry-> parentID = bswabU32_inc(p);
189 entry->nodeName.strlen = len= bswabU16_inc(p);
190 cp = entry->nodeName.name;
191 if (len > 255)
192 HFSP_ERROR(-1, "Invalid key length in record thread");
193 for (i=0; i < len; i++, cp++)
194 *cp = bswabU16_inc(p);
195 return p;
196 fail:
197 return NULL;
200 /* read a hfsp_cat_entry from memory */
201 static void* record_readentry(void *p, hfsp_cat_entry* entry)
203 UInt16 type = bswabU16_inc(p);
204 entry->type = type;
205 switch (type)
207 case HFSP_FOLDER:
208 return record_readfolder(p, &entry->u.folder);
209 case HFSP_FILE:
210 return record_readfile (p, &entry->u.file);
211 case HFSP_FOLDER_THREAD:
212 case HFSP_FILE_THREAD:
213 return record_readthread(p, &entry->u.thread);
214 default:
215 HFSP_ERROR(-1, "Unexpected record type in record_readentry");
217 fail:
218 return NULL;
222 /* Most of the functions here will not change the node in the btree,
223 But this must be changed in the future ... */
226 /* intialize the record with the given index entry in the btree. */
227 static int record_init(record* r, btree* bt, node_buf* buf, UInt16 index)
229 void *p;
230 r-> tree = bt;
231 p = btree_key_by_index(bt,buf,index);
232 if (!p)
233 return -1;
234 p = record_readkey (p, &r->key);
235 if (!p)
236 return -1;
237 p = record_readentry(p, &r->record);
238 if (!p)
239 return -1;
240 r->node_index = buf->index;
241 r-> keyind = index;
243 return 0;
246 /* intialize the record with the given index entry in the btree. */
247 static int record_init_extent(extent_record* r, btree* bt, node_buf* buf, UInt16 index)
249 void *p;
250 r-> tree = bt;
251 p = btree_key_by_index(bt, buf,index);
252 if (!p)
253 return -1;
254 p = record_extent_readkey(p, &r->key);
255 if (!p)
256 return -1;
257 p = volume_readextent(p, r->extent);
258 if (!p)
259 return -1;
260 r->node_index = buf->index;
261 r-> keyind = index;
263 return 0;
266 /* intialize the record to the first record of the tree
267 * which is (per design) the root node.
269 int record_init_root(record* r, btree* tree)
271 // Position to first leaf node ...
272 UInt32 leaf_head = tree->head.leaf_head;
273 node_buf* buf = btree_node_by_index(tree, leaf_head);
274 if (!buf)
275 return -1;
276 return record_init(r, tree, buf, 0);
279 /* Compare two cat_keys ... */
280 int record_key_compare(void* k1, void* k2)
282 hfsp_cat_key* key1 = (hfsp_cat_key*) k1;
283 hfsp_cat_key* key2 = (hfsp_cat_key*) k2;
284 int diff = key2->parent_cnid - key1->parent_cnid;
285 if (!diff) // same parent
286 diff = fast_unicode_compare(&key1->name, &key2->name);
287 return diff;
290 /* Compare two extent_keys ... */
291 int record_extent_key_compare(void* k1, void* k2)
293 hfsp_extent_key* key1 = (hfsp_extent_key*) k1;
294 hfsp_extent_key* key2 = (hfsp_extent_key*) k2;
295 int diff = key2->fork_type - key1->fork_type;
296 if (!diff) // same type
298 diff = key2->file_id - key1->file_id;
299 if (!diff) // same file
300 diff = key2->start_block - key1->start_block;
302 return diff;
305 /* Position node in btree so that key might be inside */
306 static node_buf* record_find_node(btree* tree, void *key)
308 int start, end, mid, comp; // components of a binary search
309 void *p = NULL;
310 char curr_key[tree->head.max_key_len];
311 // The current key under examination
312 hfsp_key_read readkey = tree->kread;
313 hfsp_key_compare key_compare = tree->kcomp;
314 UInt32 index;
315 node_buf* node = btree_node_by_index(tree, tree->head.root);
316 if (!node)
317 HFSP_ERROR(-1, "record_find_node: Cant position to root node");
318 while (node->desc.kind == HFSP_NODE_NDX)
320 mid = start = 0;
321 end = node->desc.num_rec;
322 comp = -1;
323 while (start < end)
325 mid = (start + end) >> 1;
326 p = btree_key_by_index(tree, node, mid);
327 if (!p)
328 HFSP_ERROR(-1, "record_find_node: unexpected error");
329 p = readkey (p, curr_key);
330 if (!p)
331 HFSP_ERROR(-1, "record_find_node: unexpected error");
332 comp = key_compare(curr_key, key);
333 if (comp > 0)
334 start = mid + 1;
335 else if (comp < 0)
336 end = mid;
337 else
338 break;
340 if (!p) // Empty tree, fascinating ...
341 HFSP_ERROR(-1, "record_find_node: unexpected empty node");
342 if (comp < 0) // mmh interesting key is before this key ...
344 if (mid == 0)
345 return NULL; // nothing before this key ..
346 p = btree_key_by_index(tree, node, mid-1);
347 if (!p)
348 HFSP_ERROR(-1, "record_find_node: unexpected error");
349 p = readkey (p, curr_key);
350 if (!p)
351 HFSP_ERROR(-1, "record_find_node: unexpected error");
354 index = bswabU32_inc(p);
355 node = btree_node_by_index(tree, index);
357 return node; // go on and use the found node
358 fail:
359 return NULL;
362 /* search for the given key in the btree.
364 * returns pointer to memory just after key or NULL
365 * In any case *keyind recives the index where the
366 * key was found (or could be inserted.)
368 static void *
369 record_find_key(btree* tree, void* key, int* keyind, UInt16* node_index)
371 node_buf* buf = record_find_node(tree, key);
372 if (buf)
374 int comp = -1;
375 int start = 0; // components of a binary search
376 int end = buf->desc.num_rec;
377 int mid = -1;
378 void *p = NULL;
379 char curr_key[tree->head.max_key_len];
380 hfsp_key_read readkey = tree->kread;
381 hfsp_key_compare key_compare = tree->kcomp;
382 while (start < end)
384 mid = (start + end) >> 1;
385 p = btree_key_by_index(tree, buf, mid);
386 if (!p)
387 HFSP_ERROR(-1, "record_init_key: unexpected error");
388 p = readkey (p, curr_key);
389 if (!p)
390 HFSP_ERROR(-1, "record_init_cat_key: unexpected error");
391 comp = key_compare(curr_key, key);
392 if (comp > 0)
393 start = mid + 1;
394 else if (comp < 0)
395 end = mid;
396 else
397 break;
399 if (!p) // Empty tree, fascinating ...
400 HFSP_ERROR(ENOENT, "record_init_key: unexpected empty node");
401 *keyind = mid;
402 *node_index = buf->index;
403 if (!comp) // found something ...
404 return p;
406 HFSP_ERROR(ENOENT, NULL);
407 fail:
408 return NULL;
411 /* intialize the record by searching for the given key in the btree.
413 * r is umodified on error.
415 static int
416 record_init_key(record* r, btree* tree, hfsp_cat_key* key)
418 int keyind;
419 UInt16 node_index;
420 void *p = record_find_key(tree, key, &keyind, &node_index);
422 if (p)
424 r -> tree = tree;
425 r -> node_index= node_index;
426 r -> keyind = keyind;
427 r -> key = *key; // Better use a record_key_copy ...
428 p = record_readentry(p, &r->record);
429 if (!p)
430 HFSP_ERROR(-1, "record_init_key: unexpected error");
431 return 0;
433 fail:
434 return -1;
437 /* intialize the extent_record to the extent identified by the
438 * (first) blockindex.
440 * forktype: either HFSP_EXTEND_DATA or HFSP_EXTEND_RSRC
442 int record_init_file(extent_record* r, btree* tree,
443 UInt8 forktype, UInt32 fileId, UInt32 blockindex)
445 int keyind;
446 UInt16 node_index;
447 hfsp_extent_key key = { 10, forktype, 0, fileId, blockindex };
448 void *p = record_find_key(tree, &key, &keyind, &node_index);
450 if (p)
452 r -> tree = tree;
453 r -> node_index= node_index;
454 r -> keyind = keyind;
455 r -> key = key; // Better use a record_key_copy ...
456 p = volume_readextent(p, r->extent);
457 if (!p)
458 HFSP_ERROR(-1, "record_init_file: unexpected error");
459 return 0;
461 fail:
462 return -1;
465 /* intialize the record to the folder identified by cnid
467 int record_init_cnid(record* r, btree* tree, UInt32 cnid)
469 hfsp_cat_key thread_key; // the thread is the first record
471 thread_key.key_length = 6; // null name (like '.' in unix )
472 thread_key.parent_cnid = cnid;
473 thread_key.name.strlen = 0;
475 return record_init_key(r, tree, &thread_key);
478 /* intialize the record to the first record of the parent.
480 int record_init_parent(record* r, record* parent)
482 if (parent->record.type == HFSP_FOLDER)
483 return record_init_cnid(r, parent->tree, parent->record.u.folder.id);
484 else if(parent->record.type == HFSP_FOLDER_THREAD)
486 if (r != parent)
487 *r = *parent; // The folder thread is in fact the first entry, like '.'
488 return 0;
490 HFSP_ERROR(EINVAL,
491 "record_init_parent: parent is neither folder nor folder thread.");
493 fail:
494 return EINVAL;
498 /* find correct node record for given node and *pindex.
500 * index of record in this (or next) node
501 * */
502 static node_buf* prepare_next(btree* tree, UInt16 node_index, UInt16* pindex)
504 node_buf* buf = btree_node_by_index(tree, node_index);
505 btree_node_desc* desc = &buf->desc;
506 UInt32 numrec = desc->num_rec;
507 if (*pindex >= numrec) // move on to next node
509 UInt16 next = desc->next;
510 *pindex = 0;
511 if (!next /* is there a next node ? */
512 || !( buf = btree_node_by_index(tree, next)))
513 return NULL;
515 return buf;
517 /* move record foreward to next entry.
519 * In case of an error the value of *r is undefined !
521 int record_next(record* r)
523 btree* tree = r->tree;
524 UInt16 index = r->keyind +1;
525 UInt32 parent;
526 node_buf* buf = prepare_next(tree, r->node_index, &index);
528 if (!buf)
529 return ENOENT; // No (more) such file or directory
531 parent = r->key.parent_cnid;
533 if (record_init(r, tree, buf, index))
534 return -1;
536 if (r->key.parent_cnid != parent || // end of current directory
537 index != r->keyind) // internal error ?
538 return ENOENT; // No (more) such file or directory
540 return 0;
543 /* move record foreward to next extent record.
545 * In case of an error the value of *r is undefined !
547 int record_next_extent(extent_record* r)
549 btree* tree = r->tree;
550 UInt16 index = r->keyind +1;
551 UInt32 file_id;
552 UInt8 fork_type;
553 node_buf* buf = prepare_next(tree, r->node_index, &index);
555 if (!buf)
556 return ENOENT; // No (more) such file or directory
558 file_id = r->key.file_id;
559 fork_type = r->key.fork_type;
561 if (record_init_extent(r, tree, buf, index))
562 return -1;
564 if (r->key.file_id != file_id || // end of current file
565 r->key.fork_type != fork_type || // end of current fork
566 index != r->keyind) // internal error ?
567 return ENOENT; // No (more) such file or directory
569 return 0;
572 /* intialize the record by searching for the given string in the given folder.
574 * parent and r may be the same.
576 int record_init_string_parent(record* r, record* parent, char* name)
578 hfsp_cat_key key;
580 if (parent->record.type == HFSP_FOLDER)
581 key.parent_cnid = parent->record.u.folder.id;
582 else if(parent->record.type == HFSP_FOLDER_THREAD)
583 key.parent_cnid = parent->key.parent_cnid;
584 else
585 HFSP_ERROR(-1, "record_init_string_parent: parent is not a folder.");
587 key.key_length = 6 + unicode_asc2uni(&key.name,name); // 6 for minumum size
588 return record_init_key(r, parent->tree, &key);
590 fail:
591 return -1;
594 /* move record up in folder hierarchy (if possible) */
595 int record_up(record* r)
597 if (r->record.type == HFSP_FOLDER)
599 // locate folder thread
600 if (record_init_cnid(r, r->tree, r->record.u.folder.id))
601 return -1;
603 else if(r->record.type == HFSP_FOLDER_THREAD)
605 // do nothing were are already where we want to be
607 else
608 HFSP_ERROR(-1, "record_up: record is neither folder nor folder thread.");
610 if(r->record.type != HFSP_FOLDER_THREAD)
611 HFSP_ERROR(-1, "record_up: unable to locate parent");
612 return record_init_cnid(r, r->tree, r->record.u.thread.parentID);
614 fail:
615 return -1;
618 #ifdef DEBUG
620 /* print Quickdraw Point */
621 static void record_print_Point(Point* p)
623 printf("[ v=%d, h=%d ]", p->v, p->h);
626 /* print Quickdraw Rect */
627 static void record_print_Rect(Rect* r)
629 printf("[ top=%d, left=%d, bottom=%d, right=%d ]",
630 r->top, r->left, r->bottom, r->right);
633 /* print the key of a record */
634 static void record_print_key(hfsp_cat_key* key)
636 char buf[255]; // mh this _might_ overflow
637 unicode_uni2asc(buf, &key->name, 255);
638 printf("parent cnid : %ld\n", key->parent_cnid);
639 printf("name : %s\n", buf);
642 /* print permissions */
643 static void record_print_perm(hfsp_perm* perm)
645 printf("owner :\t%ld\n", perm->owner);
646 printf("group :\t%ld\n", perm->group);
647 printf("perm :\t0x%lX\n",perm->mode);
648 printf("dev :\t%ld\n", perm->dev);
651 /* print Directory info */
652 static void record_print_DInfo(DInfo* dinfo)
654 printf( "frRect :\t"); record_print_Rect(&dinfo->frRect);
655 printf("\nfrFlags :\t0X%X\n", dinfo->frFlags);
656 printf( "frLocation :\t"); record_print_Point(&dinfo->frLocation);
657 printf("\nfrView :\t0X%X\n", dinfo->frView);
660 /* print extended Directory info */
661 static void record_print_DXInfo(DXInfo* xinfo)
663 printf( "frScroll :\t"); record_print_Point(&xinfo->frScroll);
664 printf("\nfrOpenChain :\t%ld\n", xinfo->frOpenChain);
665 printf( "frUnused :\t%d\n", xinfo->frUnused);
666 printf( "frComment :\t%d\n", xinfo->frComment);
667 printf( "frPutAway :\t%ld\n", xinfo->frPutAway);
670 static void record_print_folder(hfsp_cat_folder* folder)
672 printf("flags :\t0x%X\n", folder->flags);
673 printf("valence :\t0x%lX\n", folder->valence);
674 printf("id :\t%ld\n", folder->id);
675 record_print_perm (&folder->permissions);
676 record_print_DInfo (&folder->user_info);
677 record_print_DXInfo (&folder->finder_info);
678 printf("text_encoding :\t0x%lX\n", folder->text_encoding);
679 printf("reserved :\t0x%lX\n", folder->reserved);
682 /* print File info */
683 static void record_print_FInfo(FInfo* finfo)
685 printf( "fdType :\t%4.4s\n", (char*) &finfo->fdType);
686 printf( "fdCreator :\t%4.4s\n", (char*) &finfo->fdCreator);
687 printf( "fdFlags :\t0X%X\n", finfo->fdFlags);
688 printf( "fdLocation :\t"); record_print_Point(&finfo->fdLocation);
689 printf("\nfdFldr :\t%d\n", finfo->fdFldr);
692 /* print extended File info */
693 static void record_print_FXInfo(FXInfo* xinfo)
695 printf( "fdIconID :\t%d\n", xinfo->fdIconID);
696 // xinfo -> fdUnused;
697 printf( "fdComment :\t%d\n", xinfo->fdComment);
698 printf( "fdPutAway :\t%ld\n", xinfo->fdPutAway);
701 /* print folder entry */
703 /* print file entry */
704 static void record_print_file(hfsp_cat_file* file)
706 printf("flags :\t0x%X\n", file->flags);
707 printf("reserved1 :\t0x%lX\n", file->reserved1);
708 printf("id :\t%ld\n", file->id);
709 record_print_perm (&file->permissions);
710 record_print_FInfo (&file->user_info);
711 record_print_FXInfo (&file->finder_info);
712 printf("text_encoding :\t0x%lX\n", file->text_encoding);
713 printf("reserved :\t0x%lX\n", file->reserved2);
714 printf("Datafork:\n");
715 volume_print_fork (&file->data_fork);
716 printf("Rsrcfork:\n");
717 volume_print_fork (&file->res_fork);
720 /* print info for a file or folder thread */
721 static void record_print_thread(hfsp_cat_thread* entry)
723 char buf[255]; // mh this _might_ overflow
724 unicode_uni2asc(buf, &entry->nodeName, 255);
725 printf("parent cnid :\t%ld\n", entry->parentID);
726 printf("name :\t%s\n" , buf);
729 /* print the information for a record */
730 static void record_print_entry(hfsp_cat_entry* entry)
732 switch (entry->type)
734 case HFSP_FOLDER:
735 printf("=== Folder ===\n");
736 return record_print_folder(&entry->u.folder);
737 case HFSP_FILE:
738 printf("=== File ===\n");
739 return record_print_file (&entry->u.file);
740 case HFSP_FOLDER_THREAD:
741 printf("=== Folder Thread ===\n");
742 return record_print_thread(&entry->u.thread);
743 case HFSP_FILE_THREAD:
744 printf("=== File Thread ==\n");
745 return record_print_thread(&entry->u.thread);
746 default:
747 printf("=== Unknown Record Type ===\n");
751 /* Dump all the record information to stdout */
752 void record_print(record* r)
754 printf ("keyind : %u\n", r->keyind);
755 record_print_key (&r->key);
756 record_print_entry(&r->record);
759 #endif